Author: j16sdiz
Date: 2008-08-15 08:20:48 +0000 (Fri, 15 Aug 2008)
New Revision: 21886

Added:
   branches/saltedhashstore/freenet/src/freenet/config/ConfigException.java
   
branches/saltedhashstore/freenet/src/freenet/config/NodeNeedRestartException.java
   
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPullTest.java
   
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPushPullTest.java
   
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapSeedTest.java
   
branches/saltedhashstore/freenet/src/freenet/pluginmanager/FredPluginBandwidthIndicator.java
   branches/saltedhashstore/freenet/test/freenet/crypt/YarrowTest.java
Removed:
   branches/saltedhashstore/freenet/src/freenet/client/TempStoreElement.java
   branches/saltedhashstore/freenet/src/freenet/clients/http/NinjaSpider.java
   branches/saltedhashstore/freenet/src/freenet/clients/http/PluginToadlet.java
   branches/saltedhashstore/freenet/src/freenet/clients/http/Spider.java
   
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/HttpPlugin.java
   branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/Plugin.java
   
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/PluginManager.java
   
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestHttpPlugin.java
   
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestPlugin.java
   branches/saltedhashstore/freenet/src/freenet/support/StringArray.java
   
branches/saltedhashstore/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
   
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentEncryptedTempBucketFactory.java
Modified:
   branches/saltedhashstore/freenet/src/freenet/client/ArchiveManager.java
   branches/saltedhashstore/freenet/src/freenet/client/FECCodec.java
   
branches/saltedhashstore/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
   branches/saltedhashstore/freenet/src/freenet/client/RealArchiveStoreItem.java
   
branches/saltedhashstore/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
   
branches/saltedhashstore/freenet/src/freenet/client/async/ClientRequestScheduler.java
   
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleManifestPutter.java
   
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleSingleFileFetcher.java
   
branches/saltedhashstore/freenet/src/freenet/client/async/SplitFileFetcherSegment.java
   branches/saltedhashstore/freenet/src/freenet/client/async/USKChecker.java
   branches/saltedhashstore/freenet/src/freenet/clients/http/ConfigToadlet.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/ConnectionsToadlet.java
   branches/saltedhashstore/freenet/src/freenet/clients/http/FProxyToadlet.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/HTTPRequestImpl.java
   branches/saltedhashstore/freenet/src/freenet/clients/http/PageMaker.java
   branches/saltedhashstore/freenet/src/freenet/clients/http/QueueToadlet.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/SimpleToadletServer.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/StatisticsToadlet.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/SymlinkerToadlet.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/bookmark/BookmarkCategory.java
   
branches/saltedhashstore/freenet/src/freenet/clients/http/staticfiles/themes/clean/theme.css
   branches/saltedhashstore/freenet/src/freenet/config/BooleanOption.java
   branches/saltedhashstore/freenet/src/freenet/config/Config.java
   branches/saltedhashstore/freenet/src/freenet/config/ConfigCallback.java
   branches/saltedhashstore/freenet/src/freenet/config/IntOption.java
   
branches/saltedhashstore/freenet/src/freenet/config/InvalidConfigValueException.java
   branches/saltedhashstore/freenet/src/freenet/config/LongOption.java
   branches/saltedhashstore/freenet/src/freenet/config/Option.java
   branches/saltedhashstore/freenet/src/freenet/config/PersistentConfig.java
   branches/saltedhashstore/freenet/src/freenet/config/ShortOption.java
   branches/saltedhashstore/freenet/src/freenet/config/StringArrOption.java
   branches/saltedhashstore/freenet/src/freenet/config/StringOption.java
   branches/saltedhashstore/freenet/src/freenet/config/SubConfig.java
   branches/saltedhashstore/freenet/src/freenet/config/WrapperConfig.java
   branches/saltedhashstore/freenet/src/freenet/crypt/DiffieHellman.java
   branches/saltedhashstore/freenet/src/freenet/crypt/SSL.java
   branches/saltedhashstore/freenet/src/freenet/crypt/Yarrow.java
   branches/saltedhashstore/freenet/src/freenet/io/AddressIdentifier.java
   branches/saltedhashstore/freenet/src/freenet/io/NetworkInterface.java
   branches/saltedhashstore/freenet/src/freenet/io/comm/DMT.java
   branches/saltedhashstore/freenet/src/freenet/io/comm/FreenetInetAddress.java
   branches/saltedhashstore/freenet/src/freenet/io/comm/UdpSocketHandler.java
   branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.de.properties
   branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.en.properties
   branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.fr.properties
   
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties
   
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties
   branches/saltedhashstore/freenet/src/freenet/node/ConfigurablePersister.java
   branches/saltedhashstore/freenet/src/freenet/node/FNPPacketMangler.java
   branches/saltedhashstore/freenet/src/freenet/node/FailureTableEntry.java
   branches/saltedhashstore/freenet/src/freenet/node/GlobalProbe.java
   
branches/saltedhashstore/freenet/src/freenet/node/IPDetectorPluginManager.java
   branches/saltedhashstore/freenet/src/freenet/node/LoggingConfigHandler.java
   branches/saltedhashstore/freenet/src/freenet/node/Node.java
   branches/saltedhashstore/freenet/src/freenet/node/NodeClientCore.java
   branches/saltedhashstore/freenet/src/freenet/node/NodeCrypto.java
   branches/saltedhashstore/freenet/src/freenet/node/NodeCryptoConfig.java
   branches/saltedhashstore/freenet/src/freenet/node/NodeDispatcher.java
   branches/saltedhashstore/freenet/src/freenet/node/NodeIPDetector.java
   branches/saltedhashstore/freenet/src/freenet/node/NodeStarter.java
   branches/saltedhashstore/freenet/src/freenet/node/NodeStats.java
   branches/saltedhashstore/freenet/src/freenet/node/PeerManager.java
   branches/saltedhashstore/freenet/src/freenet/node/PeerNode.java
   branches/saltedhashstore/freenet/src/freenet/node/PeerNodeStatus.java
   branches/saltedhashstore/freenet/src/freenet/node/SeedClientPeerNode.java
   branches/saltedhashstore/freenet/src/freenet/node/TestnetHandler.java
   
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterface.java
   
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterfaceServer.java
   branches/saltedhashstore/freenet/src/freenet/node/UptimeEstimator.java
   branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientGet.java
   branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientPutMessage.java
   branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientRequest.java
   branches/saltedhashstore/freenet/src/freenet/node/fcp/ConfigData.java
   branches/saltedhashstore/freenet/src/freenet/node/fcp/FCPServer.java
   
branches/saltedhashstore/freenet/src/freenet/node/fcp/PutSuccessfulMessage.java
   
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeBusyNetworkTest.java
   
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeRequestInsertTest.java
   
branches/saltedhashstore/freenet/src/freenet/node/simulator/SeednodePingTest.java
   
branches/saltedhashstore/freenet/src/freenet/node/updater/NodeUpdateManager.java
   
branches/saltedhashstore/freenet/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
   
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginDownLoaderOfficial.java
   
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginInfoWrapper.java
   branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginManager.java
   branches/saltedhashstore/freenet/src/freenet/support/Fields.java
   branches/saltedhashstore/freenet/src/freenet/support/PooledExecutor.java
   branches/saltedhashstore/freenet/src/freenet/support/SizeUtil.java
   branches/saltedhashstore/freenet/src/freenet/support/TimeSortedHashtable.java
   branches/saltedhashstore/freenet/src/freenet/support/api/BooleanCallback.java
   branches/saltedhashstore/freenet/src/freenet/support/api/IntCallback.java
   branches/saltedhashstore/freenet/src/freenet/support/api/LongCallback.java
   branches/saltedhashstore/freenet/src/freenet/support/api/ShortCallback.java
   
branches/saltedhashstore/freenet/src/freenet/support/api/StringArrCallback.java
   branches/saltedhashstore/freenet/src/freenet/support/api/StringCallback.java
   
branches/saltedhashstore/freenet/src/freenet/support/compress/GzipCompressor.java
   branches/saltedhashstore/freenet/src/freenet/support/io/ArrayBucket.java
   branches/saltedhashstore/freenet/src/freenet/support/io/BaseFileBucket.java
   branches/saltedhashstore/freenet/src/freenet/support/io/BucketTools.java
   
branches/saltedhashstore/freenet/src/freenet/support/io/DelayedFreeBucket.java
   branches/saltedhashstore/freenet/src/freenet/support/io/NativeThread.java
   
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
   
branches/saltedhashstore/freenet/src/freenet/support/io/TempBucketFactory.java
Log:
Merge trunk r21884 into saltedhashstore

Modified: 
branches/saltedhashstore/freenet/src/freenet/client/ArchiveManager.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/client/ArchiveManager.java     
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/client/ArchiveManager.java     
2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,7 +3,6 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.client;

-import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Arrays;
@@ -13,17 +12,13 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;

-import freenet.crypt.RandomSource;
 import freenet.keys.FreenetURI;
 import freenet.support.LRUHashtable;
 import freenet.support.Logger;
 import freenet.support.MutableBoolean;
 import freenet.support.api.Bucket;
+import freenet.support.api.BucketFactory;
 import freenet.support.io.BucketTools;
-import freenet.support.io.FilenameGenerator;
-import freenet.support.io.PaddedEphemerallyEncryptedBucket;
-import freenet.support.io.TempFileBucket;
-import java.util.Random;

 /**
  * Cache of recently decoded archives:
@@ -38,9 +33,7 @@

        public static final String METADATA_NAME = ".metadata";
        private static boolean logMINOR;
-       
-       final RandomSource strongPRNG;
-       final Random weakPRNG;
+
        final long maxArchiveSize;
        final long maxArchivedFileSize;

@@ -57,8 +50,8 @@
        private long cachedData;
        /** Map from ArchiveKey to ArchiveStoreElement */
        private final LRUHashtable storedData;
-       /** Filename generator */
-       private final FilenameGenerator filenameGenerator;
+       /** Bucket Factory */
+       private final BucketFactory tempBucketFactory;

        /**
         * Create an ArchiveManager.
@@ -75,7 +68,7 @@
         * @param random A cryptographicaly secure random source
         * @param weakRandom A weak and cheap random source
         */
-       public ArchiveManager(int maxHandlers, long maxCachedData, long 
maxArchiveSize, long maxArchivedFileSize, int maxCachedElements, RandomSource 
random, Random weakRandom, FilenameGenerator filenameGenerator) {
+       public ArchiveManager(int maxHandlers, long maxCachedData, long 
maxArchiveSize, long maxArchivedFileSize, int maxCachedElements, BucketFactory 
tempBucketFactory) {
                maxArchiveHandlers = maxHandlers;
                archiveHandlers = new LRUHashtable();
                this.maxCachedElements = maxCachedElements;
@@ -83,9 +76,7 @@
                storedData = new LRUHashtable();
                this.maxArchiveSize = maxArchiveSize;
                this.maxArchivedFileSize = maxArchivedFileSize;
-               this.strongPRNG = random;
-               this.weakPRNG = weakRandom;
-               this.filenameGenerator = filenameGenerator;
+               this.tempBucketFactory = tempBucketFactory;
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
        }

@@ -238,8 +229,7 @@
                                } else {
                                        // Read the element
                                        long realLen = 0;
-                                       TempStoreElement temp = 
makeTempStoreBucket(size);
-                                       Bucket output = temp.bucket;
+                                       Bucket output = 
tempBucketFactory.makeBucket(size);
                                        OutputStream out = 
output.getOutputStream();

                                        int readBytes;
@@ -249,7 +239,7 @@
                                                if(readBytes > 
maxArchivedFileSize) {
                                                        addErrorElement(ctx, 
key, name, "File too big: "+maxArchivedFileSize+" greater than current archived 
file size limit "+maxArchivedFileSize);
                                                        out.close();
-                                                       temp.close();
+                                                       output.free();
                                                        continue outer;
                                                }
                                        }
@@ -257,7 +247,7 @@
                                        out.close();
                                        if(name.equals(".metadata"))
                                                gotMetadata = true;
-                                       addStoreElement(ctx, key, name, temp, 
gotElement, element, callback);
+                                       addStoreElement(ctx, key, name, output, 
gotElement, element, callback);
                                        names.add(name);
                                        trimStoredData();
                                }
@@ -313,18 +303,19 @@
                        addToDirectory(dir, name, "");
                }
                Metadata metadata = new Metadata(dir, "");
-               TempStoreElement element = makeTempStoreBucket(-1);
                int x = 0;
+               Bucket bucket = null;
                while(true) {
                        try {
+                               bucket = 
tempBucketFactory.makeBucket(Metadata.MAX_SPLITFILE_PARAMS_LENGTH);
                                byte[] buf = metadata.writeToByteArray();
-                               OutputStream os = 
element.bucket.getOutputStream();
+                               OutputStream os = bucket.getOutputStream();
                                os.write(buf);
                                os.close();
-                               return addStoreElement(ctx, key, ".metadata", 
element, gotElement, element2, callback);
+                               return addStoreElement(ctx, key, ".metadata", 
bucket, gotElement, element2, callback);
                        } catch (MetadataUnresolvedException e) {
                                try {
-                                       x = resolve(e, x, element, ctx, key, 
gotElement, element2, callback);
+                                       x = resolve(e, x, bucket, ctx, key, 
gotElement, element2, callback);
                                } catch (IOException e1) {
                                        throw new 
ArchiveFailureException("Failed to create metadata: "+e1, e1);
                                }
@@ -335,17 +326,17 @@
                }
        }

-       private int resolve(MetadataUnresolvedException e, int x, 
TempStoreElement element, ArchiveStoreContext ctx, FreenetURI key, 
MutableBoolean gotElement, String element2, ArchiveExtractCallback callback) 
throws IOException, ArchiveFailureException {
+       private int resolve(MetadataUnresolvedException e, int x, Bucket 
bucket, ArchiveStoreContext ctx, FreenetURI key, MutableBoolean gotElement, 
String element2, ArchiveExtractCallback callback) throws IOException, 
ArchiveFailureException {
                Metadata[] m = e.mustResolve;
                for(int i=0;i<m.length;i++) {
                        try {
                                byte[] buf = m[i].writeToByteArray();
-                               OutputStream os = 
element.bucket.getOutputStream();
+                               OutputStream os = bucket.getOutputStream();
                                os.write(buf);
                                os.close();
-                               addStoreElement(ctx, key, ".metadata-"+(x++), 
element, gotElement, element2, callback);
+                               addStoreElement(ctx, key, ".metadata-"+(x++), 
bucket, gotElement, element2, callback);
                        } catch (MetadataUnresolvedException e1) {
-                               x = resolve(e, x, element, ctx, key, 
gotElement, element2, callback);
+                               x = resolve(e, x, bucket, ctx, key, gotElement, 
element2, callback);
                        }
                }
                return x;
@@ -413,7 +404,7 @@
         * @throws ArchiveFailureException If a failure occurred resulting in 
the data not being readable. Only happens if
         * callback != null.
         */
-       private ArchiveStoreItem addStoreElement(ArchiveStoreContext ctx, 
FreenetURI key, String name, TempStoreElement temp, MutableBoolean gotElement, 
String callbackName, ArchiveExtractCallback callback) throws 
ArchiveFailureException {
+       private ArchiveStoreItem addStoreElement(ArchiveStoreContext ctx, 
FreenetURI key, String name, Bucket temp, MutableBoolean gotElement, String 
callbackName, ArchiveExtractCallback callback) throws ArchiveFailureException {
                RealArchiveStoreItem element = new RealArchiveStoreItem(ctx, 
key, name, temp);
                if(logMINOR) Logger.minor(this, "Adding store element: 
"+element+" ( "+key+ ' ' +name+" size "+element.spaceUsed()+" )");
                ArchiveStoreItem oldItem;
@@ -464,23 +455,6 @@
                }
        }

-       /** 
-        * Create a file Bucket in the store directory, encrypted using an 
ethereal key.
-        * This is not yet associated with a name, so will be deleted when it 
goes out
-        * of scope. Not counted towards allocated data as will be short-lived 
and will not
-        * go over the maximum size. Will obviously keep its key when we move 
it to main.
-        */
-       private TempStoreElement makeTempStoreBucket(long size) {
-               long id = filenameGenerator.makeRandomFilename();
-               File myFile = filenameGenerator.getFilename(id);
-               TempFileBucket fb = new TempFileBucket(id, filenameGenerator);
-               
-               byte[] cipherKey = new byte[32];
-               strongPRNG.nextBytes(cipherKey);
-               PaddedEphemerallyEncryptedBucket encryptedBucket = new 
PaddedEphemerallyEncryptedBucket(fb, 1024, strongPRNG, weakPRNG);
-               return new TempStoreElement(myFile, fb, encryptedBucket);
-       }
-
        /**
         * Is the given MIME type an archive type that we can deal with?
         */

Modified: branches/saltedhashstore/freenet/src/freenet/client/FECCodec.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/client/FECCodec.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/client/FECCodec.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -199,7 +199,7 @@
                for(int i = 0; i < dataBlockStatus.length; i++) {
                        Bucket data = buckets[i];
                        if(data.size() != blockLength)
-                               throw new IllegalStateException("Block " + i + 
": " + data + " : " + dataBlockStatus[i] + " length " + data.size());
+                               throw new IllegalStateException("Block " + i + 
": " + data + " : " + dataBlockStatus[i] + " length " + data.size() + " whereas 
blockLength="+blockLength);
                        dataBlockStatus[i].setData(data);
                }
        }

Modified: 
branches/saltedhashstore/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
  2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
  2008-08-15 08:20:48 UTC (rev 21886)
@@ -98,7 +98,7 @@
                curMaxTempLength = Long.MAX_VALUE;
                curMaxMetadataLength = 1024 * 1024;
                this.cacheLocalRequests = cacheLocalRequests;
-               this.persistentBucketFactory = 
node.persistentEncryptedTempBucketFactory;
+               this.persistentBucketFactory = node.persistentTempBucketFactory;
                this.healingQueue = node.getHealingQueue();
                this.blockEncoder = node.backgroundBlockEncoder;
        }

Modified: 
branches/saltedhashstore/freenet/src/freenet/client/RealArchiveStoreItem.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/client/RealArchiveStoreItem.java   
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/client/RealArchiveStoreItem.java   
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,16 +3,12 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.client;

-import java.io.File;
-
 import freenet.keys.FreenetURI;
 import freenet.support.api.Bucket;
-import freenet.support.io.FileUtil;
 import freenet.support.io.MultiReaderBucket;

 class RealArchiveStoreItem extends ArchiveStoreItem {

-       private final File myFilename;
        private final MultiReaderBucket mb;
        private final Bucket bucket;
        private final long spaceUsed;
@@ -24,13 +20,12 @@
         * @param temp The TempStoreElement currently storing the data.
         * @param manager The parent ArchiveManager within which this item is 
stored.
         */
-       RealArchiveStoreItem(ArchiveStoreContext ctx, FreenetURI key2, String 
realName, TempStoreElement temp) {
+       RealArchiveStoreItem(ArchiveStoreContext ctx, FreenetURI key2, String 
realName, Bucket bucket) {
                super(new ArchiveKey(key2, realName), ctx);
-               mb = new MultiReaderBucket(temp.bucket);
+               mb = new MultiReaderBucket(bucket);
                this.bucket = mb.getReaderBucket();
-               temp.underBucket.setReadOnly();
-               this.myFilename = temp.underBucket.getFile();
-               spaceUsed = FileUtil.estimateUsage(myFilename, 
temp.underBucket.size());
+               bucket.setReadOnly();
+               spaceUsed = bucket.size();
        }

        /**
@@ -54,6 +49,7 @@
                return spaceUsed;
        }

+       @Override
        void innerClose() {
                bucket.free();
        }

Deleted: 
branches/saltedhashstore/freenet/src/freenet/client/TempStoreElement.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/client/TempStoreElement.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/client/TempStoreElement.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,25 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.client;
-
-import java.io.File;
-
-import freenet.support.io.PaddedEphemerallyEncryptedBucket;
-import freenet.support.io.TempFileBucket;
-
-class TempStoreElement {
-       final File myFilename;
-       final PaddedEphemerallyEncryptedBucket bucket;
-       final TempFileBucket underBucket;
-       
-       TempStoreElement(File myFile, TempFileBucket fb, 
PaddedEphemerallyEncryptedBucket encryptedBucket) {
-               this.myFilename = myFile;
-               this.underBucket = fb;
-               this.bucket = encryptedBucket;
-       }
-       
-       public void close() {
-               underBucket.free();
-       }
-}
\ No newline at end of file

Modified: 
branches/saltedhashstore/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
        2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/client/async/BaseSingleFileFetcher.java
        2008-08-15 08:20:48 UTC (rev 21886)
@@ -12,6 +12,7 @@
 import freenet.node.KeysFetchingLocally;
 import freenet.node.RequestScheduler;
 import freenet.node.SendableGet;
+import freenet.support.Executor;
 import freenet.support.Logger;

 public abstract class BaseSingleFileFetcher extends SendableGet {
@@ -64,9 +65,12 @@
                return key instanceof ClientSSK;
        }

-       /** Try again - returns true if we can retry 
-        * @param sched */
-       protected boolean retry(RequestScheduler sched) {
+       /**
+        * Try again - returns true if we can retry 
+        * @param sched
+        * @param the executor we will use to run the retry off-thread
+        */
+       protected boolean retry(RequestScheduler sched, Executor exec) {
                retryCount++;
                if(Logger.shouldLog(Logger.MINOR, this))
                        Logger.minor(this, "Attempting to retry... (max 
"+maxRetries+", current "+retryCount+ ')');
@@ -79,12 +83,15 @@
                                        Logger.error(this, "Already on the 
cooldown queue for "+this, new Exception("error"));
                                else
                                cooldownWakeupTime = sched.queueCooldown(key, 
this);
-                               return true; // We will retry, just not yet. 
See requeueAfterCooldown(Key).
                        } else {
+                               exec.execute(new Runnable() {
+                                       public void run() {
                                schedule();
                        }
-                       return true;
+                               }, "Retry executor for "+sched.toString());
                }
+                       return true; // We will retry in any case, maybe not 
just not yet. See requeueAfterCooldown(Key).
+               }
                return false;
        }


Modified: 
branches/saltedhashstore/freenet/src/freenet/client/async/ClientRequestScheduler.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/client/async/ClientRequestScheduler.java
       2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/client/async/ClientRequestScheduler.java
       2008-08-15 08:20:48 UTC (rev 21886)
@@ -41,7 +41,7 @@

        private static boolean logMINOR;

-       public static class PrioritySchedulerCallback implements 
StringCallback, EnumerableOptionCallback {
+       public static class PrioritySchedulerCallback extends StringCallback 
implements EnumerableOptionCallback {
                final ClientRequestScheduler cs;
                private final String[] possibleValues = new String[]{ 
ClientRequestScheduler.PRIORITY_HARD, ClientRequestScheduler.PRIORITY_SOFT };


Modified: 
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleManifestPutter.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleManifestPutter.java
 2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleManifestPutter.java
 2008-08-15 08:20:48 UTC (rev 21886)
@@ -405,7 +405,7 @@
                                // FIXME support formats other than .zip.
                                // Only the *decoding* is generic at present.

-                               Bucket zipBucket = ctx.bf.makeBucket(-1);
+                               Bucket zipBucket = 
ctx.bf.makeBucket(baseMetadata.dataLength());
                                OutputStream os = new 
BufferedOutputStream(zipBucket.getOutputStream());
                                ZipOutputStream zos = new ZipOutputStream(os);
                                ZipEntry ze;

Modified: 
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleSingleFileFetcher.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleSingleFileFetcher.java
      2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/client/async/SimpleSingleFileFetcher.java
      2008-08-15 08:20:48 UTC (rev 21886)
@@ -94,7 +94,7 @@
                        forceFatal = true;
                }
                if(!(e.isFatal() || forceFatal) ) {
-                       if(retry(sched)) {
+                       if(retry(sched, ctx.executor)) {
                                if(logMINOR) Logger.minor(this, "Retrying");
                                return;
                        }

Modified: 
branches/saltedhashstore/freenet/src/freenet/client/async/SplitFileFetcherSegment.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/client/async/SplitFileFetcherSegment.java
      2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/client/async/SplitFileFetcherSegment.java
      2008-08-15 08:20:48 UTC (rev 21886)
@@ -227,7 +227,7 @@
                                        }
                                }
                        }
-                       decodedData = fetchContext.bucketFactory.makeBucket(-1);
+                       decodedData = 
fetchContext.bucketFactory.makeBucket(maxBlockLength * dataBuckets.length);
                        if(logMINOR) Logger.minor(this, "Copying data from data 
blocks");
                        OutputStream os = decodedData.getOutputStream();
                        for(int i=0;i<dataBuckets.length;i++) {
@@ -261,7 +261,7 @@

                // Encode any check blocks we don't have
                if(codec != null) {
-                       codec.addToQueue(new FECJob(codec, dataBuckets, 
checkBuckets, 32768, fetchContext.bucketFactory, this, false));
+                       codec.addToQueue(new FECJob(codec, dataBuckets, 
checkBuckets, CHKBlock.DATA_LENGTH, fetchContext.bucketFactory, this, false));
                }
        }


Modified: 
branches/saltedhashstore/freenet/src/freenet/client/async/USKChecker.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/client/async/USKChecker.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/client/async/USKChecker.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -61,7 +61,7 @@
                        canRetry = true;
                }

-               if(canRetry && retry(sched)) return;
+               if(canRetry && retry(sched, ctx.executor)) return;

                // Ran out of retries.
                unregister(false);

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/ConfigToadlet.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/ConfigToadlet.java    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/ConfigToadlet.java    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -10,14 +10,18 @@
 import freenet.client.HighLevelSimpleClient;
 import freenet.config.BooleanOption;
 import freenet.config.Config;
+import freenet.config.ConfigCallback;
 import freenet.config.EnumerableOptionCallback;
 import freenet.config.InvalidConfigValueException;
+import freenet.config.NodeNeedRestartException;
 import freenet.config.Option;
 import freenet.config.SubConfig;
 import freenet.config.WrapperConfig;
 import freenet.l10n.L10n;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
+import freenet.node.useralerts.AbstractUserAlert;
+import freenet.node.useralerts.UserAlert;
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.MultiValueTable;
@@ -32,7 +36,64 @@
        private final Config config;
        private final NodeClientCore core;
        private final Node node;
-       
+       private boolean needRestart = false;
+       private NeedRestartUserAlert needRestartUserAlert;
+
+       private class NeedRestartUserAlert extends AbstractUserAlert {
+               @Override
+               public String getTitle() {
+                       return l10n("needRestartTitle");
+               }
+
+               @Override
+               public String getText() {
+                       return getHTMLText().toString();
+               }
+
+               @Override
+               public String getShortText() {
+                       return l10n("needRestartShort");
+               }
+
+               @Override
+               public HTMLNode getHTMLText() {
+                       HTMLNode alertNode = new HTMLNode("div");
+                       alertNode.addChild("#", l10n("needRestart"));
+
+                       if (node.isUsingWrapper()) {
+                               alertNode.addChild("br");
+                               HTMLNode restartForm = 
alertNode.addChild("form", //
+                                               new String[] { "action", 
"method" },//
+                                       new String[] { "/", "get" });
+                               restartForm.addChild("div");
+                               restartForm.addChild("input",//
+                                               new String[] { "type", "name" 
},//
+                                               new String[] { "hidden", 
"restart" });
+                               restartForm.addChild("input", //
+                                               new String[] { "type", "name", 
"value" },//
+                                               new String[] { "submit", 
"restart2",//
+                                               l10n("restartNode") });
+                       }
+
+                       return alertNode;
+               }
+
+               @Override
+               public short getPriorityClass() {
+                       return UserAlert.WARNING;
+               }
+
+               @Override
+               public boolean isValid() {
+                       return needRestart;
+               }
+
+               @Override
+               public boolean userCanDismiss() {
+                       return false;
+               }
+       }
+
        ConfigToadlet(HighLevelSimpleClient client, Config conf, Node node, 
NodeClientCore core) {
                super(client);
                config=conf;
@@ -40,7 +101,9 @@
                this.node = node;
        }

-       public void handlePost(URI uri, HTTPRequest request, ToadletContext 
ctx) throws ToadletContextClosedException, IOException {
+       
+       @Override
+    public void handlePost(URI uri, HTTPRequest request, ToadletContext ctx) 
throws ToadletContextClosedException, IOException {
                StringBuffer errbuf = new StringBuffer();
                SubConfig[] sc = config.getConfigs();

@@ -60,7 +123,7 @@
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);

                for(int i=0; i<sc.length ; i++){
-                       Option[] o = sc[i].getOptions();
+                       Option<?>[] o = sc[i].getOptions();
                        String prefix = sc[i].getPrefix();
                        String configName;

@@ -68,7 +131,7 @@
                                configName=o[j].getName();
                                if(logMINOR) Logger.minor(this, "Setting 
"+prefix+ '.' +configName);

-                               // we ignore unreconized parameters 
+                               // we ignore unreconized parameters
                                if(request.isPartSet(prefix+ '.' +configName)) {
                                        String value = 
request.getPartAsString(prefix+ '.' +configName, MAX_PARAM_VALUE_SIZE);
                                        
if(!(o[j].getValueString().equals(value))){
@@ -77,6 +140,8 @@
                                                        o[j].setValue(value);
                                                } catch 
(InvalidConfigValueException e) {
                                                        
errbuf.append(o[j].getName()).append(' ').append(e.getMessage()).append('\n');
+                                               } catch 
(NodeNeedRestartException e) {
+                                                       needRestart = true;
                                                } catch (Exception e){
                             errbuf.append(o[j].getName()).append(' 
').append(e).append('\n');
                                                        Logger.error(this, 
"Caught "+e, e);
@@ -102,7 +167,32 @@

                if (errbuf.length() == 0) {
                        HTMLNode infobox = 
contentNode.addChild(ctx.getPageMaker().getInfobox("infobox-success", 
l10n("appliedTitle")));
-                       
ctx.getPageMaker().getContentNode(infobox).addChild("#", 
l10n("appliedSuccess"));
+                       HTMLNode content = 
ctx.getPageMaker().getContentNode(infobox);
+                       content.addChild("#", l10n("appliedSuccess"));
+                       
+                       if (needRestart) {
+                               content.addChild("br");
+                               content.addChild("#", l10n("needRestart"));
+
+                               if (node.isUsingWrapper()) {
+                                       content.addChild("br");
+                                       HTMLNode restartForm = 
content.addChild("form",//
+                                               new String[] { "action", 
"method" }, new String[] { "/", "get" }//
+                                               ).addChild("div");
+                                       restartForm.addChild("input",//
+                                               new String[] { "type", "name" 
},//
+                                               new String[] { "hidden", 
"restart" });
+                                       restartForm.addChild("input", //
+                                               new String[] { "type", "name", 
"value" },//
+                                               new String[] { "submit", 
"restart2",//
+                                                       l10n("restartNode") });
+                               }
+                               
+                               if (needRestartUserAlert == null) {
+                                       needRestartUserAlert = new 
NeedRestartUserAlert();
+                                       
node.clientCore.alerts.register(needRestartUserAlert);
+                               }
+                       }
                } else {
                        HTMLNode infobox = 
contentNode.addChild(ctx.getPageMaker().getInfobox("infobox-error", 
l10n("appliedFailureTitle")));
                        HTMLNode content = 
ctx.getPageMaker().getContentNode(infobox).addChild("div", "class", 
"infobox-content");
@@ -125,7 +215,8 @@
                return L10n.getString("ConfigToadlet." + string);
        }

-       public void handleGet(URI uri, HTTPRequest req, ToadletContext ctx) 
throws ToadletContextClosedException, IOException {
+       @Override
+    public void handleGet(URI uri, HTTPRequest req, ToadletContext ctx) throws 
ToadletContextClosedException, IOException {

                if(!ctx.isAllowedFullAccess()) {
                        super.sendErrorPage(ctx, 403, 
L10n.getString("Toadlet.unauthorizedTitle"), 
L10n.getString("Toadlet.unauthorized"));
@@ -171,7 +262,7 @@
                        String defaultValue = "128";
                        String curValue = 
WrapperConfig.getWrapperProperty(configName);
                        item.addChild("span", new String[]{ "class", "title", 
"style" },
-                                       new String[]{ "configshortdesc", 
L10n.getString("ConfigToadlet.defaultIs", new String[] { "default" }, new 
String[] { defaultValue }), 
+                                       new String[]{ "configshortdesc", 
L10n.getString("ConfigToadlet.defaultIs", new String[] { "default" }, new 
String[] { defaultValue }),
                                        "cursor: help;" 
}).addChild(L10n.getHTMLNode("WrapperConfig."+configName+".short"));
                        item.addChild("span", "class", 
"config").addChild("input", new String[] { "type", "class", "name", "value" }, 
new String[] { "text", "config", configName, curValue });
                        item.addChild("span", "class", 
"configlongdesc").addChild(L10n.getHTMLNode("WrapperConfig."+configName+".long"));
@@ -180,7 +271,7 @@
                for(int i=0; i<sc.length;i++){
                        short displayedConfigElements = 0;

-                       Option[] o = sc[i].getOptions();
+                       Option<?>[] o = sc[i].getOptions();
                        HTMLNode configGroupUlNode = new HTMLNode("ul", 
"class", "config");

                        for(int j=0; j<o.length; j++){
@@ -190,20 +281,31 @@

                                        HTMLNode configItemNode = 
configGroupUlNode.addChild("li");
                                        configItemNode.addChild("span", new 
String[]{ "class", "title", "style" },
-                                                       new String[]{ 
"configshortdesc", L10n.getString("ConfigToadlet.defaultIs", new String[] { 
"default" }, new String[] { o[j].getDefault() }) + (mode >= 
PageMaker.MODE_ADVANCED ? " ["+sc[i].getPrefix() + '.' + o[j].getName() + ']' : 
""), 
+                                                       new String[]{ 
"configshortdesc", L10n.getString("ConfigToadlet.defaultIs", new String[] { 
"default" }, new String[] { o[j].getDefault() }) + (mode >= 
PageMaker.MODE_ADVANCED ? " ["+sc[i].getPrefix() + '.' + o[j].getName() + ']' : 
""),
                                                        "cursor: help;" 
}).addChild(L10n.getHTMLNode(o[j].getShortDesc()));
                                        HTMLNode configItemValueNode = 
configItemNode.addChild("span", "class", "config");
                                        if(o[j].getValueString() == null){
                                                Logger.error(this, 
sc[i].getPrefix() + configName + "has returned null from config!);");
-                                               continue; 
+                                               continue;
                                        }

-                                       if(o[j].getCallback() instanceof 
EnumerableOptionCallback)
-                                               
configItemValueNode.addChild(addComboBox((EnumerableOptionCallback)o[j].getCallback(),
 sc[i], configName));
-                                       else if(o[j].getCallback() instanceof 
BooleanCallback)
-                                               
configItemValueNode.addChild(addBooleanComboBox(((BooleanOption)o[j]).getValue(),
 sc[i], configName));
+                                       ConfigCallback<?> callback = 
o[j].getCallback();
+                                       if(callback instanceof 
EnumerableOptionCallback)
+                                               
configItemValueNode.addChild(addComboBox((EnumerableOptionCallback) callback, 
sc[i],
+                                                       configName, 
callback.isReadOnly()));
+                                       else if(callback instanceof 
BooleanCallback)
+                                               
configItemValueNode.addChild(addBooleanComboBox(((BooleanOption) 
o[j]).getValue(), sc[i],
+                                                       configName, 
callback.isReadOnly()));
+                                       else if (callback.isReadOnly())
+                                               
configItemValueNode.addChild("input", //
+                                                       new String[] { "type", 
"class", "disabled", "alt", "name", "value" }, //
+                                                       new String[] { "text", 
"config", "disabled", o[j].getShortDesc(),
+                                                               
sc[i].getPrefix() + '.' + configName, o[j].getValueString() });
                                        else
-                                               
configItemValueNode.addChild("input", new String[] { "type", "class", "alt", 
"name", "value" }, new String[] { "text", "config", o[j].getShortDesc(), 
sc[i].getPrefix() + '.' + configName, o[j].getValueString() });
+                                               
configItemValueNode.addChild("input",//
+                                                       new String[] { "type", 
"class", "alt", "name", "value" }, //
+                                                       new String[] { "text", 
"config", o[j].getShortDesc(),
+                                                               
sc[i].getPrefix() + '.' + configName, o[j].getValueString() });

                                        configItemNode.addChild("span", 
"class", "configlongdesc").addChild(L10n.getHTMLNode(o[j].getLongDesc()));
                                }
@@ -222,12 +324,20 @@
                this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

-       public String supportedMethods() {
+       @Override
+    public String supportedMethods() {
                return "GET, POST";
        }

-       private HTMLNode addComboBox(EnumerableOptionCallback o, SubConfig sc, 
String name) {
-               HTMLNode result = new HTMLNode("select", "name", sc.getPrefix() 
+ '.' + name);
+       private HTMLNode addComboBox(EnumerableOptionCallback o, SubConfig sc, 
String name, boolean disabled) {
+               HTMLNode result;
+               if (disabled)
+                       result = new HTMLNode("select", //
+                               new String[] { "name", "disabled" }, //
+                               new String[] { sc.getPrefix() + '.' + name, 
"disabled" });
+               else
+                       result = new HTMLNode("select", "name", sc.getPrefix() 
+ '.' + name);
+               
                String[] possibleValues = o.getPossibleValues();
                for(int i=0; i<possibleValues.length; i++) {
                        if(possibleValues[i].equals(o.get()))
@@ -239,17 +349,23 @@
                return result;
        }

-       private HTMLNode addBooleanComboBox(boolean value, SubConfig sc, String 
name) {
-               HTMLNode result = new HTMLNode("select", "name", sc.getPrefix() 
+ '.' + name);
-               
-               if(value) {
-                       result.addChild("option", new String[] { "value", 
"selected" }, new String[] {
-                                       "true", "selected" }, l10n("true"));
+       private HTMLNode addBooleanComboBox(boolean value, SubConfig sc, String 
name, boolean disabled) {
+               HTMLNode result;
+               if (disabled)
+                       result = new HTMLNode("select", //
+                               new String[] { "name", "disabled" }, //
+                               new String[] { sc.getPrefix() + '.' + name, 
"disabled" });
+               else
+                       result = new HTMLNode("select", "name", sc.getPrefix() 
+ '.' + name);
+
+               if (value) {
+                       result.addChild("option", new String[] { "value", 
"selected" }, new String[] { "true", "selected" },
+                               l10n("true"));
                        result.addChild("option", "value", "false", 
l10n("false"));
                } else {
                        result.addChild("option", "value", "true", 
l10n("true"));
-                       result.addChild("option", new String[] { "value", 
"selected" }, new String[] {
-                                       "false", "selected" }, l10n("false"));
+                       result.addChild("option", new String[] { "value", 
"selected" }, new String[] { "false", "selected" },
+                               l10n("false"));
                }

                return result;

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/ConnectionsToadlet.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/ConnectionsToadlet.java
   2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/ConnectionsToadlet.java
   2008-08-15 08:20:48 UTC (rev 21886)
@@ -120,7 +120,8 @@
                                long total2 = 
secondNode.getTotalInputBytes()+secondNode.getTotalOutputBytes();
                                return compareLongs(total1, total2);
                        }else if(sortBy.equals("selection_percentage")){
-                               return 
compareLongs(firstNode.getNumberOfSelections(), 
secondNode.getNumberOfSelections());
+                               long sinceWhen = System.currentTimeMillis() - 
PeerNode.SELECTION_SAMPLING_PERIOD;
+                               return 
compareInts(firstNode.getNumberOfSelections().headSet(sinceWhen).size(), 
secondNode.getNumberOfSelections().headSet(sinceWhen).size());
                        }else if(sortBy.equals("time_delta")){
                                return compareLongs(firstNode.getClockDelta(), 
secondNode.getClockDelta());
                        }else if(sortBy.equals(("uptime"))){
@@ -418,9 +419,8 @@
                                        }
                                }

-                               long numberOfSelectionSamples = 
peers.getNumberOfSelectionSamples();
-                               for (int peerIndex = 0, peerCount = 
peerNodeStatuses.length; peerIndex < peerCount; peerIndex++) {
-                                       
+                               int numberOfSelectionSamples = 
peers.getNumberOfSelectionSamples().tailSet(now - 
PeerNode.SELECTION_SAMPLING_PERIOD).size();
+                               for (int peerIndex = 0, peerCount = 
peerNodeStatuses.length; peerIndex < peerCount; peerIndex++) {                  
                    
                                        PeerNodeStatus peerNodeStatus = 
peerNodeStatuses[peerIndex];
                                        drawRow(peerTable, peerNodeStatus, mode 
>= PageMaker.MODE_ADVANCED, fProxyJavascriptEnabled, now, path, 
enablePeerActions, endCols, drawMessageTypes, numberOfSelectionSamples);

@@ -693,9 +693,11 @@

        abstract protected SimpleFieldSet getNoderef();

-       private void drawRow(HTMLNode peerTable, PeerNodeStatus peerNodeStatus, 
boolean advancedModeEnabled, boolean fProxyJavascriptEnabled, long now, String 
path, boolean enablePeerActions, SimpleColumn[] endCols, boolean 
drawMessageTypes, long numberOfSelectionSamples) {
-               HTMLNode peerRow = peerTable.addChild("tr");
-
+       private void drawRow(HTMLNode peerTable, PeerNodeStatus peerNodeStatus, 
boolean advancedModeEnabled, boolean fProxyJavascriptEnabled, long now, String 
path, boolean enablePeerActions, SimpleColumn[] endCols, boolean 
drawMessageTypes, int numberOfSelectionSamples) {
+               int peerSelectionCount = 
peerNodeStatus.getNumberOfSelections().tailSet(now - 
PeerNode.SELECTION_SAMPLING_PERIOD).size();
+               int peerSelectionPercentage = (numberOfSelectionSamples > 0 ? 
(peerSelectionCount*100/numberOfSelectionSamples) : 0);
+               HTMLNode peerRow = peerTable.addChild("tr", "class", 
"darknet_connections_"+(peerSelectionPercentage > 
PeerNode.SELECTION_PERCENTAGE_WARNING ? "warning" : "normal"));
+               
                if(enablePeerActions) {
                        // check box column
                        peerRow.addChild("td", "class", 
"peer-marker").addChild("input", new String[] { "type", "name" }, new String[] 
{ "checkbox", "node_" + peerNodeStatus.hashCode() });
@@ -775,7 +777,7 @@
                        // percent of time connected column
                        peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", 
fix1.format(peerNodeStatus.getPercentTimeRoutableConnection()));
                        // selection stats
-                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", (numberOfSelectionSamples > 0 ? 
((peerNodeStatus.getNumberOfSelections()*100/numberOfSelectionSamples)+"%") : 
"N/A"));
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", (numberOfSelectionSamples > 0 ? (peerSelectionPercentage+"%") 
: "N/A"));
                        // total traffic column
                        peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", SizeUtil.formatSize(peerNodeStatus.getTotalInputBytes())+" / 
"+SizeUtil.formatSize(peerNodeStatus.getTotalOutputBytes())+"/"+SizeUtil.formatSize(peerNodeStatus.getResendBytesSent()));
                        // congestion control

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/FProxyToadlet.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/FProxyToadlet.java    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/FProxyToadlet.java    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -55,7 +55,7 @@
        // ?force= links become invalid after 2 hours.
        private static final long FORCE_GRAIN_INTERVAL = 60*60*1000;
        /** Maximum size for transparent pass-through, should be a config 
option */
-       static final long MAX_LENGTH = 2*1024*1024; // 2MB
+       static long MAX_LENGTH = 2*1024*1024; // 2MB

        static final URI welcome;
        static {
@@ -94,6 +94,7 @@
                return "GET";
        }

+       @Override
        public void handlePost(URI uri, HTTPRequest req, ToadletContext ctx) 
throws ToadletContextClosedException, IOException, RedirectException {
                String ks = uri.getPath();

@@ -319,9 +320,10 @@
                return false;
        }

+       @Override
        public void handleGet(URI uri, HTTPRequest httprequest, ToadletContext 
ctx) 
                        throws ToadletContextClosedException, IOException, 
RedirectException {
-               //String ks = uri.toString();
+
                String ks = uri.getPath();

                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
@@ -442,7 +444,7 @@
                        String referer = sanitizeReferer(ctx);


-                       handleDownload(ctx, data, ctx.getBucketFactory(), 
mimeType, requestedMimeType, httprequest.getParam("force", null), 
httprequest.isParameterSet("forcedownload"), "/", key, maxSize != MAX_LENGTH ? 
"&max-size="+maxSize : "", referer, true, ctx, core);
+                       handleDownload(ctx, data, ctx.getBucketFactory(), 
mimeType, requestedMimeType, httprequest.getParam("force", null), 
httprequest.isParameterSet("forcedownload"), "/", key, maxSize != MAX_LENGTH ? 
"&max-size="+SizeUtil.formatSizeWithoutSpace(maxSize) : "", referer, true, ctx, 
core);

                } catch (FetchException e) {
                        String msg = e.getMessage();
@@ -570,7 +572,7 @@
                        fileInformationList.addChild("li", l10n("sizeUnknown"));
                }
                if(mime != null) {
-                       fileInformationList.addChild("li", 
L10n.getString("FProxyToadlet."+(finalized ? "mimeType" : "expectedMimeType"), 
new String[] { "mime" }, new String[] { mime }));;
+                       fileInformationList.addChild("li", 
L10n.getString("FProxyToadlet."+(finalized ? "mimeType" : "expectedMimeType"), 
new String[] { "mime" }, new String[] { mime }));
                } else {
                        fileInformationList.addChild("li", 
l10n("unknownMIMEType"));
                }
@@ -663,9 +665,6 @@
                WelcomeToadlet welcometoadlet = new WelcomeToadlet(client, 
core, node, bookmarks);
                server.register(welcometoadlet, "/welcome/", true, false);

-               PluginToadlet pluginToadlet = new PluginToadlet(client, 
node.pluginManager2, core);
-               server.register(pluginToadlet, "/plugin/", true, true);
-               
                ConfigToadlet configtoadlet = new ConfigToadlet(client, config, 
node, core);
                server.register(configtoadlet, "/config/", true, 
"FProxyToadlet.configTitle", "FProxyToadlet.config", true, null);


Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java
       2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java
       2008-08-15 08:20:48 UTC (rev 21886)
@@ -10,11 +10,14 @@

 import freenet.client.HighLevelSimpleClient;
 import freenet.config.Config;
+import freenet.config.ConfigException;
 import freenet.config.InvalidConfigValueException;
+import freenet.config.NodeNeedRestartException;
 import freenet.config.WrapperConfig;
 import freenet.l10n.L10n;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
+import freenet.pluginmanager.FredPluginBandwidthIndicator;
 import freenet.support.Fields;
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
@@ -25,15 +28,23 @@
 /**
  * A first time wizard aimed to ease the configuration of the node.
  * 
- * @author Florent Daigni&egrave;re &lt;nextgens at freenetproject.org&gt;
- * 
- * TODO: a choose your CSS step ?
+ * TODO: a choose your CSS step?
  */
 public class FirstTimeWizardToadlet extends Toadlet {
        private final NodeClientCore core;
        private final Config config;

+       private enum WIZARD_STEP {
+               WELCOME,
+               OPENNET,
+               NAME_SELECTION,
+               BANDWIDTH,
+               DATASTORE_SIZE,
+               MEMORY,
+               CONGRATZ;
+       }

+       
        FirstTimeWizardToadlet(HighLevelSimpleClient client, Node node, 
NodeClientCore core) {
                super(client);
                this.core = core;
@@ -42,15 +53,16 @@

        public static final String TOADLET_URL = "/wizard/";

+       @Override
        public void handleGet(URI uri, HTTPRequest request, ToadletContext ctx) 
throws ToadletContextClosedException, IOException {
                if(!ctx.isAllowedFullAccess()) {
                        super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
                        return;
                }

-               int currentStep = request.getIntParam("step");
+               WIZARD_STEP currentStep = 
WIZARD_STEP.valueOf(request.getParam("step", WIZARD_STEP.WELCOME.toString()));

-               if(currentStep == 1) {
+               if(currentStep == WIZARD_STEP.OPENNET) {
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step1Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

@@ -66,14 +78,16 @@
                        opennetDiv.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "radio", "enableOpennet", "true" }, 
l10n("opennetYes"));
                        opennetDiv.addChild("br");
                        opennetDiv.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "radio", "enableOpennet", "false" }, 
l10n("opennetNo"));
-                       HTMLNode para = opennetForm.addChild("p");
-                       para.addChild("b", l10n("warningTitle")+' ');
-                       L10n.addL10nSubstitution(para, 
"FirstTimeWizardToadlet.opennetWarning", new String[] { "bold", "/bold" }, new 
String[] { "<b>", "</b>" });
                        opennetForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "opennetF", 
L10n.getString("FirstTimeWizardToadlet.continue")});
                        opennetForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
                        this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               } else if(currentStep == 2) {
+               } else if(currentStep == WIZARD_STEP.NAME_SELECTION) {
+                       // Attempt to skip one step if possible: opennet nodes 
don't need a name
+                       if(Boolean.valueOf(request.getParam("opennet"))) {
+                               super.writeTemporaryRedirect(ctx, "step3", 
TOADLET_URL+"?step="+WIZARD_STEP.BANDWIDTH);
+                               return;
+                       }
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step2Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

@@ -90,7 +104,12 @@
                        nnameForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
                        this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               } else if(currentStep == 3) {
+               } else if(currentStep == WIZARD_STEP.BANDWIDTH) {
+                       // Attempt to skip one step if possible
+                       if(canAutoconfigureBandwidth()){
+                               super.writeTemporaryRedirect(ctx, "step4", 
TOADLET_URL+"?step="+WIZARD_STEP.DATASTORE_SIZE);
+                               return;
+                       }
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step3Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

@@ -103,6 +122,7 @@
                        HTMLNode bandwidthForm = 
ctx.addFormChild(bandwidthInfoboxContent, ".", "bwForm");
                        HTMLNode result = bandwidthForm.addChild("select", 
"name", "bw");

+                       // don't forget to update handlePost too if you change 
that!
                        result.addChild("option", "value", "8K", 
l10n("bwlimitLowerSpeed"));
                        // Special case for 128kbps to increase performance at 
the cost of some link degradation. Above that we use 50% of the limit.
                        result.addChild("option", "value", "12K", "512+/128 
kbps");
@@ -115,7 +135,12 @@
                        bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
                        this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               } else if(currentStep == 4) {
+               } else if(currentStep == WIZARD_STEP.DATASTORE_SIZE) {
+                       // Attempt to skip one step if possible
+                       if(canAutoconfigureDatastoreSize()) {
+                               super.writeTemporaryRedirect(ctx, "step4", 
TOADLET_URL+"?step="+WIZARD_STEP.MEMORY);
+                               return;
+                       }
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step4Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

@@ -127,57 +152,7 @@
                        bandwidthInfoboxContent.addChild("#", 
l10n("datastoreSizeLong"));
                        HTMLNode bandwidthForm = 
ctx.addFormChild(bandwidthInfoboxContent, ".", "dsForm");
                        HTMLNode result = bandwidthForm.addChild("select", 
"name", "ds");
-                       
-                       // Use JNI to find out the free space on this partition.

-                       long freeSpace = -1;
-                       File dir = 
FileUtil.getCanonicalFile(core.node.getNodeDir());
-                       try {
-                               Class c = dir.getClass();
-                               Method m = c.getDeclaredMethod("getFreeSpace", 
new Class[0]);
-                               if(m != null) {
-                                       Long lFreeSpace = (Long) m.invoke(dir, 
new Object[0]);
-                                       if(lFreeSpace != null) {
-                                               freeSpace = 
lFreeSpace.longValue();
-                                               System.err.println("Found free 
space on node's partition: "+freeSpace+" on "+dir+" = 
"+SizeUtil.formatSize(freeSpace));
-                                       }
-                               }
-                       } catch (NoSuchMethodException e) {
-                               // Ignore
-                               freeSpace = -1;
-                       } catch (Throwable t) {
-                               System.err.println("Trying to access 1.6 
getFreeSpace(), caught "+t);
-                               freeSpace = -1;
-                       }
-                       
-                       if(freeSpace <= 0) {
-                               result.addChild("option", new String[] { 
"value", "selected" }, new String[] { "1G", "selected" }, "1GiB");
-                       } else {
-                               if(freeSpace / 10 > 1024*1024*1024) {
-                                       // If 10GB+ free, default to 10% of 
available disk space.
-                                       String size = 
SizeUtil.formatSize(freeSpace/10);
-                                       String shortSize = 
SizeUtil.stripBytesEtc(size);
-                                       result.addChild("option", new String[] 
{ "value", "selected" }, new String[] { shortSize, "selected" }, size+" 
"+l10n("tenPercentDisk"));
-                                       if(freeSpace / 20 > 1024*1024*1024) {
-                                               // If 20GB+ free, also offer 5% 
of available disk space.
-                                               size = 
SizeUtil.formatSize(freeSpace/20);
-                                               shortSize = 
SizeUtil.stripBytesEtc(size);
-                                               result.addChild("option", 
"value", shortSize, size+" "+l10n("fivePercentDisk"));
-                                       }
-                                       result.addChild("option", "value", 
"1G", "1GiB");
-                               } else if(freeSpace < 1024*1024*1024) {
-                                       // If less than 1GB free, default to 
256MB and also offer 512MB.
-                                       result.addChild("option", new String[] 
{ "value", "selected" }, new String[] { "256M", "selected" }, "256MiB");
-                                       result.addChild("option", "value", 
"512M", "512MiB");
-                               } else if(freeSpace < 5*1024*1024*1024) {
-                                       // If less than 5GB free, default to 
512MB
-                                       result.addChild("option", new String[] 
{ "value", "selected" }, new String[] { "512M", "selected" }, "512MiB");        
                                 
-                                       result.addChild("option", "value", 
"1G", "1GiB");
-                               } else {
-                                       // If unknown, or 5-10GB free, default 
to 1GB.
-                                       result.addChild("option", new String[] 
{ "value", "selected" }, new String[] { "1G", "selected" }, "1GiB");
-                               }
-                       }
                        result.addChild("option", "value", "2G", "2GiB");
                        result.addChild("option", "value", "3G", "3GiB");
                        result.addChild("option", "value", "5G", "5GiB");
@@ -191,7 +166,13 @@
                        bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
                        this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               } else if(currentStep == 6) {
+               } else if(currentStep == WIZARD_STEP.MEMORY) {
+                       // FIXME: Get rid of it when the db4o branch is merged 
or auto-detect it (be careful of classpath's bug @see <freenet.Node>)
+                       // Attempt to skip one step if possible
+                       if(!WrapperConfig.canChangeProperties()) {
+                               super.writeTemporaryRedirect(ctx, "step6", 
TOADLET_URL+"?step="+WIZARD_STEP.CONGRATZ);
+                               return;
+                       }
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step6Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

@@ -215,7 +196,7 @@
                        this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;

-               }else if(currentStep == 7) {
+               }else if(currentStep == WIZARD_STEP.CONGRATZ) {
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step7Title"), true, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

@@ -243,7 +224,7 @@
                HTMLNode firstParagraph = welcomeInfoboxContent.addChild("p");
                firstParagraph.addChild("#", l10n("welcomeInfoboxContent1"));
                HTMLNode secondParagraph = welcomeInfoboxContent.addChild("p");
-               secondParagraph.addChild("a", "href", "?step=1").addChild("#", 
L10n.getString("FirstTimeWizardToadlet.clickContinue"));
+               secondParagraph.addChild("a", "href", 
"?step="+WIZARD_STEP.OPENNET).addChild("#", 
L10n.getString("FirstTimeWizardToadlet.clickContinue"));

                HTMLNode thirdParagraph = welcomeInfoboxContent.addChild("p");
                thirdParagraph.addChild("a", "href", "/").addChild("#", 
l10n("skipWizard"));
@@ -251,6 +232,7 @@
                this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

+       @Override
        public void handlePost(URI uri, HTTPRequest request, ToadletContext 
ctx) throws ToadletContextClosedException, IOException {

                if(!ctx.isAllowedFullAccess()) {
@@ -274,51 +256,39 @@
                                enable = Fields.stringToBool(isOpennetEnabled);
                        } catch (NumberFormatException e) {
                                Logger.error(this, "Invalid opennetEnabled: 
"+isOpennetEnabled, e);
-                               super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step=1");
+                               super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step="+WIZARD_STEP.OPENNET);
                                return;
                        }
                        try {
                                config.get("node.opennet").set("enabled", 
enable);
                        } catch (InvalidConfigValueException e) {
-                               Logger.error(this, "Should not happen setting 
opennet.enabled="+enable+" please repot: "+e, e);
-                               super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step=1");
+                               Logger.error(this, "Should not happen setting 
opennet.enabled=" + enable + " please report: " + e, e);
+                               super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step="+WIZARD_STEP.OPENNET);
                                return;
+                       } catch (NodeNeedRestartException e) {
+                               Logger.error(this, "Should not happen setting 
opennet.enabled=" + enable + " please report: " + e, e);
+                               super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL + "?step=" + WIZARD_STEP.OPENNET);
+                               return;
                        }
-                       super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step=2");
+                       super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step="+WIZARD_STEP.NAME_SELECTION+"&opennet="+enable);
                        return;
                } else if(request.isPartSet("nnameF")) {
                        String selectedNName = request.getPartAsString("nname", 
128);
-                       
                        try {
                                config.get("node").set("name", selectedNName);
                                Logger.normal(this, "The node name has been set 
to "+ selectedNName);
-                       } catch (InvalidConfigValueException e) {
+                       } catch (ConfigException e) {
                                Logger.error(this, "Should not happen, please 
report!" + e, e);
                        }
-                       super.writeTemporaryRedirect(ctx, "step3", 
TOADLET_URL+"?step=3");
+                       super.writeTemporaryRedirect(ctx, "step3", 
TOADLET_URL+"?step="+WIZARD_STEP.BANDWIDTH);
                        return;
                } else if(request.isPartSet("bwF")) {
-                       String selectedUploadSpeed 
=request.getPartAsString("bw", 6);
-                       
-                       try {
-                               config.get("node").set("outputBandwidthLimit", 
selectedUploadSpeed);
-                               Logger.normal(this, "The outputBandwidthLimit 
has been set to "+ selectedUploadSpeed);
-                       } catch (InvalidConfigValueException e) {
-                               Logger.error(this, "Should not happen, please 
report!" + e, e);
-                       }
-                       super.writeTemporaryRedirect(ctx, "step4", 
TOADLET_URL+"?step=4");
+                       
_setUpstreamBandwidthLimit(request.getPartAsString("bw", 6));
+                       super.writeTemporaryRedirect(ctx, "step4", 
TOADLET_URL+"?step="+WIZARD_STEP.DATASTORE_SIZE);
                        return;
                } else if(request.isPartSet("dsF")) {
-                       String selectedStoreSize =request.getPartAsString("ds", 
6);
-                       
-                       try {
-                               config.get("node").set("storeSize", 
selectedStoreSize);
-                               Logger.normal(this, "The storeSize has been set 
to "+ selectedStoreSize);
-                       } catch (InvalidConfigValueException e) {
-                               Logger.error(this, "Should not happen, please 
report!" + e, e);
-                       }
-                       boolean canDoStepSix = 
WrapperConfig.canChangeProperties();
-                       super.writeTemporaryRedirect(ctx, "step5", 
TOADLET_URL+"?step="+(canDoStepSix?"6":"7"));
+                       _setDatastoreSize(request.getPartAsString("ds", 6));
+                       super.writeTemporaryRedirect(ctx, "step5", 
TOADLET_URL+"?step="+WIZARD_STEP.MEMORY);
                        return;
                } else if(request.isPartSet("memoryF")) {
                        String selectedMemorySize = 
request.getPartAsString("memoryF", 6);
@@ -327,7 +297,7 @@
                        if(memorySize >= 0) {
                                
WrapperConfig.setWrapperProperty("wrapper.java.maxmemory", selectedMemorySize);
                        }
-                       super.writeTemporaryRedirect(ctx, "step5", 
TOADLET_URL+"?step=7");
+                       super.writeTemporaryRedirect(ctx, "step6", 
TOADLET_URL+"?step="+WIZARD_STEP.CONGRATZ);
                        return;
                }

@@ -341,4 +311,99 @@
        public String supportedMethods() {
                return "GET, POST";
        }
+       
+       private void _setDatastoreSize(String selectedStoreSize) {
+               try {
+                       config.get("node").set("storeSize", selectedStoreSize);
+                       Logger.normal(this, "The storeSize has been set to " + 
selectedStoreSize);
+               } catch(ConfigException e) {
+                       Logger.error(this, "Should not happen, please report!" 
+ e, e);
+               }
+       }
+       
+       private void _setUpstreamBandwidthLimit(String selectedUploadSpeed) {
+               try {
+                       config.get("node").set("outputBandwidthLimit", 
selectedUploadSpeed);
+                       Logger.normal(this, "The outputBandwidthLimit has been 
set to " + selectedUploadSpeed);
+               } catch (ConfigException e) {
+                       Logger.error(this, "Should not happen, please report!" 
+ e, e);
+               }
+       }
+       
+       private void _setDownstreamBandwidthLimit(String selectedDownloadSpeed) 
{
+               try {
+                       config.get("node").set("inputBandwidthLimit", 
selectedDownloadSpeed);
+                       Logger.normal(this, "The inputBandwidthLimit has been 
set to " + selectedDownloadSpeed);
+               } catch(ConfigException e) {
+                       Logger.error(this, "Should not happen, please report!" 
+ e, e);
+               }
+       }
+       
+       private boolean canAutoconfigureBandwidth() {
+               FredPluginBandwidthIndicator bwIndicator = 
core.node.ipDetector.getBandwidthIndicator();
+               if(bwIndicator == null)
+                       return false;
+               
+               int downstreamBWLimit = bwIndicator.getDownstreamMaxBitRate();
+               if(downstreamBWLimit > 0) {
+                       int bytes = (downstreamBWLimit / 8) - 1;
+                       String downstreamBWLimitString = 
SizeUtil.formatSize(bytes * 2 / 3);
+                       _setDownstreamBandwidthLimit(downstreamBWLimitString);
+                       Logger.normal(this, "The node has a bandwidthIndicator: 
it has reported downstream=" + downstreamBWLimit + "bits/sec... we will use " + 
downstreamBWLimitString + " and skip the bandwidth selection step of the 
wizard.");
+               }
+               
+               // We don't mind if the downstreamBWLimit couldn't be set, but 
upstreamBWLimit is important
+               int upstreamBWLimit = bwIndicator.getUpstramMaxBitRate();
+               if(upstreamBWLimit > 0) {
+                       int bytes = (upstreamBWLimit / 8) - 1;
+                       String upstreamBWLimitString = (bytes < 16384 ? "8K" : 
SizeUtil.formatSize(bytes / 2));
+                       _setUpstreamBandwidthLimit(upstreamBWLimitString);
+                       Logger.normal(this, "The node has a bandwidthIndicator: 
it has reported upstream=" + upstreamBWLimit + "bits/sec... we will use " + 
upstreamBWLimitString + " and skip the bandwidth selection step of the 
wizard.");
+                       return true;
+               }else
+                       return false;
+       }
+       
+       private boolean canAutoconfigureDatastoreSize() {
+               // Use JNI to find out the free space on this partition.
+               long freeSpace = -1;
+               File dir = FileUtil.getCanonicalFile(core.node.getNodeDir());
+               try {
+                       Class c = dir.getClass();
+                       Method m = c.getDeclaredMethod("getFreeSpace", new 
Class[0]);
+                       if(m != null) {
+                               Long lFreeSpace = (Long) m.invoke(dir, new 
Object[0]);
+                               if(lFreeSpace != null) {
+                                       freeSpace = lFreeSpace.longValue();
+                                       System.err.println("Found free space on 
node's partition: on " + dir + " = " + SizeUtil.formatSize(freeSpace));
+                               }
+                       }
+               } catch(NoSuchMethodException e) {
+                       // Ignore
+                       freeSpace = -1;
+               } catch(Throwable t) {
+                       System.err.println("Trying to access 1.6 
getFreeSpace(), caught " + t);
+                       freeSpace = -1;
+               }
+               
+               if(freeSpace <= 0)
+                       return false;
+               else {
+                       String shortSize = null;
+                       if(freeSpace / 20 > 1024 * 1024 * 1024) {
+                               // If 20GB+ free, 5% of available disk space.
+                               shortSize = SizeUtil.formatSize(freeSpace / 20);
+                       }else if(freeSpace / 10 > 1024 * 1024 * 1024) {
+                               // If 10GB+ free, 10% of available disk space.
+                               shortSize = SizeUtil.formatSize(freeSpace / 10);
+                       }else if(freeSpace / 5 > 1024 * 1024 * 1024) {
+                               // If 5GB+ free, default to 512MB
+                               shortSize = "512MB";
+                       }else
+                               shortSize = "256MB";
+                       
+                       _setDatastoreSize(shortSize);
+                       return true;
+               }
+       }
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/HTTPRequestImpl.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/HTTPRequestImpl.java  
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/HTTPRequestImpl.java  
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,6 +3,7 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.clients.http;

+import freenet.support.Fields;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.DataInputStream;
@@ -619,7 +620,7 @@
                }
                String value = this.getParameterValue(name);
                try {
-                       return Long.parseLong(value);
+                       return Fields.parseLong(value);
                } catch (NumberFormatException e) {
                        return defaultValue;
                }

Deleted: 
branches/saltedhashstore/freenet/src/freenet/clients/http/NinjaSpider.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/clients/http/NinjaSpider.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/clients/http/NinjaSpider.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,739 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.clients.http;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.w3c.dom.DOMImplementation;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Text;
-
-import freenet.client.ClientMetadata;
-import freenet.client.FetchException;
-import freenet.client.FetchResult;
-import freenet.client.FetchContext;
-import freenet.client.InsertException;
-import freenet.client.async.BaseClientPutter;
-import freenet.client.async.ClientCallback;
-import freenet.client.async.ClientGetter;
-import freenet.clients.http.filter.ContentFilter;
-import freenet.clients.http.filter.FoundURICallback;
-import freenet.clients.http.filter.UnsafeContentTypeException;
-import freenet.keys.FreenetURI;
-import freenet.node.NodeClientCore;
-import freenet.node.RequestStarter;
-import freenet.oldplugins.plugin.HttpPlugin;
-import freenet.oldplugins.plugin.PluginManager;
-import freenet.support.HTMLNode;
-import freenet.support.Logger;
-import freenet.support.MultiValueTable;
-import freenet.support.api.Bucket;
-import freenet.support.api.HTTPRequest;
-import freenet.support.io.NullBucketFactory;
-
-/**
- * FIXME move to a proper plugin.
- * 
- * FIXME localise - and how to do that for plugins? Should they use the main 
L10n?
- * 
- * NinjaSpider. Produces a ninj^W err ... an XML index.
- *
- *
- * I think regarding the name, a little explanation is required:
- *
- * This name comes from my flatmate, David Anderson. It originated in the 
following discussion over dinner:
- *   him> I've just thought of something weird...
- *   me> oO
- *   him> The term "spider" for indexing software comes from the analogy "a 
spider on the web", right ?
- *   me> Yeeess... ?
- *   him> So, if you're writing a spider for a darknet, isn't it a .... *Ninja 
Spider* ? :D
- *
- * Maybe we should stop buying beers ... However, I needed a name for the 
spider,
- * and NinjaSpider sounds more fun than XmlSpider, so it stuck :)
- *
- */
-public class NinjaSpider implements HttpPlugin, ClientCallback, 
FoundURICallback {
-
-
-       long tProducedIndex;
-
-       // URIs visited, or fetching, or queued. Added once then forgotten 
about.
-       private final HashSet visitedURIs = new HashSet();
-       private final HashSet urisWithWords = new HashSet();
-       private final HashSet failedURIs = new HashSet();
-       private final HashSet queuedURISet = new HashSet();
-       private final LinkedList queuedURIList = new LinkedList();
-       private final HashMap runningFetchesByURI = new HashMap();
-       private final HashMap urisByWord = new HashMap();
-       private final HashMap titlesOfURIs = new HashMap();
-
-       private static final int minTimeBetweenEachIndexRewriting = 10;
-
-       // Can have many; this limit only exists to save memory.
-       private static final int maxParallelRequests = 20;
-       private int maxShownURIs = 50;
-
-       private NodeClientCore core;
-       private FetchContext ctx;
-       private final short PRIORITY_CLASS = 
RequestStarter.PREFETCH_PRIORITY_CLASS;
-       private boolean stopped = true;
-
-       private static final String indexFilename = "new.index.xml";
-
-       private static final String pluginName = "Ninja spider";
-
-       private static final boolean htmlOnly = true;
-
-
-       /* The ones below are required to genereate a correct index, see:
-        * http://wiki.freenetproject.org/AnotherFreenetIndexFormat
-        */
-       private static final String indexTitle= "This is an index";
-       private static final String indexOwner = "Another anonymous";
-       private static final String indexOwnerEmail = null; /* can be null */
-       private final HashMap sizeOfURIs = new HashMap(); /* String (URI) -> 
Long */
-       private final HashMap mimeOfURIs = new HashMap(); /* String (URI) -> 
String */
-       private final HashMap lastPositionByURI = new HashMap(); /* String 
(URI) -> Integer */ /* Use to determine word position on each uri */
-       private final HashMap positionsByWordByURI = new HashMap(); /* String 
(URI) -> HashMap (String (word) -> Integer[] (Positions)) */
-
-
-
-       private synchronized void queueURI(FreenetURI uri) {
-               /* Currently we don't handle PDF or other contents,
-                  so it's not interresting to download them
-               */
-
-               String tmp = uri.toString().toLowerCase();
-               
-               if(htmlOnly
-                  && (tmp.indexOf(".htm") < 0)
-                  && (tmp.charAt(tmp.length()-1) != '/'))
-                       return;
-
-               if ((!visitedURIs.contains(uri)) && queuedURISet.add(uri)) {
-                       queuedURIList.addLast(uri);
-                       visitedURIs.add(uri);
-               }
-       }
-
-       private void startSomeRequests() {
-               ArrayList toStart = null;
-               synchronized (this) {
-                       if (stopped) {
-                               return;
-                       }
-                       int running = runningFetchesByURI.size();
-                       int queued = queuedURIList.size();
-                       
-                       if ((running >= maxParallelRequests) || (queued == 0))
-                               return;
-                       
-                       toStart = new ArrayList(Math.min(maxParallelRequests - 
running, queued));
-                       
-                       for (int i = running; i < maxParallelRequests; i++) {
-                               if (queuedURIList.isEmpty())
-                                       break;
-                               FreenetURI uri = (FreenetURI) 
queuedURIList.removeFirst();
-                               queuedURISet.remove(uri);
-                               ClientGetter getter = makeGetter(uri);
-                               toStart.add(getter);
-                       }
-                       for (int i = 0; i < toStart.size(); i++) {
-                               ClientGetter g = (ClientGetter) toStart.get(i);
-                               try {
-                                       runningFetchesByURI.put(g.getURI(), g);
-                                       g.start();
-                               } catch (FetchException e) {
-                                       onFailure(e, g);
-                               }
-                       }
-               }
-       }
-
-       private ClientGetter makeGetter(FreenetURI uri) {
-               ClientGetter g = new ClientGetter(this, 
core.requestStarters.chkFetchScheduler, core.requestStarters.sskFetchScheduler, 
uri, ctx, PRIORITY_CLASS, this, null, null);
-               return g;
-       }
-
-       public void onSuccess(FetchResult result, ClientGetter state) {
-               FreenetURI uri = state.getURI();
-               synchronized (this) {
-                       runningFetchesByURI.remove(uri);
-               }
-               startSomeRequests();
-               ClientMetadata cm = result.getMetadata();
-               Bucket data = result.asBucket();
-               String mimeType = cm.getMIMEType();
-
-               sizeOfURIs.put(uri.toString(), new Long(data.size()));
-               mimeOfURIs.put(uri.toString(), mimeType);
-
-               try {
-                       ContentFilter.filter(data, new NullBucketFactory(), 
mimeType, new URI("http://127.0.0.1:8888/"; + uri.toString()), this);
-               } catch (UnsafeContentTypeException e) {
-                       return; // Ignore
-               } catch (IOException e) {
-                       Logger.error(this, "Bucket error?: " + e, e);
-               } catch (URISyntaxException e) {
-                       Logger.error(this, "Internal error: " + e, e);
-               } finally {
-                       data.free();
-               }
-       }
-
-       public void onFailure(FetchException e, ClientGetter state) {
-               FreenetURI uri = state.getURI();
-               synchronized (this) {
-                       failedURIs.add(uri);
-                       runningFetchesByURI.remove(uri);
-               }
-               if (e.newURI != null)
-                       queueURI(e.newURI);
-               else
-                       queueURI(uri);
-               startSomeRequests();
-       }
-
-       public void onSuccess(BaseClientPutter state) {
-               // Ignore
-       }
-
-       public void onFailure(InsertException e, BaseClientPutter state) {
-               // Ignore
-       }
-
-       public void onGeneratedURI(FreenetURI uri, BaseClientPutter state) {
-               // Ignore
-       }
-
-       public void foundURI(FreenetURI uri) {
-               queueURI(uri);
-               startSomeRequests();
-       }
-
-       public void foundURI(FreenetURI uri, boolean inline) {
-               queueURI(uri);
-               startSomeRequests();
-       }
-
-       public void onText(String s, String type, URI baseURI) {
-
-               FreenetURI uri;
-               try {
-                       uri = new 
FreenetURI(baseURI.getPath().substring(1));/*substring(1) because we don't want 
the initial '/' */
-               } catch (MalformedURLException e) {
-                       Logger.error(this, "Caught " + e, e);
-                       return;
-               }
-
-               if((type != null) && (type.length() != 0) && 
type.toLowerCase().equals("title")
-                  && (s != null) && (s.length() != 0) && (s.indexOf('\n') < 
0)) {
-                       /* We should have a correct title */
-                       titlesOfURIs.put(uri.toString(), s);
-                       type = "title";
-               }
-               else
-                       type = null;
-
-
-               String[] words = s.split("[^A-Za-z0-9]");
-
-               Integer lastPosition = null;
-
-               lastPosition = (Integer)lastPositionByURI.get(uri.toString());
-
-               if(lastPosition == null)
-                       lastPosition = new Integer(1); /* We start to count 
from 1 */
-
-               for (int i = 0; i < words.length; i++) {
-                       String word = words[i];
-                       if ((word == null) || (word.length() == 0))
-                               continue;
-                       word = word.toLowerCase();
-                       
-                       if(type == null)
-                               addWord(word, lastPosition.intValue() + i, uri);
-                       else
-                               addWord(word, -1 * (i+1), uri);
-               }
-               
-               if(type == null) {
-                       lastPosition = new Integer(lastPosition.intValue() + 
words.length);
-                       lastPositionByURI.put(uri.toString(), lastPosition);
-               }
-       }
-
-       private synchronized void addWord(String word, int position, FreenetURI 
uri) {
-
-               /* I know that it's bad for i18n */
-               /* But words separation or file filtering seems to already 
killed words matching [^a-zA-Z] ... */
-               if(word.length() < 3)
-                       return;
-
-
-               FreenetURI[] uris = (FreenetURI[]) urisByWord.get(word);
-
-               //Integer[] positions = (Integer[]) 
positionsByWordByURI.get(word);
-
-               urisWithWords.add(uri);
-
-
-               /* Word position indexation */
-               HashMap wordPositionsForOneUri = 
(HashMap)positionsByWordByURI.get(uri.toString()); /* For a given URI, take as 
key a word, and gives position */
-               
-               if(wordPositionsForOneUri == null) {
-                       wordPositionsForOneUri = new HashMap();
-                       wordPositionsForOneUri.put(word, new Integer[] { new 
Integer(position) });
-                       positionsByWordByURI.put(uri.toString(), 
wordPositionsForOneUri);
-               } else {
-                       Integer[] positions = 
(Integer[])wordPositionsForOneUri.get(word);
-
-                       if(positions == null) {
-                               positions = new Integer[] { new 
Integer(position) };
-                               wordPositionsForOneUri.put(word, positions);
-                       } else {
-                               Integer[] newPositions = new 
Integer[positions.length + 1];
-
-                               System.arraycopy(positions, 0, newPositions, 0, 
positions.length);
-                               newPositions[positions.length] = new 
Integer(position);
-
-                               wordPositionsForOneUri.put(word, newPositions);
-                       }
-               }
-
-
-
-               /* Words indexation */
-               if (uris == null) {
-                       urisByWord.put(word, new FreenetURI[] { uri });
-               } else {
-                       for (int i = 0; i < uris.length; i++) {
-                               if (uris[i].equals(uri))
-                                       return;
-                       }
-
-                       FreenetURI[] newURIs = new FreenetURI[uris.length + 1];
-
-                       System.arraycopy(uris, 0, newURIs, 0, uris.length);
-                       newURIs[uris.length] = uri;
-                       urisByWord.put(word, newURIs);
-               }
-
-               if (tProducedIndex + minTimeBetweenEachIndexRewriting * 1000 < 
System.currentTimeMillis()) {
-                       try {
-                               produceIndex();
-                       } catch (IOException e) {
-                               Logger.error(this, "Caught " + e + " while 
creating index", e);
-                       }
-                       tProducedIndex = System.currentTimeMillis();
-               }
-       }
-
-       /**
-        * Produce an XML index in new.index.xml
-        */
-       private synchronized void produceIndex() throws IOException {
-               File outputFile;
-               StreamResult resultStream;
-
-               if (urisByWord.isEmpty() || urisWithWords.isEmpty()) {
-                       Logger.normal(this, "No URIs with words -> no index 
generation");
-                       return;
-               }
-               
-               outputFile = new File(indexFilename);
-
-               if(outputFile.exists()
-                  && !outputFile.canWrite()) {
-                       Logger.error(this, "Spider: Unable to write '" + 
indexFilename +"'. Check permissions.");
-                       return;
-               }
-
-               resultStream = new StreamResult(outputFile);
-
-
-               /* Initialize xml builder */
-               Document xmlDoc = null;
-               DocumentBuilderFactory xmlFactory = null;
-               DocumentBuilder xmlBuilder = null;
-               DOMImplementation impl = null;
-               Element rootElement = null;
-
-               xmlFactory = DocumentBuilderFactory.newInstance();
-
-
-               try {
-                       xmlBuilder = xmlFactory.newDocumentBuilder();
-               } catch(javax.xml.parsers.ParserConfigurationException e) {
-                       /* Will (should ?) never happen */
-                       Logger.error(this, "Spider: Error while initializing 
XML generator: "+e.toString());
-                       return;
-               }
-
-
-               impl = xmlBuilder.getDOMImplementation();
-
-               /* Starting to generate index */
-
-               xmlDoc = impl.createDocument(null, "index", null);
-               rootElement = xmlDoc.getDocumentElement();
-
-               /* Adding header to the index */
-               Element headerElement = xmlDoc.createElement("header");
-
-               /* -> title */
-               Element subHeaderElement = xmlDoc.createElement("title");
-               Text subHeaderText = xmlDoc.createTextNode(indexTitle);
-               
-               subHeaderElement.appendChild(subHeaderText);
-               headerElement.appendChild(subHeaderElement);
-
-               /* -> owner */
-               subHeaderElement = xmlDoc.createElement("owner");
-               subHeaderText = xmlDoc.createTextNode(indexOwner);
-               
-               subHeaderElement.appendChild(subHeaderText);
-               headerElement.appendChild(subHeaderElement);
-               
-               /* -> owner email */
-               if(indexOwnerEmail != null) {
-                       subHeaderElement = xmlDoc.createElement("email");
-                       subHeaderText = xmlDoc.createTextNode(indexOwnerEmail);
-                       
-                       subHeaderElement.appendChild(subHeaderText);
-                       headerElement.appendChild(subHeaderElement);
-               }
-
-
-
-               String[] words = (String[]) urisByWord.keySet().toArray(new 
String[urisByWord.size()]);
-               Arrays.sort(words);
-
-               FreenetURI[] uris = (FreenetURI[]) urisWithWords.toArray(new 
FreenetURI[urisWithWords.size()]);
-               HashMap urisToNumbers = new HashMap();
-
-               /* Adding freesite list to the index */
-               Element filesElement = xmlDoc.createElement("files"); /* 
filesElement != fileElement */
-
-               for (int i = 0; i < uris.length; i++) {
-                       urisToNumbers.put(uris[i], new Integer(i));
-                       
-                       Element fileElement = xmlDoc.createElement("file");
-
-                       fileElement.setAttribute("id", Integer.toString(i));
-                       fileElement.setAttribute("key", uris[i].toString());
-                       
-                       Long size = (Long)sizeOfURIs.get(uris[i].toString());
-
-                       if(size == null) {
-                               Logger.error(this, "Spider: size is missing");
-                       } else {
-                               fileElement.setAttribute("size", 
size.toString());
-                       }
-                       fileElement.setAttribute("mime", 
((String)mimeOfURIs.get(uris[i].toString())));
-
-                       Element titleElement = xmlDoc.createElement("option");
-                       titleElement.setAttribute("name", "title");
-                       titleElement.setAttribute("value", 
(String)titlesOfURIs.get(uris[i].toString()));
-
-                       fileElement.appendChild(titleElement);
-                       filesElement.appendChild(fileElement);
-               }
-
-
-               /* Adding word index */
-               Element keywordsElement = xmlDoc.createElement("keywords");
-               
-               /* Word by word */
-               for (int i = 0; i < words.length; i++) {
-                       Element wordElement = xmlDoc.createElement("word");
-                       wordElement.setAttribute("v", words[i]);
-
-                       FreenetURI[] urisForWord = (FreenetURI[]) 
urisByWord.get(words[i]);
-
-                       /* URI by URI */
-                       for (int j = 0; j < urisForWord.length; j++) {
-                               FreenetURI uri = urisForWord[j];
-                               Integer x = (Integer) urisToNumbers.get(uri);
-                               
-                               if (x == null) {
-                                       Logger.error(this, "Eh?");
-                                       continue;
-                               }
-
-                               Element uriElement = 
xmlDoc.createElement("file");
-                               uriElement.setAttribute("id", x.toString());
-
-                               /* Position by position */
-                               HashMap positionsForGivenWord = 
(HashMap)positionsByWordByURI.get(uri.toString());
-                               Integer[] positions = 
(Integer[])positionsForGivenWord.get(words[i]);
-
-                               StringBuffer positionList = new StringBuffer();
-
-                               for(int k=0; k < positions.length ; k++) {
-                                       if(k!=0)
-                                               positionList.append(',');
-
-                                       
positionList.append(positions[k].toString());
-                               }
-                               
-                               
uriElement.appendChild(xmlDoc.createTextNode(positionList.toString()));
-
-                               wordElement.appendChild(uriElement);
-                       }
-
-                       keywordsElement.appendChild(wordElement);
-               }
-
-               rootElement.appendChild(headerElement);
-               rootElement.appendChild(filesElement);
-               rootElement.appendChild(keywordsElement);
-
-               /* Serialization */
-               DOMSource domSource = new DOMSource(xmlDoc);
-               TransformerFactory transformFactory = 
TransformerFactory.newInstance();
-               Transformer serializer;
-
-               try {
-                       serializer = transformFactory.newTransformer();
-               } catch(javax.xml.transform.TransformerConfigurationException 
e) {
-                       Logger.error(this, "Spider: Error while serializing XML 
(transformFactory.newTransformer()): "+e.toString());
-                       return;
-               }
-
-
-               serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-               serializer.setOutputProperty(OutputKeys.INDENT,"yes");
-               
-               /* final step */
-               try {
-                       serializer.transform(domSource, resultStream);
-               } catch(javax.xml.transform.TransformerException e) {
-                       Logger.error(this, "Spider: Error while serializing XML 
(transform()): "+e.toString());
-                       return;
-               }
-
-               if(Logger.shouldLog(Logger.MINOR, this))
-                       Logger.minor(this, "Spider: indexes regenerated.");
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.HttpPlugin#handleGet(freenet.clients.http.HTTPRequestImpl,
 freenet.clients.http.ToadletContext)
-        */
-       public void handleGet(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException {
-               String action = request.getParam("action");
-               PageMaker pageMaker = context.getPageMaker();
-               if ((action == null) || (action.length() == 0)) {
-                       MultiValueTable responseHeaders = new MultiValueTable();
-                       responseHeaders.put("Location", "?action=list");
-                       context.sendReplyHeaders(301, "Redirect", 
responseHeaders, "text/html; charset=utf-8", 0);
-                       return;
-               } else if ("list".equals(action)) {
-                       String listName = request.getParam("listName", null);
-
-                       HTMLNode pageNode = pageMaker.getPageNode(pluginName, 
context);
-                       HTMLNode contentNode = 
pageMaker.getContentNode(pageNode);
-
-                       /* create copies for multi-threaded use */
-                       if (listName == null) {
-                               Map runningFetches = new 
HashMap(runningFetchesByURI);
-                               List queued = new ArrayList(queuedURIList);
-                               Set visited = new HashSet(visitedURIs);
-                               Set failed = new HashSet(failedURIs);
-                               
contentNode.addChild(createNavbar(runningFetches.size(), queued.size(), 
visited.size(), failed.size()));
-                               contentNode.addChild(createAddBox());
-                               contentNode.addChildren(createList("Running 
Fetches", "running", runningFetches.keySet(), maxShownURIs));
-                               contentNode.addChildren(createList("Queued 
URIs", "queued", queued, maxShownURIs));
-                               contentNode.addChildren(createList("Visited 
URIs", "visited", visited, maxShownURIs));
-                               contentNode.addChildren(createList("Failed 
URIs", "failed", failed, maxShownURIs));
-                       } else {
-                               contentNode.addChild(createBackBox());
-                               if ("failed".equals(listName)) {
-                                       Set failed = new HashSet(failedURIs);
-                                       
contentNode.addChildren(createList("Failed URIs", "failed", failed, -1));       
-                               } else if ("visited".equals(listName)) {
-                                       Set visited = new HashSet(visitedURIs);
-                                       
contentNode.addChildren(createList("Visited URIs", "visited", visited, -1));
-                               } else if ("queued".equals(listName)) {
-                                       List queued = new 
ArrayList(queuedURIList);
-                                       
contentNode.addChildren(createList("Queued URIs", "queued", queued, -1));
-                               } else if ("running".equals(listName)) {
-                                       Map runningFetches = new 
HashMap(runningFetchesByURI);
-                                       
contentNode.addChildren(createList("Running Fetches", "running", 
runningFetches.keySet(), -1));
-                               }
-                       }
-                       MultiValueTable responseHeaders = new MultiValueTable();
-                       byte[] responseBytes = 
pageNode.generate().getBytes("UTF-8");
-                       context.sendReplyHeaders(200, "OK", responseHeaders, 
"text/html; charset=utf-8", responseBytes.length);
-                       context.writeData(responseBytes);
-               } else if ("add".equals(action)) {
-                       String uriParam = request.getParam("key");
-                       try {
-                               FreenetURI uri = new FreenetURI(uriParam);
-                               synchronized (this) {
-                                       failedURIs.remove(uri);
-                                       visitedURIs.remove(uri);
-                               }
-                               queueURI(uri);
-                               startSomeRequests();
-                       } catch (MalformedURLException mue1) {
-                               sendSimpleResponse(context, "URL invalid", "The 
given URI is not valid. Please return and try again.");
-                               return;
-                       }
-                       MultiValueTable responseHeaders = new MultiValueTable();
-                       responseHeaders.put("Location", "?action=list");
-                       context.sendReplyHeaders(301, "Redirect", 
responseHeaders, "text/html; charset=utf-8", 0);
-                       return;
-               }
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.HttpPlugin#handlePost(freenet.clients.http.HTTPRequestImpl,
 freenet.clients.http.ToadletContext)
-        */
-       public void handlePost(HTTPRequest request, ToadletContext context) 
throws IOException {
-       }
-       
-       private void sendSimpleResponse(ToadletContext context, String title, 
String message) throws ToadletContextClosedException, IOException {
-               PageMaker pageMaker = context.getPageMaker();
-               HTMLNode pageNode = pageMaker.getPageNode(title, context);
-               HTMLNode contentNode = pageMaker.getContentNode(pageNode);
-               HTMLNode infobox = contentNode.addChild("div", "class", 
"infobox infobox-alert");
-               infobox.addChild("div", "class", "infobox-header", title);
-               infobox.addChild("div", "class", "infobox-content", message);
-               byte[] responseBytes = pageNode.generate().getBytes("utf-8");
-               context.sendReplyHeaders(200, "OK", new MultiValueTable(), 
"text/html; charset=utf-8", responseBytes.length);
-               context.writeData(responseBytes);
-       }
-       
-       private HTMLNode createBackBox() {
-               HTMLNode backBox = new HTMLNode("div", "class", "infobox");
-               HTMLNode backBoxContent = backBox.addChild("div", "class", 
"infobox-content");
-               backBoxContent.addChild("#", "Return to the ");
-               backBoxContent.addChild("a", "href", "?action=list", "list of 
all URIs");
-               backBoxContent.addChild("#", ".");
-               return backBox;
-       }
-       
-       private HTMLNode createAddBox() {
-               HTMLNode addBox = new HTMLNode("div", "class", "infobox");
-               addBox.addChild("div", "class", "infobox-header", "Add a URI");
-               HTMLNode addForm = addBox.addChild("div", "class", 
"infobox-content").addChild("form", new String[] { "action", "method" }, new 
String[] { "", "get" });
-               addForm.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "hidden", "action", "add" });
-               addForm.addChild("input", new String[] { "type", "size", 
"name", "value" }, new String[] { "text", "40", "key", "" });
-               addForm.addChild("input", new String[] { "type", "value" }, new 
String[] { "submit", "Add URI" });
-               return addBox;
-       }
-
-       private HTMLNode createNavbar(int running, int queued, int visited, int 
failed) {
-               HTMLNode infobox = new HTMLNode("div", "class", "infobox 
navbar");
-               infobox.addChild("div", "class", "infobox-header", "Page 
navigation");
-               HTMLNode links = infobox.addChild("div", "class", 
"infobox-content").addChild("ul");
-               links.addChild("li").addChild("a", "href", "#running", "Running 
(" + running + ')');
-               links.addChild("li").addChild("a", "href", "#queued", "Queued 
(" + queued + ')');
-               links.addChild("li").addChild("a", "href", "#visited", "Visited 
(" + visited + ')');
-               links.addChild("li").addChild("a", "href", "#failed", "Failed 
(" + failed + ')');
-               return infobox;
-       }
-
-       private HTMLNode[] createList(String listName, String anchorName, 
Collection collection, int maxCount) {
-               HTMLNode listBox = new HTMLNode("div", "class", "infobox");
-               listBox.addChild("div", "class", "infobox-header", listName + " 
(" + collection.size() + ')');
-               HTMLNode listContent = listBox.addChild("div", "class", 
"infobox-content");
-               Iterator collectionItems = collection.iterator();
-               int itemCount = 0;
-               while (collectionItems.hasNext()) {
-                       FreenetURI uri = (FreenetURI) collectionItems.next();
-                       listContent.addChild("#", uri.toString());
-                       listContent.addChild("br");
-                       if (itemCount++ == maxCount) {
-                               listContent.addChild("br");
-                               listContent.addChild("a", "href", 
"?action=list&listName=" + anchorName, "Show all\u2026");
-                               break;
-                       }
-               }
-               return new HTMLNode[] { new HTMLNode("a", "name", anchorName), 
listBox };
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#getPluginName()
-        */
-       public String getPluginName() {
-               return pluginName;
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.Plugin#setPluginManager(freenet.oldplugins.plugin.PluginManager)
-        */
-       public void setPluginManager(PluginManager pluginManager) {
-               this.core = pluginManager.getClientCore();
-               this.ctx = core.makeClient((short) 0).getFetchContext();
-               ctx.maxSplitfileBlockRetries = 10;
-               ctx.maxNonSplitfileRetries = 10;
-               ctx.maxTempLength = 2 * 1024 * 1024;
-               ctx.maxOutputLength = 2 * 1024 * 1024;
-               tProducedIndex = System.currentTimeMillis();
-       }
-
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#startPlugin()
-        */
-       public void startPlugin() {
-               FreenetURI[] initialURIs = core.getBookmarkURIs();
-               for (int i = 0; i < initialURIs.length; i++)
-                       queueURI(initialURIs[i]);
-               stopped = false;
-               Thread starterThread = new Thread("Spider Plugin Starter") {
-                       public void run() {
-                               startSomeRequests();
-                       }
-               };
-               starterThread.setDaemon(true);
-               starterThread.start();
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#stopPlugin()
-        */
-       public void stopPlugin() {
-               synchronized (this) {
-                       stopped = true;
-                       queuedURIList.clear();
-               }
-       }
-
-       public void onMajorProgress() {
-               // Ignore
-       }
-
-       public void onFetchable(BaseClientPutter state) {
-               // Ignore
-       }
-
-
-
-}

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/PageMaker.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/clients/http/PageMaker.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/clients/http/PageMaker.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -202,11 +202,11 @@
         * 
         * @return An {@link ArrayList} containing the names of all available 
themes
         */
-       public List getThemes() {
+       public List<String> getThemes() {
                if (jarThemesCache != null) {
                        return jarThemesCache;
                }
-               List themes = new ArrayList();
+               List<String> themes = new ArrayList<String>();
                try {
                        URL url = getClass().getResource("staticfiles/themes/");
                        URLConnection urlConnection = url.openConnection();

Deleted: 
branches/saltedhashstore/freenet/src/freenet/clients/http/PluginToadlet.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/PluginToadlet.java    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/PluginToadlet.java    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,295 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.clients.http;
-
-import java.io.IOException;
-import java.net.URI;
-
-import freenet.client.HighLevelSimpleClient;
-import freenet.l10n.L10n;
-import freenet.node.NodeClientCore;
-import freenet.oldplugins.plugin.HttpPlugin;
-import freenet.oldplugins.plugin.Plugin;
-import freenet.oldplugins.plugin.PluginManager;
-import freenet.support.HTMLNode;
-import freenet.support.MultiValueTable;
-import freenet.support.api.HTTPRequest;
-
-/**
- * Toadlet for the plugin manager.
- * 
- * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
- * @version $Id$
- */
-public class PluginToadlet extends Toadlet {
-
-       private static final int MAX_PLUGIN_NAME_LENGTH = 1024;
-       /** The plugin manager backing this toadlet. */
-       private final PluginManager pluginManager;
-       private final NodeClientCore core;
-
-       /**
-        * Creates a new toadlet.
-        * 
-        * @param client
-        *            The high-level client to use
-        * @param pluginManager
-        *            The plugin manager to use
-        */
-       protected PluginToadlet(HighLevelSimpleClient client, PluginManager 
pluginManager, NodeClientCore core) {
-               super(client);
-               this.pluginManager = pluginManager;
-               this.core = core;
-       }
-
-       /**
-        * This toadlet support GET and POST operations.
-        * 
-        * @see freenet.clients.http.Toadlet#supportedMethods()
-        * @return "GET,POST"
-        */
-       public String supportedMethods() {
-               return "GET,POST";
-       }
-
-       /**
-        * Handles a GET request.
-        * 
-        * @see freenet.clients.http.Toadlet#handleGet(java.net.URI,
-        *      freenet.clients.http.ToadletContext)
-        * @param uri
-        *            The URI that was requested
-        * @param ctx
-        *            The context of this toadlet
-        */
-       public void handleGet(URI uri, HTTPRequest httpRequest, ToadletContext 
ctx) throws ToadletContextClosedException, IOException, RedirectException {
-               String uriPath = uri.getPath();
-               String pluginName = uriPath.substring(uriPath.lastIndexOf('/') 
+ 1);
-
-               if (pluginName.length() > 0) {
-                       Plugin plugin = findPlugin(pluginName);
-                       if (plugin != null) {
-                               if (plugin instanceof HttpPlugin) {
-                                       ((HttpPlugin) 
plugin).handleGet(httpRequest, ctx);
-                               } else {
-                                       writeHTMLReply(ctx, 220, "OK", 
createBox(ctx, l10n("noWebInterfaceTitle"), l10n("noWebInterface")).toString());
-                               }
-                               return;
-                       }
-                       writeHTMLReply(ctx, 220, "OK", createBox(ctx, 
l10n("pluginNotFoundTitle"), l10n("pluginNotFound")).toString());
-                       return;
-               }
-
-               if(!ctx.isAllowedFullAccess()) {
-                       super.sendErrorPage(ctx, 403, "Unauthorized", 
l10n("unauthorized"));
-                       return;
-               }
-               
-               String action = httpRequest.getParam("action");
-               if (action.length() == 0) {
-                       writePermanentRedirect(ctx, "Plugin list", 
"?action=list");
-                       return;
-               }
-
-               StringBuffer replyBuffer = new StringBuffer();
-               if ("list".equals(action)) {
-                       replyBuffer.append(listPlugins(ctx));
-               } else {
-                       writeHTMLReply(ctx, 220, "OK", createBox(ctx, 
l10n("unsupportedMethodTitle"), l10n("unsupportedMethod")).toString());
-                       return;
-               }
-               writeHTMLReply(ctx, 220, "OK", replyBuffer.toString());
-       }
-       
-       private String l10n(String key) {
-               return L10n.getString("PluginToadlet."+key);
-       }
-
-       /**
-        * @see freenet.clients.http.Toadlet#handlePost(java.net.URI, 
freenet.support.api.Bucket, freenet.clients.http.ToadletContext)
-        */
-       public void handlePost(URI uri, HTTPRequest httpRequest, ToadletContext 
ctx) throws ToadletContextClosedException, IOException, RedirectException {
-               String uriPath = uri.getPath();
-               String pluginName = uriPath.substring(uriPath.lastIndexOf('/') 
+ 1);
-               
-               if (pluginName.length() > 0) {
-                       Plugin plugin = findPlugin(pluginName);
-                       if (plugin != null) {
-                               if (plugin instanceof HttpPlugin) {
-                                       ((HttpPlugin) 
plugin).handlePost(httpRequest, ctx);
-                               } else {
-                                       writeHTMLReply(ctx, 220, "OK", 
createBox(ctx, l10n("noWebInterfaceTitle"), l10n("noWebInterface")).toString());
-                               }
-                               return;
-                       }
-                       writeHTMLReply(ctx, 220, "OK", createBox(ctx, 
l10n("pluginNotFoundTitle") , l10n("pluginNotFound")).toString());
-                       return;
-               }
-               
-               if(!ctx.isAllowedFullAccess()) {
-                       super.sendErrorPage(ctx, 403, "Unauthorized", 
l10n("unauthorized"));
-                       return;
-               }
-               
-               String pass = httpRequest.getPartAsString("formPassword", 32);
-               if((pass == null) || !pass.equals(core.formPassword)) {
-                       MultiValueTable headers = new MultiValueTable();
-                       headers.put("Location", "/plugin/");
-                       ctx.sendReplyHeaders(302, "Found", headers, null, 0);
-                       return;
-               }
-               
-               String action = httpRequest.getPartAsString("action", 32);
-               if (action.length() == 0) {
-                       writePermanentRedirect(ctx, l10n("pluginList"), 
"?action=list");
-                       return;
-               }
-
-               StringBuffer replyBuffer = new StringBuffer();
-               if ("add".equals(action)) {
-                       pluginName = httpRequest.getPartAsString("pluginName", 
MAX_PLUGIN_NAME_LENGTH);
-
-                       boolean added = false;
-                       try {
-                               pluginManager.addPlugin(pluginName, true);
-                               added = true;
-                       } catch (IllegalArgumentException iae1) {
-                               super.sendErrorPage(ctx, 
l10n("failedToLoadPluginTitle"), l10n("failedToLoadPlugin"), iae1);
-                               return;
-                       }
-                       if (added) {
-                               writePermanentRedirect(ctx, l10n("pluginList"), 
"?action=list");
-                               return;
-                       }
-                       replyBuffer.append(createBox(ctx, 
l10n("failedToLoadPluginTitle"), l10n("failedToLoadPluginCheckClass")));
-               } else if ("reload".equals(action)) {
-                       pluginName = httpRequest.getPartAsString("pluginName", 
MAX_PLUGIN_NAME_LENGTH);
-                       Plugin plugin = findPlugin(pluginName);
-                       pluginManager.removePlugin(plugin, false);
-                       pluginManager.addPlugin(plugin.getClass().getName(), 
false);
-                       writePermanentRedirect(ctx, l10n("pluginList"), 
"?action=list");
-                       return;
-               } else if ("unload".equals(action)) {
-                       pluginName = httpRequest.getPartAsString("pluginName", 
MAX_PLUGIN_NAME_LENGTH);
-                       Plugin plugin = findPlugin(pluginName);
-                       pluginManager.removePlugin(plugin, true);
-                       writePermanentRedirect(ctx, l10n("pluginList"), 
"?action=list");
-                       return;
-               }
-               writeHTMLReply(ctx, 220, "OK", replyBuffer.toString());
-       }
-
-       /**
-        * Searches the currently installed plugins for the plugin with the
-        * specified internal name.
-        * 
-        * @param internalPluginName
-        *            The internal name of the wanted plugin
-        * @return The wanted plugin, or <code>null</code> if no plugin could be
-        *         found
-        */
-       private Plugin findPlugin(String internalPluginName) {
-               Plugin[] plugins = pluginManager.getPlugins();
-               for (int pluginIndex = 0, pluginCount = plugins.length; 
pluginIndex < pluginCount; pluginIndex++) {
-                       Plugin plugin = plugins[pluginIndex];
-                       String pluginName = plugin.getClass().getName() + '@' + 
pluginIndex;
-                       if (pluginName.equals(internalPluginName)) {
-                               return plugin;
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Creates a complete HTML page containing a list of all plugins.
-        * 
-        * @param context
-        *            The toadlet context
-        * @return A StringBuffer containing the HTML page
-        */
-       private String listPlugins(ToadletContext context) {
-               Plugin[] plugins = pluginManager.getPlugins();
-               PageMaker pageMaker = context.getPageMaker();
-               HTMLNode pageNode = pageMaker.getPageNode( 
l10n("pluginListTitle"), context);
-               HTMLNode contentNode = pageMaker.getContentNode(pageNode);
-
-               HTMLNode infobox = contentNode.addChild("div", "class", 
"infobox");
-               infobox.addChild("div", "class", "infobox-header", 
l10n("pluginList"));
-               HTMLNode table = infobox.addChild("div", "class", 
"infobox-content").addChild("table", "class", "plugintable");
-               HTMLNode headerRow = table.addChild("tr");
-               headerRow.addChild("th", l10n("pluginNameTitle"));
-               headerRow.addChild("th", l10n("internalNameTitle"));
-               headerRow.addChild("th", "colspan", "3");
-               for (int pluginIndex = 0, pluginCount = plugins.length; 
pluginIndex < pluginCount; pluginIndex++) {
-                       Plugin plugin = plugins[pluginIndex];
-                       String internalName = plugin.getClass().getName() + '@' 
+ pluginIndex;
-                       HTMLNode tableRow = table.addChild("tr");
-                       tableRow.addChild("td", plugin.getPluginName());
-                       tableRow.addChild("td", internalName);
-                       if (plugin instanceof HttpPlugin) {
-                               tableRow.addChild("td").addChild("form", new 
String[] { "action", "method", "target" }, new String[] { internalName, "get", 
"_new" }).addChild("div").addChild("input", new String[] { "type", "value" }, 
new String[] { "submit", l10n("visit") });
-                       } else {
-                               tableRow.addChild("td");
-                       }
-                       HTMLNode reloadForm = context.addFormChild(tableRow, 
".", "pluginReloadForm"); 
-                       reloadForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "hidden", "action", "reload" });
-                       reloadForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "hidden", "pluginName", internalName });
-                       reloadForm.addChild("input", new String[] { "type", 
"value" }, new String[] { "submit", "Reload" });
-                       HTMLNode unloadForm = context.addFormChild(tableRow, 
".", "pluginUnloadForm");
-                       unloadForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "hidden", "action", "unload" });
-                       unloadForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "hidden", "pluginName", internalName });
-                       unloadForm.addChild("input", new String[] { "type", 
"value" }, new String[] { "submit", "Unload" });
-               }
-
-               contentNode.addChild(createAddPluginBox(context));
-
-               return pageNode.generate();
-       }
-
-       /**
-        * Creates an alert box with the specified title and message. A link to 
the
-        * plugin list is added after the message.
-        * 
-        * @param context
-        *            The toadlet context
-        * @param title
-        *            The title of the box
-        * @param message
-        *            The content of the box
-        * @return A StringBuffer containing the complete page
-        */
-       private StringBuffer createBox(ToadletContext context, String title, 
String message) {
-               PageMaker pageMaker = context.getPageMaker();
-               HTMLNode pageNode = pageMaker.getPageNode(title, context);
-               HTMLNode contentNode = pageMaker.getContentNode(pageNode);
-               HTMLNode infobox = contentNode.addChild("div", "class", 
"infobox infobox-alert");
-               infobox.addChild("div", "class", "infobox-header", title);
-               HTMLNode infoboxContent = infobox.addChild("div", "class", 
"infobox-content");
-               infoboxContent.addChild("#", message);
-               infoboxContent.addChild("br");
-               L10n.addL10nSubstitution(infoboxContent, 
"PluginToadlet.returnToPluginsWithLinks", new String[] { "link", "/link" }, 
-                               new String[] { "<a href=\"?action=list\">", 
"</a>" });
-               StringBuffer pageBuffer = new StringBuffer();
-               pageNode.generate(pageBuffer);
-               return pageBuffer;
-       }
-
-       /**
-        * Appends the HTML code for the &ldquo;add plugin&rdquo; box to the 
given
-        * StringBuffer.
-        * 
-        * @param outputBuffer
-        *            The StringBuffer to append the HTML code to
-        */
-       private HTMLNode createAddPluginBox(ToadletContext ctx) {
-               HTMLNode addPluginBox = new HTMLNode("div", "class", "infobox");
-               addPluginBox.addChild("div", "class", "infobox-header", 
l10n("addPluginTitle"));
-               HTMLNode addForm = 
ctx.addFormChild(addPluginBox.addChild("div", "class", "infobox-content"), ".", 
"addPluginBox");
-               addForm.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "hidden", "action", "add" });
-               addForm.addChild("input", new String[] { "type", "name", 
"value", "size" }, new String[] { "text", "pluginName", "", "40" });
-               addForm.addChild("input", new String[] { "type", "value" }, new 
String[] { "submit", l10n("loadPluginCommand")});
-               return addPluginBox;
-       }
-
-}

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/QueueToadlet.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/clients/http/QueueToadlet.java 
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/clients/http/QueueToadlet.java 
2008-08-15 08:20:48 UTC (rev 21886)
@@ -54,7 +54,6 @@
 import freenet.support.io.BucketTools;
 import freenet.support.io.Closer;
 import freenet.support.io.FileBucket;
-import java.util.StringTokenizer;

 public class QueueToadlet extends Toadlet implements RequestCompletionCallback 
{

@@ -319,7 +318,7 @@
                                else
                                        fnam = null;
                                /* copy bucket data */
-                               Bucket copiedBucket = 
core.persistentEncryptedTempBucketFactory.makeBucket(file.getData().size());
+                               Bucket copiedBucket = 
core.persistentTempBucketFactory.makeBucket(file.getData().size());
                                BucketTools.copy(file.getData(), copiedBucket);
                                try {
                                        ClientPut clientPut = new 
ClientPut(fcp.getGlobalClient(), insertURI, identifier, Integer.MAX_VALUE, 
RequestStarter.BULK_SPLITFILE_PRIORITY_CLASS, ClientRequest.PERSIST_FOREVER, 
null, false, !compress, -1, ClientPutMessage.UPLOAD_FROM_DIRECT, null, 
file.getContentType(), copiedBucket, null, fnam, false);

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/SimpleToadletServer.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/SimpleToadletServer.java
  2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/SimpleToadletServer.java
  2008-08-15 08:20:48 UTC (rev 21886)
@@ -38,10 +38,10 @@
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.OOMHandler;
-import freenet.support.StringArray;
 import freenet.support.api.BooleanCallback;
 import freenet.support.api.BucketFactory;
 import freenet.support.api.IntCallback;
+import freenet.support.api.LongCallback;
 import freenet.support.api.StringCallback;
 import freenet.support.io.ArrayBucketFactory;

@@ -80,12 +80,12 @@
        static boolean isPanicButtonToBeShown;
        public static final int DEFAULT_FPROXY_PORT = 8888;

-       class FProxySSLCallback implements BooleanCallback {
+       class FProxySSLCallback extends BooleanCallback  {

-               public boolean get() {
+               public Boolean get() {
                        return ssl;
                }
-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(val == get()) return;
                        if(!SSL.available()) {
                                throw new InvalidConfigValueException("Enable 
SSL support before use ssl with Fproxy");
@@ -93,22 +93,42 @@
                        ssl = val;
                        throw new InvalidConfigValueException("Cannot change 
SSL on the fly, please restart freenet");
                }
+               @Override
+               public boolean isReadOnly() {
+                       return true;
+               }
        }
+       
+       class FProxyPassthruMaxSize extends LongCallback  {
+               
+               public Long get() {
+                       return FProxyToadlet.MAX_LENGTH;
+               }
+               
+               public void set(Long val) throws InvalidConfigValueException {
+                       if(val == get()) return;
+                       FProxyToadlet.MAX_LENGTH = val;
+               }
+       }

-       class FProxyPortCallback implements IntCallback {
+       class FProxyPortCallback extends IntCallback  {

-               public int get() {
+               public Integer get() {
                        return port;
                }

-               public void set(int newPort) throws InvalidConfigValueException 
{
+               public void set(Integer newPort) throws 
InvalidConfigValueException {
                        if(port != newPort)
                                throw new 
InvalidConfigValueException(L10n.getString("cannotChangePortOnTheFly"));
                        // FIXME
                }
+               @Override
+               public boolean isReadOnly() {
+                       return true;
+               }
        }

-       class FProxyBindtoCallback implements StringCallback {
+       class FProxyBindtoCallback extends StringCallback  {

                public String get() {
                        return bindTo;
@@ -129,7 +149,7 @@
                }
        }

-       class FProxyAllowedHostsCallback implements StringCallback {
+       class FProxyAllowedHostsCallback extends StringCallback  {

                public String get() {
                        return networkInterface.getAllowedHosts();
@@ -140,10 +160,9 @@
                                networkInterface.setAllowedHosts(allowedHosts);
                        }
                }
-               
        }

-       class FProxyCSSNameCallback implements StringCallback, 
EnumerableOptionCallback {
+       class FProxyCSSNameCallback extends StringCallback implements 
EnumerableOptionCallback {

                public String get() {
                        return cssName;
@@ -161,11 +180,11 @@
                }

                public String[] getPossibleValues() {
-                       return 
StringArray.toArray(pageMaker.getThemes().toArray());
+                       return pageMaker.getThemes().toArray(new String[0]);
                }
        }

-       class FProxyCSSOverrideCallback implements StringCallback {
+       class FProxyCSSOverrideCallback extends StringCallback  {

                public String get() {
                        return (cssOverride == null ? "" : 
cssOverride.toString());
@@ -187,14 +206,14 @@
                }
        }

-       class FProxyEnabledCallback implements BooleanCallback {
+       class FProxyEnabledCallback extends BooleanCallback  {

-               public boolean get() {
+               public Boolean get() {
                        synchronized(SimpleToadletServer.this) {
                                return myThread != null;
                        }
                }
-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(val == get()) return;
                        synchronized(SimpleToadletServer.this) {
                                if(val) {
@@ -229,7 +248,7 @@
                }
        }

-       private static class FProxyAdvancedModeEnabledCallback implements 
BooleanCallback {
+       private static class FProxyAdvancedModeEnabledCallback extends 
BooleanCallback  {

                private final SimpleToadletServer ts;

@@ -237,17 +256,17 @@
                        this.ts = ts;
                }

-               public boolean get() {
+               public Boolean get() {
                        return ts.isAdvancedModeEnabled();
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(val == get()) return;
                                ts.enableAdvancedMode(val);
                }
        }

-       private static class FProxyJavascriptEnabledCallback implements 
BooleanCallback {
+       private static class FProxyJavascriptEnabledCallback extends 
BooleanCallback  {

                private final SimpleToadletServer ts;

@@ -255,11 +274,11 @@
                        this.ts = ts;
                }

-               public boolean get() {
+               public Boolean get() {
                        return ts.isFProxyJavascriptEnabled();
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(val == get()) return;
                                ts.enableFProxyJavascript(val);
                }
@@ -340,11 +359,11 @@
                                new FProxyJavascriptEnabledCallback(this));
                fproxyConfig.register("showPanicButton", false, 
configItemOrder++, true, true, "SimpleToadletServer.panicButton", 
"SimpleToadletServer.panicButtonLong",
                                new BooleanCallback(){
-                               public boolean get(){
+                               public Boolean get() {
                                        return 
SimpleToadletServer.isPanicButtonToBeShown;
                                }

-                               public void set(boolean value){
+                               public void set(Boolean value) {
                                        if(value == 
SimpleToadletServer.isPanicButtonToBeShown) return;
                                        else    
SimpleToadletServer.isPanicButtonToBeShown = value;
                                }
@@ -358,13 +377,13 @@
                fproxyConfig.register("enablePersistentConnections", false, 
configItemOrder++, true, false, 
"SimpleToadletServer.enablePersistentConnections", 
"SimpleToadletServer.enablePersistentConnectionsLong",
                                new BooleanCallback() {

-                                       public boolean get() {
+                                       public Boolean get() {
                                                
synchronized(SimpleToadletServer.this) {
                                                        return 
enablePersistentConnections;
                                                }
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                
synchronized(SimpleToadletServer.this) {
                                                        
enablePersistentConnections = val;
                                                }
@@ -381,13 +400,13 @@
                fproxyConfig.register("enableInlinePrefetch", false, 
configItemOrder++, true, false, "SimpleToadletServer.enableInlinePrefetch", 
"SimpleToadletServer.enableInlinePrefetchLong",
                                new BooleanCallback() {

-                                       public boolean get() {
+                                       public Boolean get() {
                                                
synchronized(SimpleToadletServer.this) {
                                                        return 
enableInlinePrefetch;
                                                }
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                
synchronized(SimpleToadletServer.this) {
                                                        enableInlinePrefetch = 
val;
                                                }
@@ -395,6 +414,9 @@
                });
                enableInlinePrefetch = 
fproxyConfig.getBoolean("enableInlinePrefetch");

+               fproxyConfig.register("passthroughMaxSize", 2L*1024*1024, 
configItemOrder++, true, false, "SimpleToadletServer.passthroughMaxSize", 
"SimpleToadletServer.passthroughMaxSizeLong", new FProxyPassthruMaxSize());
+               FProxyToadlet.MAX_LENGTH = 
fproxyConfig.getLong("passthroughMaxSize");
+               
                fproxyConfig.register("allowedHosts", 
"127.0.0.1,0:0:0:0:0:0:0:1", configItemOrder++, true, true, 
"SimpleToadletServer.allowedHosts", "SimpleToadletServer.allowedHostsLong",
                                new FProxyAllowedHostsCallback());
                fproxyConfig.register("allowedHostsFullAccess", 
"127.0.0.1,0:0:0:0:0:0:0:1", configItemOrder++, true, true, 
"SimpleToadletServer.allowedFullAccess", 
@@ -413,10 +435,10 @@
                allowedFullAccess = new 
AllowedHosts(fproxyConfig.getString("allowedHostsFullAccess"));
                fproxyConfig.register("doRobots", false, configItemOrder++, 
true, false, "SimpleToadletServer.doRobots", "SimpleToadletServer.doRobotsLong",
                                new BooleanCallback() {
-                                       public boolean get() {
+                                       public Boolean get() {
                                                return doRobots;
                                        }
-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                doRobots = val;
                                        }
                });

Deleted: branches/saltedhashstore/freenet/src/freenet/clients/http/Spider.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/clients/http/Spider.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/clients/http/Spider.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,472 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.clients.http;
-
-import java.io.BufferedWriter;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import freenet.client.ClientMetadata;
-import freenet.client.FetchException;
-import freenet.client.FetchResult;
-import freenet.client.FetchContext;
-import freenet.client.InsertException;
-import freenet.client.async.BaseClientPutter;
-import freenet.client.async.ClientCallback;
-import freenet.client.async.ClientGetter;
-import freenet.clients.http.filter.ContentFilter;
-import freenet.clients.http.filter.FoundURICallback;
-import freenet.clients.http.filter.UnsafeContentTypeException;
-import freenet.keys.FreenetURI;
-import freenet.node.NodeClientCore;
-import freenet.node.RequestStarter;
-import freenet.oldplugins.plugin.HttpPlugin;
-import freenet.oldplugins.plugin.PluginManager;
-import freenet.support.HTMLNode;
-import freenet.support.Logger;
-import freenet.support.MultiValueTable;
-import freenet.support.api.Bucket;
-import freenet.support.api.HTTPRequest;
-import freenet.support.io.NullBucketFactory;
-
-/**
- * Spider. Produces an index.
- */
-public class Spider implements HttpPlugin, ClientCallback, FoundURICallback {
-
-       long tProducedIndex;
-
-       // URIs visited, or fetching, or queued. Added once then forgotten 
about.
-       private final HashSet visitedURIs = new HashSet();
-       private final HashSet urisWithWords = new HashSet();
-       private final HashSet failedURIs = new HashSet();
-       private final HashSet queuedURISet = new HashSet();
-       private final LinkedList queuedURIList = new LinkedList();
-       private final HashMap runningFetchesByURI = new HashMap();
-       private final HashMap urisByWord = new HashMap();
-       private final HashMap titlesOfURIs = new HashMap();
-
-       private static final int minTimeBetweenEachIndexRewriting = 10;
-
-       // Can have many; this limit only exists to save memory.
-       private static final int maxParallelRequests = 20;
-       private int maxShownURIs = 50;
-
-       private NodeClientCore core;
-       private FetchContext ctx;
-       private final short PRIORITY_CLASS = 
RequestStarter.PREFETCH_PRIORITY_CLASS;
-       private boolean stopped = true;
-
-       private synchronized void queueURI(FreenetURI uri) {
-               if ((!visitedURIs.contains(uri)) && queuedURISet.add(uri)) {
-                       queuedURIList.addLast(uri);
-                       visitedURIs.add(uri);
-               }
-       }
-
-       private void startSomeRequests() {
-               try{
-                       Thread.sleep(30 * 1000); // Let the node start up
-               } catch (InterruptedException e){}
-               
-               FreenetURI[] initialURIs = core.getBookmarkURIs();
-               for (int i = 0; i < initialURIs.length; i++)
-                       queueURI(initialURIs[i]);
-               
-               ArrayList toStart = null;
-               synchronized (this) {
-                       if (stopped) {
-                               return;
-                       }
-                       int running = runningFetchesByURI.size();
-                       int queued = queuedURIList.size();
-                       
-                       if ((running >= maxParallelRequests) || (queued == 0))
-                               return;
-                       
-                       toStart = new ArrayList(Math.min(maxParallelRequests - 
running, queued));
-                       
-                       for (int i = running; i < maxParallelRequests; i++) {
-                               if (queuedURIList.isEmpty())
-                                       break;
-                               FreenetURI uri = (FreenetURI) 
queuedURIList.removeFirst();
-                               queuedURISet.remove(uri);
-                               ClientGetter getter = makeGetter(uri);
-                               toStart.add(getter);
-                       }
-                       for (int i = 0; i < toStart.size(); i++) {
-                               ClientGetter g = (ClientGetter) toStart.get(i);
-                               try {
-                                       runningFetchesByURI.put(g.getURI(), g);
-                                       g.start();
-                               } catch (FetchException e) {
-                                       onFailure(e, g);
-                               }
-                       }
-               }
-       }
-
-       private ClientGetter makeGetter(FreenetURI uri) {
-               ClientGetter g = new ClientGetter(this, 
core.requestStarters.chkFetchScheduler, core.requestStarters.sskFetchScheduler, 
uri, ctx, PRIORITY_CLASS, this, null, null);
-               return g;
-       }
-
-       public void onSuccess(FetchResult result, ClientGetter state) {
-               FreenetURI uri = state.getURI();
-               synchronized (this) {
-                       runningFetchesByURI.remove(uri);
-               }
-               startSomeRequests();
-               ClientMetadata cm = result.getMetadata();
-               Bucket data = result.asBucket();
-               String mimeType = cm.getMIMEType();
-               try {
-                       ContentFilter.filter(data, new NullBucketFactory(), 
mimeType, uri.toURI("http://127.0.0.1:8888/";), this);
-               } catch (UnsafeContentTypeException e) {
-                       return; // Ignore
-               } catch (IOException e) {
-                       Logger.error(this, "Bucket error?: " + e, e);
-               } catch (URISyntaxException e) {
-                       Logger.error(this, "Internal error: " + e, e);
-               } finally {
-                       data.free();
-               }
-       }
-
-       public void onFailure(FetchException e, ClientGetter state) {
-               FreenetURI uri = state.getURI();
-               synchronized (this) {
-                       failedURIs.add(uri);
-                       runningFetchesByURI.remove(uri);
-               }
-               if (e.newURI != null)
-                       queueURI(e.newURI);
-               else
-                       queueURI(uri);
-               startSomeRequests();
-       }
-
-       public void onSuccess(BaseClientPutter state) {
-               // Ignore
-       }
-
-       public void onFailure(InsertException e, BaseClientPutter state) {
-               // Ignore
-       }
-
-       public void onGeneratedURI(FreenetURI uri, BaseClientPutter state) {
-               // Ignore
-       }
-
-       public void foundURI(FreenetURI uri) {
-               queueURI(uri);
-               startSomeRequests();
-       }
-       
-       public void foundURI(FreenetURI uri, boolean inline) {
-               queueURI(uri);
-               startSomeRequests();
-       }
-
-       public void onText(String s, String type, URI baseURI) {
-
-               FreenetURI uri;
-               try {
-                       uri = new FreenetURI(baseURI.getPath());
-               } catch (MalformedURLException e) {
-                       Logger.error(this, "Caught " + e, e);
-                       return;
-               }
-
-               if((type != null) && (type.length() != 0) && 
type.toLowerCase().equals("title")
-                  && (s != null) && (s.length() != 0) && (s.indexOf('\n') < 
0)) {
-                       /* We should have a correct title */
-                       titlesOfURIs.put(uri.toString(), s);
-               }
-
-
-               String[] words = s.split("[^A-Za-z0-9]");
-               for (int i = 0; i < words.length; i++) {
-                       String word = words[i];
-                       if ((word == null) || (word.length() == 0))
-                               continue;
-                       word = word.toLowerCase();
-                       addWord(word, uri);
-               }
-       }
-
-       private synchronized void addWord(String word, FreenetURI uri) {
-               FreenetURI[] uris = (FreenetURI[]) urisByWord.get(word);
-               urisWithWords.add(uri);
-               if (uris == null) {
-                       urisByWord.put(word, new FreenetURI[] { uri });
-               } else {
-                       for (int i = 0; i < uris.length; i++) {
-                               if (uris[i].equals(uri))
-                                       return;
-                       }
-                       FreenetURI[] newURIs = new FreenetURI[uris.length + 1];
-                       System.arraycopy(uris, 0, newURIs, 0, uris.length);
-                       newURIs[uris.length] = uri;
-                       urisByWord.put(word, newURIs);
-               }
-               if (tProducedIndex + minTimeBetweenEachIndexRewriting * 1000 < 
System.currentTimeMillis()) {
-                       try {
-                               produceIndex();
-                       } catch (IOException e) {
-                               Logger.error(this, "Caught " + e + " while 
creating index", e);
-                       }
-                       tProducedIndex = System.currentTimeMillis();
-               }
-       }
-
-       private synchronized void produceIndex() throws IOException {
-               // Produce an index file.
-               FileOutputStream fos = new FileOutputStream("index.new");
-               OutputStreamWriter osw;
-               try {
-                       osw = new OutputStreamWriter(fos, "UTF-8");
-               } catch (UnsupportedEncodingException e) {
-                       throw new Error("Impossible: JVM doesn't support UTF-8: 
" + e, e);
-               }
-               
-               if (urisByWord.isEmpty() || urisWithWords.isEmpty()) {
-                       System.out.println("No URIs with words");
-                       return;
-               }
-               BufferedWriter bw = new BufferedWriter(osw);
-               String[] words = (String[]) urisByWord.keySet().toArray(new 
String[urisByWord.size()]);
-               Arrays.sort(words);
-               FreenetURI[] uris = (FreenetURI[]) urisWithWords.toArray(new 
FreenetURI[urisWithWords.size()]);
-               HashMap urisToNumbers = new HashMap();
-               for (int i = 0; i < uris.length; i++) {
-                       urisToNumbers.put(uris[i], new Integer(i));
-                       bw.write('!' + uris[i].toString() + '\n');
-                       bw.write("+" + titlesOfURIs.get(uris[i].toString()) + 
'\n');
-               }
-               for (int i = 0; i < words.length; i++) {
-                       StringBuffer s = new StringBuffer();
-                       s.append('?');
-                       s.append(words[i]);
-                       FreenetURI[] urisForWord = (FreenetURI[]) 
urisByWord.get(words[i]);
-                       for (int j = 0; j < urisForWord.length; j++) {
-                               FreenetURI uri = urisForWord[j];
-                               Integer x = (Integer) urisToNumbers.get(uri);
-                               if (x == null)
-                                       Logger.error(this, "Eh?");
-                               else {
-                                       s.append(' ');
-                                       s.append(x.toString());
-                               }
-                       }
-                       s.append('\n');
-                       bw.write(s.toString());
-               }
-               bw.close();
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.HttpPlugin#handleGet(freenet.clients.http.HTTPRequestImpl,
 freenet.clients.http.ToadletContext)
-        */
-       public void handleGet(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException {
-               String action = request.getParam("action");
-               PageMaker pageMaker = context.getPageMaker();
-               if ((action == null) || (action.length() == 0)) {
-                       MultiValueTable responseHeaders = new MultiValueTable();
-                       responseHeaders.put("Location", "?action=list");
-                       context.sendReplyHeaders(301, "Redirect", 
responseHeaders, "text/html; charset=utf-8", 0);
-                       return;
-               } else if ("list".equals(action)) {
-                       String listName = request.getParam("listName", null);
-                       HTMLNode pageNode = pageMaker.getPageNode("The 
Definitive Spider", context);
-                       HTMLNode contentNode = 
pageMaker.getContentNode(pageNode);
-                       /* create copies for multi-threaded use */
-                       if (listName == null) {
-                               Map runningFetches = new 
HashMap(runningFetchesByURI);
-                               List queued = new ArrayList(queuedURIList);
-                               Set visited = new HashSet(visitedURIs);
-                               Set failed = new HashSet(failedURIs);
-                               contentNode.addChild(createNavbar(pageMaker, 
runningFetches.size(), queued.size(), visited.size(), failed.size()));
-                               contentNode.addChild(createAddBox(pageMaker, 
context));
-                               contentNode.addChild(createList(pageMaker, 
"Running Fetches", "running", runningFetches.keySet(), maxShownURIs));
-                               contentNode.addChild(createList(pageMaker, 
"Queued URIs", "queued", queued, maxShownURIs));
-                               contentNode.addChild(createList(pageMaker, 
"Visited URIs", "visited", visited, maxShownURIs));
-                               contentNode.addChild(createList(pageMaker, 
"Failed URIs", "failed", failed, maxShownURIs));
-                       } else {
-                               contentNode.addChild(createBackBox(pageMaker));
-                               if ("failed".equals(listName)) {
-                                       Set failed = new HashSet(failedURIs);
-                                       
contentNode.addChild(createList(pageMaker, "Failed URIs", "failed", failed, 
-1));       
-                               } else if ("visited".equals(listName)) {
-                                       Set visited = new HashSet(visitedURIs);
-                                       
contentNode.addChild(createList(pageMaker, "Visited URIs", "visited", visited, 
-1));
-                               } else if ("queued".equals(listName)) {
-                                       List queued = new 
ArrayList(queuedURIList);
-                                       
contentNode.addChild(createList(pageMaker, "Queued URIs", "queued", queued, 
-1));
-                               } else if ("running".equals(listName)) {
-                                       Map runningFetches = new 
HashMap(runningFetchesByURI);
-                                       
contentNode.addChild(createList(pageMaker, "Running Fetches", "running", 
runningFetches.keySet(), -1));
-                               }
-                       }
-                       MultiValueTable responseHeaders = new MultiValueTable();
-                       byte[] responseBytes = 
pageNode.generate().getBytes("utf-8");
-                       context.sendReplyHeaders(200, "OK", responseHeaders, 
"text/html; charset=utf-8", responseBytes.length);
-                       context.writeData(responseBytes);
-               } else if ("add".equals(action)) {
-                       String uriParam = request.getParam("key");
-                       try {
-                               FreenetURI uri = new FreenetURI(uriParam);
-                               synchronized (this) {
-                                       failedURIs.remove(uri);
-                                       visitedURIs.remove(uri);
-                               }
-                               queueURI(uri);
-                               startSomeRequests();
-                       } catch (MalformedURLException mue1) {
-                               sendSimpleResponse(context, "URL invalid", "The 
given URI is not valid.");
-                               return;
-                       }
-                       MultiValueTable responseHeaders = new MultiValueTable();
-                       responseHeaders.put("Location", "?action=list");
-                       context.sendReplyHeaders(301, "Redirect", 
responseHeaders, "text/html; charset=utf-8", 0);
-                       return;
-               }
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.HttpPlugin#handlePost(freenet.clients.http.HTTPRequestImpl,
 freenet.clients.http.ToadletContext)
-        */
-       public void handlePost(HTTPRequest request, ToadletContext context) 
throws IOException {
-       }
-       
-       private void sendSimpleResponse(ToadletContext context, String title, 
String message) throws ToadletContextClosedException, IOException {
-               PageMaker pageMaker = context.getPageMaker();
-               HTMLNode pageNode = pageMaker.getPageNode(title, context);
-               HTMLNode contentNode = pageMaker.getContentNode(pageNode);
-               HTMLNode infobox = 
contentNode.addChild(pageMaker.getInfobox("infobox-alter", title));
-               HTMLNode infoboxContent = pageMaker.getContentNode(infobox);
-               infoboxContent.addChild("#", message);
-               byte[] responseBytes = pageNode.generate().getBytes("utf-8");
-               context.sendReplyHeaders(200, "OK", new MultiValueTable(), 
"text/html; charset=utf-8", responseBytes.length);
-               context.writeData(responseBytes);
-       }
-       
-       private HTMLNode createBackBox(PageMaker pageMaker) {
-               HTMLNode backbox = pageMaker.getInfobox((String) null);
-               HTMLNode backContent = pageMaker.getContentNode(backbox);
-               backContent.addChild("#", "Return to the ");
-               backContent.addChild("a", "href", "?action=list", "list of all 
URIs");
-               backContent.addChild("#", ".");
-               return backbox;
-       }
-       
-       private HTMLNode createAddBox(PageMaker pageMaker, ToadletContext ctx) {
-               HTMLNode addBox = pageMaker.getInfobox("Add a URI");
-               HTMLNode formNode = 
pageMaker.getContentNode(addBox).addChild("form", new String[] { "action", 
"method" }, new String[] { "", "get" });
-               formNode.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "hidden", "action", "add" });
-               formNode.addChild("input", new String[] { "type", "size", 
"name", "value" }, new String[] { "text", "40", "key", "" });
-               formNode.addChild("input", new String[] { "type", "value" }, 
new String[] { "submit", "Add URI" });
-               return addBox;
-       }
-
-       private HTMLNode createNavbar(PageMaker pageMaker, int running, int 
queued, int visited, int failed) {
-               HTMLNode navbar = pageMaker.getInfobox("navbar", "Page 
Navigation");
-               HTMLNode list = pageMaker.getContentNode(navbar).addChild("ul");
-               list.addChild("li").addChild("a", "href", "#running", "Running 
(" + running + ')');
-               list.addChild("li").addChild("a", "href", "#queued", "Queued (" 
+ queued + ')');
-               list.addChild("li").addChild("a", "href", "#visited", "Visited 
(" + visited + ')');
-               list.addChild("li").addChild("a", "href", "#failed", "Failed (" 
+ failed + ')');
-               return navbar;
-       }
-
-       private HTMLNode createList(PageMaker pageMaker, String listName, 
String anchorName, Collection collection, int maxCount) {
-               HTMLNode listNode = new HTMLNode("div");
-               listNode.addChild("a", "name", anchorName);
-               HTMLNode listBox = pageMaker.getInfobox(listName);
-               HTMLNode listContent = pageMaker.getContentNode(listBox);
-               listNode.addChild(listBox);
-               Iterator collectionItems = collection.iterator();
-               int itemCount = 0;
-               while (collectionItems.hasNext()) {
-                       FreenetURI uri = (FreenetURI) collectionItems.next();
-                       listContent.addChild("#", uri.toString());
-                       listContent.addChild("br");
-                       if (itemCount++ == maxCount) {
-                               listContent.addChild("br");
-                               listContent.addChild("a", "href", 
"?action=list&listName=" + anchorName, "Show all\u2026");
-                               break;
-                       }
-               }
-               return listNode;
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#getPluginName()
-        */
-       public String getPluginName() {
-               return "The Definitive Spider";
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.Plugin#setPluginManager(freenet.oldplugins.plugin.PluginManager)
-        */
-       public void setPluginManager(PluginManager pluginManager) {
-               this.core = pluginManager.getClientCore();
-               this.ctx = core.makeClient((short) 0).getFetchContext();
-               ctx.maxSplitfileBlockRetries = 10;
-               ctx.maxNonSplitfileRetries = 10;
-               ctx.maxTempLength = 2 * 1024 * 1024;
-               ctx.maxOutputLength = 2 * 1024 * 1024;
-               tProducedIndex = System.currentTimeMillis();
-       }
-
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#startPlugin()
-        */
-       public void startPlugin() {
-               stopped = false;
-               Thread starterThread = new Thread("Spider Plugin Starter") {
-                       public void run() {
-                               startSomeRequests();
-                       }
-               };
-               starterThread.setDaemon(true);
-               starterThread.start();
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#stopPlugin()
-        */
-       public void stopPlugin() {
-               synchronized (this) {
-                       stopped = true;
-                       queuedURIList.clear();
-               }
-       }
-
-       public void onMajorProgress() {
-               // Ignore
-       }
-
-       public void onFetchable(BaseClientPutter state) {
-               // Ignore
-       }
-
-}

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/StatisticsToadlet.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/StatisticsToadlet.java
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/StatisticsToadlet.java
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -345,25 +345,15 @@
                        int[] outgoingLocalRequestCountArray = new int[1];
                        int[] outgoingLocalRequestLocation = 
stats.getOutgoingLocalRequestLocation(outgoingLocalRequestCountArray);
                        int outgoingLocalRequestsCount = 
outgoingLocalRequestCountArray[0];
-                       
-                       if(outgoingLocalRequestsCount > 0) {
-                               HTMLNode nodeSpecialisationInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                               nodeSpecialisationInfobox.addChild("div", 
"class", "infobox-header", 
"Outgoing\u00a0Local\u00a0Request\u00a0Distribution");
-                               HTMLNode nodeSpecialisationTable = 
nodeSpecialisationInfobox.addChild("div", "class", 
"infobox-content").addChild("table");
-                               addSpecialisation(nodeSpecialisationTable, 
myLocation, outgoingLocalRequestsCount, outgoingLocalRequestLocation);
-                       }
-                       
-                       overviewTableRow = overviewTable.addChild("tr");
-                       nextTableCell = overviewTableRow.addChild("td", 
"class", "first");
                        int[] outgoingRequestCountArray = new int[1];
                        int[] outgoingRequestLocation = 
stats.getOutgoingRequestLocation(outgoingRequestCountArray);
                        int outgoingRequestsCount = 
outgoingRequestCountArray[0];

-                       if(outgoingRequestsCount > 0) {
+                       if(outgoingLocalRequestsCount > 0 && 
outgoingRequestsCount > 0) {
                                HTMLNode nodeSpecialisationInfobox = 
nextTableCell.addChild("div", "class", "infobox");
                                nodeSpecialisationInfobox.addChild("div", 
"class", "infobox-header", "Outgoing\u00a0Request\u00a0Distribution");
                                HTMLNode nodeSpecialisationTable = 
nodeSpecialisationInfobox.addChild("div", "class", 
"infobox-content").addChild("table");
-                               addSpecialisation(nodeSpecialisationTable, 
myLocation, outgoingRequestsCount, outgoingRequestLocation);
+                               
addCombinedSpecialisation(nodeSpecialisationTable, myLocation, 
outgoingLocalRequestsCount, outgoingLocalRequestLocation, 
outgoingRequestsCount, outgoingRequestLocation);
                        }
                }

@@ -1010,6 +1000,7 @@
                overviewList.addChild("li", "backedOffPercent:\u00a0" + 
fix3p1pct.format(backedOffPercent));
                overviewList.addChild("li", "pInstantReject:\u00a0" + 
fix3p1pct.format(stats.pRejectIncomingInstantly()));
                overviewList.addChild("li", "unclaimedFIFOSize:\u00a0" + 
node.getUnclaimedFIFOSize());
+               overviewList.addChild("li", "RAMBucketPoolSize:\u00a0" + 
SizeUtil.formatSize(core.tempBucketFactory.getRamUsed())+ " / "+ 
SizeUtil.formatSize(core.tempBucketFactory.getMaxRamUsed()));
                overviewList.addChild("li", "uptimeAverage:\u00a0" + 
fix3p1pct.format(node.uptime.getUptime()));

        }
@@ -1155,8 +1146,34 @@
                        nodeHistogramGraphCell2.addChild("#", 
fix1p1.format(((double) i) / incomingRequestLocation.length ));
                        nodeHistogramGraphCell.addChild("div", new String[] { 
"class", "style" }, new String[] { "histogramConnected", "height: " + 
fix3pctUS.format(((double)incomingRequestLocation[i]) / incomingRequestsCount) 
+ "; width: 100%;" }, "\u00a0");
                }
-
        }
+       
+       private void addCombinedSpecialisation(HTMLNode table, double 
peerLocation, int locallyOriginatingRequestsCount, int[] 
locallyOriginatingRequests, int remotelyOriginatingRequestsCount, int[] 
remotelyOriginatingRequests) {
+               assert(locallyOriginatingRequests.length == 
remotelyOriginatingRequests.length);
+               HTMLNode nodeHistogramLegendTableRow = table.addChild("tr");
+               HTMLNode nodeHistogramGraphTableRow = table.addChild("tr");
+               int myIndex = (int)(peerLocation * 
locallyOriginatingRequests.length);
+               for (int i = 0; i<locallyOriginatingRequests.length; i++) {
+                       HTMLNode nodeHistogramLegendCell = 
nodeHistogramLegendTableRow.addChild("td");
+                       HTMLNode nodeHistogramGraphCell = 
nodeHistogramGraphTableRow.addChild("td", "style", "height: 100px;");
+                       HTMLNode nodeHistogramGraphCell2 = 
nodeHistogramLegendCell.addChild("div", "class", "histogramLabel");
+                       if(i == myIndex)
+                                nodeHistogramGraphCell2 = 
nodeHistogramGraphCell2.addChild("span", "class", "me");
+                       nodeHistogramGraphCell2.addChild("#", 
fix1p1.format(((double) i) / locallyOriginatingRequests.length ));
+                       nodeHistogramGraphCell.addChild("div",
+                               new String[] { "class", "style" },
+                               new String[] { "histogramConnected", "height: " 
+
+                                       
fix3pctUS.format(((double)locallyOriginatingRequests[i]) / 
locallyOriginatingRequestsCount) +
+                                       "; width: 100%;" },
+                               "\u00a0");
+                       nodeHistogramGraphCell.addChild("div",
+                               new String[] { "class", "style" },
+                               new String[] { "histogramDisconnected", 
"height: " +
+                                       
fix3pctUS.format(((double)remotelyOriginatingRequests[i]) / 
remotelyOriginatingRequestsCount) +
+                                       "; width: 100%;" },
+                               "\u00a0");
+               }
+       }

        private void addPeerCircle (HTMLNode circleTable, PeerNodeStatus[] 
peerNodeStatuses, double myLocation) {
                int[] histogramConnected = new int[HISTOGRAM_LENGTH];

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/SymlinkerToadlet.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/SymlinkerToadlet.java 
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/SymlinkerToadlet.java 
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -35,6 +35,10 @@
                                // FIXME
                                throw new InvalidConfigValueException("Cannot 
set the plugins that's loaded.");
                        }
+
+                               public boolean isReadOnly() {
+                                       return true;
+                               }
                });

                String fns[] = tslconfig.getStringArr("symlinks");

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/bookmark/BookmarkCategory.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/bookmark/BookmarkCategory.java
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/bookmark/BookmarkCategory.java
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,8 +4,6 @@
 import freenet.support.SimpleFieldSet;
 import java.util.Vector;

-import freenet.support.StringArray;
-
 public class BookmarkCategory extends Bookmark {
     public static final String NAME = "BookmarkCategory";

@@ -112,13 +110,13 @@
     }

     public String[] toStrings() {
-        return StringArray.toArray(toStrings("").toArray());
+        return toStrings("").toArray(new String[0]);
     }

     // Iternal use only

-    private Vector toStrings(String prefix) {
-        Vector strings = new Vector();
+    private Vector<String> toStrings(String prefix) {
+        Vector<String> strings = new Vector<String>();
         BookmarkItems items = getItems();
         BookmarkCategories subCategories = getSubCategories();
         prefix += this.name + "/";

Modified: 
branches/saltedhashstore/freenet/src/freenet/clients/http/staticfiles/themes/clean/theme.css
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/clients/http/staticfiles/themes/clean/theme.css
        2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/clients/http/staticfiles/themes/clean/theme.css
        2008-08-15 08:20:48 UTC (rev 21886)
@@ -378,6 +378,10 @@
        text-align: center;
 }

+tr.darknet_connections_warning {
+       background-color: #ffff00;
+}
+
 table.sentmessagetypes {
        display: inline;
 }

Modified: branches/saltedhashstore/freenet/src/freenet/config/BooleanOption.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/BooleanOption.java      
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/BooleanOption.java      
2008-08-15 08:20:48 UTC (rev 21886)
@@ -6,65 +6,25 @@
 import freenet.l10n.L10n;
 import freenet.support.api.BooleanCallback;

-public class BooleanOption extends Option {
-       
-       final boolean defaultValue;
-       final BooleanCallback cb;
-       private boolean currentValue;
-       
+public class BooleanOption extends Option<Boolean> {
        public BooleanOption(SubConfig conf, String optionName, boolean 
defaultValue, int sortOrder, 
                        boolean expert, boolean forceWrite, String shortDesc, 
String longDesc, BooleanCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_BOOLEAN);
+               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DataType.BOOLEAN);
                this.defaultValue = defaultValue;
-               this.cb = cb;
                this.currentValue = defaultValue;
        }

-       /** Get the current value. This is the value in use if we have finished
-        * initialization, otherwise it is the value set at startup (possibly 
the default). */
-       public boolean getValue() {
-               if(config.hasFinishedInitialization())
-                       return currentValue = cb.get();
-               else return currentValue;
-       }
-
-       public void setValue(String val) throws InvalidConfigValueException {
+       public Boolean parseString(String val) throws 
InvalidConfigValueException {
                if(val.equalsIgnoreCase("true") || val.equalsIgnoreCase("yes")) 
{
-                       set(true);
+                       return true;
                } else if(val.equalsIgnoreCase("false") || 
val.equalsIgnoreCase("no")) {
-                       set(false);
+                       return false;
                } else
                        throw new 
OptionFormatException(L10n.getString("BooleanOption.parseError", "val", val));
        }
-       
-       public void set(boolean b) throws InvalidConfigValueException {
-               cb.set(b);
-               currentValue = b;
-       }
-       
-       public String getValueString() {
-               return Boolean.toString(getValue());
-       }

-       public void setInitialValue(String val) throws 
InvalidConfigValueException {
-               if(val.equalsIgnoreCase("true") || val.equalsIgnoreCase("yes")) 
{
-                       currentValue = true;
-               } else if(val.equalsIgnoreCase("false") || 
val.equalsIgnoreCase("no")) {
-                       currentValue = false;
-               } else
-                       throw new 
OptionFormatException(L10n.getString("BooleanOption.parseError", "val", val));
+       @Override
+       protected String toString(Boolean val) {
+               return val.toString();
        }
-
-       public boolean isDefault() {
-               getValue();
-               return currentValue == defaultValue;
-       }
-       
-       public String getDefault() {
-               return Boolean.toString(defaultValue);
-       }
-
-       public void setDefault() {
-               currentValue = defaultValue;
-       }
 }

Modified: branches/saltedhashstore/freenet/src/freenet/config/Config.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/Config.java     
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/Config.java     
2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,26 +3,21 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.config;

+import java.io.IOException;
 import java.util.LinkedHashMap;

 /** Global configuration object for a node. SubConfig's register here.
  * Handles writing to a file etc.
  */
 public class Config {
+       public static enum RequestType {
+               CURRENT_SETTINGS, DEFAULT_SETTINGS, SORT_ORDER, EXPERT_FLAG, 
FORCE_WRITE_FLAG, SHORT_DESCRIPTION, LONG_DESCRIPTION, DATA_TYPE
+       };

-    public static final int CONFIG_REQUEST_TYPE_CURRENT_SETTINGS = 1;
-    public static final int CONFIG_REQUEST_TYPE_DEFAULT_SETTINGS = 2;
-    public static final int CONFIG_REQUEST_TYPE_SORT_ORDER = 3;
-    public static final int CONFIG_REQUEST_TYPE_EXPERT_FLAG = 4;
-    public static final int CONFIG_REQUEST_TYPE_FORCE_WRITE_FLAG = 5;
-    public static final int CONFIG_REQUEST_TYPE_SHORT_DESCRIPTION = 6;
-    public static final int CONFIG_REQUEST_TYPE_LONG_DESCRIPTION = 7;
-    public static final int CONFIG_REQUEST_TYPE_DATA_TYPE = 8;
-
-       protected final LinkedHashMap configsByPrefix;
+       protected final LinkedHashMap<String, SubConfig> configsByPrefix;

        public Config() {
-               configsByPrefix = new LinkedHashMap();
+               configsByPrefix = new LinkedHashMap<String, SubConfig>();
        }

        public void register(SubConfig sc) {
@@ -50,10 +45,10 @@

        /** Fetch all the SubConfig's. Used by user-facing config thingies. */
        public synchronized SubConfig[] getConfigs() {
-               return (SubConfig[]) configsByPrefix.values().toArray(new 
SubConfig[configsByPrefix.size()]);
+               return configsByPrefix.values().toArray(new 
SubConfig[configsByPrefix.size()]);
        }

        public synchronized SubConfig get(String subConfig){
-               return (SubConfig)configsByPrefix.get(subConfig);
+               return configsByPrefix.get(subConfig);
        }
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/config/ConfigCallback.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/ConfigCallback.java     
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/ConfigCallback.java     
2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,6 +3,23 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.config;

-public interface ConfigCallback {
-       /** Nothing usefull here */
+public abstract class ConfigCallback<T> {
+       /**
+        * Get the current, used value of the config variable.
+        */
+       public abstract T get();
+
+       /**
+        * Set the config variable to a new value.
+        * 
+        * @param val
+        *            The new value.
+        * @throws InvalidConfigOptionException
+        *             If the new value is invalid for this particular option.
+        */
+       public abstract void set(T val) throws InvalidConfigValueException, 
NodeNeedRestartException;
+       
+       public boolean isReadOnly() {
+               return false;
+       } 
 }

Added: branches/saltedhashstore/freenet/src/freenet/config/ConfigException.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/ConfigException.java    
                        (rev 0)
+++ branches/saltedhashstore/freenet/src/freenet/config/ConfigException.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -0,0 +1,15 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.config;
+
+/**
+ * Usefull if you want to catch all exceptions the config framework can return;
+ */
+public abstract class ConfigException extends Exception {
+       private static final long serialVersionUID = -1;
+       
+       public ConfigException(String msg) {
+               super(msg);
+       }
+}
\ No newline at end of file

Modified: branches/saltedhashstore/freenet/src/freenet/config/IntOption.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/IntOption.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/IntOption.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -8,89 +8,37 @@
 import freenet.support.api.IntCallback;

 /** Integer config variable */
-public class IntOption extends Option {
-
-       final int defaultValue;
-       final IntCallback cb;
-       private int currentValue;
-       // Cache it mostly so that we can keep SI units
-       private String cachedStringValue;
+public class IntOption extends Option<Integer> {
+       public IntOption(SubConfig conf, String optionName, String 
defaultValueString, int sortOrder, boolean expert,
+               boolean forceWrite, String shortDesc, String longDesc, 
IntCallback cb) {
+               this(conf, optionName, Fields.parseInt(defaultValueString), 
sortOrder, expert, forceWrite, shortDesc, longDesc,
+                       cb);
+       }

-       public IntOption(SubConfig conf, String optionName, int defaultValue, 
String defaultValueString,
-                       int sortOrder, boolean expert, boolean forceWrite, 
String shortDesc, String longDesc, IntCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_NUMBER);
+       public IntOption(SubConfig conf, String optionName, Integer 
defaultValue, int sortOrder, boolean expert,
+               boolean forceWrite, String shortDesc, String longDesc, 
IntCallback cb) {
+               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DataType.NUMBER);
                this.defaultValue = defaultValue;
-               this.cb = cb;
                this.currentValue = defaultValue;
-               this.cachedStringValue = defaultValueString;
        }

-       public IntOption(SubConfig conf, String optionName, String 
defaultValueString,
-                       int sortOrder, boolean expert, boolean forceWrite, 
String shortDesc, String longDesc, IntCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_NUMBER);
-               this.defaultValue = Fields.parseSIInt(defaultValueString);
-               this.cb = cb;
-               this.currentValue = defaultValue;
-               this.cachedStringValue = defaultValueString;
-       }
-
-       /** Get the current value. This is the value in use if we have finished
-        * initialization, otherwise it is the value set at startup (possibly 
the default). */
-       public int getValue() {
-               if(config.hasFinishedInitialization()) {
-                       int val = cb.get();
-                       if(currentValue != val) {
-                               currentValue = val;
-                               cachedStringValue = null;
-                       }
-               }
-               return currentValue;
-       }
-
-       public void setValue(String val) throws InvalidConfigValueException {
-               int x;
-               try{
-                       x = Fields.parseSIInt(val);
+       @Override
+       protected Integer parseString(String val) throws 
InvalidConfigValueException {
+               Integer x;
+               try {
+                       x = Fields.parseInt(val);
                } catch (NumberFormatException e) {
                        throw new 
InvalidConfigValueException(l10n("parseError", "val", val));
                }
-               cb.set(x);
-               cachedStringValue = val;
-               currentValue = x;
+               return x;
        }
-       
-       public void setInitialValue(String val) throws 
InvalidConfigValueException {
-               int x;
-               try{
-                       x = Fields.parseSIInt(val);
-               } catch (NumberFormatException e) {
-                       throw new 
InvalidConfigValueException(l10n("parseError", "val", val));
-               }
-               cachedStringValue = val;
-               currentValue = x;
-       }

        private String l10n(String key, String pattern, String value) {
-               return L10n.getString("IntOption."+key, pattern, value);
+               return L10n.getString("IntOption." + key, pattern, value);
        }

-       public String getValueString() {
-               int val = getValue();
-               if(cachedStringValue != null) return cachedStringValue;
-               return Integer.toString(val);
+       @Override
+       protected String toString(Integer val) {
+               return Fields.intToString(val);
        }
-       
-       public String getDefault(){
-               return Integer.toString(defaultValue);
-       }
-
-       public boolean isDefault() {
-               getValue();
-               return currentValue == defaultValue;
-       }
-       
-       public void setDefault() {
-               currentValue = defaultValue;
-       }
-       
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/config/InvalidConfigValueException.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/config/InvalidConfigValueException.java
        2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/config/InvalidConfigValueException.java
        2008-08-15 08:20:48 UTC (rev 21886)
@@ -10,11 +10,9 @@
  * it merely means that there are no immediately detectable problems with 
  * it.
  */
-public class InvalidConfigValueException extends Exception {
-       private static final long serialVersionUID = -1;
+public class InvalidConfigValueException extends ConfigException {

        public InvalidConfigValueException(String msg) {
                super(msg);
        }
-
 }

Modified: branches/saltedhashstore/freenet/src/freenet/config/LongOption.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/LongOption.java 
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/LongOption.java 
2008-08-15 08:20:48 UTC (rev 21886)
@@ -8,91 +8,37 @@
 import freenet.support.api.LongCallback;

 /** Long config variable */
-public class LongOption extends Option {
-
-       final long defaultValue;
-       final LongCallback cb;
-       private long currentValue;
-       // Cache it mostly so that we can keep SI units
-       private String cachedStringValue;
-
-       public LongOption(SubConfig conf, String optionName, long defaultValue, 
String defaultValueString, 
-                       int sortOrder, boolean expert, boolean forceWrite, 
String shortDesc, String longDesc, LongCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_NUMBER);
-               this.defaultValue = defaultValue;
-               this.cb = cb;
-               this.currentValue = defaultValue;
-               this.cachedStringValue = defaultValueString;
+public class LongOption extends Option<Long> {
+       public LongOption(SubConfig conf, String optionName, String 
defaultValueString, int sortOrder, boolean expert,
+               boolean forceWrite, String shortDesc, String longDesc, 
LongCallback cb) {
+               this(conf, optionName, Fields.parseLong(defaultValueString), 
sortOrder, expert, forceWrite, shortDesc,
+                       longDesc, cb);
        }

-       public LongOption(SubConfig conf, String optionName, String 
defaultValueString, 
-                       int sortOrder, boolean expert, boolean forceWrite, 
String shortDesc, String longDesc, LongCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_NUMBER);
-               this.defaultValue = Fields.parseSILong(defaultValueString);
-               this.cb = cb;
+       public LongOption(SubConfig conf, String optionName, Long defaultValue, 
int sortOrder, boolean expert,
+               boolean forceWrite, String shortDesc, String longDesc, 
LongCallback cb) {
+               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DataType.NUMBER);
+               this.defaultValue = defaultValue;
                this.currentValue = defaultValue;
-               this.cachedStringValue = defaultValueString;
        }
-       
-       /** Get the current value. This is the value in use if we have finished
-        * initialization, otherwise it is the value set at startup (possibly 
the default). */
-       public long getValue() {
-               if(config.hasFinishedInitialization()) {
-                       long val = cb.get();
-                       if(currentValue != val) {
-                               currentValue = val;
-                               cachedStringValue = null;
-                       }
-               }
-               return currentValue;
-       }
-       
-       public void setValue(String val) throws InvalidConfigValueException {
-               long x;
-               try{
-                       x = Fields.parseSILong(val);
-               }catch (NumberFormatException e) {
-                       throw new 
InvalidConfigValueException(l10n("parseError", "val", val));
-               }
-               cb.set(x);
-               cachedStringValue = val;
-               currentValue = x;
-       }
-       
-       public String getValueString() {
-               long l = getValue();
-               if(cachedStringValue != null) 
-                       return cachedStringValue;
-               else 
-                       return Long.toString(l);
-       }

-       public void setInitialValue(String val) throws 
InvalidConfigValueException {
-               long x;
-               try{
-                       x = Fields.parseSILong(val);
-               }catch (NumberFormatException e) {
+       @Override
+       protected Long parseString(String val) throws 
InvalidConfigValueException {
+               Long x;
+               try {
+                       x = Fields.parseLong(val);
+               } catch (NumberFormatException e) {
                        throw new 
InvalidConfigValueException(l10n("parseError", "val", val));
                }
-               cachedStringValue = val;
-               currentValue = x;
+               return x;
        }
-
+       
        private String l10n(String key, String pattern, String value) {
-               return L10n.getString("LongOption."+key, pattern, value);
+               return L10n.getString("LongOption." + key, pattern, value);
        }
-
-       public boolean isDefault() {
-               getValue();
-               return currentValue == defaultValue;
-       }

-       public String getDefault() {
-               return Long.toString(defaultValue);
+       @Override
+       protected String toString(Long val) {
+               return Fields.longToString(val);
        }
-
-       public void setDefault() {
-               currentValue = defaultValue;
-       }
-       
 }

Copied: 
branches/saltedhashstore/freenet/src/freenet/config/NodeNeedRestartException.java
 (from rev 21885, 
branches/saltedhashstore/freenet/src/freenet/config/InvalidConfigValueException.java)
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/config/NodeNeedRestartException.java
                           (rev 0)
+++ 
branches/saltedhashstore/freenet/src/freenet/config/NodeNeedRestartException.java
   2008-08-15 08:20:48 UTC (rev 21886)
@@ -0,0 +1,19 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.config;
+
+/**
+ * Thrown when the node refuses to set a config variable to a particular
+ * value because it is invalid. Just because this is not thrown does not
+ * necessarily mean that there are no problems with the value defined,
+ * it merely means that there are no immediately detectable problems with 
+ * it.
+ */
+public class NodeNeedRestartException extends ConfigException {
+
+       public NodeNeedRestartException(String msg) {
+               super(msg);
+       }
+
+}

Modified: branches/saltedhashstore/freenet/src/freenet/config/Option.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/Option.java     
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/Option.java     
2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,11 +3,11 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.config;

+
 /**
  * A config option.
  */
-public abstract class Option {
-
+public abstract class Option<T> {
        /** The parent SubConfig object */
        protected final SubConfig config;
        /** The option name */
@@ -23,17 +23,20 @@
        /** Long description of value e.g. "The TCP port to listen for FCP 
connections on" */
        protected final String longDesc;
        /** The configCallback associated to the Option */
-       protected final ConfigCallback cb;
+       protected final ConfigCallback<T> cb;

-       public final static int DATA_TYPE_STRING = 0;
-       public final static int DATA_TYPE_NUMBER = 1;
-       public final static int DATA_TYPE_BOOLEAN = 2;
-       public final static int DATA_TYPE_STRING_ARRAY = 3;
+       protected T defaultValue;
+       protected T currentValue;

+       public static enum DataType {
+               STRING, NUMBER, BOOLEAN, STRING_ARRAY
+       };
+       
        /** Data type : used to make it possible to make user inputs more 
friendly in FCP apps */
-       final int dataType;
+       final DataType dataType;

-       Option(SubConfig config, String name, ConfigCallback cb, int sortOrder, 
boolean expert, boolean forceWrite, String shortDesc, String longDesc, int 
dataType) {
+       Option(SubConfig config, String name, ConfigCallback<T> cb, int 
sortOrder, boolean expert, boolean forceWrite,
+               String shortDesc, String longDesc, DataType dataType) {
                this.config = config;
                this.name = name;
                this.cb = cb;
@@ -46,27 +49,47 @@
        }

        /**
-        * Set this option's current value to a string. Will call the callback. 
Does not care 
-        * whether the value of the option has changed.
+        * Set this option's current value to a string. Will call the callback. 
Does not care whether
+        * the value of the option has changed.
         */
-       public abstract void setValue(String val) throws 
InvalidConfigValueException;
+       public final void setValue(String val) throws 
InvalidConfigValueException, NodeNeedRestartException {
+               T x = parseString(val);
+               set(x);
+       }

+       protected abstract T parseString(String val) throws 
InvalidConfigValueException; 
+       protected abstract String toString(T val);
+
+       protected final void set(T val) throws InvalidConfigValueException, 
NodeNeedRestartException {
+               try {
+                       cb.set(val);
+                       currentValue = val;
+               } catch (NodeNeedRestartException e) {
+                       currentValue = val;
+                       throw e;
+               }
+       }
+       
        /**
         * Get the current value of the option as a string.
         */
-       public abstract String getValueString();
+       public final String getValueString() {
+               return toString(currentValue);
+       }

        /** Set to a value from the config file; this is not passed on to the 
callback, as we
         * expect the client-side initialization to check the value. The 
callback is not valid
         * until the client calls finishedInitialization().
         * @throws InvalidConfigValueException 
         */
-       public abstract void setInitialValue(String val) throws 
InvalidConfigValueException;
+       public final void setInitialValue(String val) throws 
InvalidConfigValueException {
+               currentValue = parseString(val);
+       }

        /**
         * Call the callback with the current value of the option.
         */
-       public void forceUpdate() throws InvalidConfigValueException {
+       public void forceUpdate() throws InvalidConfigValueException, 
NodeNeedRestartException {
                setValue(getValueString());
        }

@@ -94,31 +117,56 @@
                return sortOrder;
        }

-       public int getDataType() {
+       public DataType getDataType() {
                return dataType;
        }

        public String getDataTypeStr() {
                switch(dataType) {
-               case(DATA_TYPE_STRING): return "string";
-               case(DATA_TYPE_NUMBER): return "number";
-               case(DATA_TYPE_BOOLEAN): return "boolean";
-               case(DATA_TYPE_STRING_ARRAY): return "stringArray";
+               case STRING:
+                       return "string";
+               case NUMBER:
+                       return "number";
+               case BOOLEAN:
+                       return "boolean";
+               case STRING_ARRAY:
+                       return "stringArray";
                default: return null;
                }
        }

        /**
+        * Get the current value. This is the value in use if we have finished 
initialization, otherwise
+        * it is the value set at startup (possibly the default).
+        */
+       public final T getValue() {
+               if (config.hasFinishedInitialization())
+                       return currentValue = cb.get();
+               else
+                       return currentValue;
+       }
+       
+       /**
         * Is this option set to the default?
         */
-       public abstract boolean isDefault();
+       public boolean isDefault() {
+               getValue();
+               return (currentValue == null ? false : 
currentValue.equals(defaultValue));
+       }
+
+       /**
+        * Set to the default. Don't use after completed initialization, as 
this does not call the
+        * callback.
+        */
+       public final void setDefault() {
+               currentValue = defaultValue;
+       }

-       /** Set to the default. Don't use after completed initialization, as 
this does not call the callback. FIXME fix this? */
-       public abstract void setDefault();
-       
-       public abstract String getDefault();
+       public final String getDefault() {
+               return toString(defaultValue);
+       }

-       public ConfigCallback getCallback() {
+       public final ConfigCallback<T> getCallback() {
                return cb;
        }
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/config/PersistentConfig.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/PersistentConfig.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/PersistentConfig.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -36,15 +36,15 @@
        }

        public SimpleFieldSet exportFieldSet(boolean withDefaults) {
-               return 
exportFieldSet(Config.CONFIG_REQUEST_TYPE_CURRENT_SETTINGS, withDefaults);
+               return exportFieldSet(Config.RequestType.CURRENT_SETTINGS, 
withDefaults);
        }

-       public SimpleFieldSet exportFieldSet(int configRequestType, boolean 
withDefaults) {
+       public SimpleFieldSet exportFieldSet(Config.RequestType 
configRequestType, boolean withDefaults) {
                SimpleFieldSet fs = new SimpleFieldSet(true);
                SubConfig[] configs;
                synchronized(this) {
                        // FIXME maybe keep a cache of this?
-                       configs = (SubConfig[]) 
configsByPrefix.values().toArray(new SubConfig[configsByPrefix.size()]);
+                       configs = configsByPrefix.values().toArray(new 
SubConfig[configsByPrefix.size()]);
                }
                SubConfig current;
                for(int i=0;i<configs.length;i++) {

Modified: branches/saltedhashstore/freenet/src/freenet/config/ShortOption.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/ShortOption.java        
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/ShortOption.java        
2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,68 +4,31 @@
 import freenet.support.Fields;
 import freenet.support.api.ShortCallback;

-public class ShortOption extends Option {
-       
-       final short defaultValue;
-       final ShortCallback cb;
-       private short currentValue;
-       
+public class ShortOption extends Option<Short> {
        public ShortOption(SubConfig conf, String optionName, short 
defaultValue, int sortOrder, 
                        boolean expert, boolean forceWrite, String shortDesc, 
String longDesc, ShortCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_NUMBER);
+               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DataType.NUMBER);
                this.defaultValue = defaultValue;
-               this.cb = cb;
                this.currentValue = defaultValue;
        }
-       
-       /** Get the current value. This is the value in use if we have finished
-        * initialization, otherwise it is the value set at startup (possibly 
the default). */
-       public short getValue() {
-               if(config.hasFinishedInitialization())
-                       return currentValue = cb.get();
-               else return currentValue;
+
+       private String l10n(String key, String pattern, String value) {
+               return L10n.getString("ShortOption."+key, pattern, value);
        }

-       public void setValue(String val) throws InvalidConfigValueException {
+       @Override
+       protected Short parseString(String val) throws 
InvalidConfigValueException {
                short x;
-               try{
-                       x= Fields.parseSIShort(val);
+               try {
+                       x = Fields.parseShort(val);
                } catch (NumberFormatException e) {
                        throw new 
InvalidConfigValueException(l10n("unrecognisedShort", "val", val));
                }
-               cb.set(x);
-               currentValue = x;
+               return x;
        }

-       public String getValueString() {
-               return Short.toString(getValue());
-       }
-
-       public void setInitialValue(String val) throws 
InvalidConfigValueException {
-               short x;
-               try{
-                       x = Fields.parseSIShort(val);
-               } catch (NumberFormatException e) {
-                       throw new 
InvalidConfigValueException(l10n("unrecognisedShort", "val", val));
-               }
-               currentValue = x;
-       }
-
-       private String l10n(String key, String pattern, String value) {
-               return L10n.getString("ShortOption."+key, pattern, value);
-       }
-
-       public boolean isDefault() {
-               getValue();
-               return currentValue == defaultValue;
-       }
-       
-       public String getDefault() {
-               return Short.toString(defaultValue);
-       }
-       
-       public void setDefault() {
-               currentValue = defaultValue;
-       }
-       
+       @Override
+       protected String toString(Short val) {
+               return Fields.shortToString(val);
+       }       
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/config/StringArrOption.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/StringArrOption.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/StringArrOption.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -8,80 +8,45 @@
 import freenet.support.URLEncoder;
 import freenet.support.api.StringArrCallback;

-public class StringArrOption extends Option {
-
-    private final String[] defaultValue;
-    private final StringArrCallback cb;
-    private String[] currentValue;
-       
+public class StringArrOption extends Option<String[]> {
     public static final String delimiter = ";";

        public StringArrOption(SubConfig conf, String optionName, String[] 
defaultValue, int sortOrder, 
                        boolean expert, boolean forceWrite, String shortDesc, 
String longDesc, StringArrCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_STRING_ARRAY);
+               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DataType.STRING_ARRAY);
                this.defaultValue = (defaultValue==null)?new 
String[0]:defaultValue;
-               this.cb = cb;
                this.currentValue = (defaultValue==null)?new 
String[0]:defaultValue;
        }
-       
-       /** Get the current value. This is the value in use if we have finished
-        * initialization, otherwise it is the value set at startup (possibly 
the default). */
-       public String[] getValue() {
-               if(config.hasFinishedInitialization())
-                       currentValue = cb.get();

-               return currentValue;
-       }
+       public String[] parseString(String val) throws 
InvalidConfigValueException {
+               if(val.length() == 0) return new String[0];
+               String[] out = val.split(delimiter);

-       public void setValue(String[] val) throws InvalidConfigValueException {
-               setInitialValue(val);
-               cb.set(this.currentValue);
-       }
-       
-       public void setValue(String val) throws InvalidConfigValueException {
                try {
-                       setValue(stringToArray(val));
+                       for (int i = 0; i < out.length; i++) {
+                               if (out[i].equals(":"))
+                                       out[i] = "";
+                               else
+                                       out[i] = URLDecoder.decode(out[i], true 
/* FIXME false */);
+                       }
                } catch (URLEncodedFormatException e) {
                        throw new 
InvalidConfigValueException(l10n("parseError", "error", 
e.getLocalizedMessage()));
                }
-       }
-       
-       public static String[] stringToArray(String val) throws 
URLEncodedFormatException {
-               if(val.length() == 0) return new String[0];
-               String[] out = val.split(delimiter);
-               for(int i=0;i<out.length;i++) {
-                       if(out[i].equals(":"))
-                               out[i] = "";
-                       else
-                               out[i] = URLDecoder.decode(out[i], true /* 
FIXME false */);
-               }
                return out;
        }
-
-       public String getValueString() {
-               return arrayToString(getValue());
-       }

        public void setInitialValue(String[] val) throws 
InvalidConfigValueException {
                this.currentValue = val;
        }

-       public void setInitialValue(String val) throws 
InvalidConfigValueException {
-               try {
-                       this.currentValue = stringToArray(val);
-               } catch (URLEncodedFormatException e) {
-                       throw new 
InvalidConfigValueException(l10n("parseError", "error", 
e.getLocalizedMessage()));
-               }
-       }
-       
        private String l10n(String key, String pattern, String value) {
                return L10n.getString("StringArrOption."+key, pattern, value);
        }

-       public static String arrayToString(String[] arr) {
+       public String toString(String[] arr) {
                if (arr == null)
                        return null;
-               StringBuffer sb = new StringBuffer();
+               StringBuilder sb = new StringBuilder();
                for (int i = 0 ; i < arr.length ; i++) {
                        String val = arr[i];
                        if(val.length() == 0)
@@ -101,17 +66,8 @@
                }
        }

-       public String getDefault() {
-               return arrayToString(defaultValue);
-       }
-
        public boolean isDefault() {
-               getValueString();
+               getValue();
                return currentValue == null ? false : 
Arrays.equals(currentValue, defaultValue);
        }
-       
-       public void setDefault() {
-               currentValue = defaultValue;
-       }
-       
 }

Modified: branches/saltedhashstore/freenet/src/freenet/config/StringOption.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/StringOption.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/StringOption.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -5,51 +5,21 @@

 import freenet.support.api.StringCallback;

-public class StringOption extends Option {
-
-       final String defaultValue;
-       final StringCallback cb;
-       private String currentValue;
-       
+public class StringOption extends Option<String> {
        public StringOption(SubConfig conf, String optionName, String 
defaultValue, int sortOrder, 
                        boolean expert, boolean forceWrite, String shortDesc, 
String longDesc, StringCallback cb) {
-               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DATA_TYPE_STRING);
+               super(conf, optionName, cb, sortOrder, expert, forceWrite, 
shortDesc, longDesc, Option.DataType.STRING);
                this.defaultValue = defaultValue;
-               this.cb = cb;
                this.currentValue = defaultValue;
        }
-       
-       /** Get the current value. This is the value in use if we have finished
-        * initialization, otherwise it is the value set at startup (possibly 
the default). */
-       public String getValue() {
-               if(config.hasFinishedInitialization())
-                       return currentValue = cb.get();
-               else return currentValue;
-       }

-       public void setValue(String val) throws InvalidConfigValueException {
-               cb.set(val);
-               this.currentValue = val; // Callbacks are in charge of ensuring 
it matches with possibleValues
+       @Override
+       protected String parseString(String val) throws 
InvalidConfigValueException {
+               return val;
        }
-       
-       public String getValueString() {
-               return getValue();
-       }

-       public void setInitialValue(String val) throws 
InvalidConfigValueException {
-               this.currentValue = val;
+       @Override
+       protected String toString(String val) {
+               return val;
        }
-
-       public boolean isDefault() {
-               getValue();
-               return (currentValue == null ? false : 
currentValue.equals(defaultValue));
-       }
-       
-       public void setDefault() {
-               currentValue = defaultValue;
-       }
-       
-       public String getDefault(){
-               return defaultValue;
-       }
 }

Modified: branches/saltedhashstore/freenet/src/freenet/config/SubConfig.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/SubConfig.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/SubConfig.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,8 +3,8 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.config;

+import java.util.Iterator;
 import java.util.LinkedHashMap;
-import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.Map.Entry;
@@ -22,9 +22,9 @@
 /**
  * A specific configuration block.
  */
-public class SubConfig implements Comparable {
+public class SubConfig implements Comparable<SubConfig> {

-       private final LinkedHashMap map;
+       private final LinkedHashMap<String, Option<?>> map;
        public final Config config;
        final String prefix;
        private boolean hasInitialized;
@@ -32,7 +32,7 @@
        public SubConfig(String prefix, Config config) {
                this.config = config;
                this.prefix = prefix;
-               map = new LinkedHashMap();
+               map = new LinkedHashMap<String, Option<?>>();
                hasInitialized = false;
                config.register(this);
        }
@@ -41,15 +41,15 @@
         * Return all the options registered. Each includes its name.
         * Used by e.g. webconfig.
         */
-       public synchronized Option[] getOptions() {
-               return (Option[]) map.values().toArray(new Option[map.size()]);
+       public synchronized Option<?>[] getOptions() {
+               return map.values().toArray(new Option[map.size()]);
        }

-       public synchronized Option getOption(String option){
-               return (Option)map.get(option);
+       public synchronized Option<?> getOption(String option) {
+               return map.get(option);
        }

-       public void register(Option o) {
+       public void register(Option<?> o) {
                synchronized(this) {
                        if(o.name.indexOf(SimpleFieldSet.MULTI_LEVEL_CHAR) != 
-1)
                                throw new IllegalArgumentException("Option 
names must not contain "+SimpleFieldSet.MULTI_LEVEL_CHAR);
@@ -62,12 +62,12 @@

        public void register(String optionName, int defaultValue, int sortOrder,
                        boolean expert, boolean forceWrite, String shortDesc, 
String longDesc, IntCallback cb) {
-               register(new IntOption(this, optionName, defaultValue, null, 
sortOrder, expert, forceWrite, shortDesc, longDesc, cb));
+               register(new IntOption(this, optionName, defaultValue, 
sortOrder, expert, forceWrite, shortDesc, longDesc, cb));
        }

        public void register(String optionName, long defaultValue, int 
sortOrder,
                        boolean expert, boolean forceWrite, String shortDesc, 
String longDesc, LongCallback cb) {
-               register(new LongOption(this, optionName, defaultValue, null, 
sortOrder, expert, forceWrite, shortDesc, longDesc, cb));
+               register(new LongOption(this, optionName, defaultValue, 
sortOrder, expert, forceWrite, shortDesc, longDesc, cb));
        }

        public void register(String optionName, String defaultValueString, int 
sortOrder,
@@ -171,12 +171,12 @@
         * Set options from a SimpleFieldSet. Once we process an option, we 
must remove it.
         */
        public void setOptions(SimpleFieldSet sfs) {
-               Set entrySet = map.entrySet();
-               Iterator i = entrySet.iterator();
+               Set<Map.Entry<String, Option<?>>> entrySet = map.entrySet();
+               Iterator<Entry<String, Option<?>>> i = entrySet.iterator();
                while(i.hasNext()) {
-                       Map.Entry entry = (Map.Entry) i.next();
-                       String key = (String) entry.getKey();
-                       Option o = (Option) entry.getValue();
+                       Entry<String, Option<?>> entry = i.next();
+                       String key = entry.getKey();
+                       Option<?> o = entry.getValue();
                        String val = sfs.get(key);
                        if(val != null) {
                                try {
@@ -185,6 +185,11 @@
                                        String msg = "Invalid config value: 
"+prefix+SimpleFieldSet.MULTI_LEVEL_CHAR+key+" = "+val+" : error: "+e;
                                        Logger.error(this, msg, e);
                                        System.err.println(msg); // might be 
about logging?
+                               } catch (NodeNeedRestartException e) {
+                                       // Impossible
+                                       String msg = "Impossible: " + prefix + 
SimpleFieldSet.MULTI_LEVEL_CHAR + key + " = " + val
+                                               + " : error: " + e;
+                                       Logger.error(this, msg, e);
                                }
                        }
                }
@@ -195,53 +200,55 @@
        }

        public SimpleFieldSet exportFieldSet(boolean withDefaults) {
-               return 
exportFieldSet(Config.CONFIG_REQUEST_TYPE_CURRENT_SETTINGS, withDefaults);
+               return exportFieldSet(Config.RequestType.CURRENT_SETTINGS, 
withDefaults);
        }

-       public SimpleFieldSet exportFieldSet(int configRequestType, boolean 
withDefaults) {
+    public SimpleFieldSet exportFieldSet(Config.RequestType configRequestType, 
boolean withDefaults) {
                SimpleFieldSet fs = new SimpleFieldSet(true);
+               @SuppressWarnings("unchecked")
+               Map.Entry<String, Option<?>>[] entries = new 
Map.Entry[map.size()];
                // FIXME is any locking at all necessary here? After it has 
finished init, it's constant...
-               Map.Entry[] entries;
                synchronized(this) {
-                       entries = (Entry[]) map.entrySet().toArray(new 
Map.Entry[map.size()]);
+                       entries = map.entrySet().toArray(entries);
                }
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR)
                        Logger.minor(this, "Prefix="+prefix);
                for(int i=0;i<entries.length;i++) {
-                       Map.Entry entry = (Map.Entry) entries[i];
-                       String key = (String) entry.getKey();
-                       Option o = (Option) entry.getValue();
-//                     if(logMINOR)
-//                             Logger.minor(this, "Key="+key+" 
value="+o.getValueString()+" default="+o.isDefault());
-                       if(configRequestType == 
Config.CONFIG_REQUEST_TYPE_CURRENT_SETTINGS && (!withDefaults) && o.isDefault() 
&& (!o.forceWrite)) {
+                       Map.Entry<String, Option<?>> entry = entries[i];
+                       String key = entry.getKey();
+                       Option<?> o = entry.getValue();
+                       if(logMINOR)
+                               Logger.minor(this, "Key="+key+" 
value="+o.getValueString()+" default="+o.isDefault());
+                       if (configRequestType == 
Config.RequestType.CURRENT_SETTINGS && (!withDefaults) && o.isDefault()
+                               && (!o.forceWrite)) {
                                if(logMINOR)
                                        Logger.minor(this, "Skipping "+key+" - 
"+o.isDefault());
                                continue;
                        }
                        switch (configRequestType) {
-                               case 
Config.CONFIG_REQUEST_TYPE_CURRENT_SETTINGS:
+                               case CURRENT_SETTINGS:
                                        fs.putSingle(key, o.getValueString());
                                        break;
-                               case 
Config.CONFIG_REQUEST_TYPE_DEFAULT_SETTINGS:
+                               case DEFAULT_SETTINGS:
                                        fs.putSingle(key, o.getDefault());
                                        break;
-                               case Config.CONFIG_REQUEST_TYPE_SORT_ORDER:
+                               case SORT_ORDER:
                                        fs.put(key, o.getSortOrder());
                                        break;
-                               case Config.CONFIG_REQUEST_TYPE_EXPERT_FLAG:
+                               case EXPERT_FLAG:
                                        fs.put(key, o.isExpert());
                                        break;
-                               case 
Config.CONFIG_REQUEST_TYPE_FORCE_WRITE_FLAG:
+                               case FORCE_WRITE_FLAG:
                                        fs.put(key, o.isForcedWrite());
                                        break;
-                               case 
Config.CONFIG_REQUEST_TYPE_SHORT_DESCRIPTION:
+                               case SHORT_DESCRIPTION:
                                        fs.putSingle(key, 
L10n.getString(o.getShortDesc()));
                                        break;
-                               case 
Config.CONFIG_REQUEST_TYPE_LONG_DESCRIPTION:
+                               case LONG_DESCRIPTION:
                                        fs.putSingle(key, 
L10n.getString(o.getLongDesc()));
                                        break;
-                               case Config.CONFIG_REQUEST_TYPE_DATA_TYPE:
+                               case DATA_TYPE:
                                        fs.putSingle(key, o.getDataTypeStr());
                                        break;
                                default:
@@ -256,19 +263,21 @@

        /**
         * Force an option to be updated even if it hasn't changed.
-        * @throws InvalidConfigValueException 
+        * 
+        * @throws InvalidConfigValueException
+        * @throws NodeNeedRestartException
         */
-       public void forceUpdate(String optionName) throws 
InvalidConfigValueException {
-               Option o = (Option) map.get(optionName);
+       public void forceUpdate(String optionName) throws 
InvalidConfigValueException, NodeNeedRestartException {
+               Option<?> o = map.get(optionName);
                o.forceUpdate();
        }

-       public void set(String name, String value) throws 
InvalidConfigValueException {
-               Option o = (Option) map.get(name);
+       public void set(String name, String value) throws 
InvalidConfigValueException, NodeNeedRestartException {
+               Option<?> o = map.get(name);
                o.setValue(value);
        }

-       public void set(String name, boolean value) throws 
InvalidConfigValueException {
+       public void set(String name, boolean value) throws 
InvalidConfigValueException, NodeNeedRestartException {
                BooleanOption o = (BooleanOption) map.get(name);
                o.set(value);
        }
@@ -281,7 +290,7 @@
         * @param value The value of the option.
         */
        public void fixOldDefault(String name, String value) {
-               Option o = (Option) map.get(name);
+               Option<?> o = map.get(name);
                if(o.getValueString().equals(value))
                        o.setDefault();
        }
@@ -294,7 +303,7 @@
         * @param value The value of the option.
         */
        public void fixOldDefaultRegex(String name, String value) {
-               Option o = (Option) map.get(name);
+               Option<?> o = map.get(name);
                if(o.getValueString().matches(value))
                        o.setDefault();
        }
@@ -303,15 +312,11 @@
                return prefix;
        }

-       public int compareTo(Object o){
-               if((o == null) || !(o instanceof SubConfig)) return 0;
-               else{
-                       SubConfig second = (SubConfig) o;
-                       if(this.getPrefix().compareTo(second.getPrefix())>0)
-                               return 1;
-                       else
-                               return -1;
-               }
+       public int compareTo(SubConfig second) {
+               if (this.getPrefix().compareTo(second.getPrefix()) > 0)
+                       return 1;
+               else
+                       return -1;
        }

        public String getRawOption(String name) {

Modified: branches/saltedhashstore/freenet/src/freenet/config/WrapperConfig.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/config/WrapperConfig.java      
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/config/WrapperConfig.java      
2008-08-15 08:20:48 UTC (rev 21886)
@@ -24,12 +24,12 @@
  */
 public class WrapperConfig {

-       private static HashMap overrides = new HashMap();
+       private static HashMap<String, String> overrides = new HashMap<String, 
String>();

        public static String getWrapperProperty(String name) {
                synchronized(WrapperConfig.class) {
                        if(overrides.containsKey(name))
-                               return (String) overrides.get(name);
+                               return overrides.get(name);
                }
                return WrapperManager.getProperties().getProperty(name, null);
        }

Modified: branches/saltedhashstore/freenet/src/freenet/crypt/DiffieHellman.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/crypt/DiffieHellman.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/crypt/DiffieHellman.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -6,6 +6,7 @@

 package freenet.crypt;

+import freenet.node.FNPPacketMangler;
 import java.math.BigInteger;
 import java.util.Random;
 import java.util.Stack;
@@ -20,10 +21,10 @@
         * When the number of precalculations falls below this threshold 
generation
         * starts up to make more.
         */
-       private static final int PRECALC_RESUME = 150;
+       private static final int PRECALC_RESUME = 
FNPPacketMangler.DH_CONTEXT_BUFFER_SIZE;

        /** Maximum number of precalculations to create. */
-       private static final int PRECALC_MAX = 300;
+       private static final int PRECALC_MAX = 
FNPPacketMangler.DH_CONTEXT_BUFFER_SIZE * 2;

        /**
         * How often to wake up and make sure the precalculation buffer is full
@@ -49,7 +50,7 @@
        private static class PrecalcBufferFill extends NativeThread {

                public PrecalcBufferFill() {
-                       super("Diffie-Hellman-Precalc", 
NativeThread.LOW_PRIORITY, false);
+                       super("Diffie-Hellman-Precalc", 
NativeThread.MIN_PRIORITY, false);
                        setDaemon(true);
                }

@@ -119,6 +120,7 @@
                        }

                }
+               Logger.normal(DiffieHellman.class, "DiffieHellman had to 
generate a parameter on thread! (report if that happens often)");
                return genParams();
        }


Modified: branches/saltedhashstore/freenet/src/freenet/crypt/SSL.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/crypt/SSL.java 2008-08-15 
08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/crypt/SSL.java 2008-08-15 
08:20:48 UTC (rev 21886)
@@ -15,7 +15,6 @@
  */
 package freenet.crypt;

-import com.sleepycat.je.utilint.NotImplementedYetException;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -24,16 +23,18 @@
 import java.security.Key;
 import java.security.KeyStore;
 import java.security.cert.Certificate;
+
 import javax.net.ServerSocketFactory;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;

+import com.sleepycat.je.utilint.NotImplementedYetException;
+
 import freenet.config.InvalidConfigValueException;
 import freenet.config.SubConfig;
 import freenet.support.Logger;
 import freenet.support.api.BooleanCallback;
 import freenet.support.api.StringCallback;
-
 import freenet.support.io.Closer;

 public class SSL {
@@ -65,11 +66,11 @@
                sslConfig.register("sslEnable", false, configItemOrder++, true, 
true, "SSL.enable", "SSL.enableLong",
                        new BooleanCallback() {

-                               public boolean get() {
+                               public Boolean get() {
                                        return enable;
                                }

-                               public void set(boolean newValue) throws 
InvalidConfigValueException {
+                               public void set(Boolean newValue) throws 
InvalidConfigValueException {
                                        if(newValue != get()) {
                                                enable = newValue;
                                                if(enable)

Modified: branches/saltedhashstore/freenet/src/freenet/crypt/Yarrow.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/crypt/Yarrow.java      
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/crypt/Yarrow.java      
2008-08-15 08:20:48 UTC (rev 21886)
@@ -61,14 +61,18 @@
        public final File seedfile; //A file to which seed data should be 
dumped periodically

        public Yarrow() {
-               this("prng.seed", "SHA1", "Rijndael", true);
+               this("prng.seed", "SHA1", "Rijndael", true, true);
        }
+       
+       public Yarrow(boolean canBlock) {
+               this("prng.seed", "SHA1", "Rijndael", true, canBlock);
+       }

-       public Yarrow(String seed, String digest, String cipher, boolean 
updateSeed) {
-               this(new File(seed), digest, cipher, updateSeed);
+       public Yarrow(String seed, String digest, String cipher, boolean 
updateSeed, boolean canBlock) {
+               this(new File(seed), digest, cipher, updateSeed, canBlock);
        }

-       public Yarrow(File seed, String digest, String cipher, boolean 
updateSeed) {
+       public Yarrow(File seed, String digest, String cipher, boolean 
updateSeed, boolean canBlock) {
                SecureRandom s;
                try {
                        s = SecureRandom.getInstance("SHA1PRNG");
@@ -85,7 +89,7 @@
                        throw new RuntimeException("Cannot initialize Yarrow!: 
" + e, e);
                }
                entropy_init(seed);
-               seedFromExternalStuff(true);
+               seedFromExternalStuff(canBlock);
                if(updateSeed && !(seed.toString()).equals("/dev/urandom")) 
//Dont try to update the seedfile if we know that it wont be possible anyways 
                        seedfile = seed;
                else
@@ -646,7 +650,7 @@
         * Test routine
         */
        public static void main(String[] args) throws Exception {
-               Yarrow r = new Yarrow(new File("/dev/urandom"), "SHA1", 
"Rijndael", true);
+               Yarrow r = new Yarrow(new File("/dev/urandom"), "SHA1", 
"Rijndael", true, false);

                byte[] b = new byte[1024];


Modified: branches/saltedhashstore/freenet/src/freenet/io/AddressIdentifier.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/io/AddressIdentifier.java      
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/io/AddressIdentifier.java      
2008-08-15 08:20:48 UTC (rev 21886)
@@ -46,23 +46,9 @@
                ipv6PatternWithPercentScopeID = 
Pattern.compile(ipv6AddressRegex + percentScopeIDRegex);
                ipv6ISATAPPattern = Pattern.compile(ipv6ISATAPAddressRegex);
        }
-
-       public static class AddressType {
-
-               public static final AddressType OTHER = new 
AddressType("Other");
-               public static final AddressType IPv4 = new AddressType("IPv4");
-               public static final AddressType IPv6 = new AddressType("IPv6");
-
-               private final String name;
-
-               private AddressType(String name) {
-                       this.name = name;
-               }
-
-               public String toString() {
-                       return name;
-               }
-
+       
+       public enum AddressType {
+               OTHER, IPv4, IPv6;
        }

        /**

Modified: branches/saltedhashstore/freenet/src/freenet/io/NetworkInterface.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/io/NetworkInterface.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/io/NetworkInterface.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -138,6 +138,7 @@
                        InetSocketAddress addr = null;
                        try {
                                addr = new InetSocketAddress((String) 
bindToTokenList.get(serverSocketIndex), port);
+                               serverSocket.setReuseAddress(true);
                                serverSocket.bind(addr);
                        } catch (SocketException e) {
                                if(ignoreUnbindableIP6 && addr != null && 
addr.getAddress() instanceof Inet6Address)

Modified: branches/saltedhashstore/freenet/src/freenet/io/comm/DMT.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/io/comm/DMT.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/io/comm/DMT.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -1206,18 +1206,6 @@
                msg.set(DATA, new ShortBuffer(buf));
                return msg;
        }
-
-       @Deprecated
-       public static final MessageType FNPLocChangeNotification = new 
MessageType("FNPLocationChangeNotification", PRIORITY_LOW) {{
-               addField(LOCATION, Double.class);
-       }};
-       
-       @Deprecated
-       public static final Message createFNPLocChangeNotification(double 
newLoc) {
-               Message msg = new Message(FNPLocChangeNotification);
-               msg.set(LOCATION, newLoc);
-               return msg;
-       }

        public static final MessageType FNPLocChangeNotificationNew = new 
MessageType("FNPLocationChangeNotification2", PRIORITY_LOW) {{
                addField(LOCATION, Double.class);

Modified: 
branches/saltedhashstore/freenet/src/freenet/io/comm/FreenetInetAddress.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/io/comm/FreenetInetAddress.java    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/io/comm/FreenetInetAddress.java    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -141,7 +141,7 @@
         AddressIdentifier.AddressType addressType = 
AddressIdentifier.getAddressType(host);
         boolean logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
         if(logDEBUG) Logger.debug(this, "Address type of '"+host+"' appears to 
be '"+addressType+ '\'');
-        if(!addressType.toString().equals("Other")) {
+        if(addressType != AddressIdentifier.AddressType.OTHER) {
             try {
                 addr = InetAddress.getByName(host);
             } catch (UnknownHostException e) {

Modified: 
branches/saltedhashstore/freenet/src/freenet/io/comm/UdpSocketHandler.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/io/comm/UdpSocketHandler.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/io/comm/UdpSocketHandler.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -64,6 +64,7 @@
                        _sock.setReceiveBufferSize(65536);
                try {
                        // Exit reasonably quickly
+                       _sock.setReuseAddress(true);
                        _sock.setSoTimeout(1000);
                } catch (SocketException e) {
                        throw new RuntimeException(e);

Modified: 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.de.properties
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.de.properties    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.de.properties    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -280,6 +280,7 @@
 FProxyToadlet.notEnoughMetaStrings=Nicht genug Meta-Zeichenfolgen
 FProxyToadlet.notFoundTitle=Nicht gefunden
 FProxyToadlet.openAsText=${link}Klicken Sie hier${/link} um die Datei als 
Flie?text zu ?ffnen (dies sollte nicht gef?hrlich, k?nnte allerdings 
durcheinander/unleserlich sein).
+FProxyToadlet.openAsThawIndex=${link}Hier klicken${/link} um die Datei mit dem 
Thaw Index Browser zu ?ffnen (lesen Sie die obige Warnung!).
 FProxyToadlet.openForce=${link}Klicken Sie hier${/link} um die Datei als 
${mime} zu ?ffnen (lesen Sie die obenstehende Warnung!).
 FProxyToadlet.openForceDisk=${link}Klicken Sie hier${/link} um Ihren Browser 
zu zwingen, die Datei auf einen Datentr?ger herunterzuladen.
 FProxyToadlet.openPossRSSAsPlainText=${link}Klicken Sie hier${/link} um die 
Datei als Flie?text zu ?ffnen (dies ${bold}kann gef?hrlich sein${/bold} wenn 
Sie Internet Explorer 7 oder Firefox 2 benutzen).
@@ -693,8 +694,8 @@
 Node.passOpennetPeersThroughDarknetLong=Wenn aktiviert, werden 
Opennet-Knotenreferenzen (NIEMALS unsere eigene Darknet-Knotenreferenz) ?ber 
unsere Darknet-Partner weitergeleitet. Sodass ein Knoten (dieser Knoten oder 
seine Partner) Opennet-Partner von seinen Darknet-Partnern bekommen kann. Dies 
ist n?tzlich, da es uns erlaubt einen Bootstrap (Erlangung neuer Quellen ohne 
vorher welche zu haben) nach neuen Opennet-Partnern durchzuf?hren nachdem wir 
unsere Partner, zum Beispiel durch Ausfallzeiten, verloren haben. Jedoch kann 
es eine Traffic(Verkehrs)-Analyse etwas erleichtern, deshalb sollten Sie es 
ausschalten wenn Sie paranoid sind.
 Node.port=FNP-Port Nummer (UDP)
 Node.portLong=UDP-Port f?r Knoten-zu-Knoten Kommunikationen 
(Freenet-Knoten-Protokoll)
-Node.publishOurPeersLocation=Sollen wir die Position unserer Partner an unsere 
Partner weitergeben?
-Node.publishOurPeersLocationLong=Sollen wir die Position unserer Partner an 
unsere Partner weitergeben? Dies zu tun, hilft dem Routing gibt aber 
Informationen an potenzielle Angreifer preis.
+Node.publishOurPeersLocation=Sollen wir die Positionen unserer Partner an 
unsere Partner weitergeben?
+Node.publishOurPeersLocationLong=Sollen wir die Positionen unserer Partner an 
unsere Partner weitergeben? Dies zu tun, hilft dem Routing gibt aber 
Informationen an potenzielle Angreifer preis.
 Node.routeAccordingToOurPeersLocation=Sollen wir die Positionen der Partner 
unserer Partner zu Routing-Zwecken ber?cksichtigen?
 Node.routeAccordingToOurPeersLocationLong=Sollen wir die Positionen der 
Partner unserer Partner zu Routing-Zwecken ber?cksichtigen? Dies zu tun 
verbessert das Routing, k?nnte aber einem potenziellen Angreifer helfen.
 Node.storeDirectory=Speicher-Verzeichnis
@@ -711,6 +712,8 @@
 Node.tooSmallMTU=MTU zu niedrig
 Node.tooSmallMTULong=Die MTU ihrer Verbindung scheint ${mtu} Bytes zu sein. 
Freenet wird mit einer MTU unter ${minMTU} Bytes nicht gut funktionieren: 
Verbindungen werden unzuverl?ssig und wom?glich langsam sein. Bitte beheben Sie 
dieses Problem, wenn m?glich.
 Node.tooSmallMTUShort=Verbindungsprobleme: Die MTU Ihrer Verbindung ist zu 
klein damit Freenet gut funktioniert. Es sind Probleme zu erwarten.
+Node.withAnnouncement=Dem Knoten erlauben sich mit Hilfe von Seednodes 
automatisch zu verbinden?
+Node.withAnnouncementLong=Dem Knoten erlauben sich mit Hilfe von Seednodes 
(Saatknoten) automatisch zu verbinden? Obwohl dies die Standard-Einstellung 
darstellt, ist es prinzip-bedingt unsicher.
 NodeClientCore.couldNotFindOrCreateDir=Das Verzeichnis konnte weder gefunden 
noch angelegt werden
 NodeClientCore.downloadAllowedDirs=Verzeichnisse in welche das Herunterladen 
erlaubt ist
 NodeClientCore.downloadAllowedDirsLong=Per Semikolon getrennte Liste von 
Verzeichnissen, in welche das Herunterladen erlaubt ist. "downloads" steht f?r 
das Download-Verzeichnis, wenn das Feld leer ist bedeutet dies, dass keine 
Dateien auf Datentr?ger heruntergeladen werden d?rfen, "all" erlaubt das 
Herunterladen ?berall hin. WARNUNG! Wenn diese Option auf "all" gesetzt wird, 
kann jeder Benutzer jede Datei an jeden Ort auf Ihrem Computer herunterladen!
@@ -828,6 +831,10 @@
 PeersSayKeyBlownAlert.intro=Ein oder mehrere Ihrer Partner melden, dass der 
Schl?ssel der automatischen Aktualisierung unbrauchbar sei! Dies bedeutet, dass 
ein Angreifer den privaten Schl?ssel f?r das automatische 
Aktualisierungs-System kennen k?nnte und deshalb Ihren Knoten zur Ausf?hrung 
von Code seiner Wahl veranlassen k?nnte (wenn Sie aktualisieren)! Das 
automatische Aktualisierungs-System wurde ausgeschaltet. Es ist auch m?glich, 
dass Ihre Partner diesbez?glich bewusst l?gen.
 PeersSayKeyBlownAlert.short=Einige Ihrer Partner melden, dass der Schl?ssel 
f?r die automatischen Aktualisierungen unbrauchbar gemacht wurde!
 PeersSayKeyBlownAlert.titleWithCount=Laut ${count} Partner(n) ist der 
Schl?ssel der automatischen Aktualisierung unbrauchbar!
+PluginConfig.configFile=Plugin-Konfigurations-Datei
+PluginConfig.configFileLong=Pfad/Dateiname der Datei in welcher die 
Einstellungen der Plugins (Erweiterungen) gespeichert werden.
+PluginConfig.installDir=Plugin-Installations-Verzeichnis
+PluginConfig.installDirLong=Verzeichnis in dem die Plugins (Erweiterungen) 
installiert werden.
 PluginManager.cannotSetOnceLoaded=Kann die Plugin(=Erweiterungs)-Liste nicht 
modifizieren sobald sie geladen wurde
 PluginManager.loadedOnStartup=Plugins die beim Start geladen werden
 PluginManager.loadedOnStartupLong=Klassenpfad, Name und Verzeichnis f?r 
Plugins (Erweiterungen) die beim Start des Knotens geladen werden sollen
@@ -861,13 +868,17 @@
 PproxyToadlet.changeReloadOnStartup=?ndern
 PproxyToadlet.classNameTitle=Klassen-Name
 PproxyToadlet.downloadNotAllowedFromRemoteServer=Das downloaden von Plugins 
ist nur von unserem Server erlaubt.
+PproxyToadlet.fileonly=nur lokale Dateien
 PproxyToadlet.internalIDTitle=Interne ID
+PproxyToadlet.loadFreenetPlugin=Ein inoffizielles Plugin aus dem Freenet 
hinzuf?gen
+PproxyToadlet.loadFreenetPluginText=Hier k?nnen Sie die Freenet-URI eines 
Plugins das Sie laden m?chten eingeben. Diese Plugins werden von uns nicht im 
Entferntesten unterst?tzt oder darauf ?berpr?ft, ob sie private Informationen 
herausgeben. Wenn Sie also hier ein inoffizielles Plugin laden, sind Sie auf 
sich allein gestellt.
+PproxyToadlet.loadFreenetURLLabel=Schl?ssel des Plugins
 PproxyToadlet.loadOfficialPlugin=Ein offizielles Plugin hinzuf?gen
 PproxyToadlet.loadOfficialPluginLabel=Offizielles Plugin laden
 PproxyToadlet.loadOfficialPluginText=Diese Plugins liegen auf den Servern des 
Freenet-Projekts. Wir glauben, dass diese Plugins keine pers?nlichen 
Informationen ungefragt herausgeben, auch wenn wir es nicht garantieren.
 PproxyToadlet.loadOfficialPluginWarning=WARNUNG: Das Laden eines offiziellen 
Plugins beinhaltet das Herunterladen des Plugins aus dem Internet (nicht aus 
dem Freenet). Laden Sie hier keine Plugins herunter wenn dies ein Problem 
darstellt.
 PproxyToadlet.loadOtherPlugin=Ein inoffizielles Plugin hinzuf?gen
-PproxyToadlet.loadOtherPluginText=Hier k?nnen Sie die URL eines Plugins 
eingeben, das geladen werden soll. Andere Plugins als die oben aufgelisteten, 
werden von uns noch nicht einmal im Entferntesten unterst?tzt oder darauf 
gepr?ft, ob sie pers?nliche Informationen herausgeben. Wenn Sie also hier ein 
inoffizielles Plugin laden, sind Sie auf sich allein gestellt.
+PproxyToadlet.loadOtherPluginText=Hier k?nnen Sie die URL eines Plugins 
eingeben, das geladen werden soll. Andere Plugins als die oben aufgelisteten, 
werden von uns noch nicht einmal im Entferntesten unterst?tzt oder darauf 
?berpr?ft, ob sie pers?nliche Informationen herausgeben. Wenn Sie also hier ein 
inoffizielles Plugin laden, sind Sie auf sich allein gestellt.
 PproxyToadlet.loadOtherURLLabel=Plugin-URL
 PproxyToadlet.loadPluginLabel=Plugin laden:
 PproxyToadlet.noPlugins=Keine Plugins geladen

Modified: 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.en.properties    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.en.properties    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -87,6 +87,10 @@
 ConfigToadlet.fproxy=fproxy
 ConfigToadlet.fullTitle=Freenet Node Configuration of ${name}
 ConfigToadlet.logger=logger
+ConfigToadlet.needRestartTitle=Node restart required
+ConfigToadlet.needRestart=Some config need restart to activate, please restart 
the node immediately.
+ConfigToadlet.needRestartShort=Some config need restart to activate, please 
restart the node immediately.
+ConfigToadlet.restartNode=Restart Now
 PageMaker.modeSimple=Simple interface
 PageMaker.modeSimpleTooltip=A simple interface all users can use
 PageMaker.modeAdvanced=Advanced interface
@@ -255,12 +259,10 @@
 FirstTimeWizardToadlet.chooseNodeName=Node name required!
 FirstTimeWizardToadlet.chooseNodeNameLong=Please enter a node name in the 
field below (we recommend a nickname possibly with an email address). This is 
so that your friends (trusted peers, which you have manually added) can easily 
tell your node from their other nodes. This is not visible to strangers 
(untrusted automatically added peers). Note that any friend or stranger may 
trivially identify you from your IP address, since you are connected to them, 
but they cannot easily tell what you are requesting.
 FirstTimeWizardToadlet.connectToStrangers=Connect to strangers?
-FirstTimeWizardToadlet.connectToStrangersLong=Ideally, all Freenet users would 
connect only to people they know. This is far more secure, making it very 
difficult for others to tell that you are using Freenet. However, if you don't 
know at least 5 people already running Freenet, you can choose to connect to 
strangers. Note that you can always turn this off later.
-FirstTimeWizardToadlet.enableOpennet=Do you know anyone already using Freenet?
-FirstTimeWizardToadlet.opennetYes=No, I want the node to automatically find 
strangers to connect to. 
-FirstTimeWizardToadlet.opennetNo=Yes, I have at least 5 friends already using 
Freenet and I will add their details on the Friends page.
-FirstTimeWizardToadlet.warningTitle=Warning!
-FirstTimeWizardToadlet.opennetWarning=If Freenet is illegal where you live, or 
if you are using it to access materials that may get you into trouble, telling 
the node to automatically connect to strangers may be dangerous as it makes 
life much easier for an attacker. Freenet is still experimental, and we offer 
no guarantees about security.
+FirstTimeWizardToadlet.connectToStrangersLong=To run Freenet, you must connect 
to at least five other nodes.  These should ideally be run by people whom you 
know and trust.  If you don't know enough friends running Freenet, you can 
choose to connect to strangers' nodes as well.  However, connecting to 
strangers allows others to find out that you are using Freenet and adds some 
additional security risks.  If you want to keep your use of Freenet secret, 
this may get you into trouble.
+FirstTimeWizardToadlet.enableOpennet=It is possible to turn off stranger 
connections later on, once you have added enough friends, although you will 
already have revealed some information to strangers in the meantime.  Freenet 
is still experimental, and we offer no guarantees about security.
+FirstTimeWizardToadlet.opennetYes=I want the node to automatically find 
strangers to connect to. 
+FirstTimeWizardToadlet.opennetNo=I have at least 5 friends already using 
Freenet and I will add their details on the Friends page.
 FirstTimeWizardToadlet.welcomeInfoboxTitle=Welcome to Freenet first time 
wizard!
 FirstTimeWizardToadlet.welcomeInfoboxContent1=Welcome to Freenet first time 
wizard. This tool will enable you to configure your node quickly and easily to 
get you started.
 FirstTimeWizardToadlet.bandwidthLimit=Bandwidth limits
@@ -720,6 +722,10 @@
 NodeClientCore.downloadAllowedDirsLong=Semicolon separated list of directories 
to which downloads are allowed. "downloads" means downloadsDir, empty means no 
downloads to disk allowed, "all" means downloads allowed from anywhere. 
WARNING! If this is set to "all" any user can download any file to anywhere on 
your computer!
 NodeClientCore.downloadDir=Default download directory
 NodeClientCore.downloadDirLong=The directory to save downloaded files into by 
default
+NodeClientCore.encryptPersistentTempBuckets=Encrypt the persistent temporary 
buckets? LEAVE THIS ALONE!
+NodeClientCore.encryptPersistentTempBucketsLong=Encrypt the persistent 
temporary buckets? In some cases (if you use hard-drive and swap encryption) it 
might not make sense to encrypt persistent temporary buckets. LEAVE THIS ALONE 
IF YOU DON'T KNOW WHAT YOU ARE DOING!
+NodeClientCore.encryptTempBuckets=Encrypt the temporary buckets? LEAVE THIS 
ALONE!
+NodeClientCore.encryptTempBucketsLong=Encrypt the temporary buckets? In some 
cases (if you use hard-drive and swap encryption) it might not make sense to 
encrypt temporary buckets. LEAVE THIS ALONE IF YOU DON'T KNOW WHAT YOU ARE 
DOING!
 NodeClientCore.fileForClientStats=File to store client statistics in
 NodeClientCore.fileForClientStatsLong=File to store client throttling 
statistics in (used to decide how often to send requests)
 NodeClientCore.lazyResume=Complete loading of persistent requests after 
startup? (Uses more memory)
@@ -730,6 +736,10 @@
 NodeClientCore.movingTempDirOnTheFlyNotSupported=Moving temp directory on the 
fly not supported at present
 NodeClientCore.persistentTempDir=Persistent temp files directory
 NodeClientCore.persistentTempDirLong=Name of directory to put persistent temp 
files in
+NodeClientCore.maxRAMBucketSize=Maximum size of a RAMBucket
+NodeClientCore.maxRAMBucketSizeLong=Maximum size of a RAMBucket (bigger 
buckets will be kept as files on the disk)
+NodeClientCore.ramBucketPoolSize=Amount of RAM to dedicate to temporary buckets
+NodeClientCore.ramBucketPoolSizeLong=Amount of RAM to dedicate to temporary 
buckets. The tradeoff is more memory usage against more IOs.
 NodeClientCore.startingUpTitle=Freenet is starting up
 NodeClientCore.startingUp=Please allow Freenet a few moments to complete the 
startup process, in the meantime some things may not work and the node may be 
slower than usual.
 NodeClientCore.startingUpShort=Freenet is starting up, some things may not 
work and the node may be slow.
@@ -1052,6 +1062,8 @@
 SimpleToadletServer.illegalCSSName=CSS name must not contain slashes or colons!
 SimpleToadletServer.panicButton=Show the panic button?
 SimpleToadletServer.panicButtonLong=Shows a 'panic button' on the queue page 
that will remove all requests with no confirmation.
+SimpleToadletServer.passthroughMaxSize=Maximum size for transparent 
pass-through in fproxy
+SimpleToadletServer.passthroughMaxSizeLong=Maximum size of a file for 
transparent pass-through in fproxy
 SimpleToadletServer.port=FProxy port
 SimpleToadletServer.portLong=The TCP port FProxy should listen on
 SimpleToadletServer.ssl=Enable SSL?

Modified: 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.fr.properties
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.fr.properties    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.fr.properties    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -285,6 +285,7 @@
 FProxyToadlet.notEnoughMetaStrings=Pas assez de m?ta-donn?es
 FProxyToadlet.notFoundTitle=Introuvable
 FProxyToadlet.openAsText=${link}Cliquez ici${/link} pour ouvrir le fichier en 
texte brut (ceci ne devrait pr?senter aucun danger mais risque d'?tre 
illisible).
+FProxyToadlet.openAsThawIndex=${link}Cliquer ici${/link} pour ouvrir ce 
fichier avec le navigateur d'index Thaw (lisez l'avertissement ci-dessus !).
 FProxyToadlet.openForce=${link}Cliquez ici${/link} pour ouvrir le fichier en 
tant que  ${mime} (lisez l'avertissement ci-dessus !).
 FProxyToadlet.openForceDisk=${link}Cliquez ici${/link} pour force le 
navigateur ? enregistrer le fichier sur le disque.
 FProxyToadlet.openPossRSSAsForceDisk=${link}Cliquez ici${/link} pour essayer 
de forcer votre navigateur ? enregistrer ce fichier sur le disque (${bold}ceci 
peut ?tre dangereux${/bold} si vous utilisez Firefox 2.0.0, les versions 
suivantes n'ont pas de probl?me).
@@ -719,6 +720,8 @@
 Node.tooSmallMTU=MTU trop faible
 Node.tooSmallMTULong=Le MTU de votre connexion est de ${mtu} octets. Freenet a 
besoin d'un MTU de ${minMTU} au moins pour fonctionner correctement : les 
connexions seront lentes et peu fiables. Corrigez ce probl?me si possible.
 Node.tooSmallMTUShort=Probl?mes de connexion : Le MTU de votre connexion est 
trop faible pour Freenet. Attendez-vous ? des probl?mes.
+Node.withAnnouncement=S'annoncer aux seednodes ?
+Node.withAnnouncementLong=Laisser le noeud s'annoncer aux seednodes ? C'est 
indispensable mais c'est un risque?
 NodeClientCore.couldNotFindOrCreateDir=Impossible de trouver ou de cr?er le 
dossier
 NodeClientCore.downloadAllowedDirs=Les dossiers de t?l?chargement autoris?s 
sont
 NodeClientCore.downloadAllowedDirsLong=Liste de dossiers (s?par?s par des ';') 
o? le t?l?chargement est autoris?. "downloads" correspond au dossier de 
t?l?chargement, laissez-la vide pour n'autoriser aucun t?l?chargement sur le 
disque dur, "all" autorise les t?l?chargements partout !
@@ -836,6 +839,10 @@
 PeersSayKeyBlownAlert.intro=Au moins un des noeuds auxquels vous ?tes connect? 
affirme que la cl? de mise ? jour est compromise ! Cela signifie qu'il est 
possible qu'un attaquant connaisse la cl? du syst?me de mise ? jour automatique 
et l'utilise pour essayer de vous pirater (si vous acceptez la mise ? jour) ! 
Le syst?me de mise ? jour automatique a ?t? d?sactiv?. Il est aussi possible 
que ces noeuds mentent d?lib?r?ment.
 PeersSayKeyBlownAlert.short=Certains noeuds disent que la cl? de mise ? jour 
automatique a ?t? compromise !
 PeersSayKeyBlownAlert.titleWithCount=${count} noeud(s) affirme(nt) que la cl? 
de mise ? jour automatique est compromise !
+PluginConfig.configFile=Fichier de configuration des plugins
+PluginConfig.configFileLong=Chemin du fichier de configuration des plugins
+PluginConfig.installDir=Dossier d'installation des plugins
+PluginConfig.installDirLong=Dossier d'installation des plugins
 PluginManager.cannotSetOnceLoaded=Impossible de d?finir la liste des plugins 
une fois d?marr?
 PluginManager.loadedOnStartup=Plugins ? charger au d?marrage
 PluginManager.loadedOnStartupLong=Classpath, nom et emplacement des plugins ? 
charger au d?marrage du node
@@ -869,11 +876,15 @@
 PproxyToadlet.changeReloadOnStartup=Changer
 PproxyToadlet.classNameTitle=Nom de Class
 PproxyToadlet.downloadNotAllowedFromRemoteServer=Le t?l?chargement de plugins 
n'est autoris? que depuis notre serveur.
+PproxyToadlet.fileonly=Fichiers locaux uniquement
 PproxyToadlet.internalIDTitle=ID interne
+PproxyToadlet.loadFreenetPlugin=Chargez un plugin non officiel depuis Freenet
+PproxyToadlet.loadFreenetPluginText=Vous pouvez entrer la cl? Freenet d'un 
plugin que vous voulez charger. Ces plugins ne sont ni support?s ni m?me test?s 
par le projet. Si vous en chargez un, vous ?tes livr? ? vous m?me.
+PproxyToadlet.loadFreenetURLLabel=Cl? du plugin
 PproxyToadlet.loadOfficialPlugin=Ajouter un plugin officiel
 PproxyToadlet.loadOfficialPluginLabel=Charger un plugin officiel
 PproxyToadlet.loadOfficialPluginText=Ces plugins sont h?berg?s sur les 
serveurs du projet Freenet. Nous pensons que ces plugins ne contiennent pas de 
failles de s?curit?, sans toutefois le garantir.
-PproxyToadlet.loadOfficialPluginWarning=ATTENTION : Charger un plugin officiel 
implique de le t?l?charger sur internet (pas sur freenet). Un attaquant 
pourrait le d?tecter et peut-?tre interf?rer. Ne chargez pas de plugins si vous 
pensez qu'il y a un risque.
+PproxyToadlet.loadOfficialPluginWarning=ATTENTION : Charger un plugin officiel 
implique de le t?l?charger sur internet (pas sur freenet). Ne chargez pas de 
plugins si vous pensez qu'il y a un risque.
 PproxyToadlet.loadOtherPlugin=Charger un plugin non-officiel
 PproxyToadlet.loadOtherPluginText=Vous pouvez entrer l'URL d'un plugin que 
vous voulez charger. Les plugins qui ne sont pas list?s ci-dessus ne sont pas 
support?s et leur s?curit? n'est pas v?rifi?e. Donc, si vous chargez un plugin 
ici, vous ?tes livr? ? vous m?me.
 PproxyToadlet.loadOtherURLLabel=URL du plugin
@@ -1065,7 +1076,7 @@
 SimpleToadletServer.sslLong=Activer SSL pour FProxy
 StartupToadlet.entropyErrorContent=Il n'y a pas assez d'entropy sur votre 
syst?me... Freenet ne d?marrera pas avant d'en avoir suffisament.
 StartupToadlet.entropyErrorTitle=Pas assez d'entropie disponible !
-StartupToadlet.isStartingUp=D?arrage de Freenet en cours, veuillez patienter.
+StartupToadlet.isStartingUp=D?marrage de Freenet en cours, veuillez patienter.
 StartupToadlet.title=D?marrage de Freenet
 StaticToadlet.pathInvalidChars=Cette URI contient des caract?res interdits.
 StaticToadlet.pathNotFound=Le chemin sp?cifi? n'existe pas.

Modified: 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties 
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-cn.properties 
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -878,7 +878,7 @@
 PproxyToadlet.changeReloadOnStartup=??
 PproxyToadlet.classNameTitle=????
 PproxyToadlet.downloadNotAllowedFromRemoteServer=??????????????.
-PproxyToadlet.fileonly=??????
+PproxyToadlet.fileonly=???????
 PproxyToadlet.internalIDTitle=?????
 PproxyToadlet.loadFreenetPlugin=? Freenet ????????
 PproxyToadlet.loadFreenetPluginText=????????????????? Freenet URI. 
???????????, ??????????????, ??????????, ????????????.
@@ -886,7 +886,7 @@
 PproxyToadlet.loadOfficialPlugin=??????
 PproxyToadlet.loadOfficialPluginLabel=??????
 PproxyToadlet.loadOfficialPluginText=?????? Freenet ???????. ????????????????, 
????????.
-PproxyToadlet.loadOfficialPluginWarning=??: ????????????????, ??? Freenet ??. 
?????????????, ?????. ?????????????, ??????????.
+PproxyToadlet.loadOfficialPluginWarning=??: ????????????????, ??? Freenet ??. 
?????????????, ??????????.
 PproxyToadlet.loadOtherPlugin=???????
 PproxyToadlet.loadOtherPluginText=?????????????? URL. ???????????????????, 
???????????????????. ????????????????????, ????????.
 PproxyToadlet.loadOtherURLLabel=??? URL

Modified: 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties 
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/l10n/freenet.l10n.zh-tw.properties 
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -877,7 +877,7 @@
 PproxyToadlet.changeReloadOnStartup=??
 PproxyToadlet.classNameTitle=????
 PproxyToadlet.downloadNotAllowedFromRemoteServer=????????????????.
-PproxyToadlet.fileonly=??????
+PproxyToadlet.fileonly=???????
 PproxyToadlet.internalIDTitle=?????
 PproxyToadlet.loadFreenetPlugin=? Freenet ??????????
 PproxyToadlet.loadFreenetPluginText=????????????????? Freenet URI. 
???????????, ??????????????, ??????????, ????????????.
@@ -885,7 +885,7 @@
 PproxyToadlet.loadOfficialPlugin=??????
 PproxyToadlet.loadOfficialPluginLabel=??????
 PproxyToadlet.loadOfficialPluginText=???????? Freenet ???????. 
????????????????, ????????.
-PproxyToadlet.loadOfficialPluginWarning=??: ?????????????????, ??? Freenet ??. 
?????????????, ?????. ?????????????, ??????????.
+PproxyToadlet.loadOfficialPluginWarning=??: ?????????????????, ??? Freenet ??. 
?????????????, ??????????.
 PproxyToadlet.loadOtherPlugin=???????
 PproxyToadlet.loadOtherPluginText=?????????????? URL. ???????????????????, 
???????????????????. ????????????????????, ????????.
 PproxyToadlet.loadOtherURLLabel=??? URL

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/ConfigurablePersister.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/ConfigurablePersister.java    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/ConfigurablePersister.java    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -22,7 +22,6 @@
                        public void set(String val) throws 
InvalidConfigValueException {
                                setThrottles(val);
                        }
-                       
                });

                String throttleFile = nodeConfig.getString(optionName);

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/FNPPacketMangler.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/FNPPacketMangler.java     
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/FNPPacketMangler.java     
2008-08-15 08:20:48 UTC (rev 21886)
@@ -43,7 +43,6 @@
 import freenet.support.HexUtil;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
-import freenet.support.StringArray;
 import freenet.support.TimeUtil;
 import freenet.support.WouldBlockException;
 import freenet.support.io.NativeThread;
@@ -95,7 +94,7 @@
        /* How often shall we generate a new exponential and add it to the 
FIFO? */
        public final static int DH_GENERATION_INTERVAL = 30000; // 30sec
        /* How big is the FIFO? */
-       public final static int DH_CONTEXT_BUFFER_SIZE = 10;
+       public final static int DH_CONTEXT_BUFFER_SIZE = 20;
        /*
        * The FIFO itself
        * Get a lock on dhContextFIFO before touching it!
@@ -1634,10 +1633,9 @@
         */
        private void sendAuthPacket(byte[] output, BlockCipher cipher, PeerNode 
pn, Peer replyTo, boolean anonAuth) {
                int length = output.length;
-               // FIXME shorten seednode phase 3/4 so it's within the limit
-//             if(length > sock.getMaxPacketSize()) {
-//                     throw new IllegalStateException("Cannot send auth 
packet: too long: "+length);
-//             }
+               if(length > sock.getMaxPacketSize()) {
+                       throw new IllegalStateException("Cannot send auth 
packet: too long: "+length);
+               }
                PCFBMode pcfb = PCFBMode.create(cipher);
                byte[] iv = new byte[pcfb.lengthIV()];
                node.random.nextBytes(iv);
@@ -2077,7 +2075,7 @@
                                alreadyReported[x] = mi.alreadyReportedBytes;
                                x++;
                                if(mi.cb != null) callbacksCount += 
mi.cb.length;
-                               if(logMINOR) Logger.minor(this, "Sending: 
"+mi+" length "+data.length+" cb "+ StringArray.toString(mi.cb)+" reported 
"+mi.alreadyReportedBytes);
+                               if(logMINOR) Logger.minor(this, "Sending: 
"+mi+" length "+data.length+" cb "+ Arrays.toString(mi.cb)+" reported 
"+mi.alreadyReportedBytes);
                                length += (data.length + 2);
                        }
                }
@@ -2294,7 +2292,7 @@
                if(logMINOR) {
                        String log = 
"processOutgoingPreformatted("+Fields.hashCode(buf)+", "+offset+ ',' +length+ 
',' +tracker+ ',' +packetNumber+ ',';
                        if(callbacks == null) log += "null";
-                       else log += 
""+callbacks.length+StringArray.toString(callbacks); // FIXME too verbose?
+                       else log += 
""+callbacks.length+Arrays.toString(callbacks); // FIXME too verbose?
                        Logger.minor(this, log);
                }
                if((tracker == null) || (!tracker.pn.isConnected())) {
@@ -2833,10 +2831,8 @@
                        // reset the authenticator cache
                        authenticatorCache.clear();
                }
-               if(logMINOR)
-                       Logger.minor(this, "Reset the JFK transitent key 
because "+(isCacheTooBig ? ("the cache's capacity is exeeded 
("+authenticatorCache.size()+')') : "it's time to rekey") + this);
                node.getTicker().queueTimedJob(transientKeyRekeyer, 
"JFKmaybeResetTransitentKey "+now, TRANSIENT_KEY_REKEYING_MIN_INTERVAL, false);
-               Logger.normal(this, "JFK's TransientKey has been changed and 
the message cache flushed.");
+               Logger.normal(this, "JFK's TransientKey has been changed and 
the message cache flushed because "+(isCacheTooBig ? ("the cache is oversized 
("+authenticatorCache.size()+')') : "it's time to rekey")+ " on " + this);
                return true;
        }


Modified: 
branches/saltedhashstore/freenet/src/freenet/node/FailureTableEntry.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/FailureTableEntry.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/FailureTableEntry.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -8,7 +8,7 @@

 import freenet.keys.Key;
 import freenet.support.Logger;
-import freenet.support.StringArray;
+import java.util.Arrays;

 class FailureTableEntry implements TimedOutNodesList {

@@ -75,7 +75,7 @@
         */
        public void onFailure(short htl2, PeerNode[] requestors, PeerNode[] 
requestedFrom, int timeout, long now) {
                if(logMINOR)
-                       Logger.minor(this, 
"onFailure("+htl2+",requestors="+StringArray.toString(requestors)+",requestedFrom="+StringArray.toString(requestedFrom)+",timeout="+timeout);
+                       Logger.minor(this, 
"onFailure("+htl2+",requestors="+Arrays.toString(requestors)+",requestedFrom="+Arrays.toString(requestedFrom)+",timeout="+timeout);
                synchronized(this) {
                        if(requestors != null) {
                                for(int i=0;i<requestors.length;i++)

Modified: branches/saltedhashstore/freenet/src/freenet/node/GlobalProbe.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/GlobalProbe.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/GlobalProbe.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,7 +4,7 @@
 package freenet.node;

 import freenet.support.Logger;
-import freenet.support.StringArray;
+import java.util.Arrays;

 public class GlobalProbe implements Runnable {

@@ -33,7 +33,7 @@
                        }

                        public void onTrace(long uid, double target, double 
nearest, double best, short htl, short counter, double location, long nodeUID, 
double[] peerLocs, long[] peerUIDs, double[] locsNotVisited, short forkCount, 
short linearCount, String reason, long prevUID) {
-                               String msg = "Probe trace: UID="+uid+" 
target="+target+" nearest="+nearest+" best="+best+" htl="+htl+" 
counter="+counter+" location="+location+" node UID="+nodeUID+" prev 
UID="+prevUID+" peers="+NodeDispatcher.peersUIDsToString(peerUIDs, peerLocs)+" 
locs not visited: "+StringArray.toString(locsNotVisited)+" fork count: 
"+forkCount+" linear count: "+linearCount+" from "+reason;
+                               String msg = "Probe trace: UID="+uid+" 
target="+target+" nearest="+nearest+" best="+best+" htl="+htl+" 
counter="+counter+" location="+location+" node UID="+nodeUID+" prev 
UID="+prevUID+" peers="+NodeDispatcher.peersUIDsToString(peerUIDs, peerLocs)+" 
locs not visited: "+Arrays.toString(locsNotVisited)+" fork count: "+forkCount+" 
linear count: "+linearCount+" from "+reason;
                                Logger.normal(this, msg);
                        }


Modified: 
branches/saltedhashstore/freenet/src/freenet/node/IPDetectorPluginManager.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/IPDetectorPluginManager.java  
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/IPDetectorPluginManager.java  
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -277,7 +277,7 @@
         */
        public int[] getUDPPortsNotForwarded() {
                OpennetManager om = node.getOpennet();
-               int darknetStatus = 
node.darknetCrypto.getDetectedConnectivityStatus();
+               int darknetStatus = (node.peers.anyDarknetPeers() ? 
node.darknetCrypto.getDetectedConnectivityStatus() : 
AddressTracker.MAYBE_PORT_FORWARDED);
                int opennetStatus = om == null ? AddressTracker.DONT_KNOW : 
om.crypto.getDetectedConnectivityStatus();
                if(om == null || opennetStatus > AddressTracker.DONT_KNOW) {
                        if(darknetStatus > AddressTracker.DONT_KNOW) {
@@ -289,7 +289,7 @@
                        if(darknetStatus > AddressTracker.DONT_KNOW) {
                                return new int[] { (opennetStatus < 
AddressTracker.DONT_KNOW ? -1 : 1 ) * om.crypto.portNumber };
                        } else {
-                               return new int[] { (darknetStatus < 
AddressTracker.DONT_KNOW ? -1 : 1 ) * node.getDarknetPortNumber(), 
+                               return new int[] { ((darknetStatus < 
AddressTracker.DONT_KNOW) ? -1 : 1 ) * node.getDarknetPortNumber(), 
                                                (opennetStatus < 
AddressTracker.DONT_KNOW ? -1 : 1 ) * om.crypto.portNumber };
                        }
                }

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/LoggingConfigHandler.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/LoggingConfigHandler.java 
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/LoggingConfigHandler.java 
2008-08-15 08:20:48 UTC (rev 21886)
@@ -8,6 +8,7 @@

 import freenet.config.EnumerableOptionCallback;
 import freenet.config.InvalidConfigValueException;
+import freenet.config.NodeNeedRestartException;
 import freenet.config.OptionFormatException;
 import freenet.config.SubConfig;
 import freenet.support.Executor;
@@ -23,7 +24,7 @@
 import freenet.support.api.StringCallback;

 public class LoggingConfigHandler {
-       private static class PriorityCallback implements StringCallback, 
EnumerableOptionCallback {
+       private static class PriorityCallback extends StringCallback implements 
EnumerableOptionCallback {
                private final String[] possibleValues = new String[]{ "ERROR", 
"NORMAL", "MINOR", "DEBUG" };

                public String get() {
@@ -45,7 +46,7 @@

                public void setPossibleValues(String[] val) {
                        throw new NullPointerException("Should not happen!");
-               }
+                }
        }

        protected static final String LOG_PREFIX = "freenet";
@@ -64,10 +65,10 @@

        loggingConfig.register("enabled", true, 1, true, false, 
"LogConfigHandler.enabled", "LogConfigHandler.enabledLong",
                        new BooleanCallback() {
-                                       public boolean get() {
+                                       public Boolean get() {
                                                return fileLoggerHook != null;
                                        }
-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                if(val == (fileLoggerHook != 
null)) return;
                                                if(!val) {
                                                        disableLogger();
@@ -110,11 +111,12 @@

        config.register("maxZippedLogsSize", "128M", 3, true, true, 
"LogConfigHandler.maxZippedLogsSize", "LogConfigHandler.maxZippedLogsSizeLong",
                        new LongCallback() {
-                                       public long get() {
+                                       public Long get() {
                                                return maxZippedLogsSize;
                                        }
-                                       public void set(long val) throws 
InvalidConfigValueException {
-                                               if(val < 0) val = 0;
+                                       public void set(Long val) throws 
InvalidConfigValueException {
+                                               if (val < 0)
+                                               val = 0L;
                                                maxZippedLogsSize = val;
                                                if(fileLoggerHook != null) {
                                                        
fileLoggerHook.setMaxOldLogsSize(val);
@@ -179,11 +181,12 @@
        // max cached bytes in RAM
        config.register("maxCachedBytes", "10M", 6, true, false, 
"LogConfigHandler.maxCachedBytes", "LogConfigHandler.maxCachedBytesLong", 
                        new LongCallback() {
-                                       public long get() {
+                                       public Long get() {
                                                return maxCachedLogBytes;
                                        }
-                                       public void set(long val) throws 
InvalidConfigValueException {
-                                               if(val < 0) val = 0;
+                                       public void set(Long val) throws 
InvalidConfigValueException {
+                                               if (val < 0)
+                                               val = 0L;
                                                if(val == maxCachedLogBytes) 
return;
                                                maxCachedLogBytes = val;
                                                if(fileLoggerHook != null)
@@ -196,10 +199,10 @@
        // max cached lines in RAM
        config.register("maxCachedLines", "100k", 7, true, false, 
"LogConfigHandler.maxCachedLines", "LogConfigHandler.maxCachedLinesLong",
                        new IntCallback() {
-                                       public int get() {
+                                       public Integer get() {
                                                return maxCachedLogLines;
                                        }
-                                       public void set(int val) throws 
InvalidConfigValueException {
+                                       public void set(Integer val) throws 
InvalidConfigValueException {
                                                if(val < 0) val = 0;
                                                if(val == maxCachedLogLines) 
return;
                                                maxCachedLogLines = val;
@@ -237,6 +240,10 @@
                        } catch (InvalidConfigValueException e2) {
                                System.err.println("Invalid config value for 
logger.priority in config file: "+config.getString("priority"));
                                // Leave it at the default.
+                       } catch (NodeNeedRestartException e) {
+                               // impossible
+                               System.err.println("impossible 
NodeNeedRestartException for logger.priority in config file: "
+                                       + config.getString("priority"));
                        }
                        FileLoggerHook hook;
                        try {
@@ -344,7 +351,8 @@
                return maxZippedLogsSize;
        }

-       public void setMaxZippedLogFiles(String maxSizeAsString) throws 
InvalidConfigValueException {
+       public void setMaxZippedLogFiles(String maxSizeAsString) throws 
InvalidConfigValueException,
+               NodeNeedRestartException {
                config.set("maxZippedLogsSize", maxSizeAsString);
        }


Modified: branches/saltedhashstore/freenet/src/freenet/node/Node.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/Node.java 2008-08-15 
08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/Node.java 2008-08-15 
08:20:48 UTC (rev 21886)
@@ -38,6 +38,7 @@
 import freenet.config.FreenetFilePersistentConfig;
 import freenet.config.InvalidConfigValueException;
 import freenet.config.LongOption;
+import freenet.config.NodeNeedRestartException;
 import freenet.config.PersistentConfig;
 import freenet.config.SubConfig;
 import freenet.crypt.DSAPublicKey;
@@ -133,7 +134,7 @@
        private static TimeSkewDetectedUserAlert timeSkewDetectedUserAlert;
        private final static ClockProblemDetectedUserAlert 
clockProblemDetectedUserAlert = new ClockProblemDetectedUserAlert();

-       public class NodeNameCallback implements StringCallback {
+       public class NodeNameCallback extends StringCallback  {
                GetPubkey node;

                NodeNameCallback(GetPubkey n) {
@@ -171,31 +172,40 @@
                }
        }

-       private class StoreTypeCallback implements StringCallback, 
EnumerableOptionCallback {
+       private class StoreTypeCallback extends StringCallback implements 
EnumerableOptionCallback {
+               private String cachedStoreType;

                public String get() {
-                       return storeType;
+                       if (cachedStoreType == null)
+                               cachedStoreType = storeType;
+                       return cachedStoreType;
                }

-               public void set(String val) throws InvalidConfigValueException {
-                       throw new InvalidConfigValueException("Store type 
cannot be changed on the fly");
+               public void set(String val) throws InvalidConfigValueException, 
NodeNeedRestartException {
+                       boolean found = false;
+                       for (String p : getPossibleValues()) {
+                               if (p.equals(val)) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       if (!found)
+                               throw new InvalidConfigValueException("Invalid 
store type");
+                       
+                       cachedStoreType = val;
+                       throw new NodeNeedRestartException("Store type cannot 
be changed on the fly");
                }

                public String[] getPossibleValues() {
-                       return new String[] {
-                                       "bdb-index",
-                                       "salt-hash",
-                                       "ram"
-                       };
+                       return new String[] { "bdb-index", "salt-hash", "ram" };
                }

                public void setPossibleValues(String[] val) {
                        throw new UnsupportedOperationException();
                }
-               
        }

-       private static class L10nCallback implements StringCallback, 
EnumerableOptionCallback {
+       private static class L10nCallback extends StringCallback implements 
EnumerableOptionCallback {

                public String get() {
                        return 
L10n.mapLanguageNameToLongName(L10n.getSelectedLanguage());
@@ -462,7 +472,6 @@

        // Things that's needed to keep track of
        public final PluginManager pluginManager;
-       public freenet.oldplugins.plugin.PluginManager pluginManager2;

        // Helpers
        public final InetAddress localhostAddress;
@@ -756,6 +765,9 @@
                                                // Don't translate the below as 
very few users will use it.
                                                throw new 
InvalidConfigValueException("Moving node directory on the fly not supported at 
present");
                                        }
+                                       public boolean isReadOnly() {
+                                       return true;
+                               }
                });

                nodeDir = new File(nodeConfig.getString("nodeDir"));
@@ -811,11 +823,11 @@
                nodeConfig.register("disableProbabilisticHTLs", false, 
sortOrder++, true, false, "Node.disablePHTLS", "Node.disablePHTLSLong", 
                                new BooleanCallback() {

-                                       public boolean get() {
+                                       public Boolean get() {
                                                return disableProbabilisticHTLs;
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                disableProbabilisticHTLs = val;
                                        }

@@ -825,11 +837,11 @@

                nodeConfig.register("maxHTL", DEFAULT_MAX_HTL, sortOrder++, 
true, false, "Node.maxHTL", "Node.maxHTLLong", new ShortCallback() {

-                                       public short get() {
+                                       public Short get() {
                                                return maxHTL;
                                        }

-                                       public void set(short val) throws 
InvalidConfigValueException {
+                                       public void set(Short val) throws 
InvalidConfigValueException {
                                                if(maxHTL < 0) throw new 
InvalidConfigValueException("Impossible max HTL");
                                                maxHTL = val;
                                        }
@@ -853,63 +865,75 @@

                nodeConfig.register("enableARKs", true, sortOrder++, true, 
false, "Node.enableARKs", "Node.enableARKsLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return enableARKs;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                throw new InvalidConfigValueException("Cannot 
change on the fly");
                        }
-                       
+
+                       public boolean isReadOnly() {
+                                       return true;
+                               }                       
                });
                enableARKs = nodeConfig.getBoolean("enableARKs");

                nodeConfig.register("enablePerNodeFailureTables", true, 
sortOrder++, true, false, "Node.enablePerNodeFailureTables", 
"Node.enablePerNodeFailureTablesLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return enablePerNodeFailureTables;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                throw new InvalidConfigValueException("Cannot 
change on the fly");
                        }
-                       
+
+                       public boolean isReadOnly() {
+                                       return true;
+                             }                 
                });
                enablePerNodeFailureTables = 
nodeConfig.getBoolean("enablePerNodeFailureTables");

                nodeConfig.register("enableULPRDataPropagation", true, 
sortOrder++, true, false, "Node.enableULPRDataPropagation", 
"Node.enableULPRDataPropagationLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return enableULPRDataPropagation;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                throw new InvalidConfigValueException("Cannot 
change on the fly");
                        }
-                       
+
+                       public boolean isReadOnly() {
+                                       return true;
+                               }                       
                });
                enableULPRDataPropagation = 
nodeConfig.getBoolean("enableULPRDataPropagation");

                nodeConfig.register("enableSwapping", true, sortOrder++, true, 
false, "Node.enableSwapping", "Node.enableSwappingLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return enableSwapping;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                throw new InvalidConfigValueException("Cannot 
change on the fly");
                        }
-                       
+
+                       public boolean isReadOnly() {
+                                       return true;
+                               }                       
                });
                enableSwapping = nodeConfig.getBoolean("enableSwapping");

                nodeConfig.register("publishOurPeersLocation", false, 
sortOrder++, true, false, "Node.publishOurPeersLocation", 
"Node.publishOurPeersLocationLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return publishOurPeersLocation;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                publishOurPeersLocation = val;
                        }
                });
@@ -917,22 +941,22 @@

                nodeConfig.register("routeAccordingToOurPeersLocation", false, 
sortOrder++, true, false, "Node.routeAccordingToOurPeersLocation", 
"Node.routeAccordingToOurPeersLocationLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return routeAccordingToOurPeersLocation;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                routeAccordingToOurPeersLocation = val;
                        }
                });
                routeAccordingToOurPeersLocation = 
nodeConfig.getBoolean("routeAccordingToOurPeersLocation");

                nodeConfig.register("enableSwapQueueing", true, sortOrder++, 
true, false, "Node.enableSwapQueueing", "Node.enableSwapQueueingLong", new 
BooleanCallback() {
-                       public boolean get() {
+                       public Boolean get() {
                                return enableSwapQueueing;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                enableSwapQueueing = val;
                        }

@@ -940,11 +964,11 @@
                enableSwapQueueing = 
nodeConfig.getBoolean("enableSwapQueueing");

                nodeConfig.register("enablePacketCoalescing", true, 
sortOrder++, true, false, "Node.enablePacketCoalescing", 
"Node.enablePacketCoalescingLong", new BooleanCallback() {
-                       public boolean get() {
+                       public Boolean get() {
                                return enablePacketCoalescing;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                enablePacketCoalescing = val;
                        }

@@ -988,11 +1012,11 @@
                // Bandwidth limit

                nodeConfig.register("outputBandwidthLimit", "15K", sortOrder++, 
false, true, "Node.outBWLimit", "Node.outBWLimitLong", new IntCallback() {
-                                       public int get() {
+                                       public Integer get() {
                                                //return 
BlockTransmitter.getHardBandwidthLimit();
                                                return outputBandwidthLimit;
                                        }
-                                       public void set(int obwLimit) throws 
InvalidConfigValueException {
+                                       public void set(Integer obwLimit) 
throws InvalidConfigValueException {
                                                if(obwLimit <= 0) throw new 
InvalidConfigValueException(l10n("bwlimitMustBePositive"));
                                                synchronized(Node.this) {
                                                        outputBandwidthLimit = 
obwLimit;
@@ -1013,11 +1037,11 @@
                outputThrottle = new DoubleTokenBucket(obwLimit/2, 
(1000L*1000L*1000L) / obwLimit, obwLimit/2, 0.8);

                nodeConfig.register("inputBandwidthLimit", "-1", sortOrder++, 
false, true, "Node.inBWLimit", "Node.inBWLimitLong",      new IntCallback() {
-                                       public int get() {
+                                       public Integer get() {
                                                if(inputLimitDefault) return -1;
                                                return inputBandwidthLimit;
                                        }
-                                       public void set(int ibwLimit) throws 
InvalidConfigValueException {
+                                       public void set(Integer ibwLimit) 
throws InvalidConfigValueException {
                                                synchronized(Node.this) {
                                                        if(ibwLimit == -1) {
                                                                
inputLimitDefault = true;
@@ -1042,11 +1066,11 @@

                nodeConfig.register("throttleLocalTraffic", false, sortOrder++, 
true, false, "Node.throttleLocalTraffic", "Node.throttleLocalTrafficLong", new 
BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return throttleLocalData;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                throttleLocalData = val;
                        }

@@ -1077,7 +1101,9 @@
                                try {
                                        
logConfigHandler.setMaxZippedLogFiles(TESTNET_MIN_MAX_ZIPPED_LOGFILES_STRING);
                                } catch (InvalidConfigValueException e) {
-                                       throw new Error("Impossible: "+e);
+                                       throw new Error("Impossible: " + e, e);
+                               } catch (NodeNeedRestartException e) {
+                                       throw new Error("Impossible: " + e, e);
                                }
                        }
                } else {
@@ -1142,10 +1168,10 @@

                final SubConfig opennetConfig = new SubConfig("node.opennet", 
config);
                opennetConfig.register("connectToSeednodes", true, 0, true, 
false, "Node.withAnnouncement", "Node.withAnnouncementLong", new 
BooleanCallback() {
-                       public boolean get() {
+                       public Boolean get() {
                                return isAllowedToConnectToSeednodes;
                        }
-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                if(val == get()) return;
                                synchronized(Node.this) {
                                        if(opennet != null)
@@ -1154,17 +1180,21 @@
                                                isAllowedToConnectToSeednodes = 
val;
                                }
                        }
+
+                       public boolean isReadOnly() {
+                                       return opennet != null;
+                               }
                });
                isAllowedToConnectToSeednodes = 
opennetConfig.getBoolean("connectToSeednodes");

                // Can be enabled on the fly
                opennetConfig.register("enabled", false, 0, false, true, 
"Node.opennetEnabled", "Node.opennetEnabledLong", new BooleanCallback() {
-                       public boolean get() {
+                       public Boolean get() {
                                synchronized(Node.this) {
                                        return opennet != null;
                                }
                        }
-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                OpennetManager o;
                                synchronized(Node.this) {
                                        if(val == (opennet != null)) return;
@@ -1189,10 +1219,10 @@

                opennetConfig.register("maxOpennetPeers", "20", 1, true, false, 
"Node.maxOpennetPeers",
                                "Node.maxOpennetPeersLong", new IntCallback() {
-                                       public int get() {
+                                       public Integer get() {
                                                return maxOpennetPeers;
                                        }
-                                       public void set(int 
inputMaxOpennetPeers) throws InvalidConfigValueException {
+                                       public void set(Integer 
inputMaxOpennetPeers) throws InvalidConfigValueException {
                                                if(inputMaxOpennetPeers < 0) 
throw new InvalidConfigValueException(l10n("mustBePositive"));
                                                if(inputMaxOpennetPeers > 20) 
throw new 
InvalidConfigValueException(l10n("maxOpennetPeersMustBeTwentyOrLess"));
                                                maxOpennetPeers = 
inputMaxOpennetPeers;
@@ -1217,11 +1247,11 @@

                opennetConfig.register("acceptSeedConnections", true, 2, true, 
true, "Node.acceptSeedConnectionsShort", "Node.acceptSeedConnections", new 
BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return acceptSeedConnections;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                acceptSeedConnections = val;
                        }

@@ -1234,13 +1264,13 @@
                nodeConfig.register("passOpennetPeersThroughDarknet", true, 
sortOrder++, true, false, "Node.passOpennetPeersThroughDarknet", 
"Node.passOpennetPeersThroughDarknetLong",
                                new BooleanCallback() {

-                                       public boolean get() {
+                                       public Boolean get() {
                                                synchronized(Node.this) {
                                                        return 
passOpennetRefsThroughDarknet;
                                                }
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                synchronized(Node.this) {
                                                        
passOpennetRefsThroughDarknet = val;
                                                }
@@ -1261,6 +1291,9 @@
                                                // FIXME
                                                throw new 
InvalidConfigValueException("Moving extra peer data directory on the fly not 
supported at present");
                                        }
+                                       public boolean isReadOnly() {
+                                       return true;
+                               }
                });
                extraPeerDataDir = new 
File(nodeConfig.getString("extraPeerDataDir"));
                if(!((extraPeerDataDir.exists() && 
extraPeerDataDir.isDirectory()) || (extraPeerDataDir.mkdir()))) {
@@ -1278,13 +1311,13 @@
                nodeConfig.register("storeForceBigShrinks", false, sortOrder++, 
true, false, "Node.forceBigShrink", "Node.forceBigShrinkLong",
                                new BooleanCallback() {

-                                       public boolean get() {
+                                       public Boolean get() {
                                                synchronized(Node.this) {
                                                        return 
storeForceBigShrinks;
                                                }
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                synchronized(Node.this) {
                                                        storeForceBigShrinks = 
val;
                                                }
@@ -1299,11 +1332,11 @@
                nodeConfig.register("storeSize", "1G", sortOrder++, false, 
true, "Node.storeSize", "Node.storeSizeLong", 
                                new LongCallback() {

-                                       public long get() {
+                                       public Long get() {
                                                return maxTotalDatastoreSize;
                                        }

-                                       public void set(long storeSize) throws 
InvalidConfigValueException {
+                                       public void set(Long storeSize) throws 
InvalidConfigValueException {
                                                if((storeSize < 0) || 
(storeSize < (32 * 1024 * 1024)))
                                                        throw new 
InvalidConfigValueException(l10n("invalidStoreSize"));
                                                long newMaxStoreKeys = 
storeSize / sizePerKey;
@@ -1356,6 +1389,9 @@
                                                // FIXME
                                                throw new 
InvalidConfigValueException("Moving datastore on the fly not supported at 
present");
                                        }
+                                       public boolean isReadOnly() {
+                                       return true;
+                               }
                });

                final String suffix = "-" + getDarknetPortNumber();
@@ -1571,11 +1607,11 @@
                nodeConfig.register("databaseMaxMemory", "20M", sortOrder++, 
true, false, "Node.databaseMemory", "Node.databaseMemoryLong", 
                                new LongCallback() {

-                       public long get() {
+                       public Long get() {
                                return databaseMaxMemory;
                        }

-                       public void set(long val) throws 
InvalidConfigValueException {
+                       public void set(Long val) throws 
InvalidConfigValueException {
                                if(val < 0)
                                        throw new 
InvalidConfigValueException(l10n("mustBePositive"));
                                else {
@@ -1707,14 +1743,13 @@

                nodeConfig.register("disableHangCheckers", false, sortOrder++, 
true, false, "Node.disableHangCheckers", "Node.disableHangCheckersLong", new 
BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return disableHangCheckers;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                disableHangCheckers = val;
                        }
-                       
                });

                disableHangCheckers = 
nodeConfig.getBoolean("disableHangCheckers");
@@ -1726,8 +1761,7 @@
                Logger.normal(this, "Initializing Plugin Manager");
                System.out.println("Initializing Plugin Manager");
                pluginManager = new PluginManager(this);
-               pluginManager2 = new 
freenet.oldplugins.plugin.PluginManager(this);
-               
+
                FetchContext ctx = clientCore.makeClient((short)0, 
true).getFetchContext();

                ctx.allowSplitfiles = false;
@@ -2147,16 +2181,14 @@
        }

        void addTransferringRequestHandler(long id) {
-               Long l = new Long(id);
                synchronized(transferringRequestHandlers) {
-                       transferringRequestHandlers.add(l);
+                       transferringRequestHandlers.add(id);
                }
        }

        void removeTransferringRequestHandler(long id) {
-               Long l = new Long(id);
                synchronized(transferringRequestHandlers) {
-                       transferringRequestHandlers.remove(l);
+                       transferringRequestHandlers.remove(id);
                }
        }

@@ -2491,9 +2523,8 @@
        }

        public boolean lockUID(long uid, boolean ssk, boolean insert, boolean 
offerReply, boolean local) {
-               Long l = new Long(uid);
                synchronized(runningUIDs) {
-                       if(!runningUIDs.add(l)) {
+                       if(!runningUIDs.add(uid)) {
                                // Already present.
                                return false;
                        }
@@ -2502,23 +2533,22 @@
                HashSet set = getUIDTracker(ssk, insert, offerReply, local);
                synchronized(set) {
                        if(logMINOR) Logger.minor(this, "Locking "+uid+" 
ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" 
size="+set.size());
-                       set.add(l);
+                       set.add(uid);
                        if(logMINOR) Logger.minor(this, "Locked "+uid+" 
ssk="+ssk+" insert="+insert+" offerReply="+offerReply+" local="+local+" 
size="+set.size());
                }
                return true;
        }

        public void unlockUID(long uid, boolean ssk, boolean insert, boolean 
canFail, boolean offerReply, boolean local) {
-               Long l = new Long(uid);
                completed(uid);
                HashSet set = getUIDTracker(ssk, insert, offerReply, local);
                synchronized(set) {
                        if(logMINOR) Logger.minor(this, "Unlocking "+uid+" 
ssk="+ssk+" insert="+insert+" offerReply="+offerReply+", local="+local+" 
size="+set.size());
-                       set.remove(l);
+                       set.remove(uid);
                        if(logMINOR) Logger.minor(this, "Unlocked "+uid+" 
ssk="+ssk+" insert="+insert+" offerReply="+offerReply+", local="+local+" 
size="+set.size());
                }
                synchronized(runningUIDs) {
-                       if(!runningUIDs.remove(l) && !canFail)
+                       if(!runningUIDs.remove(uid) && !canFail)
                                throw new IllegalStateException("Could not 
unlock "+uid+ '!');
                }
        }
@@ -2701,7 +2731,7 @@
         */
        public boolean recentlyCompleted(long id) {
                synchronized (recentlyCompletedIDs) {
-                       return recentlyCompletedIDs.contains(new Long(id));
+                       return recentlyCompletedIDs.contains(id);
                }
        }

@@ -2710,7 +2740,7 @@
         */
        void completed(long id) {
                synchronized (recentlyCompletedIDs) {
-                       recentlyCompletedIDs.push(new Long(id));
+                       recentlyCompletedIDs.push(id);
                        while(recentlyCompletedIDs.size() > 
MAX_RECENTLY_COMPLETED_IDS)
                                recentlyCompletedIDs.pop();
                }
@@ -3199,7 +3229,7 @@
                }
        }

-       public void setName(String key) throws InvalidConfigValueException {
+       public void setName(String key) throws InvalidConfigValueException, 
NodeNeedRestartException {
                 config.get("node").getOption("name").setValue(key);
        }


Modified: branches/saltedhashstore/freenet/src/freenet/node/NodeClientCore.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/NodeClientCore.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/NodeClientCore.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -53,14 +53,12 @@
 import freenet.support.SimpleFieldSet;
 import freenet.support.api.BooleanCallback;
 import freenet.support.api.IntCallback;
-import freenet.support.api.BucketFactory;
+import freenet.support.api.LongCallback;
 import freenet.support.api.StringArrCallback;
 import freenet.support.api.StringCallback;
 import freenet.support.io.FileUtil;
 import freenet.support.io.FilenameGenerator;
 import freenet.support.io.NativeThread;
-import freenet.support.io.PaddedEphemerallyEncryptedBucketFactory;
-import freenet.support.io.PersistentEncryptedTempBucketFactory;
 import freenet.support.io.PersistentTempBucketFactory;
 import freenet.support.io.TempBucketFactory;

@@ -83,13 +81,12 @@
        private File[] uploadAllowedDirs;
        private boolean uploadAllowedEverywhere;
        final FilenameGenerator tempFilenameGenerator;
-       public final BucketFactory tempBucketFactory;
+       public final TempBucketFactory tempBucketFactory;
+       public final PersistentTempBucketFactory persistentTempBucketFactory;
        public final Node node;
        final NodeStats nodeStats;
        public final RandomSource random;
        final File tempDir;     // Persistent temporary buckets
-       public final PersistentTempBucketFactory persistentTempBucketFactory;
-       public final PersistentEncryptedTempBucketFactory 
persistentEncryptedTempBucketFactory;
        public final UserAlertManager alerts;
        final TextModeClientInterfaceServer tmci;
        TextModeClientInterface directTMCI;
@@ -157,6 +154,9 @@
                                        // FIXME
                                        throw new 
InvalidConfigValueException(l10n("movingTempDirOnTheFlyNotSupported"));
                                }
+                               public boolean isReadOnly() {
+                                       return true;
+                               }
                        });

                tempDir = new File(nodeConfig.getString("tempDir"));
@@ -173,6 +173,18 @@
                }

                // Persistent temp files
+               nodeConfig.register("encryptPersistentTempBuckets", true, 
sortOrder++, true, false, "NodeClientCore.encryptPersistentTempBuckets", 
"NodeClientCore.encryptPersistentTempBucketsLong", new BooleanCallback() {
+
+                       public Boolean get() {
+                               return (persistentTempBucketFactory == null ? 
true : persistentTempBucketFactory.isEncrypting());
+                       }
+
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
+                               if((val == get()) || 
(persistentTempBucketFactory == null)) return;
+                               persistentTempBucketFactory.setEncryption(val);
+                       }
+               });
+               
                nodeConfig.register("persistentTempDir", new File(nodeDir, 
"persistent-temp-" + portNumber).toString(), sortOrder++, true, false, 
"NodeClientCore.persistentTempDir", "NodeClientCore.persistentTempDirLong",
                        new StringCallback() {

@@ -186,17 +198,53 @@
                                        // FIXME
                                        throw new 
InvalidConfigValueException("Moving persistent temp directory on the fly not 
supported at present");
                                }
+                               public boolean isReadOnly() {
+                                       return true;
+                               }
                        });
                try {
-                       persistentTempBucketFactory = new 
PersistentTempBucketFactory(new 
File(nodeConfig.getString("persistentTempDir")), "freenet-temp-", random, 
node.fastWeakRandom);
-                       persistentEncryptedTempBucketFactory = new 
PersistentEncryptedTempBucketFactory(persistentTempBucketFactory);
+                       persistentTempBucketFactory = new 
PersistentTempBucketFactory(new 
File(nodeConfig.getString("persistentTempDir")), "freenet-temp-", random, 
node.fastWeakRandom, nodeConfig.getBoolean("encryptPersistentTempBuckets"));
                } catch(IOException e2) {
                        String msg = "Could not find or create persistent 
temporary directory";
                        throw new 
NodeInitException(NodeInitException.EXIT_BAD_TEMP_DIR, msg);
                }

-               tempBucketFactory = new 
PaddedEphemerallyEncryptedBucketFactory(new 
TempBucketFactory(tempFilenameGenerator), random, node.fastWeakRandom, 1024);
+               nodeConfig.register("maxRAMBucketSize", "128KiB", sortOrder++, 
true, false, "NodeClientCore.maxRAMBucketSize", 
"NodeClientCore.maxRAMBucketSizeLong", new LongCallback() {

+                       public Long get() {
+                               return (tempBucketFactory == null ? 0 : 
tempBucketFactory.getMaxRAMBucketSize());
+                       }
+
+                       public void set(Long val) throws 
InvalidConfigValueException {
+                               if((val == get()) || (tempBucketFactory == 
null)) return;
+                               tempBucketFactory.setMaxRAMBucketSize(val);
+                       }
+               });
+               nodeConfig.register("RAMBucketPoolSize", "10MiB", sortOrder++, 
true, false, "NodeClientCore.ramBucketPoolSize", 
"NodeClientCore.ramBucketPoolSizeLong", new LongCallback() {
+
+                       public Long get() {
+                               return (tempBucketFactory == null ? 0 : 
tempBucketFactory.getMaxRamUsed());
+                       }
+
+                       public void set(Long val) throws 
InvalidConfigValueException {
+                               if((val == get()) || (tempBucketFactory == 
null)) return;
+                               tempBucketFactory.setMaxRamUsed(val);
+                       }
+               });
+                       
+               nodeConfig.register("encryptTempBuckets", true, sortOrder++, 
true, false, "NodeClientCore.encryptTempBuckets", 
"NodeClientCore.encryptTempBucketsLong", new BooleanCallback() {
+
+                       public Boolean get() {
+                               return (tempBucketFactory == null ? true : 
tempBucketFactory.isEncrypting());
+                       }
+
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
+                               if((val == get()) || (tempBucketFactory == 
null)) return;
+                               tempBucketFactory.setEncryption(val);
+                       }
+               });
+               tempBucketFactory = new 
TempBucketFactory(tempFilenameGenerator, 
nodeConfig.getLong("maxRAMBucketSize"), 
nodeConfig.getLong("RAMBucketPoolSize"), random, node.fastWeakRandom, 
nodeConfig.getBoolean("encryptTempBuckets"));
+
                // Downloads directory

                nodeConfig.register("downloadsDir", "downloads", sortOrder++, 
true, true, "NodeClientCore.downloadDir", "NodeClientCore.downloadDirLong", new 
StringCallback() {
@@ -267,7 +315,7 @@
                        });
                
setUploadAllowedDirs(nodeConfig.getStringArr("uploadAllowedDirs"));

-               archiveManager = new ArchiveManager(MAX_ARCHIVE_HANDLERS, 
MAX_CACHED_ARCHIVE_DATA, MAX_ARCHIVE_SIZE, MAX_ARCHIVED_FILE_SIZE, 
MAX_CACHED_ELEMENTS, random, node.fastWeakRandom, tempFilenameGenerator);
+               archiveManager = new ArchiveManager(MAX_ARCHIVE_HANDLERS, 
MAX_CACHED_ARCHIVE_DATA, MAX_ARCHIVE_SIZE, MAX_ARCHIVED_FILE_SIZE, 
MAX_CACHED_ELEMENTS, tempBucketFactory);
                Logger.normal(this, "Initializing USK Manager");
                System.out.println("Initializing USK Manager");
                uskManager = new USKManager(this);
@@ -280,11 +328,11 @@
                nodeConfig.register("lazyResume", false, sortOrder++, true, 
false, "NodeClientCore.lazyResume",
                        "NodeClientCore.lazyResumeLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return lazyResume;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                synchronized(NodeClientCore.this) {
                                        lazyResume = val;
                                }
@@ -296,11 +344,11 @@
                nodeConfig.register("maxBackgroundUSKFetchers", "64", 
sortOrder++, true, false, "NodeClientCore.maxUSKFetchers",
                        "NodeClientCore.maxUSKFetchersLong", new IntCallback() {

-                       public int get() {
+                       public Integer get() {
                                return maxBackgroundUSKFetchers;
                        }

-                       public void set(int uskFetch) throws 
InvalidConfigValueException {
+                       public void set(Integer uskFetch) throws 
InvalidConfigValueException {
                                if(uskFetch <= 0)
                                        throw new 
InvalidConfigValueException(l10n("maxUSKFetchersMustBeGreaterThanZero"));
                                maxBackgroundUSKFetchers = uskFetch;

Modified: branches/saltedhashstore/freenet/src/freenet/node/NodeCrypto.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/NodeCrypto.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/NodeCrypto.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -32,7 +32,6 @@
 import freenet.keys.InsertableClientSSK;
 import freenet.support.Base64;
 import freenet.support.Fields;
-import freenet.support.HexUtil;
 import freenet.support.IllegalBase64Exception;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
@@ -366,13 +365,9 @@
                }
        }

-       public static boolean DISABLE_GROUP_STRIP = false;
-       
        private byte[] myCompressedRef(boolean setup, boolean heavySetup, 
boolean forARK) {
                SimpleFieldSet fs = exportPublicFieldSet(setup, heavySetup, 
forARK);
-               // TODO: we should change that to ((setup || heavySetup) && 
!forARK) when all the nodes have the new code
-               boolean shouldStripGroup = heavySetup && 
Global.DSAgroupBigA.equals(cryptoGroup);
-               if(DISABLE_GROUP_STRIP) shouldStripGroup = false;
+               boolean shouldStripGroup = ((setup || heavySetup) && !forARK) 
&& Global.DSAgroupBigA.equals(cryptoGroup);
                if(shouldStripGroup)
                        fs.removeSubset("dsaGroup");


Modified: 
branches/saltedhashstore/freenet/src/freenet/node/NodeCryptoConfig.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/NodeCryptoConfig.java     
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/NodeCryptoConfig.java     
2008-08-15 08:20:48 UTC (rev 21886)
@@ -50,14 +50,14 @@
        NodeCryptoConfig(SubConfig config, int sortOrder, boolean onePerIP) 
throws NodeInitException {

                config.register("listenPort", -1 /* means random */, 
sortOrder++, true, true, "Node.port", "Node.portLong",     new IntCallback() {
-                       public int get() {
+                       public Integer get() {
                                synchronized(NodeCryptoConfig.class) {
                                        if(crypto != null)
                                                portNumber = crypto.portNumber;
                                        return portNumber;
                                }
                        }
-                       public void set(int val) throws 
InvalidConfigValueException {
+                       public void set(Integer val) throws 
InvalidConfigValueException {

                                if(portNumber < -1 || portNumber == 0 || 
portNumber > 65535) {
                                        throw new 
InvalidConfigValueException("Invalid port number");
@@ -73,6 +73,9 @@
                                        portNumber = val;
                                }
                        }
+                       public boolean isReadOnly() {
+                                       return true;
+                               }               
                });

                try{
@@ -97,13 +100,13 @@
                config.register("testingDropPacketsEvery", 0, sortOrder++, 
true, false, "Node.dropPacketEvery", "Node.dropPacketEveryLong",
                                new IntCallback() {

-                                       public int get() {
+                                       public Integer get() {
                                                
synchronized(NodeCryptoConfig.this) {
                                                        return dropProbability;
                                                }
                                        }

-                                       public void set(int val) throws 
InvalidConfigValueException {
+                                       public void set(Integer val) throws 
InvalidConfigValueException {
                                                if(val < 0) throw new 
InvalidConfigValueException("testingDropPacketsEvery must not be negative");
                                                
synchronized(NodeCryptoConfig.this) {
                                                        if(val == 
dropProbability) return;
@@ -111,7 +114,7 @@
                                                        if(crypto == null) 
return;
                                                }
                                                
crypto.onSetDropProbability(val);
-                                       }
+                                       }               

                });
                dropProbability = config.getInt("testingDropPacketsEvery"); 
@@ -119,13 +122,13 @@
                config.register("oneConnectionPerIP", onePerIP, sortOrder++, 
true, false, "Node.oneConnectionPerIP", "Node.oneConnectionPerIPLong",
                                new BooleanCallback() {

-                                       public boolean get() {
+                                       public Boolean get() {
                                                
synchronized(NodeCryptoConfig.this) {
                                                        return 
oneConnectionPerAddress;
                                                }
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                
synchronized(NodeCryptoConfig.this) {
                                                        oneConnectionPerAddress 
= val;
                                                }
@@ -137,29 +140,29 @@
                config.register("alwaysAllowLocalAddresses", false, 
sortOrder++, true, false, "Node.alwaysAllowLocalAddresses", 
"Node.alwaysAllowLocalAddressesLong",
                                new BooleanCallback() {

-                                       public boolean get() {
+                                       public Boolean get() {
                                                
synchronized(NodeCryptoConfig.this) {
                                                        return 
alwaysAllowLocalAddresses;
                                                }
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                
synchronized(NodeCryptoConfig.this) {
                                                        
alwaysAllowLocalAddresses = val;
                                                }
-                                       }
+                                       }                       
                });
                alwaysAllowLocalAddresses = 
config.getBoolean("alwaysAllowLocalAddresses");

                config.register("assumeNATed", true, sortOrder++, true, true, 
"Node.assumeNATed", "Node.assumeNATedLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return assumeNATed;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                assumeNATed = val;
-                       }
+                       }               
                });
                assumeNATed = config.getBoolean("assumeNATed");
        }
@@ -188,7 +191,7 @@
                return portNumber;
        }

-       class NodeBindtoCallback implements StringCallback {
+       class NodeBindtoCallback extends StringCallback  {

                public String get() {
                        return bindTo.toString();
@@ -199,6 +202,9 @@
                        // FIXME why not? Can't we use 
freenet.io.NetworkInterface like everywhere else, just adapt it for UDP?
                        throw new InvalidConfigValueException("Cannot be 
updated on the fly");
                }
+               public boolean isReadOnly() {
+                       return true;
+               }
        }

        public synchronized FreenetInetAddress getBindTo() {

Modified: branches/saltedhashstore/freenet/src/freenet/node/NodeDispatcher.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/NodeDispatcher.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/NodeDispatcher.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -143,13 +143,6 @@
                        }
                        // We claim it in any case
                        return true;
-               } else if(source.isRealConnection() && spec == 
DMT.FNPLocChangeNotification) {
-                       double newLoc = m.getDouble(DMT.LOCATION);
-                       source.updateLocation(newLoc);
-                       // TODO: remove dead code when 
FNPLocChangeNotificationNew is mandatory
-                       if(source.getVersionNumber() > 1153)
-                               Logger.error(this, "We received a 
FNPLocChangeNotification from a recent build: that should *not* happen! 
("+source.toString()+')');
-                       return true;
                } else if(source.isRealConnection() && spec == 
DMT.FNPLocChangeNotificationNew) {
                        double newLoc = m.getDouble(DMT.LOCATION);
                        ShortBuffer buffer = ((ShortBuffer) 
m.getObject(DMT.PEER_LOCATIONS));

Modified: branches/saltedhashstore/freenet/src/freenet/node/NodeIPDetector.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/NodeIPDetector.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/NodeIPDetector.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -18,6 +18,7 @@
 import freenet.node.useralerts.SimpleUserAlert;
 import freenet.node.useralerts.UserAlert;
 import freenet.pluginmanager.DetectedIP;
+import freenet.pluginmanager.FredPluginBandwidthIndicator;
 import freenet.pluginmanager.FredPluginIPDetector;
 import freenet.pluginmanager.FredPluginPortForward;
 import freenet.support.HTMLNode;
@@ -372,7 +373,6 @@
                                }
                                redetectAddress();
                        }
-                       
                });

                hasValidAddressOverride = true;
@@ -419,7 +419,6 @@
                                }
                                redetectAddress();
                        }
-                       
                });

                String ipHintString = nodeConfig.getString("tempIPAddressHint");
@@ -438,16 +437,15 @@

                nodeConfig.register("includeLocalAddressesInNoderefs", false, 
sortOrder++, true, false, "NodeIPDectector.inclLocalAddress", 
"NodeIPDectector.inclLocalAddressLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                return includeLocalAddressesInNoderefs;
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                includeLocalAddressesInNoderefs = val;
                                lastIPAddress = null;
                                ipDetector.clearCached();
                        }
-                       
                });

                includeLocalAddressesInNoderefs = 
nodeConfig.getBoolean("includeLocalAddressesInNoderefs");
@@ -526,6 +524,18 @@
                ipDetectorManager.unregisterPortForwardPlugin(forward);
        }

+       //TODO: ugly: deal with multiple instances properly
+       public synchronized void 
registerBandwidthIndicatorPlugin(FredPluginBandwidthIndicator indicator) {
+               bandwidthIndicator = indicator;
+       }
+       public synchronized void 
unregisterBandwidthIndicatorPlugin(FredPluginBandwidthIndicator indicator) {
+               bandwidthIndicator = null;
+       }
+       public synchronized FredPluginBandwidthIndicator 
getBandwidthIndicator() {
+               return bandwidthIndicator;
+       }
+       private FredPluginBandwidthIndicator bandwidthIndicator;
+       
        boolean hasValidAddressOverride() {
                synchronized(this) {
                        return hasValidAddressOverride;

Modified: branches/saltedhashstore/freenet/src/freenet/node/NodeStarter.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/NodeStarter.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/NodeStarter.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -393,6 +393,8 @@
                configFS.put("node.opennet.oneConnectionPerIP", false);
                configFS.put("node.opennet.assumeNATed", true);
                configFS.put("node.opennet.connectToSeednodes", 
connectToSeednodes);
+               configFS.put("node.encryptTempBuckets", false);
+               configFS.put("node.encryptPersistentTempBuckets", false);

                PersistentConfig config = new PersistentConfig(configFS);


Modified: branches/saltedhashstore/freenet/src/freenet/node/NodeStats.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/NodeStats.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/NodeStats.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -212,10 +212,10 @@

                statsConfig.register("threadLimit", 500, sortOrder++, true, 
true, "NodeStat.threadLimit", "NodeStat.threadLimitLong",
                                new IntCallback() {
-                                       public int get() {
+                                       public Integer get() {
                                                return threadLimit;
                                        }
-                                       public void set(int val) throws 
InvalidConfigValueException {
+                                       public void set(Integer val) throws 
InvalidConfigValueException {
                                                if(val == get()) return;
                                                if(val < 100)
                                                        throw new 
InvalidConfigValueException(l10n("valueTooLow"));
@@ -227,10 +227,10 @@
                // Yes it could be in seconds insteed of multiples of 0.12, but 
we don't want people to play with it :)
                statsConfig.register("aggressiveGC", aggressiveGCModificator, 
sortOrder++, true, false, "NodeStat.aggressiveGC", "NodeStat.aggressiveGCLong",
                                new IntCallback() {
-                                       public int get() {
+                                       public Integer get() {
                                                return aggressiveGCModificator;
                                        }
-                                       public void set(int val) throws 
InvalidConfigValueException {
+                                       public void set(Integer val) throws 
InvalidConfigValueException {
                                                if(val == get()) return;
                                                Logger.normal(this, "Changing 
aggressiveGCModificator to "+val);
                                                aggressiveGCModificator = val;
@@ -241,11 +241,11 @@
                myMemoryChecker = new MemoryChecker(node.ps, 
aggressiveGCModificator);
                statsConfig.register("memoryChecker", true, sortOrder++, true, 
false, "NodeStat.memCheck", "NodeStat.memCheckLong", 
                                new BooleanCallback(){
-                                       public boolean get() {
+                                       public Boolean get() {
                                                return 
myMemoryChecker.isRunning();
                                        }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
+                                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                                if(val == get()) return;

                                                if(val)
@@ -259,18 +259,17 @@

                statsConfig.register("ignoreLocalVsRemoteBandwidthLiability", 
false, sortOrder++, true, false, 
"NodeStat.ignoreLocalVsRemoteBandwidthLiability", 
"NodeStat.ignoreLocalVsRemoteBandwidthLiabilityLong", new BooleanCallback() {

-                       public boolean get() {
+                       public Boolean get() {
                                synchronized(NodeStats.this) {
                                        return 
ignoreLocalVsRemoteBandwidthLiability;
                                }
                        }

-                       public void set(boolean val) throws 
InvalidConfigValueException {
+                       public void set(Boolean val) throws 
InvalidConfigValueException {
                                synchronized(NodeStats.this) {
                                        ignoreLocalVsRemoteBandwidthLiability = 
val;
                                }
                        }
-                       
                });

                persister = new ConfigurablePersister(this, statsConfig, 
"nodeThrottleFile", "node-throttle.dat", sortOrder++, true, false, 

Modified: branches/saltedhashstore/freenet/src/freenet/node/PeerManager.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/PeerManager.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/PeerManager.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -38,6 +38,8 @@
 import freenet.support.SimpleFieldSet;
 import freenet.support.io.FileUtil;
 import freenet.support.io.Closer;
+import java.util.SortedSet;
+import java.util.TreeSet;

 /**
  * @author amphibian
@@ -79,9 +81,14 @@
        private long nextRoutableConnectionStatsUpdateTime = -1;
        /** routableConnectionStats update interval (milliseconds) */
        private static final long routableConnectionStatsUpdateInterval = 7 * 
1000;  // 7 seconds
-       /** number of samples we have to do stats on peer-selection */
-       private long numberOfSelectionSamples = 0;

+       /**
+        * Track the number of times a PeerNode has been selected by the 
routing algorithm
+        * @see PeerNode.numberOfSelections
+        */
+       private SortedSet<Long> numberOfSelectionSamples = new TreeSet<Long>();
+       private final Object numberOfSelectionSamplesSync = new Object();
+       
        public static final int PEER_NODE_STATUS_CONNECTED = 1;
        public static final int PEER_NODE_STATUS_ROUTING_BACKED_OFF = 2;
        public static final int PEER_NODE_STATUS_TOO_NEW = 3;
@@ -991,10 +998,8 @@
                                //Add the location which we did not pick, if it 
exists.
                                if(closestNotBackedOff != null && 
closestBackedOff != null)
                                        addUnpickedLocsTo.add(new 
Double(closestBackedOff.getLocation()));
-
-                       //TODO: synchronize! ; store the stats here instead of 
into PeerNode?
-                       best.incrementNumberOfSelections();
-                       numberOfSelectionSamples++;
+                                       
+                       incrementSelectionSamples(now, best);
                }

                return best;
@@ -1219,6 +1224,17 @@
                }
                return false;
        }
+       
+       public boolean anyDarknetPeers() {
+               PeerNode[] conns;
+               synchronized(this) {
+                       conns = connectedPeers;
+               }
+               for(PeerNode p : conns)
+                       if(p.isDarknet())
+                               return true;
+               return false;
+       }

        /**
         * Ask each PeerNode to read in it's extra peer data
@@ -1824,7 +1840,19 @@
                return null;
        }

-       public long getNumberOfSelectionSamples() {
-               return numberOfSelectionSamples;
+       public SortedSet<Long> getNumberOfSelectionSamples() {
+               synchronized (numberOfSelectionSamplesSync) {
+                       return new TreeSet<Long>(numberOfSelectionSamples);
+               }
        }
+               
+       private void incrementSelectionSamples(long now, PeerNode pn) {
+               // TODO: reimplement with a bit field to spare memory
+               synchronized (numberOfSelectionSamplesSync) {
+                       if(numberOfSelectionSamples.size() > 
PeerNode.SELECTION_MAX_SAMPLES * OpennetManager.MAX_PEERS_FOR_SCALING)
+                               numberOfSelectionSamples = 
numberOfSelectionSamples.tailSet(now - PeerNode.SELECTION_SAMPLING_PERIOD);
+                       numberOfSelectionSamples.add(now);
+                       pn.incrementNumberOfSelections(now);
+               }
+       }
 }

Modified: branches/saltedhashstore/freenet/src/freenet/node/PeerNode.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/PeerNode.java     
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/PeerNode.java     
2008-08-15 08:20:48 UTC (rev 21886)
@@ -74,7 +74,8 @@
 import freenet.support.math.TimeDecayingRunningAverage;
 import freenet.support.transport.ip.HostnameSyntaxException;
 import freenet.support.transport.ip.IPUtil;
-import java.util.Collection;
+import java.util.SortedSet;
+import java.util.TreeSet;

 /**
  * @author amphibian
@@ -151,9 +152,21 @@
        private long timeLastRoutable;
        /** Time added or restarted (reset on startup unlike peerAddedTime) */
        private long timeAddedOrRestarted;
-       /** Number of time that peer has been selected by the routing algorithm 
*/
-       private long numberOfSelections = 0;

+       /**
+        * Track the number of times this PeerNode has been selected by
+        * the routing algorithm over a given period of time
+        */
+       private SortedSet<Long> numberOfSelections = new TreeSet<Long>();
+       private final Object numberOfSelectionsSync = new Object();
+       // 5mins; yes it's alchemy!
+       public static final int SELECTION_SAMPLING_PERIOD = 5 * 60 * 1000;
+       // 30%; yes it's alchemy too! and probably *way* too high to serve any 
purpose
+       public static final int SELECTION_PERCENTAGE_WARNING = 30;
+       // Should be good enough provided we don't get selected more than 10 
times per/sec
+       // Lower the following value if you want to spare memory... or better 
switch from a TreeSet to a bit field.
+       public static final int SELECTION_MAX_SAMPLES = 10 * 
SELECTION_SAMPLING_PERIOD / 1000; 
+       
        /** Are we connected? If not, we need to start trying to
        * handshake.
        */
@@ -1607,24 +1620,6 @@
                        }
                }
        }
-
-       /**
-       * Update the Location to a new value.
-       * @deprecated
-       */
-       public void updateLocation(double newLoc) {
-               logMINOR = Logger.shouldLog(Logger.MINOR, PeerNode.class);
-               if(newLoc < 0.0 || newLoc > 1.0) {
-                       Logger.error(this, "Invalid location update for " + 
this+ " ("+newLoc+')', new Exception("error"));
-                       // Ignore it
-                       return;
-               }
-               synchronized(this) {
-                       currentLocation = newLoc;
-                       locSetTime = System.currentTimeMillis();
-               }
-               node.peers.writePeers();
-       }

        public void updateLocation(double newLoc, double[] newLocs) {
                logMINOR = Logger.shouldLog(Logger.MINOR, PeerNode.class);
@@ -2109,14 +2104,12 @@
        * Send any high level messages that need to be sent on connect.
        */
        protected void sendInitialMessages() {
-               Message locMsg = ((getVersionNumber() > 1153) ?
-                       
DMT.createFNPLocChangeNotificationNew(node.lm.getLocation(), 
node.peers.getPeerLocationDoubles(true)) :
-                       
DMT.createFNPLocChangeNotification(node.lm.getLocation()));
+               Message locMsg = 
DMT.createFNPLocChangeNotificationNew(node.lm.getLocation(), 
node.peers.getPeerLocationDoubles(true));
                Message ipMsg = DMT.createFNPDetectedIPAddress(detectedPeer);
                Message timeMsg = DMT.createFNPTime(System.currentTimeMillis());
                Message packetsMsg = createSentPacketsMessage();
-               Message dRouting = 
DMT.createRoutingStatus(!disableRoutingHasBeenSetLocally);
-               Message uptime = 
DMT.createFNPUptime((byte)(int)(100*node.uptime.getUptime()));
+               Message dRoutingMsg = 
DMT.createRoutingStatus(!disableRoutingHasBeenSetLocally);
+               Message uptimeMsg = 
DMT.createFNPUptime((byte)(int)(100*node.uptime.getUptime()));

                try {
                        if(isRealConnection())
@@ -2124,8 +2117,8 @@
                        sendAsync(ipMsg, null, 0, 
node.nodeStats.initialMessagesCtr);
                        sendAsync(timeMsg, null, 0, 
node.nodeStats.initialMessagesCtr);
                        sendAsync(packetsMsg, null, 0, 
node.nodeStats.initialMessagesCtr);
-                       sendAsync(dRouting, null, 0, 
node.nodeStats.initialMessagesCtr);
-                       sendAsync(uptime, null, 0, 
node.nodeStats.initialMessagesCtr);
+                       sendAsync(dRoutingMsg, null, 0, 
node.nodeStats.initialMessagesCtr);
+                       sendAsync(uptimeMsg, null, 0, 
node.nodeStats.initialMessagesCtr);
                } catch(NotConnectedException e) {
                        Logger.error(this, "Completed handshake with " + 
getPeer() + " but disconnected (" + isConnected + ':' + currentTracker + "!!!: 
" + e, e);
                }
@@ -2972,9 +2965,9 @@
                synchronized(this) {
                        count = (Long) 
localNodeSentMessageTypes.get(messageSpecName);
                        if(count == null)
-                               count = new Long(1);
+                               count = 1L;
                        else
-                               count = new Long(count.longValue() + 1);
+                               count = count.longValue() + 1;
                        localNodeSentMessageTypes.put(messageSpecName, count);
                }
        }
@@ -2988,9 +2981,9 @@
                synchronized(localNodeReceivedMessageTypes) {
                        count = (Long) 
localNodeReceivedMessageTypes.get(messageSpecName);
                        if(count == null)
-                               count = new Long(1);
+                               count = 1L;
                        else
-                               count = new Long(count.longValue() + 1);
+                               count = count.longValue() + 1;
                        localNodeReceivedMessageTypes.put(messageSpecName, 
count);
                }
        }
@@ -3990,12 +3983,19 @@
                return (short)(((int)uptime) & 0xFF);
        }

-       public long getNumberOfSelections() {
-               return numberOfSelections;
+       public SortedSet<Long> getNumberOfSelections() {
+               synchronized(numberOfSelectionsSync) {
+                       return new TreeSet<Long>(numberOfSelections);
+               }
        }

-       public void incrementNumberOfSelections() {
-               numberOfSelections++;
+       public void incrementNumberOfSelections(long time) {
+               // TODO: reimplement with a bit field to spare memory
+               synchronized(numberOfSelectionsSync) {
+                       if(numberOfSelections.size() > SELECTION_MAX_SAMPLES)
+                               numberOfSelections = 
numberOfSelections.tailSet(time - SELECTION_SAMPLING_PERIOD);
+                       numberOfSelections.add(time);
+               }
        }

        private long offeredMainJarVersion;

Modified: branches/saltedhashstore/freenet/src/freenet/node/PeerNodeStatus.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/PeerNodeStatus.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/PeerNodeStatus.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -8,6 +8,7 @@
 import freenet.io.comm.Peer;
 import freenet.io.xfer.PacketThrottle;
 import freenet.support.Logger;
+import java.util.SortedSet;

 /**
  * Contains various status information for a {@link PeerNode}. Used e.g. in
@@ -94,7 +95,7 @@

        private final int reportedUptimePercentage;

-       private final long numberOfSelections;
+       private final SortedSet<Long> numberOfSelections;

        PeerNodeStatus(PeerNode peerNode, boolean noHeavy) {
                if(Logger.shouldLog(Logger.MINOR, this)) {
@@ -397,7 +398,7 @@
                return reportedUptimePercentage;
        }

-       public long getNumberOfSelections() {
+       public SortedSet<Long> getNumberOfSelections() {
                return numberOfSelections;
        }
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/SeedClientPeerNode.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/SeedClientPeerNode.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/SeedClientPeerNode.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -100,6 +100,10 @@
                                                System.currentTimeMillis() - 
lastReceivedPacketTime() > 60*1000)
                                return true;
                        }
+               } else {
+                       // Disconnect after an hour in any event.
+                       if(System.currentTimeMillis() - 
timeLastConnectionCompleted() > 60*60*1000)
+                               return true;
                }
                return false;
        }

Modified: branches/saltedhashstore/freenet/src/freenet/node/TestnetHandler.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/TestnetHandler.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/TestnetHandler.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -208,7 +208,7 @@

        }

-       private static class TestnetEnabledCallback implements BooleanCallback {
+       private static class TestnetEnabledCallback extends BooleanCallback  {

                final Node node;

@@ -216,30 +216,33 @@
                        this.node = node;
                }

-               public boolean get() {
+               public Boolean get() {
                        return node.testnetEnabled;
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(node.testnetEnabled == val) return;
                        throw new 
InvalidConfigValueException(L10n.getString("TestnetHandler.cannotEnableDisableOnTheFly"));
                }
-               
+
+               public boolean isReadOnly() {
+                       return true;
+               }
        }


-       static class TestnetPortNumberCallback implements IntCallback {
+       static class TestnetPortNumberCallback extends IntCallback  {
                Node node;

                TestnetPortNumberCallback(Node n){
                        this.node = n;
                }

-               public int get() {
+               public Integer get() {
                        return node.testnetHandler.getPort();
                }

-               public void set(int val) throws InvalidConfigValueException {
+               public void set(Integer val) throws InvalidConfigValueException 
{
                        if(val == get()) return;
                        node.testnetHandler.rebind(val);
                }

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterface.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterface.java  
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterface.java  
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -45,12 +45,12 @@
 import freenet.support.OOMHandler;
 import freenet.support.SimpleFieldSet;
 import freenet.support.SizeUtil;
-import freenet.support.StringArray;
 import freenet.support.api.Bucket;
 import freenet.support.io.ArrayBucket;
 import freenet.support.io.ArrayBucketFactory;
 import freenet.support.io.BucketTools;
 import freenet.support.io.FileBucket;
+import java.util.Arrays;

 /**
  * @author amphibian
@@ -860,7 +860,7 @@
                                }

                                public void onTrace(long uid, double target, 
double nearest, double best, short htl, short counter, double location, long 
nodeUID, double[] peerLocs, long[] peerUIDs, double[] locsNotVisited, short 
forkCount, short linearCounter, String reason, long prevUID) {
-                                       String msg = "Probe trace: UID="+uid+" 
target="+target+" nearest="+nearest+" best="+best+" htl="+htl+" 
counter="+counter+" linear="+linearCounter+" location="+location+"node 
UID="+nodeUID+" prev UID="+prevUID+" peer 
locs="+StringArray.toString(peerLocs)+" peer 
UIDs="+StringArray.toString(peerUIDs)+" locs not visited = 
"+StringArray.toString(locsNotVisited)+" forks: "+forkCount+" 
reason="+reason+'\n';
+                                       String msg = "Probe trace: UID="+uid+" 
target="+target+" nearest="+nearest+" best="+best+" htl="+htl+" 
counter="+counter+" linear="+linearCounter+" location="+location+"node 
UID="+nodeUID+" prev UID="+prevUID+" peer locs="+Arrays.toString(peerLocs)+" 
peer UIDs="+Arrays.toString(peerUIDs)+" locs not visited = 
"+Arrays.toString(locsNotVisited)+" forks: "+forkCount+" reason="+reason+'\n';
                                        try {
                                                out.write(msg.getBytes());
                                                out.flush();

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterfaceServer.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterfaceServer.java
        2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/TextModeClientInterfaceServer.java
        2008-08-15 08:20:48 UTC (rev 21886)
@@ -103,7 +103,7 @@
        }


-    static class TMCIEnabledCallback implements BooleanCallback {
+    static class TMCIEnabledCallback extends BooleanCallback  {

        final NodeClientCore core;

@@ -111,24 +111,27 @@
                this.core = core;
        }

-       public boolean get() {
+       public Boolean get() {
                return core.getTextModeClientInterface() != null;
        }

-       public void set(boolean val) throws InvalidConfigValueException {
+       public void set(Boolean val) throws InvalidConfigValueException {
                if(val == get()) return;
                // FIXME implement - see bug #122
                throw new InvalidConfigValueException("Cannot be updated on the 
fly");
        }
+               public boolean isReadOnly() {
+                       return true;
+               }
     }

-    static class TMCISSLCallback implements BooleanCallback {
+    static class TMCISSLCallback extends BooleanCallback  {

-       public boolean get() {
+       public Boolean get() {
                return ssl;
        }

-       public void set(boolean val) throws InvalidConfigValueException {
+       public void set(Boolean val) throws InvalidConfigValueException {
                if(val == get()) return;
                        if(!SSL.available()) {
                                throw new InvalidConfigValueException("Enable 
SSL support before use ssl with TMCI");
@@ -136,9 +139,12 @@
                ssl = val;
                throw new InvalidConfigValueException("Cannot change SSL on the 
fly, please restart freenet");
        }
+               public boolean isReadOnly() {
+                       return true;
+               }
     }

-    static class TMCIDirectEnabledCallback implements BooleanCallback {
+    static class TMCIDirectEnabledCallback extends BooleanCallback  {

        final NodeClientCore core;

@@ -146,18 +152,21 @@
                this.core = core;
        }

-       public boolean get() {
+       public Boolean get() {
                return core.getDirectTMCI() != null;
        }

-       public void set(boolean val) throws InvalidConfigValueException {
+       public void set(Boolean val) throws InvalidConfigValueException {
                if(val == get()) return;
                // FIXME implement - see bug #122
                throw new InvalidConfigValueException("Cannot be updated on the 
fly");
        }
+               public boolean isReadOnly() {
+                       return true;
+               }
     }

-    static class TMCIBindtoCallback implements StringCallback {
+    static class TMCIBindtoCallback extends StringCallback  {

        final NodeClientCore core;

@@ -183,7 +192,7 @@
        }
     }

-    static class TMCIAllowedHostsCallback implements StringCallback {
+    static class TMCIAllowedHostsCallback extends StringCallback  {

        private final NodeClientCore core;

@@ -208,10 +217,9 @@
                                        throw new 
InvalidConfigValueException("Setting allowedHosts for TMCI (console) server 
when TMCI is disabled");
                        }
                }
-       
     }

-    static class TCMIPortNumberCallback implements IntCallback{
+    static class TCMIPortNumberCallback extends IntCallback {

        final NodeClientCore core;

@@ -219,7 +227,7 @@
                this.core = core;
        }

-       public int get() {
+       public Integer get() {
                if(core.getTextModeClientInterface()!=null)
                        return core.getTextModeClientInterface().port;
                else
@@ -227,7 +235,7 @@
        }

        // TODO: implement it
-       public void set(int val) throws InvalidConfigValueException {
+       public void set(Integer val) throws InvalidConfigValueException {
                if(val == get()) return;
                core.getTextModeClientInterface().setPort(val);
        }

Modified: branches/saltedhashstore/freenet/src/freenet/node/UptimeEstimator.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/UptimeEstimator.java      
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/UptimeEstimator.java      
2008-08-15 08:20:48 UTC (rev 21886)
@@ -48,8 +48,6 @@
        /** We write to disk every 5 minutes. The offset is derived from the 
node's identity. */
        private long timeOffset;

-       private final DecimalFormat fix1p2 = new DecimalFormat("0.00");
-
        public UptimeEstimator(File nodeDir, Ticker ticker, byte[] bs) {
                this.ticker = ticker;
                logFile = new File(nodeDir, "uptime.dat");
@@ -67,7 +65,7 @@
                readData(prevFile, base);
                readData(logFile, base);
                schedule(System.currentTimeMillis());
-               System.out.println("Created uptime estimator, time offset is 
"+timeOffset+" uptime at startup is "+fix1p2.format(getUptime())+'%');
+               System.out.println("Created uptime estimator, time offset is 
"+timeOffset+" uptime at startup is "+new 
DecimalFormat("0.00").format(getUptime())+'%');
        }

        private void readData(File file, int base) {

Modified: branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientGet.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientGet.java        
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientGet.java        
2008-08-15 08:20:48 UTC (rev 21886)
@@ -105,9 +105,9 @@
                        tempFile = null;
                        try {
                                if(persistenceType == PERSIST_FOREVER)
-                                       ret = 
client.server.core.persistentTempBucketFactory.makeEncryptedBucket();
+                                       ret = 
client.server.core.persistentTempBucketFactory.makeBucket(maxOutputLength);
                                else
-                                       ret = fctx.bucketFactory.makeBucket(-1);
+                                       ret = 
fctx.bucketFactory.makeBucket(maxOutputLength);
                        } catch (IOException e) {
                                Logger.error(this, "Cannot create bucket for 
temp storage: "+e, e);
                                onFailure(new 
FetchException(FetchException.BUCKET_ERROR, e), null);
@@ -174,9 +174,9 @@
                        tempFile = null;
                        try {
                                if(persistenceType == PERSIST_FOREVER)
-                                       ret = 
client.server.core.persistentTempBucketFactory.makeEncryptedBucket();
+                                       ret = 
client.server.core.persistentTempBucketFactory.makeBucket(fctx.maxOutputLength);
                                else
-                                       ret = fctx.bucketFactory.makeBucket(-1);
+                                       ret = 
fctx.bucketFactory.makeBucket(fctx.maxOutputLength);
                        } catch (IOException e) {
                                Logger.error(this, "Cannot create bucket for 
temp storage: "+e, e);
                                onFailure(new 
FetchException(FetchException.BUCKET_ERROR, e), null);
@@ -269,9 +269,9 @@
                                try {
                                        // Create a new temp bucket
                                        if(persistenceType == PERSIST_FOREVER)
-                                               ret = 
client.server.core.persistentTempBucketFactory.makeEncryptedBucket();
+                                               ret = 
client.server.core.persistentTempBucketFactory.makeBucket(fctx.maxOutputLength);
                                        else
-                                               ret = 
fctx.bucketFactory.makeBucket(-1);
+                                               ret = 
fctx.bucketFactory.makeBucket(fctx.maxOutputLength);
                                } catch (IOException e1) {
                                        Logger.error(this, "Cannot create 
bucket for temp storage: "+e, e);
                                        onFailure(new 
FetchException(FetchException.BUCKET_ERROR, e), null);

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientPutMessage.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientPutMessage.java 
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientPutMessage.java 
2008-08-15 08:20:48 UTC (rev 21886)
@@ -260,7 +260,7 @@

        Bucket createBucket(BucketFactory bf, long length, FCPServer server) 
throws IOException {
                if(persistenceType == ClientRequest.PERSIST_FOREVER) {
-                       return 
server.core.persistentTempBucketFactory.makeEncryptedBucket();
+                       return 
server.core.persistentTempBucketFactory.makeBucket(length);
                } else {
                        return super.createBucket(bf, length, server);
                }

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientRequest.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientRequest.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/fcp/ClientRequest.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -109,7 +109,7 @@
                finished = Fields.stringToBool(fs.get("Finished"), false);
                global = Fields.stringToBool(fs.get("Global"), false);
                final String stime = fs.get("StartupTime");
-               this.startupTime = stime == null ? System.currentTimeMillis() : 
Fields.parseSILong(stime);
+               this.startupTime = stime == null ? System.currentTimeMillis() : 
Fields.parseLong(stime);
                completionTime = fs.getLong("CompletionTime", 0);
                if (finished)
                        started=true;

Modified: branches/saltedhashstore/freenet/src/freenet/node/fcp/ConfigData.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/fcp/ConfigData.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/fcp/ConfigData.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -38,49 +38,49 @@
        public SimpleFieldSet getFieldSet() {
                SimpleFieldSet fs = new SimpleFieldSet(true);
                if(withCurrent) {
-                       SimpleFieldSet current = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_CURRENT_SETTINGS, true);
+                       SimpleFieldSet current = 
node.config.exportFieldSet(Config.RequestType.CURRENT_SETTINGS, true);
                        if(!current.isEmpty()) {
                                fs.put("current", current);
                        }
                }
                if(withDefaults) {
-                       SimpleFieldSet defaultSettings = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_DEFAULT_SETTINGS, false);
+                       SimpleFieldSet defaultSettings = 
node.config.exportFieldSet(Config.RequestType.DEFAULT_SETTINGS, false);
                        if(!defaultSettings.isEmpty()) {
                                fs.put("default", defaultSettings);
                        }
                }
                if(withSortOrder) {
-                       SimpleFieldSet sortOrder = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_SORT_ORDER, false);
+                       SimpleFieldSet sortOrder = 
node.config.exportFieldSet(Config.RequestType.SORT_ORDER, false);
                        if(!sortOrder.isEmpty()) {
                                fs.put("sortOrder", sortOrder);
                        }
                }
                if(withExpertFlag) {
-                       SimpleFieldSet expertFlag = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_EXPERT_FLAG, false);
+                       SimpleFieldSet expertFlag = 
node.config.exportFieldSet(Config.RequestType.EXPERT_FLAG, false);
                        if(!expertFlag.isEmpty()) {
                                fs.put("expertFlag", expertFlag);
                        }
                }
                if(withForceWriteFlag) {
-                       SimpleFieldSet forceWriteFlag = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_FORCE_WRITE_FLAG, false);
+                       SimpleFieldSet forceWriteFlag = 
node.config.exportFieldSet(Config.RequestType.FORCE_WRITE_FLAG, false);
                        if(!forceWriteFlag.isEmpty()) {
                                fs.put("forceWriteFlag", forceWriteFlag);
                        }
                }
                if(withShortDescription) {
-                       SimpleFieldSet shortDescription = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_SHORT_DESCRIPTION, false);
+                       SimpleFieldSet shortDescription = 
node.config.exportFieldSet(Config.RequestType.SHORT_DESCRIPTION, false);
                        if(!shortDescription.isEmpty()) {
                                fs.put("shortDescription", shortDescription);
                        }
                }
                if(withLongDescription) {
-                       SimpleFieldSet longDescription = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_LONG_DESCRIPTION, false);
+                       SimpleFieldSet longDescription = 
node.config.exportFieldSet(Config.RequestType.LONG_DESCRIPTION, false);
                        if(!longDescription.isEmpty()) {
                                fs.put("longDescription", longDescription);
                        }
                }
                if(withDataTypes) {
-                       SimpleFieldSet type = 
node.config.exportFieldSet(Config.CONFIG_REQUEST_TYPE_DATA_TYPE, false);
+                       SimpleFieldSet type = 
node.config.exportFieldSet(Config.RequestType.DATA_TYPE, false);
                        if(!type.isEmpty()) {
                                fs.put("dataType", type);
                        }

Modified: branches/saltedhashstore/freenet/src/freenet/node/fcp/FCPServer.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/node/fcp/FCPServer.java        
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/node/fcp/FCPServer.java        
2008-08-15 08:20:48 UTC (rev 21886)
@@ -16,6 +16,7 @@
 import java.io.OutputStreamWriter;
 import java.net.Socket;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.Vector;
 import java.util.WeakHashMap;
 import java.util.zip.GZIPInputStream;
@@ -49,7 +50,6 @@
 import freenet.support.api.StringCallback;
 import freenet.support.io.Closer;
 import freenet.support.io.FileUtil;
-import java.util.LinkedList;

 /**
  * FCP server process.
@@ -193,7 +193,7 @@
                ch.start();
        }

-       static class FCPPortNumberCallback implements IntCallback {
+       static class FCPPortNumberCallback extends IntCallback  {

                private final NodeClientCore node;

@@ -201,18 +201,22 @@
                        this.node = node;
                }

-               public int get() {
+               public Integer get() {
                        return node.getFCPServer().port;
                }

-               public void set(int val) throws InvalidConfigValueException {
+               public void set(Integer val) throws InvalidConfigValueException 
{
                        if(val != get()) {
                                throw new InvalidConfigValueException("Cannot 
change FCP port number on the fly");
                        }
                }
+
+               public boolean isReadOnly() {
+                       return true;
+               }
        }

-       static class FCPEnabledCallback implements BooleanCallback{
+       static class FCPEnabledCallback extends BooleanCallback {

                final NodeClientCore node;

@@ -220,24 +224,28 @@
                        this.node = node;
                }

-               public boolean get() {
+               public Boolean get() {
                        return node.getFCPServer().enabled;
                }
 //TODO: Allow it
-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(val != get()) {
                                throw new 
InvalidConfigValueException(l10n("cannotStartOrStopOnTheFly"));
                        }
                }
+
+               public boolean isReadOnly() {
+                       return true;
+               }
        }

-       static class FCPSSLCallback implements BooleanCallback{
+       static class FCPSSLCallback extends BooleanCallback {

-               public boolean get() {
+               public Boolean get() {
                        return ssl;
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                if(val == get()) return;
                        if(!SSL.available()) {
                                throw new InvalidConfigValueException("Enable 
SSL support before use ssl with FCP");
@@ -245,12 +253,16 @@
                        ssl = val;
                        throw new InvalidConfigValueException("Cannot change 
SSL on the fly, please restart freenet");
                }
+
+               public boolean isReadOnly() {
+                       return true;
+               }
        }

        // FIXME: Consider moving everything except enabled into constructor
        // Actually we could move enabled in too with an exception???

-       static class FCPBindtoCallback implements StringCallback{
+       static class FCPBindtoCallback extends StringCallback {

                final NodeClientCore node;

@@ -277,7 +289,7 @@
                }
        }

-       static class FCPAllowedHostsCallback implements StringCallback {
+       static class FCPAllowedHostsCallback extends StringCallback  {

                private final NodeClientCore node;

@@ -296,12 +308,10 @@
                        if (!val.equals(get())) {
                                
node.getFCPServer().networkInterface.setAllowedHosts(val);
                        }
-               }
-               
+               }               
        }

-       static class FCPAllowedHostsFullAccessCallback implements 
StringCallback {
-
+       static class FCPAllowedHostsFullAccessCallback extends StringCallback  {
                private final NodeClientCore node;

                public FCPAllowedHostsFullAccessCallback(NodeClientCore node) {
@@ -320,22 +330,22 @@

        }

-       static class PersistentDownloadsEnabledCallback implements 
BooleanCallback {
+       static class PersistentDownloadsEnabledCallback extends BooleanCallback 
 {

                FCPServer server;

-               public boolean get() {
+               public Boolean get() {
                        return server.persistentDownloadsEnabled();
                }

-               public void set(boolean set) {
+               public void set(Boolean set) {
                        if(server.persistentDownloadsEnabled() != set)
                                server.setPersistentDownloadsEnabled(set);
                }

        }

-       static class PersistentDownloadsFileCallback implements StringCallback {
+       static class PersistentDownloadsFileCallback extends StringCallback  {

                FCPServer server;

@@ -350,15 +360,15 @@
                }
        }

-       static class PersistentDownloadsIntervalCallback implements 
LongCallback {
+       static class PersistentDownloadsIntervalCallback extends LongCallback  {

                FCPServer server;

-               public long get() {
+               public Long get() {
                        return server.persistenceInterval;
                }

-               public void set(long value) {
+               public void set(Long value) {
                        server.persistenceInterval = value;
                        FCPServerPersister p = server.persister;
                        if(p != null) {
@@ -369,27 +379,27 @@
                }
        }

-       static class AssumeDDADownloadIsAllowedCallback implements 
BooleanCallback{
+       static class AssumeDDADownloadIsAllowedCallback extends BooleanCallback 
{
                FCPServer server;

-               public boolean get() {
+               public Boolean get() {
                        return server.assumeDownloadDDAIsAllowed;
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(val == get()) return;
                        server.assumeDownloadDDAIsAllowed = val;
                }
        }

-       static class AssumeDDAUploadIsAllowedCallback implements 
BooleanCallback{
+       static class AssumeDDAUploadIsAllowedCallback extends BooleanCallback {
                FCPServer server;

-               public boolean get() {
+               public Boolean get() {
                        return server.assumeUploadDDAIsAllowed;
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        if(val == get()) return;
                        server.assumeUploadDDAIsAllowed = val;
                }

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/fcp/PutSuccessfulMessage.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/fcp/PutSuccessfulMessage.java 
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/fcp/PutSuccessfulMessage.java 
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -26,7 +26,7 @@
                SimpleFieldSet fs = new SimpleFieldSet(true);
                fs.putSingle("Identifier", identifier);
                if(global) fs.putSingle("Global", "true");
-               // FIXME debug and remove!
+               // This is useful for simple clients.
                if(uri != null)
                        fs.putSingle("URI", uri.toString());
                fs.put("StartupTime", startupTime);

Added: 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPullTest.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPullTest.java
                          (rev 0)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPullTest.java
  2008-08-15 08:20:48 UTC (rev 21886)
@@ -0,0 +1,196 @@
+package freenet.node.simulator;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import org.spaceroots.mantissa.random.MersenneTwister;
+
+import freenet.client.FetchException;
+import freenet.client.HighLevelSimpleClient;
+import freenet.crypt.RandomSource;
+import freenet.keys.FreenetURI;
+import freenet.node.Node;
+import freenet.node.NodeInitException;
+import freenet.node.NodeStarter;
+import freenet.support.Logger;
+import freenet.support.PooledExecutor;
+import freenet.support.SimpleFieldSet;
+import freenet.support.TimeUtil;
+import freenet.support.LoggerHook.InvalidThresholdException;
+import freenet.support.io.FileUtil;
+import freenet.support.io.LineReadingInputStream;
+
+/**
+ * Insert a random block of data to an established node via FCP, then
+ * bootstrap a newbie node and pull it from that.
+ * @author Matthew Toseland <toad at amphibian.dyndns.org> (0xE43DA450)
+ */
+public class BootstrapPullTest {
+
+       public static int TARGET_PEERS = 10;
+       public static int TEST_SIZE = 1024*1024;
+       
+       public static int EXIT_NO_SEEDNODES = 257;
+       public static int EXIT_FAILED_TARGET = 258;
+       public static int EXIT_INSERT_FAILED = 259;
+       public static int EXIT_FETCH_FAILED = 260;
+       public static int EXIT_INSERTER_PROBLEM = 261;
+       
+       public static int DARKNET_PORT = 5004;
+       public static int OPENNET_PORT = 5005;
+       
+       /**
+        * @param args
+        * @throws InvalidThresholdException 
+        * @throws IOException 
+        * @throws NodeInitException 
+        * @throws InterruptedException 
+        */
+       public static void main(String[] args) throws 
InvalidThresholdException, IOException, NodeInitException, InterruptedException 
{
+        File dir = new File("bootstrap-pull-test");
+        FileUtil.removeAll(dir);
+        RandomSource random = NodeStarter.globalTestInit(dir.getPath(), false, 
Logger.ERROR, "");
+        byte[] seed = new byte[64];
+        random.nextBytes(seed);
+        MersenneTwister fastRandom = new MersenneTwister(seed);
+        File seednodes = new File("seednodes.fref");
+        if(!seednodes.exists() || seednodes.length() == 0 || 
!seednodes.canRead()) {
+               System.err.println("Unable to read seednodes.fref, it doesn't 
exist, or is empty");
+               System.exit(EXIT_NO_SEEDNODES);
+        }
+        File secondInnerDir = new File(dir, Integer.toString(DARKNET_PORT));
+        secondInnerDir.mkdir();
+        FileInputStream fis = new FileInputStream(seednodes);
+        FileUtil.writeTo(fis, new File(secondInnerDir, "seednodes.fref"));
+        fis.close();
+        
+        // Create the test data
+        System.out.println("Creating test data.");
+        File dataFile = File.createTempFile("testdata", ".tmp", dir);
+        OutputStream os = new FileOutputStream(dataFile);
+        byte[] buf = new byte[4096];
+        for(long written = 0; written < TEST_SIZE;) {
+               fastRandom.nextBytes(buf);
+               int toWrite = (int) Math.min(TEST_SIZE - written, buf.length);
+               os.write(buf, 0, toWrite);
+               written += toWrite;
+        }
+        os.close();
+        
+        // Insert it to the established node.
+        System.out.println("Inserting test data to an established node.");
+        FreenetURI uri = insertData(dataFile);
+        
+        // Bootstrap a second node.
+        secondInnerDir.mkdir();
+        fis = new FileInputStream(seednodes);
+        FileUtil.writeTo(fis, new File(secondInnerDir, "seednodes.fref"));
+        fis.close();
+        PooledExecutor executor = new PooledExecutor();
+        Node secondNode = NodeStarter.createTestNode(DARKNET_PORT, 
OPENNET_PORT, dir.getPath(), true, false, false, Node.DEFAULT_MAX_HTL, 0, 
random, executor, 1000, 5*1024*1024, true, true, true, true, true, true, true, 
12*1024, false, true);        
+        secondNode.start(true);
+        waitForTenNodes(secondNode);
+        
+        // Fetch the data
+        long startFetchTime = System.currentTimeMillis();
+        HighLevelSimpleClient client = 
secondNode.clientCore.makeClient((short)0);
+        try {
+                       client.fetch(uri);
+               } catch (FetchException e) {
+                       System.err.println("FETCH FAILED: "+e);
+                       e.printStackTrace();
+                       System.exit(EXIT_FETCH_FAILED);
+                       return;
+               }
+               long endFetchTime = System.currentTimeMillis();
+               System.out.println("RESULT: Fetch took 
"+(endFetchTime-startFetchTime)+"ms 
("+TimeUtil.formatTime(endFetchTime-startFetchTime)+") of "+uri+" .");
+               secondNode.park();
+               System.exit(0);
+
+       }
+
+       private static FreenetURI insertData(File dataFile) throws IOException {
+        long startInsertTime = System.currentTimeMillis();
+        InetAddress localhost = InetAddress.getByName("127.0.0.1");
+        Socket sock = new Socket(localhost, 9481);
+        OutputStream sockOS = sock.getOutputStream();
+        InputStream sockIS = sock.getInputStream();
+        System.out.println("Connected to node.");
+        LineReadingInputStream lis = new LineReadingInputStream(sockIS);
+        OutputStreamWriter osw = new OutputStreamWriter(sockOS, "UTF-8");
+        
osw.write("ClientHello\nExpectedVersion=0.7\nName=BootstrapPullTest-"+System.currentTimeMillis()+"\nEnd\n");
+        osw.flush();
+               String name = lis.readLine(65536, 128, true);
+               SimpleFieldSet fs = new SimpleFieldSet(lis, 65536, 128, true, 
true, false, true);
+               if(!name.equals("NodeHello")) {
+                       System.err.println("No NodeHello from insertor node!");
+                       System.exit(EXIT_INSERTER_PROBLEM);
+               }
+               System.out.println("Connected to "+sock);
+               
osw.write("ClientPut\nIdentifier=test-insert\nURI=CHK@\nVerbosity=1023\nUploadFrom=direct\nMaxRetries=-1\nDataLength="+TEST_SIZE+"\nData\n");
+               osw.flush();
+               InputStream is = new FileInputStream(dataFile);
+               FileUtil.copy(is, sockOS, TEST_SIZE);
+               System.out.println("Sent data");
+               while(true) {
+               name = lis.readLine(65536, 128, true);
+               fs = new SimpleFieldSet(lis, 65536, 128, true, true, false, 
true);
+                       System.out.println("Got FCP message: \n"+name);
+                       System.out.print(fs.toOrderedString());
+                       if(name.equals("ProtocolError")) {
+                               System.err.println("Protocol error when 
inserting data.");
+                               System.exit(EXIT_INSERTER_PROBLEM);
+                       }
+                       if(name.equals("PutFailed")) {
+                               System.err.println("Insert failed");
+                               System.exit(EXIT_INSERT_FAILED);
+                       }
+                       if(name.equals("PutSuccessful")) {
+                       long endInsertTime = System.currentTimeMillis();
+                               FreenetURI uri = new FreenetURI(fs.get("URI"));
+                       System.out.println("RESULT: Insert took 
"+(endInsertTime-startInsertTime)+"ms 
("+TimeUtil.formatTime(endInsertTime-startInsertTime)+") to "+uri+" .");
+                               sockOS.close();
+                               sockIS.close();
+                               sock.close();
+                               return uri;
+                       }
+               }
+       }
+       
+       private static void waitForTenNodes(Node node) throws 
InterruptedException {
+       long startTime = System.currentTimeMillis();
+        // Wait until we have 10 connected nodes...
+        int seconds = 0;
+        boolean success = false;
+        while(seconds < 600) {
+               Thread.sleep(1000);
+               int seeds = node.peers.countSeednodes();
+               int seedConns = 
node.peers.getConnectedSeedServerPeersVector(null).size();
+               int opennetPeers = node.peers.countValidPeers();
+               int opennetConns = node.peers.countConnectedOpennetPeers();
+               System.err.println(""+seconds+" : seeds: "+seeds+", connected: 
"+seedConns
+                               +" opennet: peers: "+opennetPeers+", connected: 
"+opennetConns);
+               seconds++;
+               if(opennetConns >= TARGET_PEERS) {
+                       long timeTaken = System.currentTimeMillis()-startTime;
+                       System.out.println("RESULT: Completed bootstrap 
("+TARGET_PEERS+" peers) in "+timeTaken+"ms 
("+TimeUtil.formatTime(timeTaken)+")");
+                       success = true;
+                       break;
+               }
+        }
+        if(!success) {
+               System.err.println("Failed to reach target peers count 
"+TARGET_PEERS+" in 10 minutes.");
+               node.park();
+               System.exit(EXIT_FAILED_TARGET);
+        }
+       }
+
+
+}

Added: 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPushPullTest.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPushPullTest.java
                              (rev 0)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapPushPullTest.java
      2008-08-15 08:20:48 UTC (rev 21886)
@@ -0,0 +1,143 @@
+package freenet.node.simulator;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import freenet.client.ClientMetadata;
+import freenet.client.FetchException;
+import freenet.client.HighLevelSimpleClient;
+import freenet.client.InsertBlock;
+import freenet.client.InsertException;
+import freenet.crypt.RandomSource;
+import freenet.keys.FreenetURI;
+import freenet.node.Node;
+import freenet.node.NodeInitException;
+import freenet.node.NodeStarter;
+import freenet.support.Executor;
+import freenet.support.Logger;
+import freenet.support.PooledExecutor;
+import freenet.support.TimeUtil;
+import freenet.support.LoggerHook.InvalidThresholdException;
+import freenet.support.api.Bucket;
+import freenet.support.io.FileUtil;
+
+public class BootstrapPushPullTest {
+
+       public static int TARGET_PEERS = 10;
+       public static int TEST_SIZE = 1024*1024;
+       
+       public static int EXIT_NO_SEEDNODES = 257;
+       public static int EXIT_FAILED_TARGET = 258;
+       public static int EXIT_INSERT_FAILED = 259;
+       public static int EXIT_FETCH_FAILED = 260;
+       
+       public static void main(String[] args) throws 
InvalidThresholdException, IOException, NodeInitException, InterruptedException 
{
+        File dir = new File("bootstrap-push-pull-test");
+        FileUtil.removeAll(dir);
+        RandomSource random = NodeStarter.globalTestInit(dir.getPath(), false, 
Logger.ERROR, "");
+        File seednodes = new File("seednodes.fref");
+        if(!seednodes.exists() || seednodes.length() == 0 || 
!seednodes.canRead()) {
+               System.err.println("Unable to read seednodes.fref, it doesn't 
exist, or is empty");
+               System.exit(EXIT_NO_SEEDNODES);
+        }
+        File innerDir = new File(dir, "5000");
+        innerDir.mkdir();
+        FileInputStream fis = new FileInputStream(seednodes);
+        FileUtil.writeTo(fis, new File(innerDir, "seednodes.fref"));
+        fis.close();
+        // Create one node
+        Executor executor = new PooledExecutor();
+        Node node = NodeStarter.createTestNode(5000, 5001, dir.getPath(), 
true, false, false, Node.DEFAULT_MAX_HTL, 0, random, executor, 1000, 
5*1024*1024, true, true, true, true, true, true, true, 12*1024, false, true);
+        //NodeCrypto.DISABLE_GROUP_STRIP = true;
+       //Logger.setupStdoutLogging(Logger.MINOR, 
"freenet:NORMAL,freenet.node.NodeDispatcher:MINOR,freenet.node.FNPPacketMangler:MINOR");
+       Logger.getChain().setThreshold(Logger.ERROR); // kill logging
+       // Start it
+        node.start(true);
+        waitForTenNodes(node);
+        System.out.println("Creating test data: "+TEST_SIZE+" bytes.");
+        Bucket data = node.clientCore.tempBucketFactory.makeBucket(TEST_SIZE);
+        OutputStream os = data.getOutputStream();
+        byte[] buf = new byte[4096];
+        for(long written = 0; written < TEST_SIZE;) {
+               node.fastWeakRandom.nextBytes(buf);
+               int toWrite = (int) Math.min(TEST_SIZE - written, buf.length);
+               os.write(buf, 0, toWrite);
+               written += toWrite;
+        }
+        os.close();
+        System.out.println("Inserting test data.");
+        HighLevelSimpleClient client = node.clientCore.makeClient((short)0);
+        InsertBlock block = new InsertBlock(data, new ClientMetadata(null), 
FreenetURI.EMPTY_CHK_URI);
+        long startInsertTime = System.currentTimeMillis();
+        FreenetURI uri;
+        try {
+                       uri = client.insert(block, false, null);
+               } catch (InsertException e) {
+                       System.err.println("INSERT FAILED: "+e);
+                       e.printStackTrace();
+                       System.exit(EXIT_INSERT_FAILED);
+                       return;
+               }
+        long endInsertTime = System.currentTimeMillis();
+        System.out.println("RESULT: Insert took 
"+(endInsertTime-startInsertTime)+"ms 
("+TimeUtil.formatTime(endInsertTime-startInsertTime)+") to "+uri+" .");
+        node.park();
+               
+        // Bootstrap a second node.
+        File secondInnerDir = new File(dir, "5002");
+        secondInnerDir.mkdir();
+        fis = new FileInputStream(seednodes);
+        FileUtil.writeTo(fis, new File(secondInnerDir, "seednodes.fref"));
+        fis.close();
+        executor = new PooledExecutor();
+        Node secondNode = NodeStarter.createTestNode(5002, 5003, 
dir.getPath(), true, false, false, Node.DEFAULT_MAX_HTL, 0, random, executor, 
1000, 5*1024*1024, true, true, true, true, true, true, true, 12*1024, false, 
true);        
+        secondNode.start(true);
+        waitForTenNodes(secondNode);
+        
+        // Fetch the data
+        long startFetchTime = System.currentTimeMillis();
+        client = secondNode.clientCore.makeClient((short)0);
+        try {
+                       client.fetch(uri);
+               } catch (FetchException e) {
+                       System.err.println("FETCH FAILED: "+e);
+                       e.printStackTrace();
+                       System.exit(EXIT_FETCH_FAILED);
+                       return;
+               }
+               long endFetchTime = System.currentTimeMillis();
+               System.out.println("RESULT: Fetch took 
"+(endFetchTime-startFetchTime)+"ms 
("+TimeUtil.formatTime(endFetchTime-startFetchTime)+") of "+uri+" .");
+               secondNode.park();
+               System.exit(0);
+       }
+
+       private static void waitForTenNodes(Node node) throws 
InterruptedException {
+       long startTime = System.currentTimeMillis();
+        // Wait until we have 10 connected nodes...
+        int seconds = 0;
+        boolean success = false;
+        while(seconds < 600) {
+               Thread.sleep(1000);
+               int seeds = node.peers.countSeednodes();
+               int seedConns = 
node.peers.getConnectedSeedServerPeersVector(null).size();
+               int opennetPeers = node.peers.countValidPeers();
+               int opennetConns = node.peers.countConnectedOpennetPeers();
+               System.err.println(""+seconds+" : seeds: "+seeds+", connected: 
"+seedConns
+                               +" opennet: peers: "+opennetPeers+", connected: 
"+opennetConns);
+               seconds++;
+               if(opennetConns >= TARGET_PEERS) {
+                       long timeTaken = System.currentTimeMillis()-startTime;
+                       System.out.println("RESULT: Completed bootstrap 
("+TARGET_PEERS+" peers) in "+timeTaken+"ms 
("+TimeUtil.formatTime(timeTaken)+")");
+                       success = true;
+                       break;
+               }
+        }
+        if(!success) {
+               System.err.println("Failed to reach target peers count 
"+TARGET_PEERS+" in 10 minutes.");
+               node.park();
+               System.exit(EXIT_FAILED_TARGET);
+        }
+       }
+
+}

Added: 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapSeedTest.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapSeedTest.java
                          (rev 0)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/simulator/BootstrapSeedTest.java
  2008-08-15 08:20:48 UTC (rev 21886)
@@ -0,0 +1,77 @@
+package freenet.node.simulator;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import freenet.crypt.RandomSource;
+import freenet.node.Node;
+import freenet.node.NodeInitException;
+import freenet.node.NodeStarter;
+import freenet.support.Executor;
+import freenet.support.Logger;
+import freenet.support.PooledExecutor;
+import freenet.support.TimeUtil;
+import freenet.support.LoggerHook.InvalidThresholdException;
+import freenet.support.io.FileUtil;
+
+public class BootstrapSeedTest {
+
+       public static int TARGET_PEERS = 10;
+       public static int EXIT_NO_SEEDNODES = 257;
+       public static int EXIT_FAILED_TARGET = 258;
+       
+       /**
+        * @param args
+        * @throws InvalidThresholdException 
+        * @throws NodeInitException 
+        * @throws InterruptedException 
+        * @throws IOException 
+        */
+       public static void main(String[] args) throws 
InvalidThresholdException, NodeInitException, InterruptedException, IOException 
{
+        File dir = new File("bootstrap-test");
+        FileUtil.removeAll(dir);
+        RandomSource random = NodeStarter.globalTestInit(dir.getPath(), false, 
Logger.ERROR, "");
+        File seednodes = new File("seednodes.fref");
+        if(!seednodes.exists() || seednodes.length() == 0 || 
!seednodes.canRead()) {
+               System.err.println("Unable to read seednodes.fref, it doesn't 
exist, or is empty");
+               System.exit(EXIT_NO_SEEDNODES);
+        }
+        File innerDir = new File(dir, "5000");
+        innerDir.mkdir();
+        FileInputStream fis = new FileInputStream(seednodes);
+        FileUtil.writeTo(fis, new File(innerDir, "seednodes.fref"));
+        fis.close();
+        // Create one node
+        Executor executor = new PooledExecutor();
+        Node node = NodeStarter.createTestNode(5000, 5001, "bootstrap-test", 
true, false, false, Node.DEFAULT_MAX_HTL, 0, random, executor, 1000, 
5*1024*1024, true, true, true, true, true, true, true, 12*1024, false, true);
+        //NodeCrypto.DISABLE_GROUP_STRIP = true;
+       //Logger.setupStdoutLogging(Logger.MINOR, 
"freenet:NORMAL,freenet.node.NodeDispatcher:MINOR,freenet.node.FNPPacketMangler:MINOR");
+       Logger.getChain().setThreshold(Logger.ERROR); // kill logging
+       long startTime = System.currentTimeMillis();
+       // Start it
+        node.start(true);
+        // Wait until we have 10 connected nodes...
+        int seconds = 0;
+        while(seconds < 600) {
+               Thread.sleep(1000);
+               int seeds = node.peers.countSeednodes();
+               int seedConns = 
node.peers.getConnectedSeedServerPeersVector(null).size();
+               int opennetPeers = node.peers.countValidPeers();
+               int opennetConns = node.peers.countConnectedOpennetPeers();
+               System.err.println(""+seconds+" : seeds: "+seeds+", connected: 
"+seedConns
+                               +" opennet: peers: "+opennetPeers+", connected: 
"+opennetConns);
+               seconds++;
+               if(opennetConns >= TARGET_PEERS) {
+                       long timeTaken = System.currentTimeMillis()-startTime;
+                       System.out.println("Completed bootstrap 
("+TARGET_PEERS+" peers) in "+timeTaken+"ms 
("+TimeUtil.formatTime(timeTaken)+")");
+                       node.park();
+                       System.exit(0);
+               }
+        }
+        System.err.println("Failed to reach target peers count 
"+TARGET_PEERS+" in 5 minutes.");
+               node.park();
+        System.exit(EXIT_FAILED_TARGET);
+       }
+
+}

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeBusyNetworkTest.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeBusyNetworkTest.java
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeBusyNetworkTest.java
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -157,9 +157,9 @@
                        totalRunningRequests += 
nodes[i].clientCore.countQueuedRequests();
                }
                System.err.println("Running requests: "+totalRunningRequests);
-               if(totalRunningRequests == 0) return;
+               if(totalRunningRequests == 0) break;
                Thread.sleep(1000);
         }
-        
+        System.exit(0);
     }
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeRequestInsertTest.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeRequestInsertTest.java
  2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/simulator/RealNodeRequestInsertTest.java
  2008-08-15 08:20:48 UTC (rev 21886)
@@ -24,7 +24,6 @@
 import freenet.support.Executor;
 import freenet.support.Logger;
 import freenet.support.PooledExecutor;
-import freenet.support.StringArray;
 import freenet.support.LoggerHook.InvalidThresholdException;
 import freenet.support.io.ArrayBucket;
 import freenet.support.io.FileUtil;
@@ -209,7 +208,7 @@
                 if(totalRunningUIDsAlt != 0)
                        System.err.println("Still running UIDs (alt): 
"+totalRunningUIDsAlt);
                 if(!runningUIDsList.isEmpty()) {
-                       System.err.println("List of running UIDs: 
"+StringArray.toString(runningUIDsList.toArray()));
+                       System.err.println("List of running UIDs: 
"+Arrays.toString(runningUIDsList.toArray()));
                 }
             } catch (Throwable t) {
                 Logger.error(RealNodeRequestInsertTest.class, "Caught "+t, t);

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/simulator/SeednodePingTest.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/simulator/SeednodePingTest.java
   2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/simulator/SeednodePingTest.java
   2008-08-15 08:20:48 UTC (rev 21886)
@@ -10,7 +10,6 @@
 import freenet.node.Announcer;
 import freenet.node.FSParseException;
 import freenet.node.Node;
-import freenet.node.NodeCrypto;
 import freenet.node.NodeInitException;
 import freenet.node.NodeStarter;
 import freenet.node.OpennetDisabledException;
@@ -39,7 +38,6 @@
         // Create one node
         Executor executor = new PooledExecutor();
        Node node = NodeStarter.createTestNode(5000, 5001, "seednode-pingtest", 
true, false, false, Node.DEFAULT_MAX_HTL, 0, random, executor, 1000, 
5*1024*1024, true, false, false, false, false, false, false, 0, false, false);
-       NodeCrypto.DISABLE_GROUP_STRIP = true;
        // Connect & ping
        Vector<SeedServerTestPeerNode> seedNodes = new 
Vector<SeedServerTestPeerNode>();
        Vector<SimpleFieldSet> seedNodesAsSFS = Announcer.readSeednodes(new 
File("/tmp/"));

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/updater/NodeUpdateManager.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/updater/NodeUpdateManager.java
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/updater/NodeUpdateManager.java
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -804,29 +804,29 @@

        // Config callbacks

-       class UpdaterEnabledCallback implements BooleanCallback {
+       class UpdaterEnabledCallback extends BooleanCallback  {

-               public boolean get() {
+               public Boolean get() {
                        return isEnabled();
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        enable(val);
                }
        }

-       class AutoUpdateAllowedCallback implements BooleanCallback {
+       class AutoUpdateAllowedCallback extends BooleanCallback  {

-               public boolean get() {
+               public Boolean get() {
                        return isAutoUpdateAllowed();
                }

-               public void set(boolean val) throws InvalidConfigValueException 
{
+               public void set(Boolean val) throws InvalidConfigValueException 
{
                        setAutoUpdateAllowed(val);
                }
        }

-       class UpdateURICallback implements StringCallback {
+       class UpdateURICallback extends StringCallback  {

                boolean isExt;

@@ -847,10 +847,9 @@
                        }
                        setURI(isExt, uri);
                }
-
        }

-       public class UpdateRevocationURICallback implements StringCallback {
+       public class UpdateRevocationURICallback extends StringCallback  {

                public String get() {
                        return getRevocationURI().toString(false, false);
@@ -865,7 +864,6 @@
                        }
                        setRevocationURI(uri);
                }
-               
        }

        /** Called when a peer indicates in its UOMAnnounce that it has fetched 
the revocation key

Modified: 
branches/saltedhashstore/freenet/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
       2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
       2008-08-15 08:20:48 UTC (rev 21886)
@@ -17,6 +17,7 @@
                this.node = n;
        }

+       @Override
        public String getTitle() {
                return l10n("noNodeNickTitle");
        }
@@ -25,14 +26,17 @@
                return L10n.getString("MeaningfulNodeNameUserAlert."+key);
        }

+       @Override
        public String getText() {
                return l10n("noNodeNick");
        }

+       @Override
        public String getShortText() {
                return l10n("noNodeNickShort");
        }

+       @Override
        public HTMLNode getHTMLText() {
                SubConfig sc = node.config.get("node");
                Option o = sc.getOption("name");
@@ -55,4 +59,8 @@
                return alertNode;
        }

+       @Override
+       public boolean isValid() {
+               return node.peers.anyDarknetPeers();
+       }
 }

Deleted: 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/HttpPlugin.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/HttpPlugin.java  
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/HttpPlugin.java  
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,48 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.oldplugins.plugin;
-
-import java.io.IOException;
-
-import freenet.clients.http.ToadletContext;
-import freenet.clients.http.ToadletContextClosedException;
-import freenet.support.api.HTTPRequest;
-
-/**
- * Interface for plugins that support HTTP interaction.
- * 
- * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
- * @version $Id$
- */
-public interface HttpPlugin extends Plugin {
-
-       /**
-        * Handles the GET request.
-        * 
-        * @param request
-        *            The request used to interact with this plugin
-        * @param context
-        *            The context of the HTTP request
-        * @throws IOException
-        *             if an I/O error occurs
-        * @throws ToadletContextClosedException
-        *             if the context has already been closed.
-        */
-       public void handleGet(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException;
-
-       /**
-        * Handles the POST request.
-        * 
-        * @param request
-        *            The request used to interact with this plugin
-        * @param context
-        *            The context of the HTTP request
-        * @throws IOException
-        *             if an I/O error occurs
-        * @throws ToadletContextClosedException
-        *             if the context has already been closed.
-        */
-       public void handlePost(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException;
-
-}

Deleted: 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/Plugin.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/Plugin.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/Plugin.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,40 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.oldplugins.plugin;
-
-/**
- * Interface for Fred plugins.
- * 
- * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
- * @version $Id$
- */
-public interface Plugin {
-
-       /**
-        * Returns the name of the plugin.
-        * 
-        * @return The name of the plugin
-        */
-       public String getPluginName();
-
-       /**
-        * Sets the plugin manager that manages this plugin.
-        * 
-        * @param pluginManager
-        *            The plugin manager
-        */
-       public void setPluginManager(PluginManager pluginManager);
-
-       /**
-        * Starts the plugin. If the plugin needs threads they have to be 
started
-        * here.
-        */
-       public void startPlugin();
-
-       /**
-        * Stops the plugin.
-        */
-       public void stopPlugin();
-
-}

Deleted: 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/PluginManager.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/PluginManager.java
   2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/PluginManager.java
   2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,238 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.oldplugins.plugin;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.List;
-
-import freenet.config.InvalidConfigValueException;
-import freenet.config.SubConfig;
-import freenet.l10n.L10n;
-import freenet.node.Node;
-import freenet.node.NodeClientCore;
-import freenet.support.Logger;
-import freenet.support.StringArray;
-import freenet.support.api.StringArrCallback;
-
-/**
- * Manages plugins.
- * 
- * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
- * @version $Id$
- */
-public class PluginManager {
-
-       /** Object used for synchronization. */
-       private final Object syncObject = new Object();
-
-       /** The node. */
-       private final Node node;
-
-       /** The configuration of this plugin manager. */
-       private final SubConfig config;
-
-       /** Currently loaded plugins. */
-       private List plugins = new ArrayList();
-
-       /**
-        * Creates a new plugin manager.
-        * 
-        * @param node
-        *            The node
-        */
-       public PluginManager(Node node) {
-               this.node = node;
-
-               config = new SubConfig("pluginmanager2", node.config);
-               config.register("loadedPlugins", new String[0], 9, true, true, 
"PluginManager.loadedPlugins", "PluginManager.loadedPluginsLong", new 
StringArrCallback() {
-
-                       /**
-                        * Returns the current value of this option.
-                        * 
-                        * @see freenet.support.api.StringArrCallback#get()
-                        * @return The current value of this option
-                        */
-                       public String[] get() {
-                               if(plugins.size() == 0) return new String[0];
-                               String[] retval;
-                               synchronized (syncObject) {
-                                       retval = new String[plugins.size()];
-                                       for(int i=0;i<plugins.size();i++) {
-                                               Plugin plugin = (Plugin) 
plugins.get(i);
-                                               retval[i] = 
plugin.getClass().getName();
-                                       }
-                               }
-                               if(Logger.shouldLog(Logger.MINOR, this))
-                                       Logger.minor(this, "Plugin list: " + 
StringArray.toString(retval));
-                               return retval;
-                       };
-
-                       /**
-                        * Sets the new value of this option.
-                        * 
-                        * @see 
freenet.support.api.StringArrCallback#set(java.lang.String)
-                        * @param val
-                        *            The new value
-                        * @throws InvalidConfigValueException
-                        *             if setting the value is not allowed, or 
the new value
-                        *             is not valid
-                        */
-                       public void set(String[] val) throws 
InvalidConfigValueException {
-                               throw new 
InvalidConfigValueException(L10n.getString("PluginManager.cannotSetOnceLoaded"));
-                       };
-               });
-
-               String[] loadedPluginNames = 
config.getStringArr("loadedPlugins");
-               if (loadedPluginNames != null && loadedPluginNames.length > 0) {
-                       for (int pluginIndex = 0, pluginCount = 
loadedPluginNames.length; pluginIndex < pluginCount; pluginIndex++) {
-                               String pluginName = 
loadedPluginNames[pluginIndex];
-                               try {
-                                       addPlugin(pluginName, false);
-                               } catch (Throwable t) {
-                                       Logger.error(this, "Failed to load 
plugin "+pluginName+" : "+t, t);
-                               }
-                       }
-               }
-
-               config.finishedInitialization();
-       }
-
-       /**
-        * Returns the node that created this plugin manager.
-        * 
-        * @return The node that created this plugin manager
-        */
-       public Node getNode() {
-               return node;
-       }
-
-       /**
-        * Returns all currently loaded plugins. The array is returned in no
-        * particular order.
-        * 
-        * @return All currently loaded plugins
-        */
-       public Plugin[] getPlugins() {
-               synchronized (syncObject) {
-                       return (Plugin[]) plugins.toArray(new 
Plugin[plugins.size()]);
-               }
-       }
-
-       /**
-        * Adds a plugin to the plugin manager. The name can contain a URL for 
a jar
-        * file from which the plugin is then loaded. If it does the URL and the
-        * plugin name are separated by a '@', e.g.
-        * 'plugin.TestPlugin at http://www.example.com/test.jar'. URLs can 
contain
-        * every protocol your VM understands.
-        * 
-        * @see URL
-        * @param pluginName
-        *            The name of the plugin
-        *            
-        * FIXME IllegalArgumentException is not the best way to convey an 
error here!
-        */
-       public void addPlugin(String pluginName, final boolean store) throws 
IllegalArgumentException {
-               if(Logger.shouldLog(Logger.MINOR, this)) 
-                       Logger.minor(this, "Loading plugin 
"+pluginName+(store?"" : " (don't store afterwards)"));
-               final Plugin newPlugin = createPlugin(pluginName);
-               if (newPlugin == null) {
-                       throw new IllegalArgumentException();
-               }
-               newPlugin.setPluginManager(this);
-               synchronized (syncObject) {
-                       plugins.add(newPlugin);
-               }
-               node.executor.execute(new Runnable() {
-                       public void run() {
-                               try{
-                                       while(!node.isHasStarted())
-                                               Thread.sleep(1000);
-                               }catch (InterruptedException e) {}
-                               newPlugin.startPlugin();
-                               if(store)
-                                       node.clientCore.storeConfig();
-                       }
-               }, "Plugin loader");
-               
-       }
-
-       /**
-        * Remoes the plugin from the list of running plugins. The plugin is 
stopped
-        * before removing it.
-        * 
-        * @param plugin
-        *            The plugin to remove
-        */
-       public void removePlugin(Plugin plugin, boolean store) {
-               plugin.stopPlugin();
-               synchronized (syncObject) {
-                       plugins.remove(plugin);
-               }
-               if(store)
-                       node.clientCore.storeConfig();
-       }
-
-       /**
-        * Creates a plugin from a name. The name can contain a URL for a jar 
file
-        * from which the plugin is then loaded. If it does the URL and the 
plugin
-        * name are separated by a '@', e.g.
-        * 'plugin.TestPlugin at http://www.example.com/test.jar'. URLs can 
contain
-        * every protocol your VM understands.
-        * <p>
-        * <b>WARNING:</b> The code to load JAR files from URLs has <b>not</b>
-        * been tested.
-        * 
-        * @see URL
-        * @param pluginName
-        *            The name of the plugin
-        * @return The created plugin, or <code>null</code> if the plugin could
-        *         not be created
-        */
-       private Plugin createPlugin(String pluginName) {
-               int p = pluginName.indexOf('@');
-               String pluginSource = null;
-               
-               /* split up */
-               if (p > -1) {
-                       pluginSource = pluginName.substring(p + 1);
-                       pluginName = pluginName.substring(0, p);
-               }
-
-               /* load jar file */
-               ClassLoader classLoader = getClass().getClassLoader();
-               if (pluginSource != null) {
-                       try {
-                               URL pluginSourceUrl = new URL(pluginSource);
-                               classLoader = new URLClassLoader(new URL[] { 
pluginSourceUrl });
-                       } catch (MalformedURLException mue1) {
-                               Logger.normal(this, "could not create class 
loader", mue1);
-                               return null;
-                       }
-               }
-
-               /* load class from class loader */
-               try {
-                       Class pluginClass = classLoader.loadClass(pluginName);
-                       if (Plugin.class.isAssignableFrom(pluginClass)) {
-                               Plugin plugin = (Plugin) 
pluginClass.newInstance();
-                               return plugin;
-                       }
-               } catch (ClassNotFoundException e) {
-                       Logger.normal(this, "could not find plugin class: " + 
pluginName+" : "+e, e);
-               } catch (InstantiationException e) {
-                       Logger.normal(this, "could not instantiate plugin 
class: " + pluginName+" : "+e, e);
-               } catch (IllegalAccessException e) {
-                       Logger.normal(this, "could not instantiate plugin 
class: " + pluginName+" : "+e, e);
-               }
-               return null;
-       }
-
-       public NodeClientCore getClientCore() {
-               return node.clientCore;
-       }
-
-}

Deleted: 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestHttpPlugin.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestHttpPlugin.java
  2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestHttpPlugin.java
  2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,62 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.oldplugins.plugin;
-
-import java.io.IOException;
-
-import freenet.clients.http.ToadletContext;
-import freenet.clients.http.ToadletContextClosedException;
-import freenet.support.MultiValueTable;
-import freenet.support.api.HTTPRequest;
-
-/**
- * Test HTTP plugin. Outputs "Plugin works" to the browser.
- * 
- * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
- * @version $Id$
- */
-public class TestHttpPlugin implements HttpPlugin {
-
-       /**
-        * @throws ToadletContextClosedException
-        * @see 
freenet.oldplugins.plugin.HttpPlugin#handleGet(freenet.clients.http.HTTPRequestImpl)
-        */
-       public void handleGet(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException {
-               byte[] messageBytes = "Plugin works.".getBytes("UTF-8");
-               context.sendReplyHeaders(200, "OK", new MultiValueTable(), 
"text/html; charset=utf-8", messageBytes.length);
-               context.writeData(messageBytes, 0, messageBytes.length);
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.HttpPlugin#handlePost(freenet.clients.http.HTTPRequestImpl)
-        */
-       public void handlePost(HTTPRequest request, ToadletContext context) 
throws IOException, ToadletContextClosedException {
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#getPluginName()
-        */
-       public String getPluginName() {
-               return "Simple HTTP Test Plugin";
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.Plugin#setPluginManager(freenet.oldplugins.plugin.PluginManager)
-        */
-       public void setPluginManager(PluginManager pluginManager) {
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#startPlugin()
-        */
-       public void startPlugin() {
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#stopPlugin()
-        */
-       public void stopPlugin() {
-       }
-
-}

Deleted: 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestPlugin.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestPlugin.java  
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/oldplugins/plugin/TestPlugin.java  
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,39 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.oldplugins.plugin;
-
-/**
- * Test plugin. Does absolutely nothing.
- * 
- * @author David 'Bombe' Roden &lt;bombe at freenetproject.org&gt;
- * @version $Id$
- */
-public class TestPlugin implements Plugin {
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#getPluginName()
-        */
-       public String getPluginName() {
-               return "Simple Test Plugin";
-       }
-
-       /**
-        * @see 
freenet.oldplugins.plugin.Plugin#setPluginManager(freenet.oldplugins.plugin.PluginManager)
-        */
-       public void setPluginManager(PluginManager pluginManager) {
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#startPlugin()
-        */
-       public void startPlugin() {
-       }
-
-       /**
-        * @see freenet.oldplugins.plugin.Plugin#stopPlugin()
-        */
-       public void stopPlugin() {
-       }
-
-}

Added: 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/FredPluginBandwidthIndicator.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/FredPluginBandwidthIndicator.java
                                (rev 0)
+++ 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/FredPluginBandwidthIndicator.java
        2008-08-15 08:20:48 UTC (rev 21886)
@@ -0,0 +1,17 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.pluginmanager;
+
+public interface FredPluginBandwidthIndicator {
+
+       /**
+        * @return the reported upstream bit rate in bits per second. -1 if 
it's not available. Blocking.
+        */
+       public int getUpstramMaxBitRate();
+
+       /**
+        * @return the reported downstream bit rate in bits per second. -1 if 
it's not available. Blocking.
+        */
+       public int getDownstreamMaxBitRate();
+}

Modified: 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginDownLoaderOfficial.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginDownLoaderOfficial.java
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginDownLoaderOfficial.java
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -3,12 +3,15 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.pluginmanager;

+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLConnection;
 import java.security.KeyStore;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
@@ -35,7 +38,29 @@

        @Override
        String getSHA1sum() throws PluginNotFoundException {
-               return null;
+               try {
+                       URL sha1url = new URL(getSource().toString()+".sha1");
+                       URLConnection urlConnection = sha1url.openConnection();
+                       urlConnection.setUseCaches(false);
+                       urlConnection.setAllowUserInteraction(false);
+                       
+                       InputStream is = 
openConnectionCheckRedirects(urlConnection);
+                       ByteArrayOutputStream bos = new ByteArrayOutputStream();
+               
+                       byte[] buffer = new byte[1024];
+                       int read;
+               
+                       while ((read = is.read(buffer)) != -1) {
+                               bos.write(buffer, 0, read);
+                       }
+                       
+                       return new String(bos.toByteArray()).split(" ")[0];
+       
+               } catch (MalformedURLException e) {
+                       throw new PluginNotFoundException("impossible: "+e,e);
+               } catch (IOException e) {
+                       throw new PluginNotFoundException("Error while fetching 
sha1 for plugin: "+e,e);
+               }
        }

        @Override

Modified: 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginInfoWrapper.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginInfoWrapper.java
   2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginInfoWrapper.java
   2008-08-15 08:20:48 UTC (rev 21886)
@@ -5,37 +5,37 @@

 import freenet.l10n.L10n;
 import freenet.support.Logger;
-import freenet.support.StringArray;

 public class PluginInfoWrapper {
        // Parameters to make the object OTP
        private boolean fedPluginThread = false;
        // Public since only PluginHandler will know about it
-       private String className;
+       private final String className;
        private Thread thread;
-       private long start;
-       private String threadName;
+       private final long start;
+       private final String threadName;
        final FredPlugin plug;
-       private boolean isPproxyPlugin;
-       private boolean isThreadlessPlugin;
-       private boolean isIPDetectorPlugin;
-       private boolean isPortForwardPlugin;
-       private boolean isMultiplePlugin;
-       private boolean isFCPPlugin;
-       private boolean isVersionedPlugin;
-       private String filename;
-       private HashSet toadletLinks=new HashSet();
-       private boolean stopping = false;
-       private boolean unregistered = false;
+       private final boolean isPproxyPlugin;
+       private final boolean isThreadlessPlugin;
+       private final boolean isIPDetectorPlugin;
+       private final boolean isBandwidthIndicator;
+       private final boolean isPortForwardPlugin;
+       private final boolean isMultiplePlugin;
+       private final boolean isFCPPlugin;
+       private final boolean isVersionedPlugin;
+       private final String filename;
+       private HashSet<String> toadletLinks = new HashSet<String>();
+       private volatile boolean stopping = false;
+       private volatile boolean unregistered = false;

        public PluginInfoWrapper(FredPlugin plug, String filename) {
                this.plug = plug;
-               if (fedPluginThread) return;
                className = plug.getClass().toString();
                this.filename = filename;
                threadName = 'p' + className.replaceAll("^class ", "") + '_' + 
hashCode();
                start = System.currentTimeMillis();
                fedPluginThread = true;
+               isBandwidthIndicator = (plug instanceof 
FredPluginBandwidthIndicator);
                isPproxyPlugin = (plug instanceof FredPluginHTTP);
                isThreadlessPlugin = (plug instanceof FredPluginThreadless);
                isIPDetectorPlugin = (plug instanceof FredPluginIPDetector);
@@ -52,6 +52,7 @@
                thread.setName(threadName);
        }

+       @Override
        public String toString() {
                return "ID: \"" +threadName + "\", Name: "+ className +", 
Started: " + (new Date(start)).toString();
        }
@@ -77,7 +78,7 @@
        }

        public synchronized String[] getPluginToadletSymlinks(){
-               return StringArray.toArray(toadletLinks.toArray());
+               return toadletLinks.toArray(new String[0]);
        }

        public synchronized boolean addPluginToadletSymlink(String linkfrom){
@@ -143,6 +144,8 @@
                        
manager.node.ipDetector.unregisterIPDetectorPlugin((FredPluginIPDetector)plug);
                if(isPortForwardPlugin)
                        
manager.node.ipDetector.unregisterPortForwardPlugin((FredPluginPortForward)plug);
+               if(isBandwidthIndicator)
+                       
manager.node.ipDetector.unregisterBandwidthIndicatorPlugin((FredPluginBandwidthIndicator)plug);
        }

        public boolean isPproxyPlugin() {
@@ -152,6 +155,10 @@
        public String getFilename() {
                return filename;
        }
+       
+       public boolean isBandwidthIndicator() {
+               return isBandwidthIndicator;
+       }

        public boolean isThreadlessPlugin() {
                return isThreadlessPlugin;

Modified: 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginManager.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginManager.java   
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/pluginmanager/PluginManager.java   
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -6,20 +6,15 @@
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
 import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.Vector;
@@ -45,7 +40,6 @@
 import freenet.support.Logger;
 import freenet.support.api.HTTPRequest;
 import freenet.support.api.StringArrCallback;
-import freenet.support.api.StringCallback;
 import freenet.support.io.Closer;
 import freenet.support.io.FileUtil;

@@ -60,16 +54,11 @@
         * TODO: Synchronize
         *
         */
-
-       private String configFile;
-       private String installDir;
-       
        private final HashMap toadletList;

        /* All currently starting plugins. */
-       private final Set/* <PluginProgress> */startingPlugins = new HashSet/* 
<PluginProgress> */();
-
-       private final Vector/* <PluginInfoWrapper> */pluginWrappers;
+       private final Set<PluginProgress> startingPlugins = new 
HashSet<PluginProgress>();
+       private final Vector<PluginInfoWrapper> pluginWrappers;
        final Node node;
        private final NodeClientCore core;
        SubConfig pmconfig;
@@ -78,23 +67,25 @@
        private final HighLevelSimpleClient client;

        public PluginManager(Node node) {
-               
+
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
                // config 
-               
+
                toadletList = new HashMap();
                pluginWrappers = new Vector();
                this.node = node;
                this.core = node.clientCore;
-               
-               if(logMINOR) Logger.minor(this, "Starting Plugin Manager");
-               
-               if(logDEBUG) Logger.debug(this, "Initialize Plugin Manager 
config");
-               
+
+               if(logMINOR)
+                       Logger.minor(this, "Starting Plugin Manager");
+
+               if(logDEBUG)
+                       Logger.debug(this, "Initialize Plugin Manager config");
+
                client = 
core.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS, true);
-               

+
                pmconfig = new SubConfig("pluginmanager", node.config);
 //             pmconfig.register("configfile", "fplugins.ini", 9, true, true, 
"PluginConfig.configFile", "PluginConfig.configFileLong",
 //                             new StringCallback() {
@@ -124,44 +115,43 @@
 //             installDir = pmconfig.getString("installdir");

                // Start plugins in the config
-               pmconfig.register("loadplugin", null, 9, true, false, 
"PluginManager.loadedOnStartup", "PluginManager.loadedOnStartupLong",
-                               new StringArrCallback() {
-                       public String[] get() {
-                               return getConfigLoadString();
-                       }
+               pmconfig.register("loadplugin", null, 0, true, false, 
"PluginManager.loadedOnStartup", "PluginManager.loadedOnStartupLong",
+                       new StringArrCallback() {

-                       public void set(String[] val) throws 
InvalidConfigValueException {
-                               //if(storeDir.equals(new File(val))) return;
-                               // FIXME
-                               throw new 
InvalidConfigValueException(L10n.getString("PluginManager.cannotSetOnceLoaded"));
-                       }
-               });
+                               public String[] get() {
+                                       return getConfigLoadString();
+                               }

+                               public void set(String[] val) throws 
InvalidConfigValueException {
+                                       //if(storeDir.equals(new File(val))) 
return;
+                                       // FIXME
+                                       throw new 
InvalidConfigValueException(L10n.getString("PluginManager.cannotSetOnceLoaded"));
+                               }
+
+                       @Override
+                               public boolean isReadOnly() {
+                                       return true;
+                               }
+                       });
+
                String fns[] = pmconfig.getStringArr("loadplugin");
-               if (fns != null) {
-                       for (String name : fns)
+               if(fns != null)
+                       for(String name : fns)
                                startPluginAuto(name, false);
-               }

                pmconfig.finishedInitialization();
        }

        private String[] getConfigLoadString() {
-               try{
-                       Iterator it = getPlugins().iterator();
-
-                       Vector v = new Vector();
-
-                       while (it.hasNext()) {
-                               PluginInfoWrapper pluginInfoWrapper = 
(PluginInfoWrapper) it.next();
-                               v.add(pluginInfoWrapper.getFilename());
+               Vector<String> v = new Vector<String>();
+               
+               synchronized(pluginWrappers) {
+                       for(PluginInfoWrapper pi : pluginWrappers) {
+                               v.add(pi.getFilename());
                        }
-
-                       return (String[]) v.toArray(new String[v.size()]);
-               }catch (NullPointerException e){
-                       Logger.error(this, "error while loading plugins: 
disabling them:"+e);
-                       return new String[0];
                }
+               
+               return v.toArray(new String[v.size()]);
        }

        /**
@@ -169,32 +159,30 @@
         * 
         * @return All currently starting plugins
         */
-       public Set/* <PluginProgess> */getStartingPlugins() {
-               synchronized (startingPlugins) {
+       public Set/* <PluginProgess> */ getStartingPlugins() {
+               synchronized(startingPlugins) {
                        return new HashSet/* <PluginProgress> 
*/(startingPlugins);
                }
        }
-       
-       
        // try to guess around...
        public void startPluginAuto(final String pluginname, boolean store) {
-               
-               if (isOfficialPlugin(pluginname)) {
+
+               if(isOfficialPlugin(pluginname)) {
                        startPluginOfficial(pluginname, store);
                        return;
                }
-               
+
                try {
                        FreenetURI uri = new FreenetURI(pluginname);
                        startPluginFreenet(pluginname, store);
                        return;
-               } catch (MalformedURLException e) {
+               } catch(MalformedURLException e) {
                        // not a freenet key
                }
-               
+
                File[] roots = File.listRoots();
-               for (File f: roots) {
-                       if (pluginname.startsWith(f.getName())) {
+               for(File f : roots) {
+                       if(pluginname.startsWith(f.getName())) {
                                startPluginFile(pluginname, store);
                                return;
                        }
@@ -202,28 +190,28 @@

                startPluginURL(pluginname, store);
        }
-       
+
        public void startPluginOfficial(final String pluginname, boolean store) 
{
                realStartPlugin(new PluginDownLoaderOfficial(), pluginname, 
store);
        }
-       
+
        public void startPluginFile(final String filename, boolean store) {
                realStartPlugin(new PluginDownLoaderFile(), filename, store);
        }
-       
+
        public void startPluginURL(final String filename, boolean store) {
                realStartPlugin(new PluginDownLoaderURL(), filename, store);
        }
-       
+
        public void startPluginFreenet(final String filename, boolean store) {
                realStartPlugin(new PluginDownLoaderFreenet(client), filename, 
true);
        }
-       
+
        private void realStartPlugin(final PluginDownLoader pdl, final String 
filename, final boolean store) {
-               if (filename.trim().length() == 0)
+               if(filename.trim().length() == 0)
                        return;
                final PluginProgress pluginProgress = new 
PluginProgress(filename);
-               synchronized (startingPlugins) {
+               synchronized(startingPlugins) {
                        startingPlugins.add(pluginProgress);
                }
                node.executor.execute(new Runnable() {
@@ -235,32 +223,29 @@
                                        plug = loadPlugin(pdl, filename);
                                        
pluginProgress.setProgress(PluginProgress.STARTING);
                                        PluginInfoWrapper pi = 
PluginHandler.startPlugin(PluginManager.this, filename, plug, new 
PluginRespirator(node, PluginManager.this));
-                                       synchronized (pluginWrappers) {
+                                       synchronized(pluginWrappers) {
                                                pluginWrappers.add(pi);
                                        }
                                        Logger.normal(this, "Plugin loaded: " + 
filename);
-                               } catch (PluginNotFoundException e) {
+                               } catch(PluginNotFoundException e) {
                                        Logger.normal(this, "Loading plugin 
failed (" + filename + ')', e);
                                        String message = e.getMessage();
-                                       core.alerts.register(new 
SimpleUserAlert(true, l10n("pluginLoadingFailedTitle"), 
l10n("pluginLoadingFailedWithMessage", new String[] { "name", "message" }, new 
String[] { filename, message }), l10n("pluginLoadingFailedShort", "name", 
filename), UserAlert.ERROR, PluginManager.class));
-                               } catch (UnsupportedClassVersionError e) {
+                                       core.alerts.register(new 
SimpleUserAlert(true, l10n("pluginLoadingFailedTitle"), 
l10n("pluginLoadingFailedWithMessage", new String[]{"name", "message"}, new 
String[]{filename, message}), l10n("pluginLoadingFailedShort", "name", 
filename), UserAlert.ERROR, PluginManager.class));
+                               } catch(UnsupportedClassVersionError e) {
                                        Logger.error(this, "Could not load 
plugin " + filename + " : " + e, e);
                                        System.err.println("Could not load 
plugin " + filename + " : " + e);
                                        e.printStackTrace();
-                                       String jvmVersion = 
System.getProperty("java.version");
-                                       if (jvmVersion.startsWith("1.4.") || 
jvmVersion.equals("1.4")) {
-                                               System.err.println("Plugin " + 
filename + " appears to require a later JVM");
-                                               Logger.error(this, "Plugin " + 
filename + " appears to require a later JVM");
-                                               core.alerts.register(new 
SimpleUserAlert(true, l10n("pluginReqNewerJVMTitle", "name", filename), 
l10n("pluginReqNewerJVM", "name", filename), l10n("pluginLoadingFailedShort", 
"name", filename), UserAlert.ERROR, PluginManager.class));
-                                       }
+                                       System.err.println("Plugin " + filename 
+ " appears to require a later JVM");
+                                       Logger.error(this, "Plugin " + filename 
+ " appears to require a later JVM");
+                                       core.alerts.register(new 
SimpleUserAlert(true, l10n("pluginReqNewerJVMTitle", "name", filename), 
l10n("pluginReqNewerJVM", "name", filename), l10n("pluginLoadingFailedShort", 
"name", filename), UserAlert.ERROR, PluginManager.class));
                                } finally {
-                                       synchronized (startingPlugins) {
+                                       synchronized(startingPlugins) {
                                                
startingPlugins.remove(pluginProgress);
                                        }
                                }
                                /* try not to destroy the config. */
-                               synchronized (this) {
-                                       if (store)
+                               synchronized(this) {
+                                       if(store)
                                                core.storeConfig();
                                }
                        }
@@ -270,15 +255,15 @@
        void register(FredPlugin plug, PluginInfoWrapper pi) {
                // handles FProxy? If so, register

-               if (pi.isPproxyPlugin())
+               if(pi.isPproxyPlugin())
                        registerToadlet(plug);

-               if(pi.isIPDetectorPlugin()) {
+               if(pi.isIPDetectorPlugin())
                        
node.ipDetector.registerIPDetectorPlugin((FredPluginIPDetector) plug);
-               }
-               if(pi.isPortForwardPlugin()) {
+               if(pi.isPortForwardPlugin())
                        
node.ipDetector.registerPortForwardPlugin((FredPluginPortForward) plug);
-               }
+               if(pi.isBandwidthIndicator())
+                       
node.ipDetector.registerBandwidthIndicatorPlugin((FredPluginBandwidthIndicator) 
plug);
        }

        /**
@@ -294,7 +279,7 @@
        }

        private String l10n(String key, String pattern, String value) {
-               return L10n.getString("PluginManager."+key, pattern, value);
+               return L10n.getString("PluginManager." + key, pattern, value);
        }

        /**
@@ -313,20 +298,21 @@
                return L10n.getString("PluginManager." + key, patterns, values);
        }

-       private void registerToadlet(FredPlugin pl){
+       private void registerToadlet(FredPlugin pl) {
                //toadletList.put(e.getStackTrace()[1].getClass().toString(), 
pl);
-               synchronized (toadletList) {
+               synchronized(toadletList) {
                        toadletList.put(pl.getClass().getName(), pl);
                }
-               Logger.normal(this, "Added HTTP handler for 
/plugins/"+pl.getClass().getName()+ '/');
+               Logger.normal(this, "Added HTTP handler for /plugins/" + 
pl.getClass().getName() + '/');
        }

        /**
         * Remove a plugin from the plugin list.
         */
        public void removePlugin(PluginInfoWrapper pi) {
-               synchronized (pluginWrappers) {
-                       if(!pluginWrappers.remove(pi)) return;
+               synchronized(pluginWrappers) {
+                       if(!pluginWrappers.remove(pi))
+                               return;
                }
                core.storeConfig();
        }
@@ -340,79 +326,77 @@
        public void removeCachedCopy(String pluginSpecification) {
                int lastSlash = pluginSpecification.lastIndexOf('/');
                File pluginFile;
-               if (lastSlash == -1) {
+               if(lastSlash == -1)
                        /* Windows, maybe? */
                        lastSlash = pluginSpecification.lastIndexOf('\\');
-               }
                File pluginDirectory = new File(node.getNodeDir(), "plugins");
-               if (lastSlash == -1) {
+               if(lastSlash == -1)
                        /* it's an official plugin! */
                        pluginFile = new File(pluginDirectory, 
pluginSpecification + ".jar");
-               } else {
+               else
                        pluginFile = new File(pluginDirectory, 
pluginSpecification.substring(lastSlash + 1));
-               }
-               if(logDEBUG) Logger.minor(this, "Delete plugin - plugname: " + 
pluginSpecification + "filename: " + pluginFile.getAbsolutePath() , new 
Exception("debug"));
-               if (pluginFile.exists()) {
+               if(logDEBUG)
+                       Logger.minor(this, "Delete plugin - plugname: " + 
pluginSpecification + "filename: " + pluginFile.getAbsolutePath(), new 
Exception("debug"));
+               if(pluginFile.exists())
                        pluginFile.delete();
-               }
        }

        public void unregisterPluginToadlet(PluginInfoWrapper pi) {
-               synchronized (toadletList) {
+               synchronized(toadletList) {
                        try {
                                toadletList.remove(pi.getPluginClassName());
-                               Logger.normal(this, "Removed HTTP handler for 
/plugins/"+
-                                               pi.getPluginClassName()+ '/', 
new Exception("debug"));
-                       } catch (Throwable ex) {
+                               Logger.normal(this, "Removed HTTP handler for 
/plugins/" +
+                                       pi.getPluginClassName() + '/', new 
Exception("debug"));
+                       } catch(Throwable ex) {
                                Logger.error(this, "removing Plugin", ex);
                        }
                }
        }

        public void addToadletSymlinks(PluginInfoWrapper pi) {
-               synchronized (toadletList) {
+               synchronized(toadletList) {
                        try {
                                String targets[] = 
pi.getPluginToadletSymlinks();
-                               if (targets == null)
+                               if(targets == null)
                                        return;

-                               for (int i = 0 ; i < targets.length ; i++) {
+                               for(int i = 0; i < targets.length; i++) {
                                        toadletList.remove(targets[i]);
                                        Logger.normal(this, "Removed HTTP 
symlink: " + targets[i] +
-                                                       " => 
/plugins/"+pi.getPluginClassName()+ '/');
+                                               " => /plugins/" + 
pi.getPluginClassName() + '/');
                                }
-                       } catch (Throwable ex) {
+                       } catch(Throwable ex) {
                                Logger.error(this, "removing Toadlet-link", ex);
                        }
                }
        }

        public void removeToadletSymlinks(PluginInfoWrapper pi) {
-               synchronized (toadletList) {
+               synchronized(toadletList) {
                        String rm = null;
                        try {
                                String targets[] = 
pi.getPluginToadletSymlinks();
-                               if (targets == null)
+                               if(targets == null)
                                        return;

-                               for (int i = 0 ; i < targets.length ; i++) {
+                               for(int i = 0; i < targets.length; i++) {
                                        rm = targets[i];
                                        toadletList.remove(targets[i]);
                                        
pi.removePluginToadletSymlink(targets[i]);
                                        Logger.normal(this, "Removed HTTP 
symlink: " + targets[i] +
-                                                       " => 
/plugins/"+pi.getPluginClassName()+ '/');
+                                               " => /plugins/" + 
pi.getPluginClassName() + '/');
                                }
-                       } catch (Throwable ex) {
+                       } catch(Throwable ex) {
                                Logger.error(this, "removing Toadlet-link: " + 
rm, ex);
                        }
                }
        }

        public String dumpPlugins() {
-               StringBuffer out= new StringBuffer();
-               synchronized (pluginWrappers) {
-                       for(int i=0;i<pluginWrappers.size();i++) {
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginWrappers.get(i);
+               StringBuffer out = new StringBuffer();
+               synchronized(pluginWrappers) {
+                       for(int i = 0; i < pluginWrappers.size(); i++) {
+                               PluginInfoWrapper pi = pluginWrappers.get(i);
                                out.append(pi.toString());
                                out.append('\n');
                        }
@@ -422,57 +406,57 @@

        public Set getPlugins() {
                HashSet out = new HashSet();
-               synchronized (pluginWrappers) {
-                       for(int i=0;i<pluginWrappers.size();i++) {
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginWrappers.get(i);
+               synchronized(pluginWrappers) {
+                       for(int i = 0; i < pluginWrappers.size(); i++) {
+                               PluginInfoWrapper pi = pluginWrappers.get(i);
                                out.add(pi);
                        }
                }
                return out;
        }
-       
+
        /**
         * look for PluginInfo for a FCPPlugin with given classname
         * @param plugname
         * @return the PluginInfo or null if not found
         */
        public PluginInfoWrapper getFCPPluginInfo(String plugname) {
-               synchronized (pluginWrappers) {
-                       for(int i=0;i<pluginWrappers.size();i++) {
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginWrappers.get(i);
-                               if (pi.getPluginClassName().equals(plugname))
+               synchronized(pluginWrappers) {
+                       for(int i = 0; i < pluginWrappers.size(); i++) {
+                               PluginInfoWrapper pi = pluginWrappers.get(i);
+                               if(pi.getPluginClassName().equals(plugname))
                                        return pi;
                        }
                }
                return null;
        }
-       
+
        /**
         * look for a FCPPlugin with given classname
         * @param plugname
         * @return the plugin or null if not found
         */
        public FredPluginFCP getFCPPlugin(String plugname) {
-               synchronized (pluginWrappers) {
-                       for(int i=0;i<pluginWrappers.size();i++) {
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginWrappers.get(i);
-                               if (pi.isFCPPlugin() && 
pi.getPluginClassName().equals(plugname))
+               synchronized(pluginWrappers) {
+                       for(int i = 0; i < pluginWrappers.size(); i++) {
+                               PluginInfoWrapper pi = pluginWrappers.get(i);
+                               if(pi.isFCPPlugin() && 
pi.getPluginClassName().equals(plugname))
                                        return (FredPluginFCP) pi.plug;
                        }
                }
                return null;
        }
-       
+
        /**
         * look for a Plugin with given classname
         * @param plugname
         * @return the true if not found
         */
        public boolean isPluginLoaded(String plugname) {
-               synchronized (pluginWrappers) {
-                       for(int i=0;i<pluginWrappers.size();i++) {
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginWrappers.get(i);
-                               if (pi.getPluginClassName().equals(plugname))
+               synchronized(pluginWrappers) {
+                       for(int i = 0; i < pluginWrappers.size(); i++) {
+                               PluginInfoWrapper pi = pluginWrappers.get(i);
+                               if(pi.getPluginClassName().equals(plugname))
                                        return true;
                        }
                }
@@ -481,30 +465,30 @@

        public String handleHTTPGet(String plugin, HTTPRequest request) throws 
PluginHTTPException {
                FredPlugin handler = null;
-               synchronized (toadletList) {
-                       handler = (FredPlugin)toadletList.get(plugin);
+               synchronized(toadletList) {
+                       handler = (FredPlugin) toadletList.get(plugin);
                }
                /*if (handler == null)
-                 return null;
+               return null;
                 */

-               if (handler instanceof FredPluginHTTP)
-                       return ((FredPluginHTTP)handler).handleHTTPGet(request);
+               if(handler instanceof FredPluginHTTP)
+                       return ((FredPluginHTTP) 
handler).handleHTTPGet(request);

                throw new NotFoundPluginHTTPException("Plugin not found!", 
"/plugins");
        }

        public String handleHTTPPost(String plugin, HTTPRequest request) throws 
PluginHTTPException {
                FredPlugin handler = null;
-               synchronized (toadletList) {
-                       handler = (FredPlugin)toadletList.get(plugin);
+               synchronized(toadletList) {
+                       handler = (FredPlugin) toadletList.get(plugin);
                }
                /*if (handler == null)
-                 return null;
+               return null;
                 */

-               if (handler instanceof FredPluginHTTP)
-                       return 
((FredPluginHTTP)handler).handleHTTPPost(request);
+               if(handler instanceof FredPluginHTTP)
+                       return ((FredPluginHTTP) 
handler).handleHTTPPost(request);

                throw new NotFoundPluginHTTPException("Plugin not found!", 
"/plugins");
        }
@@ -512,36 +496,33 @@
        public void killPlugin(String name, int maxWaitTime) {
                PluginInfoWrapper pi = null;
                boolean found = false;
-               synchronized (pluginWrappers) {
-                       for(int i=0;i<pluginWrappers.size() && !found;i++) {
-                               pi = (PluginInfoWrapper) pluginWrappers.get(i);
-                               if (pi.getThreadName().equals(name)) {
+               synchronized(pluginWrappers) {
+                       for(int i = 0; i < pluginWrappers.size() && !found; 
i++) {
+                               pi = pluginWrappers.get(i);
+                               if(pi.getThreadName().equals(name)) {
                                        found = true;
                                        break;
                                }
                        }
                }
-               if (found) {
+               if(found)
                        pi.stopPlugin(this, maxWaitTime);
-               }
        }

        public void killPlugin(FredPlugin plugin, int maxWaitTime) {
                PluginInfoWrapper pi = null;
                boolean found = false;
-               synchronized (pluginWrappers) {
-                       for(int i=0;i<pluginWrappers.size() && !found;i++) {
-                               pi = (PluginInfoWrapper) pluginWrappers.get(i);
-                               if (pi.plug == plugin) {
+               synchronized(pluginWrappers) {
+                       for(int i = 0; i < pluginWrappers.size() && !found; 
i++) {
+                               pi = pluginWrappers.get(i);
+                               if(pi.plug == plugin)
                                        found = true;
-                               }
                        }
                }
-               if (found) {
+               if(found)
                        pi.stopPlugin(this, maxWaitTime);
-               }
        }
-       
+
        /**
         * Returns a list of the names of all available official plugins. Right 
now
         * this list is hardcoded but in future we could retrieve this list 
from emu
@@ -550,7 +531,7 @@
         * @return A list of all available plugin names
         */
        public List<String> findAvailablePlugins() {
-               List<String> availablePlugins = new ArrayList<String> ();
+               List<String> availablePlugins = new ArrayList<String>();
                availablePlugins.add("Echo");
                availablePlugins.add("Freemail");
                availablePlugins.add("HelloWorld");
@@ -566,12 +547,14 @@
                availablePlugins.add("XMLSpider");
                return availablePlugins;
        }
-       
+
        public boolean isOfficialPlugin(String name) {
-               if ((name == null) || (name.trim().length() == 0)) return false;
+               if((name == null) || (name.trim().length() == 0))
+                       return false;
                List<String> availablePlugins = findAvailablePlugins();
-               for(String n:availablePlugins) {
-                       if (n.equals(name)) return true;                        
+               for(String n : availablePlugins) {
+                       if(n.equals(name))
+                               return true;
                }
                return false;
        }
@@ -592,12 +575,12 @@
         *             If anything goes wrong.
         */
        private FredPlugin loadPlugin(PluginDownLoader pdl, String name) throws 
PluginNotFoundException {
-               
+
                pdl.setSource(name);
-               
+
                /* check for plugin directory. */
                File pluginDirectory = new File(node.getNodeDir(), "plugins");
-               if ((pluginDirectory.exists() && 
!pluginDirectory.isDirectory()) || (!pluginDirectory.exists() && 
!pluginDirectory.mkdirs())) {
+               if((pluginDirectory.exists() && !pluginDirectory.isDirectory()) 
|| (!pluginDirectory.exists() && !pluginDirectory.mkdirs())) {
                        Logger.error(this, "could not create plugin directory");
                        throw new PluginNotFoundException("could not create 
plugin directory");
                }
@@ -607,12 +590,11 @@
                File pluginFile = new File(pluginDirectory, filename);

                /* check if file needs to be downloaded. */
-               if (logMINOR) {
+               if(logMINOR)
                        Logger.minor(this, "plugin file " + 
pluginFile.getAbsolutePath() + " exists: " + pluginFile.exists());
-               }
                int RETRIES = 5;
-               for(int i=0;i<RETRIES;i++) {
-                       if (!pluginFile.exists() || pluginFile.length() == 0) {
+               for(int i = 0; i < RETRIES; i++) {
+                       if(!pluginFile.exists() || pluginFile.length() == 0)
                                try {
                                        File tempPluginFile = null;
                                        OutputStream pluginOutputStream = null;
@@ -620,13 +602,13 @@
                                        try {
                                                tempPluginFile = 
File.createTempFile("plugin-", ".jar", pluginDirectory);
                                                tempPluginFile.deleteOnExit();
-                                               
-                                               
+
+
                                                pluginOutputStream = new 
FileOutputStream(tempPluginFile);
                                                pluginInputStream = 
pdl.getInputStream();
                                                byte[] buffer = new byte[1024];
                                                int read;
-                                               while ((read = 
pluginInputStream.read(buffer)) != -1) {
+                                               while((read = 
pluginInputStream.read(buffer)) != -1) {
                                                        
pluginOutputStream.write(buffer, 0, read);
                                                }
                                                pluginOutputStream.close();
@@ -636,33 +618,32 @@
                                                        Logger.error(this, 
"could not rename temp file to plugin file");
                                                        throw new 
PluginNotFoundException("could not rename temp file to plugin file");
                                                }
-                                               
+
                                                String digest = 
pdl.getSHA1sum();
-                                               if (digest != null) {
+                                               if(digest != null) {
                                                        String testsum = 
getFileSHA1(pluginFile);
-                                                       if 
(!(digest.equalsIgnoreCase(testsum))) {
-                                                               
Logger.error(this, "Checksum verification failed, should be " +digest+ " but 
was " + testsum);
-                                                               throw new 
PluginNotFoundException("Checksum verification failed, should be " +digest+ " 
but was " + testsum);
+                                                       
if(!(digest.equalsIgnoreCase(testsum))) {
+                                                               
Logger.error(this, "Checksum verification failed, should be " + digest + " but 
was " + testsum);
+                                                               throw new 
PluginNotFoundException("Checksum verification failed, should be " + digest + " 
but was " + testsum);
                                                        }
                                                }
-                                               
-                                       } catch (IOException ioe1) {
+
+                                       } catch(IOException ioe1) {
                                                Logger.error(this, "could not 
load plugin", ioe1);
-                                               if (tempPluginFile != null) {
+                                               if(tempPluginFile != null)
                                                        tempPluginFile.delete();
-                                               }
                                                throw new 
PluginNotFoundException("could not load plugin: " + ioe1.getMessage(), ioe1);
                                        } finally {
                                                
Closer.close(pluginOutputStream);
                                                Closer.close(pluginInputStream);
                                        }
-                               } catch (PluginNotFoundException e) {
-                                       if(i < RETRIES-1) {
-                                               Logger.normal(this, "Failed to 
load plugin: "+e, e);
+                               } catch(PluginNotFoundException e) {
+                                       if(i < RETRIES - 1) {
+                                               Logger.normal(this, "Failed to 
load plugin: " + e, e);
                                                continue;
-                                       } else throw e;
+                                       } else
+                                               throw e;
                                }
-                       }
                }

                /* now get the manifest file. */
@@ -671,32 +652,32 @@
                try {
                        pluginJarFile = new JarFile(pluginFile);
                        Manifest manifest = pluginJarFile.getManifest();
-                       if (manifest == null) {
+                       if(manifest == null) {
                                Logger.error(this, "could not load manifest 
from plugin file");
                                pluginFile.delete();
                                throw new PluginNotFoundException("could not 
load manifest from plugin file");
                        }
                        Attributes mainAttributes = 
manifest.getMainAttributes();
-                       if (mainAttributes == null) {
+                       if(mainAttributes == null) {
                                Logger.error(this, "manifest does not contain 
attributes");
                                pluginFile.delete();
                                throw new PluginNotFoundException("manifest 
does not contain attributes");
                        }
                        pluginMainClassName = 
mainAttributes.getValue("Plugin-Main-Class");
-                       if (pluginMainClassName == null) {
+                       if(pluginMainClassName == null) {
                                Logger.error(this, "manifest does not contain a 
Plugin-Main-Class attribute");
                                pluginFile.delete();
                                throw new PluginNotFoundException("manifest 
does not contain a Plugin-Main-Class attribute");
                        }
-               } catch (JarException je1) {
+               } catch(JarException je1) {
                        Logger.error(this, "could not process jar file", je1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("could not process 
jar file", je1);
-               } catch (ZipException ze1) {
+               } catch(ZipException ze1) {
                        Logger.error(this, "could not process jar file", ze1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("could not process 
jar file", ze1);
-               } catch (IOException ioe1) {
+               } catch(IOException ioe1) {
                        Logger.error(this, "error processing jar file", ioe1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("error procesesing 
jar file", ioe1);
@@ -708,36 +689,36 @@
                        JarClassLoader jarClassLoader = new 
JarClassLoader(pluginFile);
                        Class pluginMainClass = 
jarClassLoader.loadClass(pluginMainClassName);
                        Object object = pluginMainClass.newInstance();
-                       if (!(object instanceof FredPlugin)) {
+                       if(!(object instanceof FredPlugin)) {
                                Logger.error(this, "plugin main class is not a 
plugin");
                                pluginFile.delete();
                                throw new PluginNotFoundException("plugin main 
class is not a plugin");
                        }
                        return (FredPlugin) object;
-               } catch (IOException ioe1) {
+               } catch(IOException ioe1) {
                        Logger.error(this, "could not load plugin", ioe1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("could not load 
plugin", ioe1);
-               } catch (ClassNotFoundException cnfe1) {
+               } catch(ClassNotFoundException cnfe1) {
                        Logger.error(this, "could not find plugin class", 
cnfe1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("could not find 
plugin class", cnfe1);
-               } catch (InstantiationException ie1) {
+               } catch(InstantiationException ie1) {
                        Logger.error(this, "could not instantiate plugin", ie1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("could not 
instantiate plugin", ie1);
-               } catch (IllegalAccessException iae1) {
+               } catch(IllegalAccessException iae1) {
                        Logger.error(this, "could not access plugin main 
class", iae1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("could not access 
plugin main class", iae1);
-               } catch (NoClassDefFoundError ncdfe1) {
+               } catch(NoClassDefFoundError ncdfe1) {
                        Logger.error(this, "could not find class def, may a 
missing lib?", ncdfe1);
                        pluginFile.delete();
                        throw new PluginNotFoundException("could not find class 
def, may a missing lib?", ncdfe1);
-               } catch (Throwable t) {
+               } catch(Throwable t) {
                        Logger.error(this, "unexcpected error while plugin 
loading", t);
                        pluginFile.delete();
-                       throw new PluginNotFoundException("unexcpected error 
while plugin loading "+t, t);
+                       throw new PluginNotFoundException("unexcpected error 
while plugin loading " + t, t);
                }
        }

@@ -746,7 +727,7 @@
                MessageDigest hash = null;
                FileInputStream fis = null;
                BufferedInputStream bis = null;
-               
+
                try {
                        hash = MessageDigest.getInstance("SHA-1");
                        // We compute the hash
@@ -757,9 +738,9 @@
                        byte[] buffer = new byte[BUFFERSIZE];
                        while((len = bis.read(buffer)) > -1) {
                                hash.update(buffer, 0, len);
-                       }       
-               } catch (Exception e) {
-                       throw new PluginNotFoundException("Error while 
computing sha1 hash of the downloaded plugin: "+e, e);
+                       }
+               } catch(Exception e) {
+                       throw new PluginNotFoundException("Error while 
computing sha1 hash of the downloaded plugin: " + e, e);
                } finally {
                        Closer.close(bis);
                        Closer.close(fis);
@@ -781,16 +762,12 @@

                /** State for downloading. */
                public static final PluginProgress DOWNLOADING = new 
PluginProgress();
-
                /** State for starting. */
                public static final PluginProgress STARTING = new 
PluginProgress();
-
                /** The starting time. */
                private long startingTime = System.currentTimeMillis();
-
                /** The current state. */
                private PluginProgress pluginProgress;
-
                /** The name by which the plugin is loaded. */
                private String name;

@@ -859,16 +836,13 @@
                 * 
                 * @return The name of a constant, or the plugin progress
                 */
-               /* @Override */
+               @Override
                public String toString() {
-                       if (this == DOWNLOADING) {
+                       if(this == DOWNLOADING)
                                return "downloading";
-                       } else if (this == STARTING) {
+                       else if(this == STARTING)
                                return "starting";
-                       }
                        return "PluginProgress[name=" + name + ",startingTime=" 
+ startingTime + ",progress=" + pluginProgress + "]";
                }
-
        }
-
 }

Modified: branches/saltedhashstore/freenet/src/freenet/support/Fields.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/Fields.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/Fields.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -60,6 +60,15 @@
                        'y',
                        'z' };

+       private static final long[] MULTIPLES = {
+               1000,                                           1l << 10,
+               1000 * 1000,                                    1l << 20,
+               1000l * 1000l * 1000l,                          1l << 30,
+               1000l * 1000l * 1000l * 1000l,                  1l << 40,
+               1000l * 1000l * 1000l * 1000l * 1000,           1l << 50,
+               1000l * 1000l * 1000l * 1000l * 1000l * 1000l,  1l << 60
+       };
+       
        /**
         * Converts a hex string into a long. Long.parseLong(hex, 16) assumes 
the
         * input is nonnegative unless there is a preceding minus sign. This 
method
@@ -597,23 +606,20 @@
        }

     /**
-     * Parse a human-readable string possibly including SI units into a short.
+     * Parse a human-readable string possibly including SI and ICE units into 
a short.
         * @throws NumberFormatException
         *             if the string is not parseable
         */
-       public static short parseSIShort(String s) throws NumberFormatException 
{
+       public static short parseShort(String s) throws NumberFormatException {
+               s = s.replaceFirst("(i)*B$", "");
                short res = 1;
                int x = s.length() - 1;
                int idx;
                try {
-                       long[] l =
-                               {
-                                       1000,
-                                       1 << 10 };
                        while ((x >= 0)
                                && ((idx = "kK".indexOf(s.charAt(x))) != -1)) {
                                x--;
-                               res *= l[idx];
+                               res *= MULTIPLES[idx];
                        }
                        res *= Double.parseDouble(s.substring(0, x + 1));
                } catch (ArithmeticException e) {
@@ -624,27 +630,20 @@
        }

        /**
-        * Parse a human-readable string possibly including SI units into an 
integer.
+        * Parse a human-readable string possibly including SI and ICE units 
into an integer.
         * @throws NumberFormatException
         *             if the string is not parseable
         */
-       public static int parseSIInt(String s) throws NumberFormatException {
+       public static int parseInt(String s) throws NumberFormatException {
+               s = s.replaceFirst("(i)*B$", "");
                int res = 1;
                int x = s.length() - 1;
                int idx;
                try {
-                       long[] l =
-                               {
-                                       1000,
-                                       1 << 10,
-                                       1000 * 1000,
-                                       1 << 20,
-                                       1000 * 1000 * 1000,
-                                       1 << 30 };
                        while ((x >= 0)
                                && ((idx = "kKmMgG".indexOf(s.charAt(x))) != 
-1)) {
                                x--;
-                               res *= l[idx];
+                               res *= MULTIPLES[idx];
                        }
                        res *= Double.parseDouble(s.substring(0, x + 1));
                } catch (ArithmeticException e) {
@@ -655,33 +654,20 @@
        }

        /**
-        * Parse a human-readable string possibly including SI units into a 
long.
+        * Parse a human-readable string possibly including SI and ICE units 
into a long.
         * @throws NumberFormatException
         *             if the string is not parseable
         */
-       public static long parseSILong(String s) throws NumberFormatException {
+       public static long parseLong(String s) throws NumberFormatException {
+               s = s.replaceFirst("(i)*B$", "");
                long res = 1;
                int x = s.length() - 1;
                int idx;
                try {
-                       long[] l =
-                               {
-                                       1000,
-                                       1 << 10,
-                                       1000 * 1000,
-                                       1 << 20,
-                                       1000l * 1000l * 1000l,
-                                       1l << 30,
-                                       1000l * 1000l * 1000l * 1000l,
-                                       1l << 40,
-                                       1000l * 1000l * 1000l * 1000l * 1000,
-                                       1l << 50,
-                                       1000l * 1000l * 1000l * 1000l * 1000l * 
1000l,
-                                       1l << 60 };
                        while ((x >= 0)
                                && ((idx = "kKmMgGtTpPeE".indexOf(s.charAt(x))) 
!= -1)) {
                                x--;
-                               res *= l[idx];
+                               res *= MULTIPLES[idx];
                        }
                        String multiplier = s.substring(0, x + 1).trim();
                        if(multiplier.indexOf('.') > -1 || 
multiplier.indexOf('E') > -1) {
@@ -698,6 +684,60 @@
                return res;
        }

+       public static String longToString(long val) {
+               String[] u = { "k", "K", "m", "M", "g", "G", "t", "T", "p", 
"P", "e", "E" };
+               String ret = Long.toString(val);
+
+               if (val <= 0)
+                       return ret;
+
+               for (int i = MULTIPLES.length - 1; i >= 0; i--) {
+                       if (val > MULTIPLES[i] && val % MULTIPLES[i] == 0) {
+                               ret = (val / MULTIPLES[i]) + u[i];
+                               if (!u[i].toLowerCase().equals(u[i]))
+                                       ret += "iB";
+                               break;
+                       }
+               }
+               return ret;
+       }
+
+       public static String intToString(int val) {
+               String[] u = { "k", "K", "m", "M", "g", "G", "t", "T", "p", 
"P", "e", "E" };
+               String ret = Integer.toString(val);
+
+               if (val <= 0)
+                       return ret;
+
+               for (int i = MULTIPLES.length - 1; i >= 0; i--) {
+                       if (val > MULTIPLES[i] && val % MULTIPLES[i] == 0) {
+                               ret = (val / MULTIPLES[i]) + u[i];
+                               if (!u[i].toLowerCase().equals(u[i]))
+                                       ret += "iB";
+                               break;
+                       }
+               }
+               return ret;
+       }
+
+       public static String shortToString(short val) {
+               String[] u = { "k", "K", "m", "M", "g", "G", "t", "T", "p", 
"P", "e", "E" };
+               String ret = Short.toString(val);
+
+               if (val <= 0)
+                       return ret;
+               
+               for (int i = MULTIPLES.length - 1; i >= 0; i--) {
+                       if (val > MULTIPLES[i] && val % MULTIPLES[i] == 0) {
+                               ret = (val / MULTIPLES[i]) + u[i];
+                               if (!u[i].toLowerCase().equals(u[i]))
+                                       ret += "iB";
+                               break;
+                       }
+               }
+               return ret;
+       }
+
        public static double[] bytesToDoubles(byte[] data, int offset, int 
length) {
                long[] longs = bytesToLongs(data, offset, length);
                double[] doubles = new double[longs.length];

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/PooledExecutor.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/PooledExecutor.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/PooledExecutor.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -16,9 +16,9 @@
 public class PooledExecutor implements Executor {

        /** All threads running or waiting */
-       private final ArrayList[] runningThreads /* <MyThread> */ = new 
ArrayList[NativeThread.JAVA_PRIORITY_RANGE + 1];
+       private final ArrayList<MyThread>[] runningThreads = new 
ArrayList[NativeThread.JAVA_PRIORITY_RANGE + 1];
        /** Threads waiting for a job */
-       private final ArrayList[] waitingThreads /* <MyThread> */ = new 
ArrayList[runningThreads.length];
+       private final ArrayList<MyThread>[] waitingThreads = new 
ArrayList[runningThreads.length];
        long[] threadCounter = new long[runningThreads.length];
        private long jobCount;
        private long jobMisses;
@@ -64,7 +64,7 @@
                        synchronized(this) {
                                jobCount++;
                                if(!waitingThreads[prio - 1].isEmpty()) {
-                                       t = (MyThread) waitingThreads[prio - 
1].remove(waitingThreads[prio - 1].size() - 1);
+                                       t = waitingThreads[prio - 
1].remove(waitingThreads[prio - 1].size() - 1);
                                        if(logMINOR)
                                                Logger.minor(this, "Reusing 
thread " + t);
                                } else {
@@ -140,6 +140,7 @@
                        threadNo = threadCounter;
                }

+               @Override
                public void run() {
                        super.run();
                        long ranJobs = 0;

Modified: branches/saltedhashstore/freenet/src/freenet/support/SizeUtil.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/SizeUtil.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/SizeUtil.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -10,7 +10,17 @@
                return formatSize(sz, false);
        }

+       public static String formatSizeWithoutSpace(long sz) {
+               String[] result = _formatSize(sz);
+               return result[0].concat(result[1]);
+       }
+       
        public static String formatSize(long sz, boolean useNonBreakingSpace) {
+               String[] result = _formatSize(sz);
+               return result[0].concat((useNonBreakingSpace ? "\u00a0" : " 
")).concat(result[1]);
+       }
+       
+       public static String[] _formatSize(long sz) {
                long s = 1;
                int i;
                for(i=0;i<SizeUtil.suffixes.length;i++) {
@@ -24,39 +34,19 @@
                s /= 1024; // we use the previous unit
                if (s == 1)  // Bytes? Then we don't need real numbers with a 
comma
                {
-                       return sz + " " + SizeUtil.suffixes[0];
+                       return new String[] { String.valueOf(sz), 
SizeUtil.suffixes[0] };
                }
                else
                {
                        double mantissa = (double)sz / (double)s;
-                       String o = Double.toString(mantissa);
+                       String o = String.valueOf(mantissa);
                        if(o.indexOf('.') == 3)
                                o = o.substring(0, 3);
                        else if((o.indexOf('.') > -1) && (o.indexOf('E') == -1) 
&& (o.length() > 4))
                                o = o.substring(0, 4);
                        if(i < SizeUtil.suffixes.length) // handle the case 
where the mantissa is Infinity
-                               if(useNonBreakingSpace) {
-                                       o += '\u00a0' + SizeUtil.suffixes[i];
-                               } else {
-                                       o += ' ' + SizeUtil.suffixes[i];
-                               }
-                       return o;
+                               return new String[] { o , SizeUtil.suffixes[i] 
};
+                       return new String[] { o , "" };
                }
        }
-
-       public static String stripBytesEtc(String size) {
-               if(size.length() > 0 && size.charAt(size.length()-1) == 'B')
-                       size = size.substring(0, size.length()-1);
-               if(size.length() > 0 && size.charAt(size.length()-1) == 'i')
-                       size = size.substring(0, size.length()-1);
-               if(size.indexOf(' ') != -1) {
-                       StringBuffer sb = new StringBuffer(size.length()-1);
-                       for(int i=0;i<size.length();i++) {
-                               char c = size.charAt(i);
-                               if(c != ' ') sb.append(c);
-                       }
-                       size = sb.toString();
-               }
-               return size;
-       }
 }

Deleted: branches/saltedhashstore/freenet/src/freenet/support/StringArray.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/StringArray.java       
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/StringArray.java       
2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,101 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-
-package freenet.support;
-
-/**
- * This class implements various toString methods available in java 1.5 but 
not 1.4
- * 
- * @author Florent Daigni&egrave;re &lt;nextgens at freenetproject.org&gt;
- */
-public class StringArray {
-       
-       /**
-        * This method implements the equivalent of Arrays.valueOf() (java 1.5)
-        * @param array
-        * @return string
-        */
-       public static String toString(Object[] array){
-               if((array != null) && (array.length > 0)){
-                       StringBuffer sb = new StringBuffer();
-                       for(int i=0; i<array.length; i++)
-                               sb.append(array[i].toString()+'|');
-                       return '[' + sb.substring(0, sb.length() - 1) + ']';
-               }else
-                       return "";
-       }
-       
-       /**
-        * This method implements the equivalent of Arrays.valueOf() (java 1.5)
-        * @param array
-        * @return string
-        */
-       public static String toString(String[] array){
-               if((array != null) && (array.length > 0)){
-                       StringBuffer sb = new StringBuffer();
-                       for(int i=0; i<array.length; i++)
-                               sb.append(array[i]+'|');
-                       return '[' + sb.substring(0, sb.length() - 1) + ']';
-               }else
-                       return "";
-       }
-       
-       /**
-        * This methods returns a String[] from Object[]
-        * @param array
-        * @return string[]
-        */
-       public static String[] toArray(Object[] array){
-               if((array != null) && (array.length > 0)){
-                       String[] result = new String[array.length];
-                       for(int i=0; i<array.length; i++)
-                               result[i] = (array[i]).toString();
-                       return result;
-               }else
-                       return null;
-       }
-
-       public static String[] toArray(double[] array) {
-               if((array != null) && (array.length > 0)){
-                       String[] result = new String[array.length];
-                       for(int i=0; i<array.length; i++)
-                               result[i] = Double.toString(array[i]);
-                       return result;
-               }else
-                       return null;
-       }
-       
-       public static String toString(double[] array) {
-               return toString(toArray(array));
-       }
-       
-       public static String[] toArray(long[] array) {
-               if((array != null) && (array.length > 0)){
-                       String[] result = new String[array.length];
-                       for(int i=0; i<array.length; i++)
-                               result[i] = Long.toString(array[i]);
-                       return result;
-               }else
-                       return null;
-       }
-       
-       public static String toString(long[] array) {
-               return toString(toArray(array));
-       }
-
-       public static String[] toArray(int[] array) {
-               if((array != null) && (array.length > 0)){
-                       String[] result = new String[array.length];
-                       for(int i=0; i<array.length; i++)
-                               result[i] = Long.toString(array[i]);
-                       return result;
-               }else
-                       return null;
-       }
-       
-       public static String toString(int[] array) {
-               return toString(toArray(array));
-       }
-       
-}

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/TimeSortedHashtable.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/TimeSortedHashtable.java   
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/TimeSortedHashtable.java   
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -9,8 +9,18 @@
 /**
  * Variant on LRUHashtable which provides an efficient how-many-since-time-T 
operation.
  */
-public class TimeSortedHashtable {
-
+public class TimeSortedHashtable implements Cloneable {
+       
+       public TimeSortedHashtable() {
+               this.elements = new TreeSet(new MyComparator());
+               this.valueToElement = new HashMap();
+       }
+       
+       private TimeSortedHashtable(TimeSortedHashtable c) {
+               this.elements = new TreeSet(c.elements);
+               this.valueToElement = new HashMap(c.valueToElement);
+       }
+       
        private static class Element {

                Element(long t, Comparable v) {
@@ -54,9 +64,13 @@

        }

-    private final TreeSet elements = new TreeSet(new MyComparator());
-    private final HashMap valueToElement = new HashMap();
+    private final TreeSet elements;
+    private final HashMap valueToElement;

+    public TimeSortedHashtable clone() {
+           return new TimeSortedHashtable(this);
+    }
+    
     public final void push(Comparable value) {
        push(value, System.currentTimeMillis());
     }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/api/BooleanCallback.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/api/BooleanCallback.java   
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/api/BooleanCallback.java   
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,25 +4,10 @@
 package freenet.support.api;

 import freenet.config.ConfigCallback;
-import freenet.config.InvalidConfigValueException;

 /**
  * A callback to be called when a config value of integer type changes.
  * Also reports the current value.
  */
-public interface BooleanCallback extends ConfigCallback {
-       
-       /**
-        * Get the current, used value of the config variable.
-        */
-       boolean get();
-       
-       /**
-        * Set the config variable to a new value.
-        * @param val The new value.
-        * @throws InvalidConfigOptionException If the new value is invalid for 
-        * this particular option.
-        */
-       void set(boolean val) throws InvalidConfigValueException;
-
+public abstract class BooleanCallback extends ConfigCallback<Boolean> {
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/api/IntCallback.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/api/IntCallback.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/api/IntCallback.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,25 +4,10 @@
 package freenet.support.api;

 import freenet.config.ConfigCallback;
-import freenet.config.InvalidConfigValueException;

 /**
  * A callback to be called when a config value of integer type changes.
  * Also reports the current value.
  */
-public interface IntCallback extends ConfigCallback {
-
-       /**
-        * Get the current, used value of the config variable.
-        */
-       int get();
-       
-       /**
-        * Set the config variable to a new value.
-        * @param val The new value.
-        * @throws InvalidConfigOptionException If the new value is invalid for 
-        * this particular option.
-        */
-       void set(int val) throws InvalidConfigValueException;
-       
+public abstract class IntCallback extends ConfigCallback<Integer> {
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/api/LongCallback.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/api/LongCallback.java  
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/api/LongCallback.java  
2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,25 +4,10 @@
 package freenet.support.api;

 import freenet.config.ConfigCallback;
-import freenet.config.InvalidConfigValueException;

 /**
  * A callback to be called when a config value of long type changes.
  * Also reports the current value.
  */
-public interface LongCallback extends ConfigCallback {
-
-       /**
-        * Get the current, used value of the config variable.
-        */
-       long get();
-       
-       /**
-        * Set the config variable to a new value.
-        * @param val The new value.
-        * @throws InvalidConfigOptionException If the new value is invalid for 
-        * this particular option.
-        */
-       void set(long val) throws InvalidConfigValueException;
-       
+public abstract class LongCallback extends ConfigCallback<Long> {
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/api/ShortCallback.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/api/ShortCallback.java 
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/api/ShortCallback.java 
2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,25 +1,10 @@
 package freenet.support.api;

 import freenet.config.ConfigCallback;
-import freenet.config.InvalidConfigValueException;

 /**
  * A callback to be called when a config value of short type changes.
  * Also reports the current value.
  */
-public interface ShortCallback extends ConfigCallback {
-
-       /**
-        * Get the current, used value of the config variable.
-        */
-       short get();
-       
-       /**
-        * Set the config variable to a new value.
-        * @param val The new value.
-        * @throws InvalidConfigOptionException If the new value is invalid for 
-        * this particular option.
-        */
-       void set(short val) throws InvalidConfigValueException;
-       
+public abstract class ShortCallback extends ConfigCallback<Short> {
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/api/StringArrCallback.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/api/StringArrCallback.java 
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/api/StringArrCallback.java 
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,22 +4,7 @@
 package freenet.support.api;

 import freenet.config.ConfigCallback;
-import freenet.config.InvalidConfigValueException;

 /** Callback (getter/setter) for a string config variable */
-public interface StringArrCallback extends ConfigCallback {
-       
-       /**
-        * Get the current, used value of the config variable.
-        */
-       String[] get();
-
-       /**
-        * Set the config variable to a new value.
-        * @param val The new value.
-        * @throws InvalidConfigOptionException If the new value is invalid for 
-        * this particular option.
-        */
-       void set(String[] val) throws InvalidConfigValueException;
-       
+public abstract class StringArrCallback extends ConfigCallback<String[]> {
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/api/StringCallback.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/api/StringCallback.java    
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/api/StringCallback.java    
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,22 +4,8 @@
 package freenet.support.api;

 import freenet.config.ConfigCallback;
-import freenet.config.InvalidConfigValueException;

 /** Callback (getter/setter) for a string config variable */
-public interface StringCallback extends ConfigCallback {
-       
-       /**
-        * Get the current, used value of the config variable.
-        */
-       String get();
+public abstract class StringCallback extends ConfigCallback<String> {

-       /**
-        * Set the config variable to a new value.
-        * @param val The new value.
-        * @throws InvalidConfigOptionException If the new value is invalid for 
-        * this particular option.
-        */
-       void set(String val) throws InvalidConfigValueException;
-       
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/compress/GzipCompressor.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/compress/GzipCompressor.java
   2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/compress/GzipCompressor.java
   2008-08-15 08:20:48 UTC (rev 21886)
@@ -16,7 +16,7 @@
 public class GzipCompressor extends Compressor {

        public Bucket compress(Bucket data, BucketFactory bf, long maxLength) 
throws IOException, CompressionOutputSizeException {
-               Bucket output = bf.makeBucket(-1);
+               Bucket output = bf.makeBucket(maxLength);
                InputStream is = null;
                OutputStream os = null;
                GZIPOutputStream gos = null;
@@ -53,7 +53,7 @@
                if(preferred != null)
                        output = preferred;
                else
-                       output = bf.makeBucket(-1);
+                       output = bf.makeBucket(maxLength);
                InputStream is = data.getInputStream();
                OutputStream os = output.getOutputStream();
                decompress(is, os, maxLength, maxCheckSizeLength);

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/io/ArrayBucket.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/io/ArrayBucket.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/io/ArrayBucket.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -13,15 +13,13 @@
 /**
  * A bucket that stores data in the memory.
  * 
- * FIXME: No synchronization, should there be?
- * 
  * @author oskar
  */
 public class ArrayBucket implements Bucket {

-       private final ArrayList data;
-       private String name;
-       private boolean readOnly;
+       private final ArrayList<byte[]> data;
+       private final String name;
+       private volatile boolean readOnly;

        public ArrayBucket() {
                this("ArrayBucket");
@@ -33,20 +31,21 @@
        }

        ArrayBucket(String name) {
-               data = new ArrayList();
+               data = new ArrayList<byte[]>();
                this.name = name;
        }

-       public OutputStream getOutputStream() throws IOException {
+       public synchronized OutputStream getOutputStream() throws IOException {
                if(readOnly) throw new IOException("Read only");
                return new ArrayBucketOutputStream();
        }

-       public InputStream getInputStream() {
+       public synchronized InputStream getInputStream() {
                return new ArrayBucketInputStream();
        }

-       public String toString() {
+       @Override
+       public synchronized String toString() {
                StringBuffer s = new StringBuffer(250);
                for (Iterator i = data.iterator(); i.hasNext();) {
                        byte[] b = (byte[]) i.next();
@@ -55,7 +54,7 @@
                return s.toString();
        }

-       public void read(InputStream in) throws IOException {
+       public synchronized void read(InputStream in) throws IOException {
                OutputStream out = new ArrayBucketOutputStream();
                int i;
                byte[] b = new byte[8 * 1024];
@@ -65,13 +64,13 @@
                out.close();
        }

-       public long size() {
-               long size = 0;
-               for (Iterator i = data.iterator(); i.hasNext();) {
-                       byte[] b = (byte[]) i.next();
-                       size += b.length;
-               }
-               return size;
+       public synchronized long size() {
+               long currentSize = 0;
+               
+               for(byte[] buf : data)
+                       currentSize += buf.length;
+               
+               return currentSize;
        }

        public String getName() {
@@ -79,15 +78,30 @@
        }

        private class ArrayBucketOutputStream extends ByteArrayOutputStream {
+               boolean hasBeenClosed = false;

                public ArrayBucketOutputStream() {
                        super();
                }
+               
+               @Override
+               public synchronized void write(byte b[], int off, int len) {
+                       if(readOnly) throw new IllegalStateException("Read 
only");
+                       super.write(b, off, len);
+               }
+               
+               @Override
+               public synchronized void write(int b) {
+                       if(readOnly) throw new IllegalStateException("Read 
only");
+                       super.write(b);
+               }

-               public void close() throws IOException {
+               @Override
+               public synchronized void close() throws IOException {
+                       if(hasBeenClosed) return;
+                       hasBeenClosed = true;
                        data.add(super.toByteArray());
                        if(readOnly) throw new IOException("Read only");
-                       // FIXME maybe we should throw on write instead? :)
                }
        }

@@ -100,11 +114,11 @@
                        i = data.iterator();
                }

-               public int read() {
+               public synchronized int read() {
                        return priv_read();
                }

-               private int priv_read() {
+               private synchronized int priv_read() {
                        if (in == null) {
                                if (i.hasNext()) {
                                        in = new ByteArrayInputStream((byte[]) 
i.next());
@@ -121,15 +135,17 @@
                        }
                }

-               public int read(byte[] b) {
+               @Override
+               public synchronized int read(byte[] b) {
                        return priv_read(b, 0, b.length);
                }

-               public int read(byte[] b, int off, int len) {
+               @Override
+               public synchronized int read(byte[] b, int off, int len) {
                        return priv_read(b, off, len);
                }

-               private int priv_read(byte[] b, int off, int len) {
+               private synchronized int priv_read(byte[] b, int off, int len) {
                        if (in == null) {
                                if (i.hasNext()) {
                                        in = new ByteArrayInputStream((byte[]) 
i.next());
@@ -146,7 +162,8 @@
                        }
                }

-               public int available() {
+               @Override
+               public synchronized int available() {
                        if (in == null) {
                                if (i.hasNext()) {
                                        in = new ByteArrayInputStream((byte[]) 
i.next());
@@ -167,12 +184,12 @@
                readOnly = true;
        }

-       public void free() {
+       public synchronized void free() {
                data.clear();
                // Not much else we can do.
        }

-       public byte[] toByteArray() {
+       public synchronized byte[] toByteArray() {
                long sz = size();
                int size = (int)sz;
                byte[] buf = new byte[size];

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/io/BaseFileBucket.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/io/BaseFileBucket.java 
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/io/BaseFileBucket.java 
2008-08-15 08:20:48 UTC (rev 21886)
@@ -14,8 +14,8 @@
 import freenet.support.Fields;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
-import freenet.support.StringArray;
 import freenet.support.api.Bucket;
+import java.util.Arrays;

 public abstract class BaseFileBucket implements Bucket, 
SerializableToFieldSetBucket {

@@ -392,7 +392,7 @@
                }

                if(toClose != null) {
-                       Logger.error(this, "Streams open free()ing "+this+" : 
"+StringArray.toString(toClose), new Exception("debug"));
+                       Logger.error(this, "Streams open free()ing "+this+" : 
"+Arrays.toString(toClose), new Exception("debug"));
                        for(int i=0;i<toClose.length;i++) {
                                try {
                                        if(toClose[i] instanceof 
FileBucketOutputStream) {
@@ -419,6 +419,7 @@
                }
        }

+       @Override
        public synchronized String toString() {
                return super.toString()+ ':' 
+getFile().getPath()+":streams="+(streams == null ? 0 : streams.size());
        }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/io/BucketTools.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/io/BucketTools.java    
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/io/BucketTools.java    
2008-08-15 08:20:48 UTC (rev 21886)
@@ -288,6 +288,7 @@
                                if(bytes <= 0) {
                                        if(truncateLength == Long.MAX_VALUE)
                                                break;
+                                       new IOException().printStackTrace();
                                        throw new IOException("Could not move 
required quantity of data in copyTo: "+bytes+" (moved "+moved+" of 
"+truncateLength+"): unable to read from "+is);
                                }
                                os.write(buf, 0, bytes);
@@ -419,9 +420,11 @@
                                os.write(buf, 0, thisCycle);
                                x += thisCycle;
                        }
+                       os.close();
+                       os = null;
                        if(b.size() != blockLength)
-                               throw new IllegalStateException();
+                               throw new IllegalStateException("The bucket's 
size is "+b.size()+" whereas it should be "+blockLength+'!');
                        return b;
-               } finally { os.close(); }
+               } finally { Closer.close(os); }
        }
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/io/DelayedFreeBucket.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/io/DelayedFreeBucket.java  
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/io/DelayedFreeBucket.java  
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -20,7 +20,7 @@
        Bucket bucket;
        boolean freed;

-       public DelayedFreeBucket(PersistentTempBucketFactory factory, 
PaddedEphemerallyEncryptedBucket bucket) {
+       public DelayedFreeBucket(PersistentTempBucketFactory factory, Bucket 
bucket) {
                this.factory = factory;
                this.bucket = bucket;
        }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/io/NativeThread.java
===================================================================
--- branches/saltedhashstore/freenet/src/freenet/support/io/NativeThread.java   
2008-08-15 08:18:12 UTC (rev 21885)
+++ branches/saltedhashstore/freenet/src/freenet/support/io/NativeThread.java   
2008-08-15 08:20:48 UTC (rev 21886)
@@ -4,7 +4,6 @@

 package freenet.support.io;

-import java.io.File;
 import freenet.node.NodeStarter;
 import freenet.support.LibraryLoader;
 import freenet.support.Logger;
@@ -93,6 +92,7 @@
         */
        private static native int getLinuxPriority();   

+       @Override
        public void run() {
                if(!setNativePriority(currentPriority))
                        
System.err.println("setNativePriority("+currentPriority+") has failed!");

Deleted: 
branches/saltedhashstore/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
        2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
        2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,31 +0,0 @@
-package freenet.support.io;
-
-import freenet.crypt.RandomSource;
-import java.io.IOException;
-
-import freenet.support.api.Bucket;
-import freenet.support.api.BucketFactory;
-import java.util.Random;
-
-/**
- * Factory wrapper for PaddedEphemerallyEncryptedBucket's, which are themselves
- * wrappers.
- */
-public class PaddedEphemerallyEncryptedBucketFactory implements BucketFactory {
-
-       final BucketFactory baseFactory;
-       final RandomSource strongPRNG;
-       final Random weakPRNG;
-       final int minSize;
-       
-       public PaddedEphemerallyEncryptedBucketFactory(BucketFactory factory, 
RandomSource strongPRNG, Random weakPRNG, int minSize) {
-               baseFactory = factory;
-               this.minSize = minSize;
-               this.strongPRNG = strongPRNG;
-               this.weakPRNG = weakPRNG;
-       }
-
-       public Bucket makeBucket(long size) throws IOException {
-               return new 
PaddedEphemerallyEncryptedBucket(baseFactory.makeBucket(size), minSize, 
strongPRNG, weakPRNG);
-       }
-}

Deleted: 
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentEncryptedTempBucketFactory.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentEncryptedTempBucketFactory.java
   2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentEncryptedTempBucketFactory.java
   2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,23 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.support.io;
-
-import java.io.IOException;
-
-import freenet.support.api.Bucket;
-import freenet.support.api.BucketFactory;
-
-
-public class PersistentEncryptedTempBucketFactory implements BucketFactory {
-
-       PersistentTempBucketFactory bf;
-       
-       public PersistentEncryptedTempBucketFactory(PersistentTempBucketFactory 
bf) {
-               this.bf = bf;
-       }
-
-       public Bucket makeBucket(long size) throws IOException {
-               return bf.makeEncryptedBucket();
-       }
-}

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -42,12 +42,15 @@

        /** Buckets to free */
        private LinkedList bucketsToFree;
+       
+       private volatile boolean encrypt;

-       public PersistentTempBucketFactory(File dir, final String prefix, 
RandomSource strongPRNG, Random weakPRNG) throws IOException {
+       public PersistentTempBucketFactory(File dir, final String prefix, 
RandomSource strongPRNG, Random weakPRNG, boolean encrypt) throws IOException {
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                this.dir = dir;
                this.strongPRNG = strongPRNG;
                this.weakPRNG = weakPRNG;
+               this.encrypt = encrypt;
                this.fg = new FilenameGenerator(weakPRNG, false, dir, prefix);
                if(!dir.exists()) {
                        dir.mkdir();
@@ -104,19 +107,11 @@
                originalFiles = null;
        }

-       private Bucket makeRawBucket(long size) throws IOException {
-               return new PersistentTempFileBucket(fg.makeRandomFilename(), 
fg);
-       }
-
        public Bucket makeBucket(long size) throws IOException {
-               Bucket b = makeRawBucket(size);
-               return new DelayedFreeBucket(this, new 
PaddedEphemerallyEncryptedBucket(b, 1024, strongPRNG, weakPRNG));
+               PersistentTempFileBucket rawBucket = new 
PersistentTempFileBucket(fg.makeRandomFilename(), fg);
+               Bucket maybeEncryptedBucket = (encrypt ? new 
PaddedEphemerallyEncryptedBucket(rawBucket, 1024, strongPRNG, weakPRNG) : 
rawBucket);
+               return new DelayedFreeBucket(this, maybeEncryptedBucket);
        }
-       
-       public Bucket makeEncryptedBucket() throws IOException {
-               Bucket b = makeRawBucket(-1);
-               return new DelayedFreeBucket(this, new 
PaddedEphemerallyEncryptedBucket(b, 1024, strongPRNG, weakPRNG));
-       }

        /**
         * Free an allocated bucket, but only after the change has been written 
to disk.
@@ -150,5 +145,12 @@
        public long getID(File file) {
                return fg.getID(file);
        }
+       
+       public boolean isEncrypting() {
+               return encrypt;
+       }

+       public void setEncryption(boolean encrypt) {
+               this.encrypt = encrypt;
+       }
 }

Modified: 
branches/saltedhashstore/freenet/src/freenet/support/io/TempBucketFactory.java
===================================================================
--- 
branches/saltedhashstore/freenet/src/freenet/support/io/TempBucketFactory.java  
    2008-08-15 08:18:12 UTC (rev 21885)
+++ 
branches/saltedhashstore/freenet/src/freenet/support/io/TempBucketFactory.java  
    2008-08-15 08:20:48 UTC (rev 21886)
@@ -1,5 +1,6 @@
 package freenet.support.io;

+import freenet.crypt.RandomSource;
 import java.io.IOException;

 import freenet.support.api.Bucket;
@@ -10,6 +11,7 @@
  * distributed under the GNU Public Licence (GPL) version 2. See
  * http://www.gnu.org/ for further details of the GPL.
  */
+import java.util.Random;

 /**
  * Temporary Bucket Factory
@@ -18,15 +20,43 @@
  */
 public class TempBucketFactory implements BucketFactory {

+       private class RAMBucket extends ArrayBucket {
+               private final long size;
+               
+               public RAMBucket(long size) {
+                       super("RAMBucket");
+                       this.size = size;
+               }
+               
+               @Override
+               public void free() {
+                       super.free();
+                       _hasFreed(size);
+               }
+       }
+       
        private final FilenameGenerator filenameGenerator;
+       private long bytesInUse = 0;

        public final static long defaultIncrement = 4096;

        public final static float DEFAULT_FACTOR = 1.25F;
+       
+       public long maxRAMBucketSize;
+       public long maxRamUsed;

+       final RandomSource strongPRNG;
+       final Random weakPRNG;
+       private volatile boolean reallyEncrypt;
+
        // Storage accounting disabled by default.
-       public TempBucketFactory(FilenameGenerator filenameGenerator) {
+       public TempBucketFactory(FilenameGenerator filenameGenerator, long 
maxBucketSizeKeptInRam, long maxRamUsed, RandomSource strongPRNG, Random 
weakPRNG, boolean reallyEncrypt) {
                this.filenameGenerator = filenameGenerator;
+               this.maxRamUsed = maxRamUsed;
+               this.maxRAMBucketSize = maxBucketSizeKeptInRam;
+               this.strongPRNG = strongPRNG;
+               this.weakPRNG = weakPRNG;
+               this.reallyEncrypt = reallyEncrypt;
        }

        public Bucket makeBucket(long size) throws IOException {
@@ -36,6 +66,38 @@
        public Bucket makeBucket(long size, float factor) throws IOException {
                return makeBucket(size, factor, defaultIncrement);
        }
+       
+       protected synchronized void _hasFreed(long size) {
+               bytesInUse -= size;
+       }
+       
+       public synchronized long getRamUsed() {
+               return bytesInUse;
+       }
+       
+       public synchronized void setMaxRamUsed(long size) {
+               maxRamUsed = size;
+       }
+       
+       public synchronized long getMaxRamUsed() {
+               return maxRamUsed;
+       }
+       
+       public synchronized void setMaxRAMBucketSize(long size) {
+               maxRAMBucketSize = size;
+       }
+       
+       public synchronized long getMaxRAMBucketSize() {
+               return maxRAMBucketSize;
+       }
+       
+       public void setEncryption(boolean value) {
+               reallyEncrypt = value;
+       }
+       
+       public boolean isEncrypting() {
+               return reallyEncrypt;
+       }

        /**
         * Create a temp bucket
@@ -49,11 +111,19 @@
         *                If it is not possible to create a temp bucket due to 
an
         *                I/O error
         */
-       public TempFileBucket makeBucket(long size, float factor, long 
increment)
-               throws IOException {
-               long id = filenameGenerator.makeRandomFilename();
-
-               return new TempFileBucket(id, filenameGenerator);
+       public Bucket makeBucket(long size, float factor, long increment) 
throws IOException {
+               Bucket realBucket = null;
+               boolean isARAMBucket = false;
+               
+               synchronized(this) {
+                       if((size > 0) && (size <= maxRAMBucketSize) && 
(bytesInUse <= maxRamUsed)) {
+                               bytesInUse += size;
+                               isARAMBucket = true;
+                       }
+               }
+               
+               realBucket = (isARAMBucket ? new RAMBucket(size) : new 
TempFileBucket(filenameGenerator.makeRandomFilename(), filenameGenerator));
+               
+               return (!reallyEncrypt ? realBucket : new 
PaddedEphemerallyEncryptedBucket(realBucket, 1024, strongPRNG, weakPRNG));
        }
-
 }

Added: branches/saltedhashstore/freenet/test/freenet/crypt/YarrowTest.java
===================================================================
--- branches/saltedhashstore/freenet/test/freenet/crypt/YarrowTest.java         
                (rev 0)
+++ branches/saltedhashstore/freenet/test/freenet/crypt/YarrowTest.java 
2008-08-15 08:20:48 UTC (rev 21886)
@@ -0,0 +1,23 @@
+/* This code is part of Freenet. It is distributed under the GNU General
+ * Public License, version 2 (or at your option any later version). See
+ * http://www.gnu.org/ for further details of the GPL. */
+package freenet.crypt;
+
+import org.spaceroots.mantissa.random.ScalarSampleStatistics;
+import junit.framework.*;
+
+public class YarrowTest extends TestCase {
+
+       
+// REDFLAG: for some reason that test fails on emu... investigate why and 
review our implementation of Yarrow!
+       public void testDouble() {
+//             Yarrow mt = new Yarrow(false);
+               ScalarSampleStatistics sample = new ScalarSampleStatistics();
+//             for(int i = 0; i < 1000; ++i) {
+//                     sample.add(mt.nextDouble());
+//             }
+//
+//             assertEquals(0.5, sample.getMean(), 0.02);
+//             assertEquals(1.0 / (2.0 * Math.sqrt(3.0)), 
sample.getStandardDeviation(), 0.002);
+       }
+}


Reply via email to