Author: kryptos
Date: 2007-08-21 20:26:59 +0000 (Tue, 21 Aug 2007)
New Revision: 14828

Added:
   branches/freenet-jfk/src/freenet/client/ArchiveExtractCallback.java
   branches/freenet-jfk/src/freenet/client/async/BlockSet.java
   branches/freenet-jfk/src/freenet/client/async/SimpleBlockSet.java
   branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/LinkEnabledCallback.java
   branches/freenet-jfk/src/freenet/clients/http/OpennetConnectionsToadlet.java
   branches/freenet-jfk/src/freenet/io/comm/MessageCore.java
   branches/freenet-jfk/src/freenet/io/comm/PacketSocketHandler.java
   branches/freenet-jfk/src/freenet/io/comm/SocketHandler.java
   branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties
   branches/freenet-jfk/src/freenet/pluginmanager/FredPluginHTTPAdvanced.java
   branches/freenet-jfk/src/freenet/support/Executor.java
   branches/freenet-jfk/src/freenet/support/HTMLEntities.java
   branches/freenet-jfk/src/freenet/support/MutableBoolean.java
   branches/freenet-jfk/src/freenet/support/PooledExecutor.java
   branches/freenet-jfk/src/freenet/support/WeakHashSet.java
   branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java
   branches/freenet-jfk/src/freenet/support/io/MultiReaderBucket.java
   branches/freenet-jfk/src/freenet/support/io/PersistentTempFileBucket.java
   branches/freenet-jfk/src/freenet/tools/MergeSFS.java
   branches/freenet-jfk/test/freenet/support/
   branches/freenet-jfk/test/freenet/support/Base64Test.java
   branches/freenet-jfk/test/freenet/support/BitArrayTest.java
   branches/freenet-jfk/test/freenet/support/HTMLEncoderDecoderTest.java
   branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java
   branches/freenet-jfk/test/freenet/support/HexUtilTest.java
   branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java
   branches/freenet-jfk/test/freenet/support/LRUQueueTest.java
   branches/freenet-jfk/test/freenet/support/MultiValueTableTest.java
   branches/freenet-jfk/test/freenet/support/SimpleFieldSetTest.java
   branches/freenet-jfk/test/freenet/support/SizeUtilTest.java
   branches/freenet-jfk/test/freenet/support/TimeUtilTest.java
   branches/freenet-jfk/test/freenet/support/URIPreEncoderTest.java
   branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java
   branches/freenet-jfk/test/freenet/utils/
   branches/freenet-jfk/test/freenet/utils/UTFUtil.java
Removed:
   branches/freenet-jfk/src/freenet/io/comm/UdpSocketManager.java
   branches/freenet-jfk/src/freenet/support/io/FileBucketFactory.java
   branches/freenet-jfk/src/freenet/support/io/RandomAccessFileBucket.java
   branches/freenet-jfk/src/freenet/support/io/SpyInputStream.java
   branches/freenet-jfk/src/freenet/support/io/SpyOutputStream.java
   branches/freenet-jfk/src/freenet/support/io/TempBucketHook.java
   branches/freenet-jfk/test/freenet/support/Base64Test.java
   branches/freenet-jfk/test/freenet/support/BitArrayTest.java
   branches/freenet-jfk/test/freenet/support/HTMLEncoderDecoderTest.java
   branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java
   branches/freenet-jfk/test/freenet/support/HexUtilTest.java
   branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java
   branches/freenet-jfk/test/freenet/support/LRUQueueTest.java
   branches/freenet-jfk/test/freenet/support/MultiValueTableTest.java
   branches/freenet-jfk/test/freenet/support/SimpleFieldSetTest.java
   branches/freenet-jfk/test/freenet/support/SizeUtilTest.java
   branches/freenet-jfk/test/freenet/support/TimeUtilTest.java
   branches/freenet-jfk/test/freenet/support/URIPreEncoderTest.java
   branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java
   branches/freenet-jfk/test/freenet/utils/UTFUtil.java
Modified:
   branches/freenet-jfk/.classpath
   branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs
   branches/freenet-jfk/src/freenet/client/ArchiveManager.java
   branches/freenet-jfk/src/freenet/client/ArchiveStoreContext.java
   branches/freenet-jfk/src/freenet/client/ArchiveStoreItem.java
   branches/freenet-jfk/src/freenet/client/ClientMetadata.java
   branches/freenet-jfk/src/freenet/client/ErrorArchiveStoreItem.java
   branches/freenet-jfk/src/freenet/client/FECCodec.java
   branches/freenet-jfk/src/freenet/client/FetchContext.java
   branches/freenet-jfk/src/freenet/client/FetchException.java
   branches/freenet-jfk/src/freenet/client/HighLevelSimpleClientImpl.java
   branches/freenet-jfk/src/freenet/client/InsertContext.java
   branches/freenet-jfk/src/freenet/client/Metadata.java
   branches/freenet-jfk/src/freenet/client/MetadataParseException.java
   branches/freenet-jfk/src/freenet/client/RealArchiveStoreItem.java
   branches/freenet-jfk/src/freenet/client/TempStoreElement.java
   branches/freenet-jfk/src/freenet/client/async/BaseSingleFileFetcher.java
   branches/freenet-jfk/src/freenet/client/async/BinaryBlob.java
   branches/freenet-jfk/src/freenet/client/async/BinaryBlobFormatException.java
   branches/freenet-jfk/src/freenet/client/async/BinaryBlobInserter.java
   branches/freenet-jfk/src/freenet/client/async/ClientGetter.java
   branches/freenet-jfk/src/freenet/client/async/ClientRequestScheduler.java
   branches/freenet-jfk/src/freenet/client/async/SimpleManifestPutter.java
   branches/freenet-jfk/src/freenet/client/async/SimpleSingleFileFetcher.java
   branches/freenet-jfk/src/freenet/client/async/SingleBlockInserter.java
   branches/freenet-jfk/src/freenet/client/async/SingleFileFetcher.java
   branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java
   branches/freenet-jfk/src/freenet/client/async/SplitFileFetcher.java
   branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSegment.java
   branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSubSegment.java
   branches/freenet-jfk/src/freenet/client/async/USKChecker.java
   branches/freenet-jfk/src/freenet/client/async/USKFetcher.java
   branches/freenet-jfk/src/freenet/client/async/USKInserter.java
   branches/freenet-jfk/src/freenet/client/async/USKManager.java
   branches/freenet-jfk/src/freenet/client/async/USKProxyCompletionCallback.java
   branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/BrowserTestToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/ConfigToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/DarknetConnectionsToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/FProxyToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/FirstTimeWizardToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/HTTPRequestImpl.java
   branches/freenet-jfk/src/freenet/clients/http/LocalFileInsertToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/N2NTMToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/PageMaker.java
   branches/freenet-jfk/src/freenet/clients/http/PluginToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/PproxyToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/QueueToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/SimpleToadletServer.java
   branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/SymlinkerToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/Toadlet.java
   branches/freenet-jfk/src/freenet/clients/http/ToadletContainer.java
   branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java
   branches/freenet-jfk/src/freenet/clients/http/TranslationToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/TrivialToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java
   
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategories.java
   branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategory.java
   branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItem.java
   branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItems.java
   branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkManager.java
   branches/freenet-jfk/src/freenet/config/FreenetFilePersistentConfig.java
   branches/freenet-jfk/src/freenet/config/PersistentConfig.java
   branches/freenet-jfk/src/freenet/config/SubConfig.java
   branches/freenet-jfk/src/freenet/crypt/DSAPrivateKey.java
   branches/freenet-jfk/src/freenet/crypt/DiffieHellman.java
   branches/freenet-jfk/src/freenet/crypt/KEProtocol.java
   branches/freenet-jfk/src/freenet/crypt/PCFBMode.java
   branches/freenet-jfk/src/freenet/crypt/SHA256.java
   branches/freenet-jfk/src/freenet/crypt/Yarrow.java
   branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Algorithm.java
   branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Properties.java
   branches/freenet-jfk/src/freenet/io/Inet6AddressMatcher.java
   branches/freenet-jfk/src/freenet/io/NetworkInterface.java
   branches/freenet-jfk/src/freenet/io/comm/DMT.java
   branches/freenet-jfk/src/freenet/io/comm/FreenetInetAddress.java
   branches/freenet-jfk/src/freenet/io/comm/IOStatisticCollector.java
   branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java
   branches/freenet-jfk/src/freenet/io/comm/MessageType.java
   branches/freenet-jfk/src/freenet/io/comm/Peer.java
   branches/freenet-jfk/src/freenet/io/comm/PeerContext.java
   branches/freenet-jfk/src/freenet/io/comm/RetrievalException.java
   branches/freenet-jfk/src/freenet/io/xfer/BlockReceiver.java
   branches/freenet-jfk/src/freenet/io/xfer/BlockTransmitter.java
   branches/freenet-jfk/src/freenet/io/xfer/BulkReceiver.java
   branches/freenet-jfk/src/freenet/io/xfer/BulkTransmitter.java
   branches/freenet-jfk/src/freenet/io/xfer/PartiallyReceivedBulk.java
   branches/freenet-jfk/src/freenet/keys/ClientCHK.java
   branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java
   branches/freenet-jfk/src/freenet/keys/FreenetURI.java
   branches/freenet-jfk/src/freenet/keys/Key.java
   branches/freenet-jfk/src/freenet/keys/NodeCHK.java
   branches/freenet-jfk/src/freenet/keys/TooBigException.java
   branches/freenet-jfk/src/freenet/keys/USK.java
   branches/freenet-jfk/src/freenet/l10n/L10n.java
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.en.properties
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.fr.properties
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.it.properties
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.no.properties
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.se.properties
   
branches/freenet-jfk/src/freenet/pluginmanager/AccessDeniedPluginHTTPException.java
   
branches/freenet-jfk/src/freenet/pluginmanager/DownloadPluginHTTPException.java
   branches/freenet-jfk/src/freenet/pluginmanager/FredPlugin.java
   
branches/freenet-jfk/src/freenet/pluginmanager/NotFoundPluginHTTPException.java
   branches/freenet-jfk/src/freenet/pluginmanager/PluginHTTPException.java
   branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java
   branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java
   
branches/freenet-jfk/src/freenet/pluginmanager/RedirectPluginHTTPException.java
   branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java
   branches/freenet-jfk/src/freenet/support/BitArray.java
   branches/freenet-jfk/src/freenet/support/Fields.java
   branches/freenet-jfk/src/freenet/support/FileLoggerHook.java
   branches/freenet-jfk/src/freenet/support/HTMLDecoder.java
   branches/freenet-jfk/src/freenet/support/HTMLEncoder.java
   branches/freenet-jfk/src/freenet/support/HTMLNode.java
   branches/freenet-jfk/src/freenet/support/HexUtil.java
   branches/freenet-jfk/src/freenet/support/LRUHashtable.java
   branches/freenet-jfk/src/freenet/support/LRUQueue.java
   branches/freenet-jfk/src/freenet/support/RandomGrabArray.java
   branches/freenet-jfk/src/freenet/support/ShortBuffer.java
   branches/freenet-jfk/src/freenet/support/SimpleFieldSet.java
   branches/freenet-jfk/src/freenet/support/StringArray.java
   branches/freenet-jfk/src/freenet/support/TimeUtil.java
   branches/freenet-jfk/src/freenet/support/TokenBucket.java
   branches/freenet-jfk/src/freenet/support/URLDecoder.java
   branches/freenet-jfk/src/freenet/support/compress/GzipCompressor.java
   branches/freenet-jfk/src/freenet/support/io/ArrayBucket.java
   branches/freenet-jfk/src/freenet/support/io/FileBucket.java
   branches/freenet-jfk/src/freenet/support/io/FileUtil.java
   branches/freenet-jfk/src/freenet/support/io/FilenameGenerator.java
   branches/freenet-jfk/src/freenet/support/io/NullPersistentFileTracker.java
   
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
   
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
   branches/freenet-jfk/src/freenet/support/io/PersistentFileTracker.java
   branches/freenet-jfk/src/freenet/support/io/PersistentTempBucketFactory.java
   branches/freenet-jfk/src/freenet/support/io/RandomAccessFileWrapper.java
   branches/freenet-jfk/src/freenet/support/io/RandomAccessThing.java
   
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
   branches/freenet-jfk/src/freenet/support/io/TempBucketFactory.java
   branches/freenet-jfk/src/freenet/support/io/TempFileBucket.java
   branches/freenet-jfk/src/freenet/support/math/TimeDecayingRunningAverage.java
   branches/freenet-jfk/src/org/spaceroots/mantissa/random/MersenneTwister.java
Log:
Merged with trunk(r14796) and r13448:Link level encryption using JFK

Modified: branches/freenet-jfk/.classpath
===================================================================
--- branches/freenet-jfk/.classpath     2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/.classpath     2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-       <classpathentry 
excluding="freenet/node/*Test.java|org/spaceroots/mantissa/random/MersenneTwisterTest.java|plugins/JSTUN/**|test/**"
 kind="src" path="src"/>
-       <classpathentry including="freenet/**/*java" kind="src" path="test"/>
+       <classpathentry 
excluding="freenet/node/*Test.java|plugins/JSTUN/**|test/**" kind="src" 
path="src"/>
+       <classpathentry including="freenet/|org/" kind="src" path="test"/>
        <classpathentry exported="true" kind="lib" path="lib/freenet-ext.jar"/>
        <classpathentry kind="con" 
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
        <classpathentry kind="lib" path="/usr/share/java/junit.jar"/>

Modified: branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs
===================================================================
--- branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,5 +1,6 @@
-#Thu May 31 21:45:29 BST 2007
+#Fri Jul 27 18:21:13 BST 2007
 eclipse.preferences.version=1
+encoding//src/freenet/l10n/freenet.l10n.de.properties=UTF-8
 encoding//src/freenet/l10n/freenet.l10n.en.properties=UTF-8
 encoding//src/freenet/l10n/freenet.l10n.fr.properties=UTF-8
 encoding//src/freenet/l10n/freenet.l10n.it.properties=UTF-8

Copied: branches/freenet-jfk/src/freenet/client/ArchiveExtractCallback.java 
(from rev 14796, trunk/freenet/src/freenet/client/ArchiveExtractCallback.java)
===================================================================
--- branches/freenet-jfk/src/freenet/client/ArchiveExtractCallback.java         
                (rev 0)
+++ branches/freenet-jfk/src/freenet/client/ArchiveExtractCallback.java 
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,15 @@
+package freenet.client;
+
+import freenet.support.api.Bucket;
+
+/** Called when we have extracted an archive, and a specified file either is
+ * or isn't in it. */
+public interface ArchiveExtractCallback {
+
+       /** Got the data */
+       public void gotBucket(Bucket data);
+       
+       /** Not in the archive */
+       public void notInArchive();
+       
+}

Modified: branches/freenet-jfk/src/freenet/client/ArchiveManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/ArchiveManager.java 2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/ArchiveManager.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -17,11 +17,12 @@
 import freenet.keys.FreenetURI;
 import freenet.support.LRUHashtable;
 import freenet.support.Logger;
+import freenet.support.MutableBoolean;
 import freenet.support.api.Bucket;
 import freenet.support.io.BucketTools;
-import freenet.support.io.FileBucket;
 import freenet.support.io.FilenameGenerator;
 import freenet.support.io.PaddedEphemerallyEncryptedBucket;
+import freenet.support.io.TempFileBucket;

 /**
  * Cache of recently decoded archives:
@@ -34,11 +35,33 @@
  */
 public class ArchiveManager {

+       public static final String METADATA_NAME = ".metadata";
        private static boolean logMINOR;

+       final RandomSource random;
+       final long maxArchiveSize;
+       final long maxArchivedFileSize;
+       
+       // ArchiveHandler's
+       final int maxArchiveHandlers;
+       private final LRUHashtable archiveHandlers;
+       
+       // Data cache
+       /** Maximum number of cached ArchiveStoreItems */
+       final int maxCachedElements;
+       /** Maximum cached data in bytes */
+       final long maxCachedData;
+       /** Currently cached data in bytes */
+       private long cachedData;
+       /** Map from ArchiveKey to ArchiveStoreElement */
+       private final LRUHashtable storedData;
+       /** Filename generator */
+       private final FilenameGenerator filenameGenerator;
+
        /**
         * Create an ArchiveManager.
-        * @param maxHandlers The maximum number of cached ArchiveHandler's.
+        * @param maxHandlers The maximum number of cached ArchiveHandler's 
i.e. the
+        * maximum number of containers to track.
         * @param maxCachedData The maximum size of the cache directory, in 
bytes.
         * @param maxArchiveSize The maximum size of an archive.
         * @param maxArchivedFileSize The maximum extracted size of a single 
file in any
@@ -52,7 +75,6 @@
        public ArchiveManager(int maxHandlers, long maxCachedData, long 
maxArchiveSize, long maxArchivedFileSize, int maxCachedElements, RandomSource 
random, FilenameGenerator filenameGenerator) {
                maxArchiveHandlers = maxHandlers;
                archiveHandlers = new LRUHashtable();
-               cachedElements = new LRUHashtable();
                this.maxCachedElements = maxCachedElements;
                this.maxCachedData = maxCachedData;
                storedData = new LRUHashtable();
@@ -63,16 +85,6 @@
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
        }

-       final RandomSource random;
-       final long maxArchiveSize;
-       final long maxArchivedFileSize;
-       
-       
-       // ArchiveHandler's
-       
-       final int maxArchiveHandlers;
-       final LRUHashtable archiveHandlers;
-       
        /** Add an ArchiveHandler by key */
        private synchronized void putCached(FreenetURI key, ArchiveHandler zip) 
{
                if(logMINOR) Logger.minor(this, "Put cached AH for "+key+" : 
"+zip);
@@ -90,24 +102,6 @@
                return handler;
        }

-       // Element cache
-       
-       /** Cache of ArchiveElement's by MyKey */
-       final LRUHashtable cachedElements;
-       /** Maximum number of cached ArchiveElement's */
-       final int maxCachedElements;
-
-       // Data cache
-       
-       /** Maximum cached data in bytes */
-       final long maxCachedData;
-       /** Currently cached data in bytes */
-       private long cachedData;
-       /** Map from ArchiveKey to ArchiveStoreElement */
-       final LRUHashtable storedData;
-       /** Filename generator */
-       final FilenameGenerator filenameGenerator;
-
        /**
         * Create an archive handler. This does not need to know how to
         * fetch the key, because the methods called later will ask.
@@ -138,24 +132,30 @@
                if(logMINOR) Logger.minor(this, "Fetch cached: "+key+ ' ' 
+filename);
                ArchiveKey k = new ArchiveKey(key, filename);
                ArchiveStoreItem asi = null;
-               synchronized (storedData) {
+               synchronized (this) {
                        asi = (ArchiveStoreItem) storedData.get(k);     
                        if(asi == null) return null;
                        // Promote to top of LRU
                        storedData.push(k, asi);
                }
                if(logMINOR) Logger.minor(this, "Found data");
-               return asi.getDataOrThrow();
+               return asi.getReaderBucket();
        }

        /**
-        * Remove a file from the cache.
+        * Remove a file from the cache. Called after it has been removed from 
its 
+        * ArchiveHandler.
         * @param item The ArchiveStoreItem to remove.
         */
-       void removeCachedItem(ArchiveStoreItem item) {
-               synchronized (storedData) {
-                       storedData.removeKey(item.key); 
-               }
+       synchronized void removeCachedItem(ArchiveStoreItem item) {
+               long size = item.spaceUsed();
+               storedData.removeKey(item.key);
+               // Hard disk space limit = remove it here.
+               // Soft disk space limit would be to remove it outside the lock.
+               // Soft disk space limit = we go over the limit significantly 
when we
+               // are overloaded.
+               cachedData -= size;
+               item.close();
        }

        /**
@@ -165,15 +165,19 @@
         * @param data The actual data fetched.
         * @param archiveContext The context for the whole fetch process.
         * @param ctx The ArchiveStoreContext for this key.
+        * @param element A particular element that the caller is especially 
interested in, or null.
+        * @param callback A callback to be called if we find that element, or 
if we don't.
         * @throws ArchiveFailureException If we could not extract the data, or 
it was too big, etc.
         * @throws ArchiveRestartException 
         * @throws ArchiveRestartException If the request needs to be restarted 
because the archive
         * changed.
         */
-       public void extractToCache(FreenetURI key, short archiveType, Bucket 
data, ArchiveContext archiveContext, ArchiveStoreContext ctx) throws 
ArchiveFailureException, ArchiveRestartException {
+       public void extractToCache(FreenetURI key, short archiveType, Bucket 
data, ArchiveContext archiveContext, ArchiveStoreContext ctx, String element, 
ArchiveExtractCallback callback) throws ArchiveFailureException, 
ArchiveRestartException {

                logMINOR = Logger.shouldLog(Logger.MINOR, this);

+               MutableBoolean gotElement = element != null ? new 
MutableBoolean() : null;
+               
                if(logMINOR) Logger.minor(this, "Extracting "+key);
                ctx.onExtract();
                ctx.removeAllCachedItems(); // flush cache anyway
@@ -211,7 +215,7 @@
                        // MINOR: Assumes the first entry in the zip is a 
directory. 
                        ZipEntry entry;

-                       byte[] buf = new byte[4096];
+                       byte[] buf = new byte[32768];
                        HashSet names = new HashSet();
                        boolean gotMetadata = false;

@@ -249,16 +253,22 @@
                                        out.close();
                                        if(name.equals(".metadata"))
                                                gotMetadata = true;
-                                       addStoreElement(ctx, key, name, temp);
+                                       addStoreElement(ctx, key, name, temp, 
gotElement, element, callback);
                                        names.add(name);
+                                       trimStoredData();
                                }
                        }

                        // If no metadata, generate some
                        if(!gotMetadata) {
-                               generateMetadata(ctx, key, names);
+                               generateMetadata(ctx, key, names, gotElement, 
element, callback);
+                               trimStoredData();
                        }
                        if(throwAtExit) throw new 
ArchiveRestartException("Archive changed on re-fetch");
+                       
+                       if((!gotElement.value) && element != null)
+                               callback.notInArchive();
+                       
                } catch (IOException e) {
                        throw new ArchiveFailureException("Error reading 
archive: "+e.getMessage(), e);
                } finally {
@@ -277,9 +287,12 @@
         * @param ctx The context object.
         * @param key The key from which the archive we are unpacking was 
fetched.
         * @param names Set of names in the archive.
+        * @param element2 
+        * @param gotElement 
+        * @param callbackName If we generate a 
         * @throws ArchiveFailureException 
         */
-       private void generateMetadata(ArchiveStoreContext ctx, FreenetURI key, 
HashSet names) throws ArchiveFailureException {
+       private ArchiveStoreItem generateMetadata(ArchiveStoreContext ctx, 
FreenetURI key, HashSet names, MutableBoolean gotElement, String element2, 
ArchiveExtractCallback callback) throws ArchiveFailureException {
                /* What we have to do is to:
                 * - Construct a filesystem tree of the names.
                 * - Turn each level of the tree into a Metadata object, 
including those below it, with
@@ -304,21 +317,21 @@
                                OutputStream os = 
element.bucket.getOutputStream();
                                os.write(buf);
                                os.close();
-                               addStoreElement(ctx, key, ".metadata", element);
-                               break;
+                               return addStoreElement(ctx, key, ".metadata", 
element, gotElement, element2, callback);
                        } catch (MetadataUnresolvedException e) {
                                try {
-                                       x = resolve(e, x, element, ctx, key);
+                                       x = resolve(e, x, element, ctx, key, 
gotElement, element2, callback);
                                } catch (IOException e1) {
                                        throw new 
ArchiveFailureException("Failed to create metadata: "+e1, e1);
                                }
                        } catch (IOException e1) {
+                               Logger.error(this, "Failed to create metadata: 
"+e1, e1);
                                throw new ArchiveFailureException("Failed to 
create metadata: "+e1, e1);
                        }
                }
        }

-       private int resolve(MetadataUnresolvedException e, int x, 
TempStoreElement element, ArchiveStoreContext ctx, FreenetURI key) throws 
IOException {
+       private int resolve(MetadataUnresolvedException e, int x, 
TempStoreElement element, 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 {
@@ -326,9 +339,9 @@
                                OutputStream os = 
element.bucket.getOutputStream();
                                os.write(buf);
                                os.close();
-                               addStoreElement(ctx, key, ".metadata-"+(x++), 
element);
+                               addStoreElement(ctx, key, ".metadata-"+(x++), 
element, gotElement, element2, callback);
                        } catch (MetadataUnresolvedException e1) {
-                               x = resolve(e, x, element, ctx, key);
+                               x = resolve(e, x, element, ctx, key, 
gotElement, element2, callback);
                        }
                }
                return x;
@@ -350,7 +363,7 @@
                        } else
                                after = name.substring(x+1, name.length());
                        Object o = dir.get(before);
-                       HashMap map;
+                       HashMap map = (HashMap) o;
                        if(o == null) {
                                map = new HashMap();
                                dir.put(before, map);
@@ -358,7 +371,6 @@
                        if(o instanceof String) {
                                throw new ArchiveFailureException("Invalid 
archive: contains "+name+" as both file and dir");
                        }
-                       map = (HashMap) o;
                        addToDirectory(map, after, prefix + before + '/');
                }
        }
@@ -375,21 +387,51 @@
        private void addErrorElement(ArchiveStoreContext ctx, FreenetURI key, 
String name, String error) {
                ErrorArchiveStoreItem element = new ErrorArchiveStoreItem(ctx, 
key, name, error);
                if(logMINOR) Logger.minor(this, "Adding error element: 
"+element+" for "+key+ ' ' +name);
-               synchronized (storedData) {
+               ArchiveStoreItem oldItem;
+               synchronized (this) {
+                       oldItem = (ArchiveStoreItem) 
storedData.get(element.key);
                        storedData.push(element.key, element);  
+                       if(oldItem != null) {
+                               oldItem.close();
+                               cachedData -= oldItem.spaceUsed();
+                       }
                }
        }

        /**
         * Add a store element.
+        * @param callbackName If set, the name of the file for which we must 
call the callback if this file happens to
+        * match.
+        * @param gotElement Flag indicating whether we've already found the 
file for the callback. If so we must not call
+        * it again.
+        * @param callback Callback to be called if we do find it. We must 
getReaderBucket() before adding the data to the 
+        * LRU, otherwise it may be deleted before it reaches the client.
+        * @throws ArchiveFailureException If a failure occurred resulting in 
the data not being readable. Only happens if
+        * callback != null.
         */
-       private void addStoreElement(ArchiveStoreContext ctx, FreenetURI key, 
String name, TempStoreElement temp) {
-               RealArchiveStoreItem element = new RealArchiveStoreItem(this, 
ctx, key, name, temp);
+       private ArchiveStoreItem addStoreElement(ArchiveStoreContext ctx, 
FreenetURI key, String name, TempStoreElement 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()+" )");
-               synchronized (storedData) {
+               ArchiveStoreItem oldItem;
+               // Let it throw, if it does something is drastically wrong
+               Bucket matchBucket = null;
+               if((!gotElement.value) && name.equals(callbackName)) {
+                       matchBucket = element.getReaderBucket();
+               }
+               synchronized (this) {
+                       oldItem = (ArchiveStoreItem) 
storedData.get(element.key);
                        storedData.push(element.key, element);
+                       cachedData += element.spaceUsed();
+                       if(oldItem != null) {
+                               cachedData -= oldItem.spaceUsed();
+                               oldItem.close();
+                       }
                }
-               trimStoredData();
+               if(matchBucket != null) {
+                       callback.gotBucket(matchBucket);
+                       gotElement.value = true;
+               }
+               return element;
        }

        /**
@@ -397,15 +439,25 @@
         * Call synchronized on storedData.
         */
        private void trimStoredData() {
+               synchronized(this) {
                while(true) {
-                       synchronized(this) {
+                       ArchiveStoreItem item;
                                if(cachedData <= maxCachedData && 
storedData.size() <= maxCachedElements) return;
-                       }
-                       ArchiveStoreItem e = (ArchiveStoreItem) 
storedData.popValue();  
+                               if(storedData.isEmpty()) {
+                                       // Race condition? cachedData out of 
sync?
+                                       Logger.error(this, "storedData is empty 
but still over limit: cachedData="+cachedData+" / "+maxCachedData);
+                                       return;
+                               }
+                               item = (ArchiveStoreItem) storedData.popValue();
+                               long space = item.spaceUsed();
+                               cachedData -= space;
+                               // Hard limits = delete file within lock, soft 
limits = delete outside of lock
+                               // Here we use a hard limit
                        if(logMINOR)
-                               Logger.minor(this, "Dropping "+e+" : 
cachedData="+cachedData+" of "+maxCachedData);
-                       e.close();
+                               Logger.minor(this, "Dropping "+item+" : 
cachedData="+cachedData+" of "+maxCachedData+" stored items : 
"+storedData.size()+" of "+maxCachedElements);
+                       item.close();
                }
+               }
        }

        /** 
@@ -415,12 +467,13 @@
         * go over the maximum size. Will obviously keep its key when we move 
it to main.
         */
        private TempStoreElement makeTempStoreBucket(long size) {
-               File myFile = filenameGenerator.makeRandomFilename();
-               FileBucket fb = new FileBucket(myFile, false, false, true, 
true, true);
+               long id = filenameGenerator.makeRandomFilename();
+               File myFile = filenameGenerator.getFilename(id);
+               TempFileBucket fb = new TempFileBucket(id, filenameGenerator);

                byte[] cipherKey = new byte[32];
                random.nextBytes(cipherKey);
-               PaddedEphemerallyEncryptedBucket encryptedBucket = new 
PaddedEphemerallyEncryptedBucket(fb, 1024, random, true);
+               PaddedEphemerallyEncryptedBucket encryptedBucket = new 
PaddedEphemerallyEncryptedBucket(fb, 1024, random);
                return new TempStoreElement(myFile, fb, encryptedBucket);
        }

@@ -440,12 +493,4 @@
                        return Metadata.ARCHIVE_ZIP;
                else throw new IllegalArgumentException(); 
        }
-
-       synchronized void decrementSpace(long spaceUsed) {
-               cachedData -= spaceUsed;
-       }
-
-       synchronized void incrementSpace(long spaceUsed) {
-               cachedData += spaceUsed;
-       }
 }

Modified: branches/freenet-jfk/src/freenet/client/ArchiveStoreContext.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/ArchiveStoreContext.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/ArchiveStoreContext.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -26,6 +26,15 @@
        private FreenetURI key;
        private short archiveType;
        private boolean forceRefetchArchive;
+       /** Archive size */
+       private long lastSize = -1;
+       /** Archive hash */
+       private byte[] lastHash;
+       /** Index of still-cached ArchiveStoreItems with this key.
+        * Note that we never ever hold this and then take another lock! In 
particular
+        * we must not take the ArchiveManager lock while holding this lock. It 
must be
+        * the inner lock to avoid deadlocks. */
+       private final DoublyLinkedListImpl myItems;

        public ArchiveStoreContext(ArchiveManager manager, FreenetURI key, 
short archiveType, boolean forceRefetchArchive) {
                this.manager = manager;
@@ -46,6 +55,8 @@

        /**
         * Fetch a file in an archive.
+        * @return A Bucket containing the data. This will not be freed until 
the 
+        * client is finished with it i.e. calls free() or it is finalized.
         */
        public Bucket get(String internalName, ArchiveContext archiveContext, 
ClientMetadata dm, int recursionLevel, 
                        boolean dontEnterImplicitArchives) throws 
ArchiveFailureException, ArchiveRestartException, MetadataParseException, 
FetchException {
@@ -67,9 +78,6 @@
                return null;
        }

-       // Archive size
-       long lastSize = -1;
-       
        /** Returns the size of the archive last time we fetched it, or -1 */
        long getLastSize() {
                return lastSize;
@@ -80,10 +88,7 @@
                lastSize = size;
        }

-       // Archive hash

-       byte[] lastHash;
-       
        /** Returns the hash of the archive last time we fetched it, or null */
        public byte[] getLastHash() {
                return lastHash;
@@ -93,11 +98,6 @@
        public void setLastHash(byte[] realHash) {
                lastHash = realHash;
        }
-       
-       // Index of still-cached ArchiveStoreItems with this key
-       
-       /** Index of still-cached ArchiveStoreItems with this key */
-       final DoublyLinkedListImpl myItems;

        /**
         * Remove all ArchiveStoreItems with this key from the cache.
@@ -110,7 +110,6 @@
                        }
                        if(item == null) break;
                        manager.removeCachedItem(item);
-                       item.context.removeItem(item);
                }
        }

@@ -121,14 +120,14 @@
                }
        }

-       /** Notify that an archive store item with this key has been expelled 
from the cache. */
+       /** Notify that an archive store item with this key has been expelled 
from the 
+        * cache. Remove it from our local cache and ask it to free the bucket 
if 
+        * necessary. */
        public void removeItem(ArchiveStoreItem item) {
-               long spaceUsed = item.spaceUsed();
                synchronized(myItems) {
                        if(myItems.remove(item) == null) return; // only 
removed once
                }
                item.innerClose();
-               manager.decrementSpace(spaceUsed);
        }

        public short getArchiveType() {
@@ -139,10 +138,11 @@
                return key;
        }

-       public void extractToCache(Bucket bucket, ArchiveContext actx) throws 
ArchiveFailureException, ArchiveRestartException {
-               manager.extractToCache(key, archiveType, bucket, actx, this);
+       public void extractToCache(Bucket bucket, ArchiveContext actx, String 
element, ArchiveExtractCallback callback) throws ArchiveFailureException, 
ArchiveRestartException {
+               manager.extractToCache(key, archiveType, bucket, actx, this, 
element, callback);
        }

+       /** Called just before extracting this container to the cache */
        public void onExtract() {
                forceRefetchArchive = false;
        }

Modified: branches/freenet-jfk/src/freenet/client/ArchiveStoreItem.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/ArchiveStoreItem.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/ArchiveStoreItem.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -20,7 +20,10 @@
                context.addItem(this);
        }

-       /** Delete any stored data on disk etc. Override in subtypes for 
specific cleanup. */
+       /** Delete any stored data on disk etc. 
+        * Override in subtypes for specific cleanup.
+        * Will be called with locks held, so should only do low level 
operations 
+        * such as deletes.. */
        void innerClose() { } // override in subtypes for cleanup

        /** 
@@ -36,7 +39,14 @@
        abstract Bucket getDataOrThrow() throws ArchiveFailureException;

        /**
-        * Return the amount of cache space used by the item.
+        * Return the amount of cache space used by the item. May be called 
inside
+        * locks so should not take any nontrivial locks or take long.
         */
        abstract long spaceUsed();
+       
+       /**
+        * Get the data as a Bucket, and guarantee that it won't be freed until 
the
+        * returned object is either finalized or freed.
+        */
+       abstract Bucket getReaderBucket() throws ArchiveFailureException;
 }

Modified: branches/freenet-jfk/src/freenet/client/ClientMetadata.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/ClientMetadata.java 2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/ClientMetadata.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -52,4 +52,18 @@
        public String toString() {
                return getMIMEType();
        }
+
+       public void clear() {
+               mimeType = null;
+       }
+
+       public String getMIMETypeNoParams() {
+               String s = mimeType;
+               if(s == null) return null;
+               int i = s.indexOf(';');
+               if(i > -1) {
+                       s = s.substring(i);
+               }
+               return s;
+       }
 }

Modified: branches/freenet-jfk/src/freenet/client/ErrorArchiveStoreItem.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/ErrorArchiveStoreItem.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/ErrorArchiveStoreItem.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -35,5 +35,9 @@
        public long spaceUsed() {
                return 0;
        }
+
+       Bucket getReaderBucket() throws ArchiveFailureException {
+               throw new ArchiveFailureException(error);
+       }

 }

Modified: branches/freenet-jfk/src/freenet/client/FECCodec.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/FECCodec.java       2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/FECCodec.java       2007-08-21 
20:26:59 UTC (rev 14828)
@@ -343,7 +343,7 @@
                synchronized (_awaitingJobs) {
                        if(fecRunnerThread == null) {
                                if(fecRunnerThread != null) 
Logger.error(FECCodec.class, "The callback died!! restarting a new one, please 
report that error.");
-                               fecRunnerThread = new Thread(fecRunner, "FEC 
Pool");
+                               fecRunnerThread = new Thread(fecRunner, "FEC 
Pool "+(fecPoolCounter++));
                                fecRunnerThread.setDaemon(true);
                                
fecRunnerThread.setPriority(Thread.MIN_PRIORITY);

@@ -361,6 +361,7 @@
        private static final LinkedList _awaitingJobs = new LinkedList();
        private static final FECRunner fecRunner = new FECRunner();
        private static Thread fecRunnerThread;
+       private static int fecPoolCounter;

        /**
         * A private Thread started by {@link FECCodec}...

Modified: branches/freenet-jfk/src/freenet/client/FetchContext.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/FetchContext.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/FetchContext.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -3,11 +3,16 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.client;

+import java.util.Set;
+
+import freenet.client.async.BlockSet;
 import freenet.client.async.HealingQueue;
 import freenet.client.async.USKManager;
 import freenet.client.events.ClientEventProducer;
 import freenet.client.events.SimpleEventProducer;
 import freenet.crypt.RandomSource;
+import freenet.node.Ticker;
+import freenet.support.Executor;
 import freenet.support.api.BucketFactory;

 /** Context for a Fetcher. Contains all the settings a Fetcher needs to know 
about. */
@@ -45,6 +50,11 @@
        public boolean returnZIPManifests;
        public final HealingQueue healingQueue;
        public final boolean ignoreTooManyPathComponents;
+       /** If set, contains a set of blocks to be consulted before checking 
the datastore. */
+       public BlockSet blocks;
+       public Set allowedMIMETypes;
+       public final Ticker ticker;
+       public final Executor executor;

        public FetchContext(long curMaxLength, 
                        long curMaxTempLength, int maxMetadataSize, int 
maxRecursionLevel, int maxArchiveRestarts, int maxArchiveLevels,
@@ -53,7 +63,10 @@
                        boolean allowSplitfiles, boolean followRedirects, 
boolean localRequestOnly,
                        int maxDataBlocksPerSegment, int 
maxCheckBlocksPerSegment,
                        RandomSource random, ArchiveManager archiveManager, 
BucketFactory bucketFactory,
-                       ClientEventProducer producer, boolean 
cacheLocalRequests, USKManager uskManager, HealingQueue hq, boolean 
ignoreTooManyPathComponents) {
+                       ClientEventProducer producer, boolean 
cacheLocalRequests, USKManager uskManager, 
+                       HealingQueue hq, boolean ignoreTooManyPathComponents, 
Ticker ticker, Executor executor) {
+               this.ticker = ticker;
+               this.executor = executor;
                this.maxOutputLength = curMaxLength;
                this.uskManager = uskManager;
                this.maxTempLength = curMaxTempLength;
@@ -85,8 +98,12 @@
                        this.eventProducer = ctx.eventProducer;
                else
                        this.eventProducer = new SimpleEventProducer();
+               this.ticker = ctx.ticker;
+               this.executor = ctx.executor;
                this.uskManager = ctx.uskManager;
                this.ignoreTooManyPathComponents = 
ctx.ignoreTooManyPathComponents;
+               this.blocks = ctx.blocks;
+               this.allowedMIMETypes = ctx.allowedMIMETypes;
                if(maskID == IDENTICAL_MASK) {
                        this.maxOutputLength = ctx.maxOutputLength;
                        this.maxMetadataSize = ctx.maxMetadataSize;

Modified: branches/freenet-jfk/src/freenet/client/FetchException.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/FetchException.java 2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/FetchException.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -127,6 +127,18 @@
                        Logger.minor(this, 
"FetchException("+getMessage(mode)+"): "+t.getMessage(),t);
        }

+       public FetchException(int mode, String reason, Throwable t) {
+               super(reason+" : "+getMessage(mode)+": "+t.getMessage());
+               extraMessage = t.getMessage();
+               this.mode = mode;
+               errorCodes = null;
+               initCause(t);
+               newURI = null;
+               expectedSize = -1;
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, 
"FetchException("+getMessage(mode)+"): "+t.getMessage(),t);
+       }
+
        public FetchException(int mode, FailureCodeTracker errorCodes) {
                super(getMessage(mode));
                extraMessage = null;
@@ -182,6 +194,18 @@
                this.finalizedSizeAndMimeType = e.finalizedSizeAndMimeType;
        }

+       public FetchException(FetchException e, FreenetURI uri) {
+               super(e.getMessage());
+               initCause(e);
+               this.mode = e.mode;
+               this.newURI = uri;
+               this.errorCodes = e.errorCodes;
+               this.expectedMimeType = e.expectedMimeType;
+               this.expectedSize = e.expectedSize;
+               this.extraMessage = e.extraMessage;
+               this.finalizedSizeAndMimeType = e.finalizedSizeAndMimeType;
+       }
+
        public static String getShortMessage(int mode) {
                String ret = L10n.getString("FetchException.shortError."+mode);
                if(ret == null)
@@ -273,6 +297,10 @@
        public static final int ARCHIVE_RESTART = 26;
        /** There is a more recent version of the USK, ~= HTTP 301; FProxy will 
turn this into a 301 */
        public static final int PERMANENT_REDIRECT = 27;
+       /** Requestor specified a list of allowed MIME types, and the key's 
type wasn't in the list */
+       public static final int WRONG_MIME_TYPE = 29;
+       /** A node killed the request because it had recently been tried and 
had DNFed */
+       public static final int RECENTLY_FAILED = 30;

        /** Is an error fatal i.e. is there no point retrying? */
        public boolean isFatal() {
@@ -307,6 +335,7 @@
                case REJECTED_OVERLOAD:
                case TRANSFER_FAILED:
                case ALL_DATA_NOT_FOUND:
+               case RECENTLY_FAILED: // wait a bit, but fine
                // Not usually fatal
                case SPLITFILE_ERROR:
                        return false;
@@ -320,6 +349,7 @@
                case CANCELLED:
                case ARCHIVE_RESTART:
                case PERMANENT_REDIRECT:
+               case WRONG_MIME_TYPE:
                        // Fatal
                        return true;


Modified: branches/freenet-jfk/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/HighLevelSimpleClientImpl.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/HighLevelSimpleClientImpl.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -191,7 +191,7 @@
                                MAX_SPLITFILE_BLOCKS_PER_SEGMENT, 
MAX_SPLITFILE_CHECK_BLOCKS_PER_SEGMENT,
                                random, archiveManager, bucketFactory, 
globalEventProducer, 
                                cacheLocalRequests, core.uskManager, 
healingQueue, 
-                               forceDontIgnoreTooManyPathComponents ? false : 
core.ignoreTooManyPathComponents);
+                               forceDontIgnoreTooManyPathComponents ? false : 
core.ignoreTooManyPathComponents, core.getTicker(), core.getExecutor());
        }

        public InsertContext getInsertContext(boolean forceNonPersistent) {
@@ -199,6 +199,6 @@
                                forceNonPersistent ? new 
NullPersistentFileTracker() : persistentFileTracker,
                                random, INSERT_RETRIES, 
CONSECUTIVE_RNFS_ASSUME_SUCCESS,
                                SPLITFILE_INSERT_THREADS, 
SPLITFILE_BLOCKS_PER_SEGMENT, SPLITFILE_CHECK_BLOCKS_PER_SEGMENT, 
-                               globalEventProducer, cacheLocalRequests, 
core.uskManager, blockEncoder);
+                               globalEventProducer, cacheLocalRequests, 
core.uskManager, blockEncoder, core.getExecutor());
        }
 }

Modified: branches/freenet-jfk/src/freenet/client/InsertContext.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/InsertContext.java  2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/InsertContext.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -8,6 +8,7 @@
 import freenet.client.events.ClientEventProducer;
 import freenet.client.events.SimpleEventProducer;
 import freenet.crypt.RandomSource;
+import freenet.support.Executor;
 import freenet.support.api.BucketFactory;
 import freenet.support.io.NullPersistentFileTracker;
 import freenet.support.io.PersistentFileTracker;
@@ -32,10 +33,11 @@
        public final boolean cacheLocalRequests;
        public final USKManager uskManager;
        public final BackgroundBlockEncoder backgroundBlockEncoder;
+       public final Executor executor;

        public InsertContext(BucketFactory bf, BucketFactory persistentBF, 
PersistentFileTracker tracker, RandomSource random,
                        int maxRetries, int rnfsToSuccess, int maxThreads, int 
splitfileSegmentDataBlocks, int splitfileSegmentCheckBlocks,
-                       ClientEventProducer eventProducer, boolean 
cacheLocalRequests, USKManager uskManager, BackgroundBlockEncoder blockEncoder) 
{
+                       ClientEventProducer eventProducer, boolean 
cacheLocalRequests, USKManager uskManager, BackgroundBlockEncoder blockEncoder, 
Executor executor) {
                this.bf = bf;
                this.persistentFileTracker = tracker;
                this.persistentBucketFactory = persistentBF;
@@ -51,6 +53,7 @@
                this.splitfileSegmentCheckBlocks = splitfileSegmentCheckBlocks;
                this.cacheLocalRequests = cacheLocalRequests;
                this.backgroundBlockEncoder = blockEncoder;
+               this.executor = executor;
        }

        public InsertContext(InsertContext ctx, SimpleEventProducer producer, 
boolean forceNonPersistent) {
@@ -69,6 +72,7 @@
                this.splitfileSegmentCheckBlocks = 
ctx.splitfileSegmentCheckBlocks;
                this.cacheLocalRequests = ctx.cacheLocalRequests;
                this.backgroundBlockEncoder = ctx.backgroundBlockEncoder;
+               this.executor = ctx.executor;
        }

        public InsertContext(InsertContext ctx, SimpleEventProducer producer) {
@@ -87,6 +91,7 @@
                this.splitfileSegmentCheckBlocks = 
ctx.splitfileSegmentCheckBlocks;
                this.cacheLocalRequests = ctx.cacheLocalRequests;
                this.backgroundBlockEncoder = ctx.backgroundBlockEncoder;
+               this.executor = ctx.executor;
        }

 }

Modified: branches/freenet-jfk/src/freenet/client/Metadata.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/Metadata.java       2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/Metadata.java       2007-08-21 
20:26:59 UTC (rev 14828)
@@ -157,8 +157,9 @@
        }

        /** Parse some metadata from a byte[]. 
-        * @throws IOException If the data is incomplete, or something wierd 
happens. */
-       private Metadata(byte[] data) throws IOException {
+        * @throws IOException If the data is incomplete, or something wierd 
happens. 
+        * @throws MetadataParseException */
+       private Metadata(byte[] data) throws IOException, 
MetadataParseException {
                this(new DataInputStream(new ByteArrayInputStream(data)), 
data.length);
        }

@@ -848,6 +849,7 @@
                                                unresolvedMetadata = new 
LinkedList();
                                        for(int j=0;j<m.length;j++)
                                                
unresolvedMetadata.addFirst(m[j]);
+                                       kill = true;
                                }
                        }
                        if(kill) {
@@ -916,4 +918,10 @@
        public boolean isResolved() {
                return resolvedURI != null;
        }
+
+       public void setArchiveManifest() {
+               archiveType = 
ArchiveManager.getArchiveType(clientMetadata.getMIMEType());
+               clientMetadata.clear();
+               documentType = ZIP_MANIFEST;
+       }
 }

Modified: branches/freenet-jfk/src/freenet/client/MetadataParseException.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/MetadataParseException.java 
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/MetadataParseException.java 
2007-08-21 20:26:59 UTC (rev 14828)
@@ -6,7 +6,7 @@
 import java.io.IOException;

 /** Thrown when Metadata parse fails. */
-public class MetadataParseException extends IOException {
+public class MetadataParseException extends Exception {

        private static final long serialVersionUID = 4910650977022715220L;


Modified: branches/freenet-jfk/src/freenet/client/RealArchiveStoreItem.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/RealArchiveStoreItem.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/RealArchiveStoreItem.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -7,16 +7,14 @@

 import freenet.keys.FreenetURI;
 import freenet.support.api.Bucket;
-import freenet.support.io.FileBucket;
 import freenet.support.io.FileUtil;
-import freenet.support.io.PaddedEphemerallyEncryptedBucket;
+import freenet.support.io.MultiReaderBucket;

 class RealArchiveStoreItem extends ArchiveStoreItem {

-       private final ArchiveManager manager;
        private final File myFilename;
-       private final PaddedEphemerallyEncryptedBucket bucket;
-       private final FileBucket underBucket;
+       private final MultiReaderBucket mb;
+       private final Bucket bucket;
        private final long spaceUsed;

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

        /**
@@ -59,10 +55,14 @@
        }

        void innerClose() {
-               underBucket.free();
+               bucket.free();
        }

        Bucket getDataOrThrow() throws ArchiveFailureException {
                return dataAsBucket();
        }
+
+       Bucket getReaderBucket() throws ArchiveFailureException {
+               return mb.getReaderBucket();
+       }
 }

Modified: branches/freenet-jfk/src/freenet/client/TempStoreElement.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/TempStoreElement.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/TempStoreElement.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -5,15 +5,15 @@

 import java.io.File;

-import freenet.support.io.FileBucket;
 import freenet.support.io.PaddedEphemerallyEncryptedBucket;
+import freenet.support.io.TempFileBucket;

 class TempStoreElement {
        final File myFilename;
        final PaddedEphemerallyEncryptedBucket bucket;
-       final FileBucket underBucket;
+       final TempFileBucket underBucket;

-       TempStoreElement(File myFile, FileBucket fb, 
PaddedEphemerallyEncryptedBucket encryptedBucket) {
+       TempStoreElement(File myFile, TempFileBucket fb, 
PaddedEphemerallyEncryptedBucket encryptedBucket) {
                this.myFilename = myFile;
                this.underBucket = fb;
                this.bucket = encryptedBucket;

Modified: 
branches/freenet-jfk/src/freenet/client/async/BaseSingleFileFetcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/BaseSingleFileFetcher.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/BaseSingleFileFetcher.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -6,9 +6,11 @@
 import freenet.client.FetchContext;
 import freenet.keys.ClientKey;
 import freenet.keys.ClientSSK;
+import freenet.keys.Key;
+import freenet.keys.KeyBlock;
+import freenet.keys.KeyVerifyException;
 import freenet.node.SendableGet;
 import freenet.support.Logger;
-import freenet.support.RandomGrabArray;

 public abstract class BaseSingleFileFetcher extends SendableGet {

@@ -80,8 +82,7 @@
                synchronized(this) {
                        cancelled = true;
                }
-               RandomGrabArray arr = getParentGrabArray();
-               if(arr != null) arr.remove(this);
+               super.unregister();
        }

        public synchronized boolean isCancelled() {
@@ -101,4 +102,20 @@
                return true;
        }

+       public void onGotKey(Key key, KeyBlock block) {
+               synchronized(this) {
+                       if(isCancelled()) return;
+                       if(!key.equals(this.key.getNodeKey())) {
+                               Logger.normal(this, "Got sent key "+key+" but 
want "+this.key+" for "+this);
+                               return;
+                       }
+               }
+               try {
+                       onSuccess(Key.createKeyBlock(this.key, block), false, 
0);
+               } catch (KeyVerifyException e) {
+                       Logger.error(this, "onGotKey("+key+","+block+") got 
"+e+" for "+this, e);
+                       // FIXME if we get rid of the direct route this must 
call onFailure()
+               }
+       }
+       
 }

Modified: branches/freenet-jfk/src/freenet/client/async/BinaryBlob.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/BinaryBlob.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/BinaryBlob.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,10 +1,15 @@
 package freenet.client.async;

+import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.EOFException;
 import java.io.IOException;

+import com.onionnetworks.util.FileUtil;
+
 import freenet.keys.Key;
 import freenet.keys.KeyBlock;
+import freenet.keys.KeyVerifyException;

 public abstract class BinaryBlob {

@@ -46,4 +51,70 @@
                writeBlobHeader(binaryBlobStream, BinaryBlob.BLOB_END, 
BinaryBlob.BLOB_END_VERSION, 0);
        }

+       public static void readBinaryBlob(DataInputStream dis, BlockSet blocks, 
boolean tolerant) throws IOException, BinaryBlobFormatException {
+               long magic = dis.readLong();
+               if(magic != BinaryBlob.BINARY_BLOB_MAGIC)
+                       throw new BinaryBlobFormatException("Bad magic");
+               short version = dis.readShort();
+               if(version != BinaryBlob.BINARY_BLOB_OVERALL_VERSION)
+                       throw new BinaryBlobFormatException("Unknown overall 
version");
+               
+               int i=0;
+               while(true) {
+                       long blobLength;
+                       try {
+                               blobLength = dis.readInt() & 0xFFFFFFFFL;
+                       } catch (EOFException e) {
+                               // End of file
+                               dis.close();
+                               break;
+                       }
+                       short blobType = dis.readShort();
+                       short blobVer = dis.readShort();
+                       
+                       if(blobType == BinaryBlob.BLOB_END) {
+                               dis.close();
+                               break;
+                       } else if(blobType == BinaryBlob.BLOB_BLOCK) {
+                               if(blobVer != BinaryBlob.BLOB_BLOCK_VERSION)
+                                       // Even if tolerant, if we can't read a 
blob there probably isn't much we can do.
+                                       throw new 
BinaryBlobFormatException("Unknown block blob version");
+                               if(blobLength < 9)
+                                       throw new 
BinaryBlobFormatException("Block blob too short");
+                               short keyType = dis.readShort();
+                               int keyLen = dis.readUnsignedByte();
+                               int headersLen = dis.readUnsignedShort();
+                               int dataLen = dis.readUnsignedShort();
+                               int pubkeyLen = dis.readUnsignedShort();
+                               int total = 9 + keyLen + headersLen + dataLen + 
pubkeyLen;
+                               if(blobLength != total)
+                                       throw new 
BinaryBlobFormatException("Binary blob not same length as data: 
blobLength="+blobLength+" total="+total);
+                               byte[] keyBytes = new byte[keyLen];
+                               byte[] headersBytes = new byte[headersLen];
+                               byte[] dataBytes = new byte[dataLen];
+                               byte[] pubkeyBytes = new byte[pubkeyLen];
+                               dis.readFully(keyBytes);
+                               dis.readFully(headersBytes);
+                               dis.readFully(dataBytes);
+                               dis.readFully(pubkeyBytes);
+                               KeyBlock block;
+                               try {
+                                       block = Key.createBlock(keyType, 
keyBytes, headersBytes, dataBytes, pubkeyBytes);
+                               } catch (KeyVerifyException e) {
+                                       throw new 
BinaryBlobFormatException("Invalid key: "+e.getMessage(), e);
+                               }
+                               
+                               blocks.add(block);
+                               
+                       } else {
+                               if(tolerant) {
+                                       FileUtil.skipFully(dis, blobLength);
+                               } else {
+                                       throw new 
BinaryBlobFormatException("Unknown blob type: "+blobType);
+                               }
+                       }
+                       i++;
+               }
+
+       }
 }

Modified: 
branches/freenet-jfk/src/freenet/client/async/BinaryBlobFormatException.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/client/async/BinaryBlobFormatException.java    
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/client/async/BinaryBlobFormatException.java    
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -4,6 +4,11 @@

 public class BinaryBlobFormatException extends Exception {

+       /**
+        * 
+        */
+       private static final long serialVersionUID = 1L;
+
        public BinaryBlobFormatException(String message) {
                super(message);
        }

Modified: branches/freenet-jfk/src/freenet/client/async/BinaryBlobInserter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/BinaryBlobInserter.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/BinaryBlobInserter.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,19 +1,16 @@
 package freenet.client.async;

 import java.io.DataInputStream;
-import java.io.EOFException;
 import java.io.IOException;
+import java.util.Iterator;
 import java.util.Vector;

-import com.onionnetworks.util.FileUtil;
-
 import freenet.client.FailureCodeTracker;
 import freenet.client.InsertContext;
 import freenet.client.InsertException;
 import freenet.keys.CHKBlock;
 import freenet.keys.Key;
 import freenet.keys.KeyBlock;
-import freenet.keys.KeyVerifyException;
 import freenet.keys.SSKBlock;
 import freenet.node.LowLevelPutException;
 import freenet.node.SimpleSendableInsert;
@@ -44,74 +41,24 @@
                this.parent = parent;
                this.clientContext = clientContext;
                this.errors = new FailureCodeTracker(true);
-               Vector myInserters = new Vector();
                DataInputStream dis = new 
DataInputStream(blob.getInputStream());
-               long magic = dis.readLong();
-               if(magic != BinaryBlob.BINARY_BLOB_MAGIC)
-                       throw new BinaryBlobFormatException("Bad magic");
-               short version = dis.readShort();
-               if(version != BinaryBlob.BINARY_BLOB_OVERALL_VERSION)
-                       throw new BinaryBlobFormatException("Unknown overall 
version");

-               int i=0;
-               while(true) {
-                       long blobLength;
-                       try {
-                               blobLength = dis.readInt() & 0xFFFFFFFFL;
-                       } catch (EOFException e) {
-                               // End of file
-                               dis.close();
-                               break;
-                       }
-                       short blobType = dis.readShort();
-                       short blobVer = dis.readShort();
-                       
-                       if(blobType == BinaryBlob.BLOB_END) {
-                               dis.close();
-                               break;
-                       } else if(blobType == BinaryBlob.BLOB_BLOCK) {
-                               if(blobVer != BinaryBlob.BLOB_BLOCK_VERSION)
-                                       // Even if tolerant, if we can't read a 
blob there probably isn't much we can do.
-                                       throw new 
BinaryBlobFormatException("Unknown block blob version");
-                               if(blobLength < 9)
-                                       throw new 
BinaryBlobFormatException("Block blob too short");
-                               short keyType = dis.readShort();
-                               int keyLen = dis.readUnsignedByte();
-                               int headersLen = dis.readUnsignedShort();
-                               int dataLen = dis.readUnsignedShort();
-                               int pubkeyLen = dis.readUnsignedShort();
-                               int total = 9 + keyLen + headersLen + dataLen + 
pubkeyLen;
-                               if(blobLength != total)
-                                       throw new 
BinaryBlobFormatException("Binary blob not same length as data: 
blobLength="+blobLength+" total="+total);
-                               byte[] keyBytes = new byte[keyLen];
-                               byte[] headersBytes = new byte[headersLen];
-                               byte[] dataBytes = new byte[dataLen];
-                               byte[] pubkeyBytes = new byte[pubkeyLen];
-                               dis.readFully(keyBytes);
-                               dis.readFully(headersBytes);
-                               dis.readFully(dataBytes);
-                               dis.readFully(pubkeyBytes);
-                               KeyBlock block;
-                               try {
-                                       block = Key.createBlock(keyType, 
keyBytes, headersBytes, dataBytes, pubkeyBytes);
-                               } catch (KeyVerifyException e) {
-                                       throw new 
BinaryBlobFormatException("Invalid key: "+e.getMessage(), e);
-                               }
-                               
-                               MySendableInsert inserter =
-                                       new MySendableInsert(i, block, 
prioClass, getScheduler(block), clientContext);
-                               
-                               myInserters.add(inserter);
-                               
-                       } else {
-                               if(tolerant) {
-                                       FileUtil.skipFully(dis, blobLength);
-                               } else {
-                                       throw new 
BinaryBlobFormatException("Unknown blob type: "+blobType);
-                               }
-                       }
-                       i++;
+               BlockSet blocks = new SimpleBlockSet();
+               
+               BinaryBlob.readBinaryBlob(dis, blocks, tolerant);
+               
+               Vector myInserters = new Vector();
+               Iterator i = blocks.keys().iterator();
+               
+               int x=0;
+               while(i.hasNext()) {
+                       Key key = (Key) i.next();
+                       KeyBlock block = blocks.get(key);
+                       MySendableInsert inserter =
+                               new MySendableInsert(x++, block, prioClass, 
getScheduler(block), clientContext);
+                       myInserters.add(inserter);
                }
+               
                inserters = (MySendableInsert[]) myInserters.toArray(new 
MySendableInsert[myInserters.size()]);
                parent.addMustSucceedBlocks(inserters.length);
                parent.notifyClients();

Copied: branches/freenet-jfk/src/freenet/client/async/BlockSet.java (from rev 
14796, trunk/freenet/src/freenet/client/async/BlockSet.java)
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/BlockSet.java                 
        (rev 0)
+++ branches/freenet-jfk/src/freenet/client/async/BlockSet.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,42 @@
+/* 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.async;
+
+import java.util.Set;
+
+import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
+import freenet.keys.Key;
+import freenet.keys.KeyBlock;
+
+/**
+ * A set of KeyBlock's.
+ * @author toad
+ */
+public interface BlockSet {
+
+       /**
+        * Get a block by its key.
+        * @param key The key of the block to get.
+        * @return A block, or null if there is no block with that key.
+        */
+       public KeyBlock get(Key key);
+       
+       /**
+        * Add a block.
+        * @param block The block to add.
+        */
+       public void add(KeyBlock block);
+       
+       /**
+        * Get the set of all the keys of all the blocks.
+        * @return A set of the keys of the blocks in the BlockSet. Not 
guaranteed to be
+        * kept up to date. Read only.
+        */
+       public Set keys();
+
+       /** Get a high level block, given a high level key */
+       public ClientKeyBlock get(ClientKey key);
+       
+}

Modified: branches/freenet-jfk/src/freenet/client/async/ClientGetter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/ClientGetter.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/ClientGetter.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -3,6 +3,7 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.client.async;

+import java.io.BufferedOutputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -10,14 +11,13 @@

 import freenet.client.ArchiveContext;
 import freenet.client.ClientMetadata;
+import freenet.client.FetchContext;
 import freenet.client.FetchException;
 import freenet.client.FetchResult;
-import freenet.client.FetchContext;
 import freenet.client.events.SplitfileProgressEvent;
 import freenet.keys.ClientKeyBlock;
 import freenet.keys.FreenetURI;
 import freenet.keys.Key;
-import freenet.keys.KeyBlock;
 import freenet.support.Logger;
 import freenet.support.api.Bucket;
 import freenet.support.io.BucketTools;
@@ -41,7 +41,7 @@
        /** If not null, HashSet to track keys already added for a binary blob 
*/
        final HashSet binaryBlobKeysAddedAlready;
        private DataOutputStream binaryBlobStream;
-
+       
        /**
         * Fetch a key.
         * @param client
@@ -98,10 +98,10 @@
                        if(currentState != null && !finished) {
                                if(binaryBlobBucket != null) {
                                        try {
-                                               binaryBlobStream = new 
DataOutputStream(binaryBlobBucket.getOutputStream());
+                                               binaryBlobStream = new 
DataOutputStream(new BufferedOutputStream(binaryBlobBucket.getOutputStream()));
                                                
BinaryBlob.writeBinaryBlobHeader(binaryBlobStream);
                                        } catch (IOException e) {
-                                               onFailure(new 
FetchException(FetchException.BUCKET_ERROR, "Failed to open binary blob 
bucket"), null);
+                                               onFailure(new 
FetchException(FetchException.BUCKET_ERROR, "Failed to open binary blob 
bucket", e), null);
                                                return false;
                                        }
                                }
@@ -142,7 +142,13 @@
                        if(returnBucket != null && 
Logger.shouldLog(Logger.MINOR, this))
                                Logger.minor(this, "client.async returned data 
in returnBucket");
                }
-               client.onSuccess(result, this);
+               final FetchResult res = result;
+               ctx.executor.execute(new Runnable() {
+                       public void run() {
+                               client.onSuccess(res, ClientGetter.this);
+                       }
+               }, "ClientGetter onSuccess callback");
+               
        }

        public void onFailure(FetchException e, ClientGetState state) {
@@ -170,13 +176,20 @@
                        }
                        synchronized(this) {
                                finished = true;
+                               currentState = null;
                        }
                        if(e.errorCodes != null && e.errorCodes.isOneCodeOnly())
                                e = new 
FetchException(e.errorCodes.getFirstCode(), e);
                        if(e.mode == FetchException.DATA_NOT_FOUND && 
super.successfulBlocks > 0)
                                e = new FetchException(e, 
FetchException.ALL_DATA_NOT_FOUND);
                        Logger.minor(this, "onFailure("+e+", "+state+") on 
"+this+" for "+uri, e);
-                       client.onFailure(e, this);
+                       final FetchException e1 = e;
+                       ctx.executor.execute(new Runnable() {
+                               public void run() {
+                                       client.onFailure(e1, ClientGetter.this);
+                               }
+                       }, "ClientGetter onFailure callback");
+                       
                        return;
                }
        }
@@ -267,8 +280,9 @@
         * called onFailure() with an appropriate error.
         */
        private boolean closeBinaryBlobStream() {
-               if(binaryBlobBucket == null) return true;
+               if(binaryBlobKeysAddedAlready == null) return true;
                synchronized(binaryBlobKeysAddedAlready) {
+                       if(binaryBlobStream == null) return true;
                        try {
                                BinaryBlob.writeEndBlob(binaryBlobStream);
                                binaryBlobStream.close();

Modified: 
branches/freenet-jfk/src/freenet/client/async/ClientRequestScheduler.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/ClientRequestScheduler.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/ClientRequestScheduler.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -14,6 +14,8 @@
 import freenet.crypt.RandomSource;
 import freenet.keys.ClientKey;
 import freenet.keys.ClientKeyBlock;
+import freenet.keys.Key;
+import freenet.keys.KeyBlock;
 import freenet.keys.KeyVerifyException;
 import freenet.node.LowLevelGetException;
 import freenet.node.Node;
@@ -24,8 +26,8 @@
 import freenet.node.SendableRequest;
 import freenet.support.Logger;
 import freenet.support.RandomGrabArray;
+import freenet.support.SectoredRandomGrabArrayWithInt;
 import freenet.support.SectoredRandomGrabArrayWithObject;
-import freenet.support.SectoredRandomGrabArrayWithInt;
 import freenet.support.SortedVectorByNumber;
 import freenet.support.api.StringCallback;

@@ -94,6 +96,11 @@
        public final String name;
        private final LinkedList /* <WeakReference <RandomGrabArray> > */ 
recentSuccesses = new LinkedList();

+       /** All pending gets by key. Used to automatically satisfy pending 
requests when either the key is fetched by
+        * an overlapping request, or it is fetched by a request from another 
node. Operations on this are synchronized on
+        * itself. */
+       private final HashMap /* <Key, SendableGet[]> */ pendingKeys;
+       
        public static final String PRIORITY_NONE = "NONE";
        public static final String PRIORITY_SOFT = "SOFT";
        public static final String PRIORITY_HARD = "HARD";
@@ -159,6 +166,10 @@
                this.isSSKScheduler = forSSKs;
                priorities = new 
SortedVectorByNumber[RequestStarter.NUMBER_OF_PRIORITY_CLASSES];
                allRequestsByClientRequest = new HashMap();
+               if(forInserts)
+                       pendingKeys = null;
+               else
+                       pendingKeys = new HashMap();

                this.name = name;
                sc.register(name+"_priority_policy", PRIORITY_HARD, 
name.hashCode(), true, false,
@@ -190,15 +201,22 @@
                                int[] keyTokens = getter.allKeys();
                                for(int i=0;i<keyTokens.length;i++) {
                                        int tok = keyTokens[i];
-                                       ClientKeyBlock block;
+                                       ClientKeyBlock block = null;
                                        try {
                                                ClientKey key = 
getter.getKey(tok);
                                                if(key == null) {
                                                        if(logMINOR)
                                                                
Logger.minor(this, "No key for "+tok+" for "+getter+" - already finished?");
                                                        continue;
-                                               } else
-                                                       block = 
node.fetchKey(key, getter.dontCache());
+                                               } else {
+                                                       
if(getter.getContext().blocks != null)
+                                                               block = 
getter.getContext().blocks.get(key);
+                                                       if(block == null)
+                                                               block = 
node.fetchKey(key, getter.dontCache());
+                                                       if(block == null) {
+                                                               
addPendingKey(key, getter);
+                                                       }
+                                               }
                                        } catch (KeyVerifyException e) {
                                                // Verify exception, probably 
bogus at source;
                                                // verifies at low-level, but 
not at decode.
@@ -223,6 +241,36 @@
                }
        }

+       private void addPendingKey(ClientKey key, SendableGet getter) {
+               Key nodeKey = key.getNodeKey();
+               synchronized(pendingKeys) {
+                       Object o = pendingKeys.get(nodeKey);
+                       if(o == null) {
+                               pendingKeys.put(nodeKey, getter);
+                       } else if(o instanceof SendableGet) {
+                               SendableGet oldGet = (SendableGet) o;
+                               if(oldGet != getter) {
+                                       pendingKeys.put(nodeKey, new 
SendableGet[] { oldGet, getter });
+                               }
+                       } else {
+                               SendableGet[] gets = (SendableGet[]) o;
+                               boolean found = false;
+                               for(int j=0;j<gets.length;j++) {
+                                       if(gets[j] == getter) {
+                                               found = true;
+                                               break;
+                                       }
+                               }
+                               if(!found) {
+                                       SendableGet[] newGets = new 
SendableGet[gets.length+1];
+                                       System.arraycopy(gets, 0, newGets, 0, 
gets.length);
+                                       newGets[gets.length] = getter;
+                                       pendingKeys.put(nodeKey, newGets);
+                               }
+                       }
+               }
+       }
+
        private synchronized void innerRegister(SendableRequest req) {
                if(logMINOR) Logger.minor(this, "Still registering "+req+" at 
prio "+req.getPriorityClass()+" retry "+req.getRetryCount());
                addToGrabArray(req.getPriorityClass(), req.getRetryCount(), 
req.getClient(), req.getClientRequest(), req);
@@ -384,6 +432,9 @@
                                                        
allRequestsByClientRequest.remove(cr);
                                                if(logMINOR) Logger.minor(this, 
"Removed from HashSet for "+cr+" which now has "+v.size()+" elements");
                                        }
+                                       if(!isInsertScheduler) {
+                                               removePendingKeys((SendableGet) 
req, true);
+                                       }
                                }
                                if(logMINOR) Logger.minor(this, "removeFirst() 
returning "+req);
                                return req;
@@ -393,6 +444,71 @@
                return null;
        }

+       public void removePendingKey(SendableGet getter, boolean complain, Key 
key) {
+               synchronized(pendingKeys) {
+                       Object o = pendingKeys.get(key);
+                       if(o == null) {
+                               if(complain)
+                                       Logger.normal(this, "Not found: 
"+getter+" for "+key+" removing (no such key)");
+                       } else if(o instanceof SendableGet) {
+                               SendableGet oldGet = (SendableGet) o;
+                               if(oldGet != getter) {
+                                       if(complain)
+                                               Logger.normal(this, "Not found: 
"+getter+" for "+key+" removing (1 getter)");
+                               } else {
+                                       pendingKeys.remove(key);
+                               }
+                       } else {
+                               SendableGet[] gets = (SendableGet[]) o;
+                               SendableGet[] newGets = new 
SendableGet[gets.length-1];
+                               boolean found = false;
+                               int x = 0;
+                               for(int j=0;j<gets.length;j++) {
+                                       if(j > newGets.length) {
+                                               if(!found) {
+                                                       if(complain)
+                                                               
Logger.normal(this, "Not found: "+getter+" for "+key+" removing 
("+gets.length+" getters)");
+                                                       return; // not here
+                                               }
+                                               if(gets[j] == getter || gets[j] 
== null || gets[j].isCancelled()) continue;
+                                               newGets[x++] = gets[j];
+                                       }
+                               }
+                               if(x != gets.length-1) {
+                                       SendableGet[] newNewGets = new 
SendableGet[x];
+                                       System.arraycopy(newGets, 0, 
newNewGets, 0, x);
+                                       newGets = newNewGets;
+                               }
+                               if(newGets.length == 0) {
+                                       pendingKeys.remove(key);
+                               } else if(newGets.length == 1) {
+                                       pendingKeys.put(key, newGets[0]);
+                               } else {
+                                       pendingKeys.put(key, newGets);
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * Remove a SendableGet from the list of getters we maintain for each 
key, indicating that we are no longer interested
+        * in that key.
+        * @param getter
+        * @param complain
+        */
+       public void removePendingKeys(SendableGet getter, boolean complain) {
+               int[] keyTokens = getter.allKeys();
+               for(int i=0;i<keyTokens.length;i++) {
+                       int tok = keyTokens[i];
+                       ClientKey ckey = getter.getKey(tok);
+                       if(ckey == null) {
+                               Logger.error(this, "Key "+tok+" is null for 
"+getter);
+                               continue;
+                       }
+                       removePendingKey(getter, complain, ckey.getNodeKey());
+               }
+       }
+
        public void reregisterAll(ClientRequester request) {
                SendableRequest[] reqs;
                synchronized(this) {
@@ -403,8 +519,7 @@

                for(int i=0;i<reqs.length;i++) {
                        SendableRequest req = reqs[i];
-                       RandomGrabArray array = req.getParentGrabArray();
-                       if(array != null) array.remove(req);
+                       req.unregister();
                        innerRegister(req);
                }
                synchronized(starter) {
@@ -425,4 +540,34 @@
                                recentSuccesses.removeLast();
                }
        }
+
+       public void tripPendingKey(final KeyBlock block) {
+               final Key key = block.getKey();
+               final SendableGet[] gets;
+               Object o;
+               synchronized(pendingKeys) {
+                       o = pendingKeys.get(key);
+               }
+               if(o == null) return;
+               if(o instanceof SendableGet) {
+                       gets = new SendableGet[] { (SendableGet) o };
+               } else {
+                       gets = (SendableGet[]) o;
+               }
+               if(gets == null) return;
+               Runnable r = new Runnable() {
+                       public void run() {
+                               for(int i=0;i<gets.length;i++) {
+                                       gets[i].onGotKey(key, block);
+                               }
+                       }
+               };
+               node.getTicker().queueTimedJob(r, 0); // FIXME ideally these 
would be completed on a single thread; when we have 1.5, use a dedicated 
non-parallel Executor
+       }
+
+       public boolean anyWantKey(Key key) {
+               synchronized(pendingKeys) {
+                       return pendingKeys.get(key) != null;
+               }
+       }
 }

Copied: branches/freenet-jfk/src/freenet/client/async/SimpleBlockSet.java (from 
rev 14796, trunk/freenet/src/freenet/client/async/SimpleBlockSet.java)
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SimpleBlockSet.java           
                (rev 0)
+++ branches/freenet-jfk/src/freenet/client/async/SimpleBlockSet.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,45 @@
+package freenet.client.async;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import freenet.keys.ClientKey;
+import freenet.keys.ClientKeyBlock;
+import freenet.keys.Key;
+import freenet.keys.KeyBlock;
+import freenet.keys.KeyVerifyException;
+import freenet.support.Logger;
+
+/** 
+ * Simple BlockSet implementation, keeps all keys in RAM.
+ * 
+ * @author toad
+ */
+public class SimpleBlockSet implements BlockSet {
+
+       private final HashMap blocksByKey = new HashMap();
+       
+       public synchronized void add(KeyBlock block) {
+               blocksByKey.put(block.getKey(), block);
+       }
+
+       public synchronized KeyBlock get(Key key) {
+               return (KeyBlock) blocksByKey.get(key);
+       }
+
+       public synchronized Set keys() {
+               return blocksByKey.keySet();
+       }
+
+       public ClientKeyBlock get(ClientKey key) {
+               KeyBlock block = get(key.getNodeKey());
+               if(block == null) return null;
+               try {
+                       return Key.createKeyBlock(key, block);
+               } catch (KeyVerifyException e) {
+                       Logger.error(this, "Caught decoding block with "+key+" 
: "+e, e);
+                       return null;
+               }
+       }
+
+}

Modified: 
branches/freenet-jfk/src/freenet/client/async/SimpleManifestPutter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SimpleManifestPutter.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/SimpleManifestPutter.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,5 +1,6 @@
 package freenet.client.async;

+import java.io.BufferedOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.HashMap;
@@ -404,7 +405,7 @@
                                // Only the *decoding* is generic at present.

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


Modified: 
branches/freenet-jfk/src/freenet/client/async/SimpleSingleFileFetcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SimpleSingleFileFetcher.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/SimpleSingleFileFetcher.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -47,6 +47,9 @@
                case LowLevelGetException.DATA_NOT_FOUND_IN_STORE:
                        onFailure(new 
FetchException(FetchException.DATA_NOT_FOUND));
                        return;
+               case LowLevelGetException.RECENTLY_FAILED:
+                       onFailure(new 
FetchException(FetchException.RECENTLY_FAILED));
+                       return;
                case LowLevelGetException.DECODE_FAILED:
                        onFailure(new 
FetchException(FetchException.BLOCK_DECODE_ERROR));
                        return;
@@ -95,6 +98,7 @@
                        }
                }
                // :(
+               unregister();
                if(e.isFatal() || forceFatal)
                        parent.fatallyFailedBlock();
                else
@@ -104,6 +108,7 @@

        /** Will be overridden by SingleFileFetcher */
        protected void onSuccess(FetchResult data) {
+               unregister();
                if(parent.isCancelled()) {
                        data.asBucket().free();
                        onFailure(new FetchException(FetchException.CANCELLED));

Modified: branches/freenet-jfk/src/freenet/client/async/SingleBlockInserter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SingleBlockInserter.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/SingleBlockInserter.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -21,7 +21,6 @@
 import freenet.node.RequestScheduler;
 import freenet.node.SendableInsert;
 import freenet.support.Logger;
-import freenet.support.RandomGrabArray;
 import freenet.support.SimpleFieldSet;
 import freenet.support.api.Bucket;

@@ -163,7 +162,7 @@
                        Logger.error(this, "Unknown LowLevelPutException code: 
"+e.code);
                        errors.inc(InsertException.INTERNAL_ERROR);
                }
-               if(e.code == LowLevelPutException.ROUTE_NOT_FOUND) {
+               if(e.code == LowLevelPutException.ROUTE_NOT_FOUND || e.code == 
LowLevelPutException.ROUTE_REALLY_NOT_FOUND) {
                        consecutiveRNFs++;
                        if(logMINOR) Logger.minor(this, "Consecutive RNFs: 
"+consecutiveRNFs+" / "+ctx.consecutiveRNFsCountAsSuccess);
                        if(consecutiveRNFs == 
ctx.consecutiveRNFsCountAsSuccess) {
@@ -276,8 +275,7 @@
                        if(finished) return;
                        finished = true;
                }
-               RandomGrabArray arr = getParentGrabArray();
-               if(arr != null) arr.remove(this);
+               super.unregister();
                cb.onFailure(new InsertException(InsertException.CANCELLED), 
this);
        }


Modified: branches/freenet-jfk/src/freenet/client/async/SingleFileFetcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SingleFileFetcher.java        
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/SingleFileFetcher.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -8,7 +8,9 @@
 import java.util.LinkedList;

 import freenet.client.ArchiveContext;
+import freenet.client.ArchiveExtractCallback;
 import freenet.client.ArchiveFailureException;
+import freenet.client.ArchiveManager;
 import freenet.client.ArchiveRestartException;
 import freenet.client.ArchiveStoreContext;
 import freenet.client.ClientMetadata;
@@ -158,23 +160,12 @@
                                onFailure(new 
FetchException(FetchException.BUCKET_ERROR, e));
                                return;
                        }
-                       try {
-                               handleMetadata();
-                       } catch (MetadataParseException e) {
-                               onFailure(new FetchException(e));
-                               return;
-                       } catch (FetchException e) {
-                               onFailure(e);
-                               return;
-                       } catch (ArchiveFailureException e) {
-                               onFailure(new FetchException(e));
-                       } catch (ArchiveRestartException e) {
-                               onFailure(new FetchException(e));
-                       }
+                       wrapHandleMetadata(false);
                }
        }

        protected void onSuccess(FetchResult result) {
+               unregister();
                if(parent.isCancelled()) {
                        if(logMINOR)
                                Logger.minor(this, "Parent is cancelled");
@@ -227,20 +218,29 @@
                }
        }

-       private void handleMetadata() throws FetchException, 
MetadataParseException, ArchiveFailureException, ArchiveRestartException {
+       /**
+        * Handle the current metadata. I.e. do something with it: transition 
to a splitfile, look up a manifest, etc.
+        * LOCKING: Synchronized as it changes so many variables; if we want to 
write the structure to disk, we don't
+        * want this running at the same time.
+        * @throws FetchException
+        * @throws MetadataParseException
+        * @throws ArchiveFailureException
+        * @throws ArchiveRestartException
+        */
+       private synchronized void handleMetadata() throws FetchException, 
MetadataParseException, ArchiveFailureException, ArchiveRestartException {
                while(true) {
                        if(metadata.isSimpleManifest()) {
                                if(logMINOR) Logger.minor(this, "Is simple 
manifest");
                                String name;
                                if(metaStrings.isEmpty())
-                                       throw new 
FetchException(FetchException.NOT_ENOUGH_PATH_COMPONENTS);
+                                       throw new 
FetchException(FetchException.NOT_ENOUGH_PATH_COMPONENTS, -1, false, null, 
thisKey.addMetaStrings(new String[] { "" }));
                                else name = removeMetaString();
                                // Since metadata is a document, we just 
replace metadata here
                                if(logMINOR) Logger.minor(this, "Next 
meta-string: "+name);
                                if(name == null) {
                                        metadata = 
metadata.getDefaultDocument();
                                        if(metadata == null)
-                                               throw new 
FetchException(FetchException.NOT_ENOUGH_PATH_COMPONENTS);
+                                               throw new 
FetchException(FetchException.NOT_ENOUGH_PATH_COMPONENTS, -1, false, null, 
thisKey.addMetaStrings(new String[] { "" }));
                                } else {
                                        metadata = metadata.getDocument(name);
                                        thisKey = thisKey.pushMetaString(name);
@@ -274,13 +274,35 @@
                                                throw new 
FetchException(FetchException.BUCKET_ERROR, e);
                                        }
                                } else {
-                                       fetchArchive(false, archiveMetadata); 
// will result in this function being called again
+                                       fetchArchive(false, archiveMetadata, 
ArchiveManager.METADATA_NAME, new ArchiveExtractCallback() {
+                                               public void gotBucket(Bucket 
data) {
+                                                       try {
+                                                               metadata = 
Metadata.construct(data);
+                                                       } catch 
(MetadataParseException e) {
+                                                               // Invalid 
metadata
+                                                               onFailure(new 
FetchException(FetchException.INVALID_METADATA, e));
+                                                               return;
+                                                       } catch (IOException e) 
{
+                                                               // Bucket error?
+                                                               onFailure(new 
FetchException(FetchException.BUCKET_ERROR, e));
+                                                               return;
+                                                       }
+                                                       
wrapHandleMetadata(true);
+                                               }
+                                               public void notInArchive() {
+                                                       onFailure(new 
FetchException(FetchException.INTERNAL_ERROR, "No metadata in container! Cannot 
happen as ArchiveManager should synthesise some!"));
+                                               }
+                                       }); // will result in this function 
being called again
                                        return;
                                }
                                continue;
                        } else if(metadata.isArchiveInternalRedirect()) {
                                if(logMINOR) Logger.minor(this, "Is 
archive-internal redirect");
                                
clientMetadata.mergeNoOverwrite(metadata.getClientMetadata());
+                               if(metaStrings.isEmpty() && isFinal && 
clientMetadata.getMIMETypeNoParams() != null && ctx.allowedMIMETypes != null &&
+                                               
!ctx.allowedMIMETypes.contains(clientMetadata.getMIMETypeNoParams())) {
+                                       throw new 
FetchException(FetchException.WRONG_MIME_TYPE, -1, false, 
clientMetadata.getMIMEType());
+                               }
                                // Fetch it from the archive
                                if(ah == null)
                                        throw new 
FetchException(FetchException.UNKNOWN_METADATA, "Archive redirect not in an 
archive manifest");
@@ -289,30 +311,56 @@
                                Bucket dataBucket = ah.get(filename, actx, 
null, recursionLevel+1, true);
                                if(dataBucket != null) {
                                        if(logMINOR) Logger.minor(this, 
"Returning data");
-                                       // The client may free it, which is 
bad, or it may hang on to it for so long that it gets
-                                       // freed by us, which is also bad.
-                                       // So copy it.
-                                       // FIXME this is stupid, reconsider how 
we determine when to free buckets; refcounts maybe?
-                                       Bucket out;
+                                       final Bucket out;
                                        try {
-                                               if(returnBucket != null)
+                                               // Data will not be freed until 
client is finished with it.
+                                               if(returnBucket != null) {
                                                        out = returnBucket;
-                                               else
-                                                       out = 
ctx.bucketFactory.makeBucket(dataBucket.size());
-                                               BucketTools.copy(dataBucket, 
out);
+                                                       
BucketTools.copy(dataBucket, out);
+                                                       dataBucket.free();
+                                               } else {
+                                                       out = dataBucket;
+                                               }
                                        } catch (IOException e) {
-                                               onFailure(new 
FetchException(FetchException.BUCKET_ERROR));
-                                               return;
+                                               throw new 
FetchException(FetchException.BUCKET_ERROR);
                                        }
                                        // Return the data
-                                       onSuccess(new 
FetchResult(this.clientMetadata, out));
+                                       ctx.executor.execute(new Runnable() {
+                                               public void run() {
+                                                       onSuccess(new 
FetchResult(clientMetadata, out));
+                                               }
+                                       }, "SingleFileFetcher onSuccess 
callback for "+this);
+                                       
                                        return;
                                } else {
                                        if(logMINOR) Logger.minor(this, 
"Fetching archive (thisKey="+thisKey+ ')');
                                        // Metadata cannot contain pointers to 
files which don't exist.
                                        // We enforce this in ArchiveHandler.
                                        // Therefore, the archive needs to be 
fetched.
-                                       fetchArchive(true, archiveMetadata);
+                                       fetchArchive(true, archiveMetadata, 
filename, new ArchiveExtractCallback() {
+                                               public void gotBucket(Bucket 
data) {
+                                                       if(logMINOR) 
Logger.minor(this, "Returning data");
+                                                       Bucket out;
+                                                       try {
+                                                               // Data will 
not be freed until client is finished with it.
+                                                               if(returnBucket 
!= null) {
+                                                                       out = 
returnBucket;
+                                                                       
BucketTools.copy(data, out);
+                                                                       
data.free();
+                                                               } else {
+                                                                       out = 
data;
+                                                               }
+                                                       } catch (IOException e) 
{
+                                                               onFailure(new 
FetchException(FetchException.BUCKET_ERROR));
+                                                               return;
+                                                       }
+                                                       // Return the data
+                                                       onSuccess(new 
FetchResult(clientMetadata, out));
+                                               }
+                                               public void notInArchive() {
+                                                       onFailure(new 
FetchException(FetchException.NOT_IN_ARCHIVE));
+                                               }
+                                       });
                                        // Will call back into this function 
when it has been fetched.
                                        return;
                                }
@@ -321,14 +369,31 @@
                                // Fetch on a second SingleFileFetcher, like 
with archives.
                                Metadata newMeta = (Metadata) metadata.clone();
                                newMeta.setSimpleRedirect();
-                               SingleFileFetcher f = new 
SingleFileFetcher(this, newMeta, new MultiLevelMetadataCallback(), ctx);
-                               f.handleMetadata();
+                               final SingleFileFetcher f = new 
SingleFileFetcher(this, newMeta, new MultiLevelMetadataCallback(), ctx);
+                               ctx.ticker.queueTimedJob(new Runnable() {
+                                       public void run() {
+                                               f.wrapHandleMetadata(true);
+                                       }
+                               }, 0);
                                return;
                        } else if(metadata.isSingleFileRedirect()) {
                                if(logMINOR) Logger.minor(this, "Is single-file 
redirect");
                                
clientMetadata.mergeNoOverwrite(metadata.getClientMetadata()); // even 
splitfiles can have mime types!
-                               // FIXME implement implicit archive support
+
+                               String mimeType = 
clientMetadata.getMIMETypeNoParams();
+                               if(mimeType != null && 
ArchiveManager.isUsableArchiveType(mimeType) && metaStrings.size() > 0) {
+                                       // Looks like an implicit archive, 
handle as such
+                                       metadata.setArchiveManifest();
+                                       // Pick up MIME type from inside archive
+                                       clientMetadata.clear();
+                                       continue;
+                               }

+                               if(metaStrings.isEmpty() && isFinal && mimeType 
!= null && ctx.allowedMIMETypes != null && 
+                                               
!ctx.allowedMIMETypes.contains(mimeType)) {
+                                       throw new 
FetchException(FetchException.WRONG_MIME_TYPE, -1, false, 
clientMetadata.getMIMEType());
+                               }
+                               
                                // Simple redirect
                                // Just create a new SingleFileFetcher
                                // Which will then fetch the target URI, and 
call the rcd.success
@@ -358,7 +423,7 @@
                                }

                                // **FIXME** Is key in the call to 
SingleFileFetcher here supposed to be this.key or the same key used in the try 
block above?  MultiLevelMetadataCallback.onSuccess() below uses this.key, thus 
the question
-                               SingleFileFetcher f = new 
SingleFileFetcher(parent, rcb, clientMetadata, key, metaStrings, this.uri, 
addedMetaStrings, ctx, actx, ah, maxRetries, recursionLevel, false, token, 
true, returnBucket, isFinal);
+                               final SingleFileFetcher f = new 
SingleFileFetcher(parent, rcb, clientMetadata, key, metaStrings, this.uri, 
addedMetaStrings, ctx, actx, ah, maxRetries, recursionLevel, false, token, 
true, returnBucket, isFinal);
                                if((key instanceof ClientCHK) && 
!((ClientCHK)key).isMetadata())
                                        rcb.onBlockSetFinished(this);
                                if(metadata.isCompressed()) {
@@ -366,15 +431,32 @@
                                        f.addDecompressor(codec);
                                }
                                parent.onTransition(this, f);
-                               f.schedule();
+                               ctx.executor.execute(new Runnable() {
+                                       public void run() {
+                                               f.schedule();
+                                       }
+                               }, "Schedule");
                                // All done! No longer our problem!
                                return;
                        } else if(metadata.isSplitfile()) {
                                if(logMINOR) Logger.minor(this, "Fetching 
splitfile");
-                               // FIXME implicit archive support

                                
clientMetadata.mergeNoOverwrite(metadata.getClientMetadata()); // even 
splitfiles can have mime types!

+                               String mimeType = 
clientMetadata.getMIMETypeNoParams();
+                               if(mimeType != null && 
ArchiveManager.isUsableArchiveType(mimeType) && metaStrings.size() > 0) {
+                                       // Looks like an implicit archive, 
handle as such
+                                       metadata.setArchiveManifest();
+                                       // Pick up MIME type from inside archive
+                                       clientMetadata.clear();
+                                       continue;
+                               }
+                               
+                               if(metaStrings.isEmpty() && isFinal && mimeType 
!= null && ctx.allowedMIMETypes != null &&
+                                               
!ctx.allowedMIMETypes.contains(mimeType)) {
+                                       throw new 
FetchException(FetchException.WRONG_MIME_TYPE, 
metadata.uncompressedDataLength(), false, clientMetadata.getMIMEType());
+                               }
+                               
                                // Splitfile (possibly compressed)

                                if(metadata.isCompressed()) {
@@ -410,8 +492,7 @@
                                if((len > ctx.maxOutputLength) ||
                                                (len > ctx.maxTempLength)) {

-                                       onFailure(new 
FetchException(FetchException.TOO_BIG, len, isFinal && decompressors.size() <= 
(metadata.isCompressed() ? 1 : 0), clientMetadata.getMIMEType()));
-                                       return;
+                                       throw new 
FetchException(FetchException.TOO_BIG, len, isFinal && decompressors.size() <= 
(metadata.isCompressed() ? 1 : 0), clientMetadata.getMIMEType());
                                }

                                SplitFileFetcher sf = new 
SplitFileFetcher(metadata, rcb, parent, ctx, 
@@ -440,7 +521,7 @@
                decompressors.addLast(codec);
        }

-       private void fetchArchive(boolean forData, Metadata meta) throws 
FetchException, MetadataParseException, ArchiveFailureException, 
ArchiveRestartException {
+       private void fetchArchive(boolean forData, Metadata meta, String 
element, ArchiveExtractCallback callback) throws FetchException, 
MetadataParseException, ArchiveFailureException, ArchiveRestartException {
                if(logMINOR) Logger.minor(this, "fetchArchive()");
                // Fetch the archive
                // How?
@@ -450,42 +531,61 @@
                // reschedules us.
                Metadata newMeta = (Metadata) meta.clone();
                newMeta.setSimpleRedirect();
-               SingleFileFetcher f;
-               f = new SingleFileFetcher(this, newMeta, new 
ArchiveFetcherCallback(forData), new FetchContext(ctx, 
FetchContext.SET_RETURN_ARCHIVES, true));
-               f.handleMetadata();
-               // When it is done (if successful), the ArchiveCallback will 
re-call this function.
-               // Which will then discover that the metadata *is* available.
-               // And will also discover that the data is available, and will 
complete.
+               final SingleFileFetcher f;
+               f = new SingleFileFetcher(this, newMeta, new 
ArchiveFetcherCallback(forData, element, callback), new FetchContext(ctx, 
FetchContext.SET_RETURN_ARCHIVES, true));
+               ctx.ticker.queueTimedJob(new Runnable() {
+                       public void run() {
+                               // Fetch the archive. The archive fetcher 
callback will unpack it, and either call the element 
+                               // callback, or just go back around 
handleMetadata() on this, which will see that the data is now
+                               // available.
+                               f.wrapHandleMetadata(true);
+                       }
+               }, 0);
        }

+       /**
+        * Call handleMetadata(), and deal with any resulting exceptions
+        */
+       private void wrapHandleMetadata(boolean notFinalizedSize) {
+               try {
+                       handleMetadata();
+               } catch (MetadataParseException e) {
+                       onFailure(new FetchException(e));
+               } catch (FetchException e) {
+                       if(notFinalizedSize)
+                               e.setNotFinalizedSize();
+                       onFailure(e);
+               } catch (ArchiveFailureException e) {
+                       onFailure(new FetchException(e));
+               } catch (ArchiveRestartException e) {
+                       onFailure(new FetchException(e));
+               }
+       }
+       
        class ArchiveFetcherCallback implements GetCompletionCallback {

                private final boolean wasFetchingFinalData;
+               private final String element;
+               private final ArchiveExtractCallback callback;

-               ArchiveFetcherCallback(boolean wasFetchingFinalData) {
+               ArchiveFetcherCallback(boolean wasFetchingFinalData, String 
element, ArchiveExtractCallback cb) {
                        this.wasFetchingFinalData = wasFetchingFinalData;
+                       this.element = element;
+                       this.callback = cb;
                }

                public void onSuccess(FetchResult result, ClientGetState state) 
{
                        try {
-                               ah.extractToCache(result.asBucket(), actx);
+                               ah.extractToCache(result.asBucket(), actx, 
element, callback);
                        } catch (ArchiveFailureException e) {
                                SingleFileFetcher.this.onFailure(new 
FetchException(e));
+                               return;
                        } catch (ArchiveRestartException e) {
                                SingleFileFetcher.this.onFailure(new 
FetchException(e));
+                               return;
                        }
-                       try {
-                               handleMetadata();
-                       } catch (MetadataParseException e) {
-                               SingleFileFetcher.this.onFailure(new 
FetchException(e));
-                       } catch (FetchException e) {
-                               e.setNotFinalizedSize();
-                               SingleFileFetcher.this.onFailure(e);
-                       } catch (ArchiveFailureException e) {
-                               SingleFileFetcher.this.onFailure(new 
FetchException(e));
-                       } catch (ArchiveRestartException e) {
-                               SingleFileFetcher.this.onFailure(new 
FetchException(e));
-                       }
+                       if(callback != null) return;
+                       wrapHandleMetadata(true);
                }

                public void onFailure(FetchException e, ClientGetState state) {
@@ -510,22 +610,15 @@
                public void onSuccess(FetchResult result, ClientGetState state) 
{
                        try {
                                metadata = 
Metadata.construct(result.asBucket());
-                               handleMetadata();
                        } catch (MetadataParseException e) {
-                               SingleFileFetcher.this.onFailure(new 
FetchException(e));
+                               SingleFileFetcher.this.onFailure(new 
FetchException(FetchException.INVALID_METADATA, e));
                                return;
                        } catch (IOException e) {
                                // Bucket error?
                                SingleFileFetcher.this.onFailure(new 
FetchException(FetchException.BUCKET_ERROR, e));
                                return;
-                       } catch (FetchException e) {
-                               e.setNotFinalizedSize();
-                               onFailure(e, SingleFileFetcher.this);
-                       } catch (ArchiveFailureException e) {
-                               onFailure(new 
FetchException(FetchException.ARCHIVE_FAILURE), SingleFileFetcher.this);
-                       } catch (ArchiveRestartException e) {
-                               onFailure(new 
FetchException(FetchException.ARCHIVE_RESTART), SingleFileFetcher.this);
                        }
+                       wrapHandleMetadata(true);
                }

                public void onFailure(FetchException e, ClientGetState state) {

Modified: branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -106,10 +106,7 @@
                if(data.size() > COMPRESS_OFF_THREAD_LIMIT) {
                        // Run off thread
                        OffThreadCompressor otc = new OffThreadCompressor();
-                       Thread t = new Thread(otc, "Compressor for "+this);
-                       if(logMINOR) Logger.minor(this, "Compressing 
off-thread: "+t);
-                       t.setDaemon(true);
-                       t.start();
+                       ctx.executor.execute(otc, "Compressor for "+this);
                } else {
                        tryCompress();
                }
@@ -214,7 +211,7 @@
                boolean fitsInOneBlockAsIs = bestCodec == null ? 
compressedDataSize < blockSize : compressedDataSize < oneBlockCompressedSize;
                boolean fitsInOneCHK = bestCodec == null ? compressedDataSize < 
CHKBlock.DATA_LENGTH : compressedDataSize < CHKBlock.MAX_COMPRESSED_DATA_LENGTH;

-               if(block.getData().size() > Integer.MAX_VALUE)
+               if((fitsInOneBlockAsIs || fitsInOneCHK) && 
block.getData().size() > Integer.MAX_VALUE)
                        throw new 
InsertException(InsertException.INTERNAL_ERROR, "2GB+ should not encode to one 
block!", null);

                boolean noMetadata = ((block.clientMetadata == null) || 
block.clientMetadata.isTrivial()) && targetFilename == null;

Modified: branches/freenet-jfk/src/freenet/client/async/SplitFileFetcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SplitFileFetcher.java 
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/SplitFileFetcher.java 
2007-08-21 20:26:59 UTC (rev 14828)
@@ -46,10 +46,6 @@
        final int segmentCount;
        /** The detailed information on each segment */
        final SplitFileFetcherSegment[] segments;
-       /** The splitfile data blocks. */
-       final ClientCHK[] splitfileDataBlocks;
-       /** The splitfile check blocks. */
-       final ClientCHK[] splitfileCheckBlocks;
        /** Maximum temporary length */
        final long maxTempLength;
        /** Have all segments finished? Access synchronized. */
@@ -77,8 +73,8 @@
                        throw new FetchException(FetchException.CANCELLED);
                overrideLength = metadata.dataLength();
                this.splitfileType = metadata.getSplitfileType();
-               splitfileDataBlocks = metadata.getSplitfileDataKeys();
-               splitfileCheckBlocks = metadata.getSplitfileCheckKeys();
+               ClientCHK[] splitfileDataBlocks = 
metadata.getSplitfileDataKeys();
+               ClientCHK[] splitfileCheckBlocks = 
metadata.getSplitfileCheckKeys();
                for(int i=0;i<splitfileDataBlocks.length;i++)
                        if(splitfileDataBlocks[i] == null) throw new 
MetadataParseException("Null: data block "+i+" of "+splitfileDataBlocks.length);
                for(int i=0;i<splitfileCheckBlocks.length;i++)
@@ -283,13 +279,11 @@
        }

        public void scheduleOffThread() {
-               Thread t = new Thread(new Runnable() {
+               fetchContext.executor.execute(new Runnable() {
                        public void run() {
                                schedule();
                        }
                }, "Splitfile scheduler thread for "+this);
-               t.setDaemon(true);
-               t.start();
        }

 }

Modified: 
branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSegment.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSegment.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSegment.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -22,6 +22,7 @@
 import freenet.keys.ClientCHK;
 import freenet.keys.ClientCHKBlock;
 import freenet.keys.ClientKeyBlock;
+import freenet.keys.NodeCHK;
 import freenet.support.Logger;
 import freenet.support.api.Bucket;
 import freenet.support.io.BucketTools;
@@ -190,13 +191,6 @@
                // Now decode
                if(logMINOR) Logger.minor(this, "Decoding 
"+SplitFileFetcherSegment.this);

-               boolean[] dataBlocksSucceeded = new boolean[dataBuckets.length];
-               boolean[] checkBlocksSucceeded = new 
boolean[checkBuckets.length];
-               for(int i=0;i<dataBuckets.length;i++)
-                       dataBlocksSucceeded[i] = dataBuckets[i].data != null;
-               for(int i=0;i<checkBuckets.length;i++)
-                       checkBlocksSucceeded[i] = checkBuckets[i].data != null;
-
                codec = FECCodec.getCodec(splitfileType, dataKeys.length, 
checkKeys.length);

                if(splitfileType != Metadata.SPLITFILE_NONREDUNDANT) {
@@ -210,7 +204,12 @@
                        if(isCollectingBinaryBlob()) {
                                for(int i=0;i<dataBuckets.length;i++) {
                                        Bucket data = dataBuckets[i].getData();
-                                       maybeAddToBinaryBlob(data, i, false);
+                                       try {
+                                               maybeAddToBinaryBlob(data, i, 
false);
+                                       } catch (FetchException e) {
+                                               fail(e);
+                                               return;
+                                       }
                                }
                        }
                        decodedData = fetchContext.bucketFactory.makeBucket(-1);
@@ -268,7 +267,12 @@
                for(int i=0;i<checkBuckets.length;i++) {
                        boolean heal = false;
                        Bucket data = checkBuckets[i].getData();
-                       maybeAddToBinaryBlob(data, i, true);
+                       try {
+                               maybeAddToBinaryBlob(data, i, true);
+                       } catch (FetchException e) {
+                               fail(e);
+                               return;
+                       }
                        if(checkRetries[i] > 0)
                                heal = true;
                        if(heal) {
@@ -291,7 +295,7 @@
                } else return false;
        }

-       private void maybeAddToBinaryBlob(Bucket data, int i, boolean check) {
+       private void maybeAddToBinaryBlob(Bucket data, int i, boolean check) 
throws FetchException {
                if(parentFetcher.parent instanceof ClientGetter) {
                        ClientGetter getter = (ClientGetter) 
(parentFetcher.parent);
                        if(getter.collectingBinaryBlob()) {
@@ -301,11 +305,9 @@
                                        getter.addKeyToBinaryBlob(block);
                                } catch (CHKEncodeException e) {
                                        Logger.error(this, "Failed to encode 
(collecting binary blob) "+(check?"check":"data")+" block "+i+": "+e, e);
-                                       fail(new 
FetchException(FetchException.INTERNAL_ERROR, "Failed to encode for binary 
blob: "+e));
-                                       return;
+                                       throw new 
FetchException(FetchException.INTERNAL_ERROR, "Failed to encode for binary 
blob: "+e);
                                } catch (IOException e) {
-                                       fail(new 
FetchException(FetchException.BUCKET_ERROR, "Failed to encode for binary blob: 
"+e));
-                                       return;
+                                       throw new 
FetchException(FetchException.BUCKET_ERROR, "Failed to encode for binary blob: 
"+e);
                                }
                        }
                }
@@ -321,6 +323,9 @@
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR) Logger.minor(this, "Permanently failed block: 
"+blockNo+" on "+this+" : "+e, e);
                boolean allFailed;
+               // Since we can't keep the key, we need to unregister for it at 
this point to avoid a memory leak
+               NodeCHK key = getBlockNodeKey(blockNo);
+               if(key != null) seg.unregisterKey(key);
                synchronized(this) {
                        if(isFinishing()) return; // this failure is now 
irrelevant, and cleanup will occur on the decoder thread
                        if(blockNo < dataKeys.length) {
@@ -345,11 +350,13 @@
                                failedBlocks++;
                                parentFetcher.parent.failedBlock();
                        }
-                       allFailed = failedBlocks + fatallyFailedBlocks <= 
(dataKeys.length + checkKeys.length - minFetched);
+                       // Once it is no longer possible to have a successful 
fetch, fail...
+                       allFailed = failedBlocks + fatallyFailedBlocks > 
(dataKeys.length + checkKeys.length - minFetched);
                }
                if(allFailed)
                        fail(new FetchException(FetchException.SPLITFILE_ERROR, 
errors));
-               seg.possiblyRemoveFromParent();
+               else
+                       seg.possiblyRemoveFromParent();
        }

        /** A request has failed non-fatally, so the block may be retried */
@@ -418,6 +425,7 @@
                                checkBuckets[i] = null;
                        }
                }
+               removeSubSegments();
                parentFetcher.segmentFinished(this);
        }

@@ -449,11 +457,19 @@
        }

        public ClientCHK getBlockKey(int blockNum) {
-               if(blockNum < dataKeys.length)
+               if(blockNum < 0) return null;
+               else if(blockNum < dataKeys.length)
                        return dataKeys[blockNum];
-               else
+               else if(blockNum < dataKeys.length + checkKeys.length)
                        return checkKeys[blockNum - dataKeys.length];
+               else return null;
        }
+       
+       public NodeCHK getBlockNodeKey(int blockNum) {
+               ClientCHK key = getBlockKey(blockNum);
+               if(key != null) return key.getNodeCHK();
+               else return null;
+       }

        public synchronized void removeSeg(SplitFileFetcherSubSegment segment) {
                for(int i=0;i<subSegments.size();i++) {
@@ -464,4 +480,11 @@
                }
        }

+       private void removeSubSegments() {
+               for(int i=0;i<subSegments.size();i++) {
+                       SplitFileFetcherSubSegment seg = 
(SplitFileFetcherSubSegment) subSegments.get(i);
+                       seg.kill();
+               }
+       }
+
 }

Modified: 
branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSubSegment.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSubSegment.java   
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/client/async/SplitFileFetcherSubSegment.java   
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -7,7 +7,10 @@
 import freenet.client.FetchException;
 import freenet.keys.ClientKey;
 import freenet.keys.ClientKeyBlock;
+import freenet.keys.Key;
+import freenet.keys.KeyBlock;
 import freenet.keys.KeyDecodeException;
+import freenet.keys.KeyVerifyException;
 import freenet.keys.TooBigException;
 import freenet.node.LowLevelGetException;
 import freenet.node.SendableGet;
@@ -96,6 +99,9 @@
                case LowLevelGetException.DATA_NOT_FOUND_IN_STORE:
                        onFailure(new 
FetchException(FetchException.DATA_NOT_FOUND), token);
                        return;
+               case LowLevelGetException.RECENTLY_FAILED:
+                       onFailure(new 
FetchException(FetchException.RECENTLY_FAILED), token);
+                       return;
                case LowLevelGetException.DECODE_FAILED:
                        onFailure(new 
FetchException(FetchException.BLOCK_DECODE_ERROR), token);
                        return;
@@ -253,9 +259,46 @@
                return 
super.toString()+":"+retryCount+"/"+segment+'('+blockNums.size()+')';
        }

-       public synchronized void possiblyRemoveFromParent() {
-               if(blockNums.isEmpty())
-                       segment.removeSeg(this);
+       public void possiblyRemoveFromParent() {
+               synchronized(this) {
+                       if(!blockNums.isEmpty()) return;
+               }
+               segment.removeSeg(this);
+               unregister();
        }
+
+       public void onGotKey(Key key, KeyBlock block) {
+               int blockNum = -1;
+               ClientKey ckey = null;
+               synchronized(this) {
+                       for(int i=0;i<blockNums.size();i++) {
+                               int num = 
((Integer)blockNums.get(i)).intValue();
+                               ckey = segment.getBlockKey(num);
+                               if(ckey == null) return; // Already got this key
+                               Key k = ckey.getNodeKey();
+                               if(k.equals(key)) {
+                                       blockNum = num;
+                                       blockNums.remove(i);
+                                       break;
+                               }
+                       }
+               }
+               if(blockNum == -1) return;
+               try {
+                       onSuccess(Key.createKeyBlock(ckey, block), false, 
blockNum);
+               } catch (KeyVerifyException e) {
+                       // FIXME if we ever abolish the direct route, this must 
be turned into an onFailure().
+                       Logger.error(this, "Failed to parse in 
onGotKey("+key+","+block+") - believed to be "+ckey+" (block #"+blockNum+")");
+               }
+       }

+       public void kill() {
+               // Do unregister() first so can get and unregister each key and 
avoid a memory leak
+               unregister();
+               synchronized(this) {
+                       blockNums.clear();
+               }
+               segment.removeSeg(this);
+       }
+
 }

Modified: branches/freenet-jfk/src/freenet/client/async/USKChecker.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/USKChecker.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/USKChecker.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -26,6 +26,7 @@
        }

        public void onSuccess(ClientKeyBlock block, boolean fromStore, int 
token) {
+               unregister();
                cb.onSuccess((ClientSSKBlock)block);
        }

@@ -42,6 +43,7 @@
                        break;
                case LowLevelGetException.DATA_NOT_FOUND:
                case LowLevelGetException.DATA_NOT_FOUND_IN_STORE:
+               case LowLevelGetException.RECENTLY_FAILED:
                        dnfs++;
                        canRetry = true;
                        break;
@@ -61,7 +63,7 @@
                if(canRetry && retry()) return;

                // Ran out of retries.
-               
+               unregister();
                if(e.code == LowLevelGetException.CANCELLED){
                        cb.onCancelled();
                        return;

Modified: branches/freenet-jfk/src/freenet/client/async/USKFetcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/USKFetcher.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/USKFetcher.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -150,9 +150,10 @@
                }

                public void schedule() {
-                       if(checker == null)
-                               Logger.error(this, "Checker == null in 
schedule()", new Exception("error"));
-                       else
+                       if(checker == null) {
+                               if(logMINOR)
+                                       Logger.minor(this, "Checker == null in 
schedule() for "+this, new Exception("debug"));
+                       } else
                                checker.schedule();
                }


Modified: branches/freenet-jfk/src/freenet/client/async/USKInserter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/USKInserter.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/USKInserter.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -69,11 +69,13 @@
         * The Fetcher must be insert-mode, in other words, it must know that 
we want the latest edition,
         * including author errors and so on.
         */
-       private synchronized void scheduleFetcher() {
-               if(Logger.shouldLog(Logger.MINOR, this))
-                       Logger.minor(this, "scheduling fetcher for 
"+pubUSK.getURI());
-               if(finished) return;
-               fetcher = 
ctx.uskManager.getFetcherForInsertDontSchedule(pubUSK, parent.priorityClass, 
this, parent.getClient());
+       private void scheduleFetcher() {
+               synchronized(this) {
+                       if(Logger.shouldLog(Logger.MINOR, this))
+                               Logger.minor(this, "scheduling fetcher for 
"+pubUSK.getURI());
+                       if(finished) return;
+                       fetcher = 
ctx.uskManager.getFetcherForInsertDontSchedule(pubUSK, parent.priorityClass, 
this, parent.getClient());
+               }
                fetcher.schedule();
        }


Modified: branches/freenet-jfk/src/freenet/client/async/USKManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/USKManager.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/client/async/USKManager.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -10,6 +10,7 @@
 import freenet.keys.USK;
 import freenet.node.NodeClientCore;
 import freenet.node.RequestStarter;
+import freenet.node.Ticker;
 import freenet.support.LRUQueue;
 import freenet.support.Logger;

@@ -40,6 +41,8 @@
        final FetchContext backgroundFetchContext;
        final ClientRequestScheduler chkRequestScheduler;
        final ClientRequestScheduler sskRequestScheduler;
+       
+       final Ticker ticker;


        public USKManager(NodeClientCore core) {
@@ -54,6 +57,7 @@
                checkersByUSK = new HashMap();
                backgroundFetchersByClearUSK = new HashMap();
                temporaryBackgroundFetchersLRU = new LRUQueue();
+               ticker = core.getTicker();
        }

        /**
@@ -125,11 +129,11 @@
                if(sched != null) sched.schedule();
        }

-       void update(USK origUSK, long number) {
+       void update(final USK origUSK, final long number) {
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR) Logger.minor(this, "Updating "+origUSK.getURI()+" 
: "+number);
                USK clear = origUSK.clearCopy();
-               USKCallback[] callbacks;
+               final USKCallback[] callbacks;
                synchronized(this) {
                        Long l = (Long) latestVersionByClearUSK.get(clear);
                        if(logMINOR) Logger.minor(this, "Old value: "+l);
@@ -141,9 +145,14 @@
                        callbacks = (USKCallback[]) 
subscribersByClearUSK.get(clear);
                }
                if(callbacks != null) {
-                       USK usk = origUSK.copy(number);
-                       for(int i=0;i<callbacks.length;i++)
-                               callbacks[i].onFoundEdition(number, usk);
+                       // Run off-thread, because of locking, and because 
client callbacks may take some time
+                       ticker.queueTimedJob(new Runnable() {
+                               public void run() {
+                                       USK usk = origUSK.copy(number);
+                                       for(int i=0;i<callbacks.length;i++)
+                                               
callbacks[i].onFoundEdition(number, usk);
+                               }
+                       }, 0);
                }
        }

@@ -163,6 +172,8 @@
                        if(callbacks == null)
                                callbacks = new USKCallback[1];
                        else {
+                               for(int i=0;i<callbacks.length;i++)
+                                       if(callbacks[i] == cb) return;
                                USKCallback[] newCallbacks = new 
USKCallback[callbacks.length+1];
                                System.arraycopy(callbacks, 0, newCallbacks, 0, 
callbacks.length);
                                callbacks = newCallbacks;
@@ -181,8 +192,14 @@
                }
                if(curEd > ed)
                        cb.onFoundEdition(curEd, origUSK.copy(curEd));
-               if(sched != null)
-                       sched.schedule();
+               final USKFetcher fetcher = sched;
+               if(fetcher != null) {
+                       ticker.queueTimedJob(new Runnable() {
+                               public void run() {
+                                       fetcher.schedule();
+                               }
+                       }, 0);
+               }
        }

        public void unsubscribe(USK origUSK, USKCallback cb, boolean 
runBackgroundFetch) {

Modified: 
branches/freenet-jfk/src/freenet/client/async/USKProxyCompletionCallback.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/client/async/USKProxyCompletionCallback.java   
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/client/async/USKProxyCompletionCallback.java   
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -5,6 +5,7 @@

 import freenet.client.FetchException;
 import freenet.client.FetchResult;
+import freenet.keys.FreenetURI;
 import freenet.keys.USK;

 public class USKProxyCompletionCallback implements GetCompletionCallback {
@@ -25,6 +26,11 @@
        }

        public void onFailure(FetchException e, ClientGetState state) {
+               FreenetURI uri = e.newURI;
+               if(uri != null) {
+                       uri = usk.turnMySSKIntoUSK(uri);
+                       e = new FetchException(e, uri);
+               }
                cb.onFailure(e, state);
        }


Modified: 
branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -16,6 +16,9 @@
 import freenet.node.NodeClientCore;
 import freenet.client.HighLevelSimpleClient;
 import freenet.support.HTMLNode;
+import freenet.support.URLDecoder;
+import freenet.support.URLEncodedFormatException;
+import freenet.support.URLEncoder;
 import freenet.support.api.HTTPRequest;

 public class BookmarkEditorToadlet extends Toadlet {
@@ -29,7 +32,7 @@
        private final BookmarkManager bookmarkManager;
        private String cutedPath;

-       
+
        BookmarkEditorToadlet(HighLevelSimpleClient client, NodeClientCore core)
        {
                super(client);
@@ -37,39 +40,39 @@
                this.bookmarkManager = core.bookmarkManager;
                this.cutedPath = null;
        }
-       
+
        private void addCategoryToList(BookmarkCategory cat, String path, 
HTMLNode list)
        {
                BookmarkItems items = cat.getItems();
-               
-               String edit = L10n.getString("BookmarkEditorToadlet.edit");
-               String delete = L10n.getString("BookmarkEditorToadlet.delete");
-               String cut = L10n.getString("BookmarkEditorToadlet.cut");
-               String moveUp = L10n.getString("BookmarkEditorToadlet.moveUp");
-               String moveDown = 
L10n.getString("BookmarkEditorToadlet.moveDown");
-               String paste = L10n.getString("BookmarkEditorToadlet.paste");
-               String addBookmark = 
L10n.getString("BookmarkEditorToadlet.addBookmark");
-               String addCategory = 
L10n.getString("BookmarkEditorToadlet.addCategory");
-               
+
+               final String edit = 
L10n.getString("BookmarkEditorToadlet.edit");
+               final String delete = 
L10n.getString("BookmarkEditorToadlet.delete");
+               final String cut = L10n.getString("BookmarkEditorToadlet.cut");
+               final String moveUp = 
L10n.getString("BookmarkEditorToadlet.moveUp");
+               final String moveDown = 
L10n.getString("BookmarkEditorToadlet.moveDown");
+               final String paste = 
L10n.getString("BookmarkEditorToadlet.paste");
+               final String addBookmark = 
L10n.getString("BookmarkEditorToadlet.addBookmark");
+               final String addCategory = 
L10n.getString("BookmarkEditorToadlet.addCategory");
+
                for(int i = 0; i < items.size(); i++) {

-                       String itemPath = path + items.get(i).getName();
-                       HTMLNode li = new HTMLNode("li", "class","item" , 
items.get(i).getName());
+                       String itemPath = URLEncoder.encode(path + 
items.get(i).getName());
+                       HTMLNode li = new HTMLNode("li", "class", "item" , 
items.get(i).getName());

                        HTMLNode actions = new HTMLNode("span", "class", 
"actions");
                        actions.addChild("a", "href", "?action=edit&bookmark=" 
+ itemPath).addChild("img", new String[] {"src", "alt", "title"}, new String[] 
{"/static/icon/edit.png", edit, edit});
-                       
+
                        actions.addChild("a", "href", "?action=del&bookmark=" + 
itemPath).addChild("img", new String[] {"src", "alt", "title"}, new String[] 
{"/static/icon/delete.png", delete, delete});
-                       
+
                        if(cutedPath == null)
                                actions.addChild("a", "href", 
"?action=cut&bookmark=" + itemPath).addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/cut.png", cut, cut});
-                       
+
                        if(i != 0)
                                actions.addChild("a", "href", 
"?action=up&bookmark=" + itemPath).addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/go-up.png", moveUp, moveUp});
-                       
+
                        if(i != items.size()-1)
                                actions.addChild("a", "href", 
"?action=down&bookmark=" + itemPath).addChild("img", new String[] {"src", 
"alt", "title"}, new String[] {"/static/icon/go-down.png", moveDown, moveDown});
-                       
+
                        li.addChild(actions);
                        list.addChild(li);
                }
@@ -77,32 +80,32 @@
                BookmarkCategories cats = cat.getSubCategories();
                for(int i = 0; i < cats.size(); i++) {

-                       String catPath = path + cats.get(i).getName() + "/";
-                       
+                       String catPath = URLEncoder.encode(path + 
cats.get(i).getName() + "/");
+
                        HTMLNode subCat = list.addChild("li", "class", "cat", 
cats.get(i).getName());

                        HTMLNode actions = new HTMLNode("span", "class", 
"actions");
-                       
+
                        actions.addChild("a", "href", "?action=edit&bookmark=" 
+ catPath).addChild("img", new String[] {"src", "alt", "title"}, new String[] 
{"/static/icon/edit.png", edit, edit});
-                       
+
                        actions.addChild("a", "href", "?action=del&bookmark=" + 
catPath).addChild("img", new String[] {"src", "alt", "title"}, new String[] 
{"/static/icon/delete.png", delete, delete});
-                       
+
                        actions.addChild("a", "href", 
"?action=addItem&bookmark=" + catPath).addChild("img", new String[] {"src", 
"alt", "title"}, new String[] {"/static/icon/bookmark-new.png", addBookmark, 
addBookmark});
-                       
+
                        actions.addChild("a", "href", 
"?action=addCat&bookmark=" + catPath).addChild("img", new String[] {"src", 
"alt", "title"}, new String[] {"/static/icon/folder-new.png", addCategory, 
addCategory});
-                       
+
                        if(cutedPath == null)
                                actions.addChild("a", "href", 
"?action=cut&bookmark=" + catPath).addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/cut.png", cut, cut});
-                       
+
                        if(i != 0)
                                actions.addChild("a", "href", 
"?action=up&bookmark=" + catPath).addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/go-up.png", moveUp, moveUp});
-                       
+
                        if(i != cats.size() -1)
                                actions.addChild("a", "href", 
"?action=down&bookmark=" + catPath).addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/go-down.png", moveDown, moveDown});

                        if(cutedPath != null && ! catPath.startsWith(cutedPath) 
&& ! catPath.equals(bookmarkManager.parentPath(cutedPath)))
                                actions.addChild("a", "href", 
"?action=paste&bookmark=" + catPath).addChild("img", new String[] {"src", 
"alt", "title"}, new String[] {"/static/icon/paste.png", paste, paste});
-                       
+
                        subCat.addChild(actions);
                        if(cats.get(i).size() != 0)
                                addCategoryToList(cats.get(i), catPath, 
list.addChild("li").addChild("ul"));
@@ -112,111 +115,120 @@
        public HTMLNode getBookmarksList()
        {
                HTMLNode bookmarks = new HTMLNode("ul", "id", "bookmarks");
-               
-               HTMLNode root = bookmarks.addChild("li", "class", "cat,root", 
"/");
+
+               HTMLNode root = bookmarks.addChild("li", "class", "cat root", 
"/");
                HTMLNode actions = new HTMLNode("span", "class", "actions");
                String addBookmark = 
L10n.getString("BookmarkEditorToadlet.addBookmark");
                String addCategory = 
L10n.getString("BookmarkEditorToadlet.addCategory");
                String paste = L10n.getString("BookmarkEditorToadlet.paste");
                actions.addChild("a", "href", 
"?action=addItem&bookmark=/").addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/bookmark-new.png", addBookmark, 
addBookmark});
                actions.addChild("a", "href", 
"?action=addCat&bookmark=/").addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/folder-new.png", addCategory, 
addCategory});
-               
+
                if(cutedPath != null && ! 
"/".equals(bookmarkManager.parentPath(cutedPath)))
                        actions.addChild("a", "href", 
"?action=paste&bookmark=/").addChild("img", new String[] {"src", "alt", 
"title"}, new String[] {"/static/icon/paste.png", paste, paste});
-               
+
                root.addChild(actions);
-               addCategoryToList(bookmarkManager.getMainCategory(), "/", 
root.addChild("li").addChild("ul"));
-               
+               addCategoryToList(bookmarkManager.getMainCategory(), "/", 
root.addChild("ul"));
+
                return bookmarks;
        }
-       
+
        public void handleGet(URI uri, HTTPRequest req, ToadletContext ctx) 
-                       throws ToadletContextClosedException, IOException 
+       throws ToadletContextClosedException, IOException 
        {
-               
+
                String editorTitle = 
L10n.getString("BookmarkEditorToadlet.title");
                String error = L10n.getString("BookmarkEditorToadlet.error");
                HTMLNode pageNode = ctx.getPageMaker().getPageNode(editorTitle, 
ctx);
                HTMLNode content = ctx.getPageMaker().getContentNode(pageNode);
-               
+
                if (req.getParam("action").length() > 0 && 
req.getParam("bookmark").length() > 0) {
                        String action = req.getParam("action");
-                       String bookmarkPath = req.getParam("bookmark");
+                       String bookmarkPath;
+                       try {
+                               bookmarkPath = 
URLDecoder.decode(req.getParam("bookmark"), false);
+                       } catch (URLEncodedFormatException e) {
+                               HTMLNode errorBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-error", error));
+                               errorBox.addChild("#", 
L10n.getString("BookmarkEditorToadlet.urlDecodeError"));
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
+                               return;
+                       }
                        Bookmark bookmark;
-                       
+
                        if (bookmarkPath.endsWith("/"))
                                bookmark = 
bookmarkManager.getCategoryByPath(bookmarkPath);
                        else
                                bookmark = 
bookmarkManager.getItemByPath(bookmarkPath);
-                               
+
                        if(bookmark == null) {
                                HTMLNode errorBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-error", error));
                                errorBox.addChild("#", 
L10n.getString("BookmarkEditorToadlet.bookmarkDoesNotExist", new String[] { 
"bookmark" }, new String[] { bookmarkPath }));
+                               this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
+                               return;
                        } else {
-                       
-                       if(action.equals("del")){
-                               
-                               String[] bm = new String[] { "bookmark" };
-                               String[] path = new String[] { bookmarkPath };
-                               String queryTitle = 
L10n.getString("BookmarkEditorToadlet." + ((bookmark instanceof BookmarkItem) ? 
"deleteBookmark" : "deleteCategory"));
-                               HTMLNode infoBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-query", queryTitle));

-                               String query = 
L10n.getString("BookmarkEditorToadlet." + ((bookmark instanceof BookmarkItem) ? 
"deleteBookmarkConfirm" : "deleteCategoryConfirm"), bm, path);
-                               infoBox.addChild("p").addChild("#", query);
-                               
-                               HTMLNode confirmForm = 
ctx.addFormChild(infoBox.addChild("p"), "", "confirmDeleteForm");
-                               confirmForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "hidden", "bookmark", bookmarkPath});
-                               confirmForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
-                               confirmForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "confirmdelete", 
L10n.getString("BookmarkEditorToadlet.confirmDelete") });
-                       
-                       } else if (action.equals("cut")) {
+                               if("del".equals(action)){

-                               cutedPath = bookmarkPath;
+                                       String[] bm = new String[] { "bookmark" 
};
+                                       String[] path = new String[] { 
bookmarkPath };
+                                       String queryTitle = 
L10n.getString("BookmarkEditorToadlet." + ((bookmark instanceof BookmarkItem) ? 
"deleteBookmark" : "deleteCategory"));
+                                       HTMLNode infoBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-query", queryTitle));

-                       } else if ("paste".equals(action) && cutedPath != null) 
{
-                               
-                               bookmarkManager.moveBookmark(cutedPath, 
bookmarkPath, true);
-                               cutedPath = null;
-                               
-                       } else if (action.equals("edit") || 
action.equals("addItem") || action.equals("addCat")) {
-                               
-                               String header;
-                               if(action.equals("edit")) {
-                                       header = 
L10n.getString("BookmarkEditorToadlet.edit" + ((bookmark instanceof 
BookmarkItem) ? "Bookmark" : "Category") + "Title");
-                               } else if(action.equals("addItem")) {
-                                       header = 
L10n.getString("BookmarkEditorToadlet.addNewBookmark");
-                               } else {
-                                       header = 
L10n.getString("BookmarkEditorToadlet.addNewCategory");
+                                       String query = 
L10n.getString("BookmarkEditorToadlet." + ((bookmark instanceof BookmarkItem) ? 
"deleteBookmarkConfirm" : "deleteCategoryConfirm"), bm, path);
+                                       infoBox.addChild("p").addChild("#", 
query);
+
+                                       HTMLNode confirmForm = 
ctx.addFormChild(infoBox.addChild("p"), "", "confirmDeleteForm");
+                                       confirmForm.addChild("input", new 
String[] { "type", "name", "value" }, new String[] { "hidden", "bookmark", 
bookmarkPath});
+                                       confirmForm.addChild("input", new 
String[] { "type", "name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
+                                       confirmForm.addChild("input", new 
String[] { "type", "name", "value" }, new String[] { "submit", "confirmdelete", 
L10n.getString("BookmarkEditorToadlet.confirmDelete") });
+
+                               } else if ("cut".equals(action)) {
+
+                                       cutedPath = bookmarkPath;
+
+                               } else if ("paste".equals(action) && cutedPath 
!= null) {
+
+                                       bookmarkManager.moveBookmark(cutedPath, 
bookmarkPath, true);
+                                       cutedPath = null;
+
+                               } else if ("edit".equals(action) || 
"addItem".equals(action) || "addCat".equals(action)) {
+
+                                       String header;
+                                       if("edit".equals(action)) {
+                                               header = 
L10n.getString("BookmarkEditorToadlet.edit" + ((bookmark instanceof 
BookmarkItem) ? "Bookmark" : "Category") + "Title");
+                                       } else if("addItem".equals(action)) {
+                                               header = 
L10n.getString("BookmarkEditorToadlet.addNewBookmark");
+                                       } else {
+                                               header = 
L10n.getString("BookmarkEditorToadlet.addNewCategory");
+                                       }
+
+                                       HTMLNode actionBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-query", header));
+
+                                       HTMLNode form = 
ctx.addFormChild(actionBox, "", "editBookmarkForm");
+
+                                       form.addChild("label", "for", "name", 
(L10n.getString("BookmarkEditorToadlet.nameLabel") + ' '));
+                                       form.addChild("input", new 
String[]{"type", "id", "name", "size", "value"}, new String []{"text", "name", 
"name", "20", "edit".equals(action)?bookmark.getName():""});
+
+                                       form.addChild("br");
+                                       if (("edit".equals(action) && bookmark 
instanceof BookmarkItem) || "addItem".equals(action)) {
+                                               String key = 
(action.equals("edit") ? ((BookmarkItem) bookmark).getKey() : "");
+                                               form.addChild("label", "for", 
"key", (L10n.getString("BookmarkEditorToadlet.keyLabel") + ' '));
+                                               form.addChild("input", new 
String[]{"type", "id", "name", "size", "value"}, new String []{"text", "key", 
"key", "50", key});
+                                       }
+
+                                       form.addChild("input", new String[] 
{"type", "name", "value"}, new String[] {"hidden", "bookmark",bookmarkPath});
+
+                                       form.addChild("input", new String[] 
{"type", "name", "value"}, new String[] {"hidden", 
"action",req.getParam("action")});
+
+                                       form.addChild("br");
+                                       form.addChild("input", new 
String[]{"type", "value"}, new String[]{"submit", 
L10n.getString("BookmarkEditorToadlet.save")});
+                               } else if ("up".equals(action)) {
+                                               
bookmarkManager.moveBookmarkUp(bookmarkPath, true);
+                               } else if("down".equals(action)) {
+                                               
bookmarkManager.moveBookmarkDown(bookmarkPath, true);
                                }
-                               
-                               HTMLNode actionBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-query", header));
-                               
-                               HTMLNode form = ctx.addFormChild(actionBox, "", 
"editBookmarkForm");
-                               
-                               form.addChild("label", "for", "name", 
(L10n.getString("BookmarkEditorToadlet.nameLabel") + ' '));
-                               form.addChild("input", new String[]{"type", 
"id", "name", "size", "value"}, new String []{"text", "name", "name", "20", 
action.equals("edit")?bookmark.getName():""});
-                               
-                               form.addChild("br");
-                               if ((action.equals("edit") && bookmark 
instanceof BookmarkItem) || action.equals("addItem")) {
-                                       String key = (action.equals("edit") ? 
((BookmarkItem) bookmark).getKey() : "");
-                                       form.addChild("label", "for", "key", 
(L10n.getString("BookmarkEditorToadlet.keyLabel") + ' '));
-                                       form.addChild("input", new 
String[]{"type", "id", "name", "size", "value"}, new String []{"text", "key", 
"key", "50", key});
-                               }
-                               
-                               form.addChild("input", new String[] {"type", 
"name", "value"}, new String[] {"hidden", "bookmark",bookmarkPath});
-                               
-                               form.addChild("input", new String[] {"type", 
"name", "value"}, new String[] {"hidden", "action",req.getParam("action")});
-                               
-                               form.addChild("br");
-                               form.addChild("input", new String[]{"type", 
"value"}, new String[]{"submit", L10n.getString("BookmarkEditorToadlet.save")});
-                       } else if (action.equals("up") || 
action.equals("down")) {
-                               if(action.equals("up"))
-                                       
bookmarkManager.moveBookmarkUp(bookmarkPath, true);
-                               else
-                                       
bookmarkManager.moveBookmarkDown(bookmarkPath, true);
                        }
-                       }
-                       
+
                }

                if(cutedPath != null) {
@@ -225,90 +237,90 @@
                        HTMLNode cancelForm = 
ctx.addFormChild(infoBox.addChild("p"), "", "cancelCutForm");
                        cancelForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancelCut", 
L10n.getString("BookmarkEditorToadlet.cancelCut") });
                }
-               
+
                HTMLNode bookmarksBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-normal", 
L10n.getString("BookmarkEditorToadlet.myBookmarksTitle")));
                bookmarksBox.addChild(getBookmarksList());

-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

-       
+
        public void handlePost(URI uri, HTTPRequest req, ToadletContext ctx) 
-               throws ToadletContextClosedException, IOException 
+       throws ToadletContextClosedException, IOException 
        {
                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(L10n.getString("BookmarkEditorToadlet.title"), 
ctx);
                HTMLNode content = ctx.getPageMaker().getContentNode(pageNode);
-               
+
                String passwd = req.getPartAsString("formPassword", 32);
                boolean noPassword = (passwd == null) || 
!passwd.equals(core.formPassword);
                if(noPassword) 
                        return;
-               
-               
+
+
                String bookmarkPath = req.getPartAsString("bookmark", 
MAX_BOOKMARK_PATH_LENGTH);
                try {
-                       
+
                        Bookmark bookmark;
                        if(bookmarkPath.endsWith("/"))
                                bookmark = 
bookmarkManager.getCategoryByPath(bookmarkPath);
                        else
                                bookmark = 
bookmarkManager.getItemByPath(bookmarkPath);
-                       
+                       if(bookmark == null) {
+                               HTMLNode errorBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-error", 
L10n.getString("BookmarkEditorToadlet.error")));
+                               errorBox.addChild("#", 
L10n.getString("BookmarkEditorToadlet.bookmarkDoesNotExist", new String[] { 
"bookmark" } , new String[] { bookmarkPath }));
+                               return;
+                       }
+
+
                        String action = req.getPartAsString("action", 
MAX_ACTION_LENGTH);
-                       
+
                        if (req.isPartSet("confirmdelete")) {
                                bookmarkManager.removeBookmark(bookmarkPath, 
true);
                                HTMLNode successBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-success", 
L10n.getString("BookmarkEditorToadlet.deleteSucceededTitle")));
                                successBox.addChild("p", 
L10n.getString("BookmarkEditorToadlet.deleteSucceeded"));
-                               
+
                        } else if (req.isPartSet("cancelCut")) {
                                cutedPath = null;
-                       
-                       } else if (action.equals("edit") || 
action.equals("addItem") || action.equals("addCat")) {
-                               
+
+                       } else if ("edit".equals(action) || 
"addItem".equals(action) || "addCat".equals(action)) {
+
                                String name = "unnamed";
                                if (req.getPartAsString("name", 
MAX_NAME_LENGTH).length() > 0)
                                        name = req.getPartAsString("name", 
MAX_NAME_LENGTH);
-                               
-                               if(action.equals("edit")) {
+
+                               if("edit".equals(action)) {
                                        
bookmarkManager.renameBookmark(bookmarkPath, name);
                                        if(bookmark instanceof BookmarkItem)
                                                ((BookmarkItem) 
bookmark).setKey(new FreenetURI(req.getPartAsString("key", MAX_KEY_LENGTH)));
-                                       
+
                                        HTMLNode successBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-success", 
L10n.getString("BookmarkEditorToadlet.changesSavedTitle")));
                                        successBox.addChild("p", 
L10n.getString("BookmarkEditorToadlet.changesSaved"));
-                                               
-                               } else if (action.equals("addItem") || 
action.equals("addCat")) {
-                                       
+
+                               } else if ("addItem".equals(action) || 
"addCat".equals(action)) {
+
                                        Bookmark newBookmark;
-                                       if(action.equals("addItem")) {
+                                       if("addItem".equals(action)) {
                                                FreenetURI key = new 
FreenetURI(req.getPartAsString("key", MAX_KEY_LENGTH));
                                                newBookmark = new 
BookmarkItem(key, name, core.alerts);
                                        } else
                                                newBookmark = new 
BookmarkCategory(name);
-                                       
+
                                        
bookmarkManager.addBookmark(bookmarkPath, newBookmark, true);
-                                       
+
                                        HTMLNode successBox =  
content.addChild(ctx.getPageMaker().getInfobox("infobox-success", 
L10n.getString("BookmarkEditorToadlet.addedNewBookmarkTitle")));
                                        successBox.addChild("p", 
L10n.getString("BookmarkEditorToadlet.addedNewBookmark"));
-                                       
                                }
-                               
                        }
-
-               } catch (NullPointerException npo) {
-                       HTMLNode errorBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-error", 
L10n.getString("BookmarkEditorToadlet.error")));
-                       errorBox.addChild("#", 
L10n.getString("BookmarkEditorToadlet.bookmarkDoesNotExist", new String[] { 
"bookmark" } , new String[] { bookmarkPath }));
                } catch (MalformedURLException mue) {
                        HTMLNode errorBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-error", 
L10n.getString("BookmarkEditorToadlet.invalidKeyTitle")));
                        errorBox.addChild("#", 
L10n.getString("BookmarkEditorToadlet.invalidKey"));
                }
                HTMLNode bookmarksBox = 
content.addChild(ctx.getPageMaker().getInfobox("infobox-normal", 
L10n.getString("BookmarkEditorToadlet.myBookmarksTitle")));
                bookmarksBox.addChild(getBookmarksList());
-               
-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }
-       
+
        public String supportedMethods()
        {
                return "GET, POST";

Modified: branches/freenet-jfk/src/freenet/clients/http/BrowserTestToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/BrowserTestToadlet.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/BrowserTestToadlet.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -178,7 +178,7 @@
                // Yes, we need that in order to test the browser (number of 
connections per server)
                if (request.isParameterSet("wontload")) return;
                else if (request.isParameterSet("mimeTest")){
-                       this.writeReply(ctx, 200, "text/html", "OK", 
imgWarningMime);
+                       this.writeHTMLReply(ctx, 200, "OK", imgWarningMime);
                        return;         
                }

@@ -210,7 +210,7 @@
                jsTest.addChild("img", new String[]{"id", "src", "alt"}, new 
String[]{"JSTEST", "/static/themes/clean/success.gif", "fail!"});
                jsTest.addChild("script", "type", 
"text/javascript").addChild("%", "document.getElementById('JSTEST').src = 
'/static/themes/clean/warning.gif';");

-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

        public String supportedMethods() {

Modified: branches/freenet-jfk/src/freenet/clients/http/ConfigToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/ConfigToadlet.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/ConfigToadlet.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -11,6 +11,7 @@
 import freenet.config.BooleanOption;
 import freenet.config.Config;
 import freenet.config.EnumerableOptionCallback;
+import freenet.config.InvalidConfigValueException;
 import freenet.config.Option;
 import freenet.config.SubConfig;
 import freenet.l10n.L10n;
@@ -73,7 +74,9 @@
                                                if(logMINOR) Logger.minor(this, 
"Setting "+prefix+ '.' +configName+" to "+value);
                                                try{
                                                        o[j].setValue(value);
-                                               }catch(Exception e){
+                                               } catch 
(InvalidConfigValueException e) {
+                                                       
errbuf.append(o[j].getName()).append(' ').append(e.getMessage()).append('\n');
+                                               } catch (Exception e){
                             errbuf.append(o[j].getName()).append(' 
').append(e).append('\n');
                                                        Logger.error(this, 
"Caught "+e, e);
                                                }
@@ -103,7 +106,7 @@
                content.addChild("br");
                addHomepageLink(content);

-               writeReply(ctx, 200, "text/html", "OK", pageNode.generate());
+               writeHTMLReply(ctx, 200, "OK", pageNode.generate());

        }

@@ -130,6 +133,7 @@
                if(advancedModeEnabled){
                        HTMLNode navigationBar = 
ctx.getPageMaker().getInfobox("navbar", l10n("configNavTitle"));
                        HTMLNode navigationContent = 
ctx.getPageMaker().getContentNode(navigationBar).addChild("ul");
+                       navigationContent.addChild("a", "href", 
TranslationToadlet.TOADLET_URL, l10n("contributeTranslation"));
                        HTMLNode navigationTable = 
navigationContent.addChild("table", "class", "config_navigation");
                        HTMLNode navigationTableRow = 
navigationTable.addChild("tr");
                        HTMLNode nextTableCell = navigationTableRow;
@@ -158,7 +162,7 @@

                                        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() }), 
+                                                       new String[]{ 
"configshortdesc", L10n.getString("ConfigToadlet.defaultIs", new String[] { 
"default" }, new String[] { o[j].getDefault() }) + (advancedModeEnabled ? " 
["+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){
@@ -179,7 +183,7 @@

                        if(displayedConfigElements>0) {
                                formNode.addChild("div", "class", 
"configprefix", sc[i].getPrefix());
-                               formNode.addChild("a", "name", 
sc[i].getPrefix());
+                               formNode.addChild("a", "id", sc[i].getPrefix());
                                formNode.addChild(configGroupUlNode);
                        }
                }
@@ -187,7 +191,7 @@
                formNode.addChild("input", new String[] { "type", "value" }, 
new String[] { "submit", l10n("apply")});
                formNode.addChild("input", new String[] { "type", "value" }, 
new String[] { "reset",  l10n("reset")});

-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

        public String supportedMethods() {

Copied: branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.java 
(from rev 14796, trunk/freenet/src/freenet/clients/http/ConnectionsToadlet.java)
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.java       
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,872 @@
+package freenet.clients.http;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import freenet.client.HighLevelSimpleClient;
+import freenet.io.comm.PeerParseException;
+import freenet.io.comm.ReferenceSignatureVerificationException;
+import freenet.io.xfer.PacketThrottle;
+import freenet.l10n.L10n;
+import freenet.node.DarknetPeerNode;
+import freenet.node.FSParseException;
+import freenet.node.Node;
+import freenet.node.NodeClientCore;
+import freenet.node.NodeStats;
+import freenet.node.PeerManager;
+import freenet.node.PeerNode;
+import freenet.node.PeerNodeStatus;
+import freenet.node.Version;
+import freenet.support.HTMLNode;
+import freenet.support.Logger;
+import freenet.support.MultiValueTable;
+import freenet.support.SimpleFieldSet;
+import freenet.support.SizeUtil;
+import freenet.support.TimeUtil;
+import freenet.support.api.HTTPRequest;
+
+public abstract class ConnectionsToadlet extends Toadlet {
+
+       protected class ComparatorByStatus implements Comparator {
+               
+               protected final String sortBy;
+               protected final boolean reversed;
+               
+               ComparatorByStatus(String sortBy, boolean reversed) {
+                       this.sortBy = sortBy;
+                       this.reversed = reversed;
+               }
+               
+               public int compare(Object first, Object second) {
+                       int result = 0;
+                       boolean isSet = true;
+                       PeerNodeStatus firstNode = (PeerNodeStatus) first;
+                       PeerNodeStatus secondNode = (PeerNodeStatus) second;
+                       
+                       if(sortBy != null){
+                               result = customCompare(firstNode, secondNode, 
sortBy);
+                               isSet = (result != 0);
+                               
+                       }else
+                               isSet=false;
+                       
+                       if(!isSet){
+                               int statusDifference = 
firstNode.getStatusValue() - secondNode.getStatusValue();
+                               if (statusDifference != 0) 
+                                       result = (statusDifference < 0 ? -1 : 
1);
+                               else
+                                       result = lastResortCompare(firstNode, 
secondNode);
+                       }
+
+                       if(result == 0){
+                               return 0;
+                       }else if(reversed){
+                               isReversed = true;
+                               return result > 0 ? -1 : 1;
+                       }else{
+                               isReversed = false;
+                               return result < 0 ? -1 : 1;
+                       }
+               }
+
+               protected int customCompare(PeerNodeStatus firstNode, 
PeerNodeStatus secondNode, String sortBy2) {
+                       if(sortBy.equals("address")){
+                               return 
firstNode.getPeerAddress().compareToIgnoreCase(secondNode.getPeerAddress());
+                       }else if(sortBy.equals("location")){
+                               return compareLocations(firstNode, secondNode);
+                       }else if(sortBy.equals("version")){
+                               return 
Version.getArbitraryBuildNumber(firstNode.getVersion()) - 
Version.getArbitraryBuildNumber(secondNode.getVersion());
+                       }else
+                               return 0;
+               }
+
+               private int compareLocations(PeerNodeStatus firstNode, 
PeerNodeStatus secondNode) {
+                       double diff = firstNode.getLocation() - 
secondNode.getLocation(); // Can occasionally be the same, and we must have a 
consistent sort order
+                       if(Double.MIN_VALUE*2 > Math.abs(diff)) return 0;
+                       return diff > 0 ? 1 : -1;
+               }
+
+               /** Default comparison, after taking into account status */
+               protected int lastResortCompare(PeerNodeStatus firstNode, 
PeerNodeStatus secondNode) {
+                       return compareLocations(firstNode, secondNode);
+               }
+       }
+
+       protected final Node node;
+       protected final NodeClientCore core;
+       protected final NodeStats stats;
+       protected final PeerManager peers;
+       protected boolean isReversed = false;
+       protected final DecimalFormat fix1 = new DecimalFormat("##0.0%");
+       
+       public String supportedMethods() {
+               if(this.acceptRefPosts())
+                       return "GET, POST";
+               else
+                       return "GET";
+       }
+
+       protected ConnectionsToadlet(Node n, NodeClientCore core, 
HighLevelSimpleClient client) {
+               super(client);
+               this.node = n;
+               this.core = core;
+               this.stats = n.nodeStats;
+               this.peers = n.peers;
+       }
+
+       public void handleGet(URI uri, final HTTPRequest request, 
ToadletContext ctx) throws ToadletContextClosedException, IOException, 
RedirectException {
+               String path = uri.getPath();
+               if(path.endsWith("myref.fref")) {
+                       SimpleFieldSet fs = getNoderef();
+                       StringWriter sw = new StringWriter();
+                       fs.writeTo(sw);
+                       MultiValueTable extraHeaders = new MultiValueTable();
+                       // Force download to disk
+                       extraHeaders.put("Content-Disposition", "attachment; 
filename=myref.fref");
+                       this.writeReply(ctx, 200, 
"application/x-freenet-reference", "OK", extraHeaders, sw.toString());
+                       return;
+               }
+
+               if(path.endsWith("myref.txt")) {
+                       SimpleFieldSet fs = getNoderef();
+                       StringWriter sw = new StringWriter();
+                       fs.writeTo(sw);
+                       this.writeTextReply(ctx, 200, "OK", sw.toString());
+                       return;
+               }
+               
+               if(!ctx.isAllowedFullAccess()) {
+                       super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
+                       return;
+               }
+               
+               final boolean advancedModeEnabled = 
node.isAdvancedModeEnabled();
+               final boolean fProxyJavascriptEnabled = 
node.isFProxyJavascriptEnabled();
+               
+               /* gather connection statistics */
+               PeerNodeStatus[] peerNodeStatuses = getPeerNodeStatuses();
+               Arrays.sort(peerNodeStatuses, 
comparator(request.getParam("sortBy", null), 
request.isParameterSet("reversed")));
+               
+               int numberOfConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CONNECTED);
+               int numberOfRoutingBackedOff = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF);
+               int numberOfTooNew = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_NEW);
+               int numberOfTooOld = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_OLD);
+               int numberOfDisconnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISCONNECTED);
+               int numberOfNeverConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED);
+               int numberOfDisabled = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISABLED);
+               int numberOfBursting = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_BURSTING);
+               int numberOfListening = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTENING);
+               int numberOfListenOnly = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTEN_ONLY);
+               int numberOfClockProblem = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM);
+               int numberOfConnError = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CONN_ERROR);
+               
+               int numberOfSimpleConnected = numberOfConnected + 
numberOfRoutingBackedOff;
+               int numberOfNotConnected = numberOfTooNew + numberOfTooOld + 
numberOfDisconnected + numberOfNeverConnected + numberOfDisabled + 
numberOfBursting + numberOfListening + numberOfListenOnly + 
numberOfClockProblem + numberOfConnError;
+               String titleCountString = null;
+               if(advancedModeEnabled) {
+                       titleCountString = "(" + numberOfConnected + '/' + 
numberOfRoutingBackedOff + '/' + numberOfTooNew + '/' + numberOfTooOld + '/' + 
numberOfNotConnected + ')';
+               } else {
+                       titleCountString = (numberOfNotConnected + 
numberOfSimpleConnected)>0 ? String.valueOf(numberOfSimpleConnected) : "";
+               }
+               
+               HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(getPageTitle(titleCountString, 
node.getMyName()), ctx);
+               HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
+               
+               // FIXME! We need some nice images
+               long now = System.currentTimeMillis();
+       
+               if(ctx.isAllowedFullAccess())
+                       contentNode.addChild(core.alerts.createSummary());
+               
+               if(peerNodeStatuses.length>0){
+
+                       /* node status values */
+                       long nodeUptimeSeconds = (now - node.startupTime) / 
1000;
+                       int bwlimitDelayTime = (int) 
stats.getBwlimitDelayTime();
+                       int nodeAveragePingTime = (int) 
stats.getNodeAveragePingTime();
+                       int networkSizeEstimateSession = 
stats.getNetworkSizeEstimate(-1);
+                       int networkSizeEstimateRecent = 0;
+                       if(nodeUptimeSeconds > (48*60*60)) {  // 48 hours
+                               networkSizeEstimateRecent = 
stats.getNetworkSizeEstimate(now - (48*60*60*1000));  // 48 hours
+                       }
+                       DecimalFormat fix4 = new DecimalFormat("0.0000");
+                       double routingMissDistance =  
stats.routingMissDistance.currentValue();
+                       double backedOffPercent =  
stats.backedOffPercent.currentValue();
+                       String nodeUptimeString = 
TimeUtil.formatTime(nodeUptimeSeconds * 1000);  // *1000 to convert to 
milliseconds
+
+                       // BEGIN OVERVIEW TABLE
+                       HTMLNode overviewTable = contentNode.addChild("table", 
"class", "column");
+                       HTMLNode overviewTableRow = 
overviewTable.addChild("tr");
+                       HTMLNode nextTableCell = 
overviewTableRow.addChild("td", "class", "first");
+
+                       /* node status overview box */
+                       if(advancedModeEnabled) {
+                               HTMLNode overviewInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                               overviewInfobox.addChild("div", "class", 
"infobox-header", "Node status overview");
+                               HTMLNode overviewInfoboxContent = 
overviewInfobox.addChild("div", "class", "infobox-content");
+                               HTMLNode overviewList = 
overviewInfoboxContent.addChild("ul");
+                               overviewList.addChild("li", 
"bwlimitDelayTime:\u00a0" + bwlimitDelayTime + "ms");
+                               overviewList.addChild("li", 
"nodeAveragePingTime:\u00a0" + nodeAveragePingTime + "ms");
+                               overviewList.addChild("li", 
"networkSizeEstimateSession:\u00a0" + networkSizeEstimateSession + 
"\u00a0nodes");
+                               if(nodeUptimeSeconds > (48*60*60)) {  // 48 
hours
+                                       overviewList.addChild("li", 
"networkSizeEstimateRecent:\u00a0" + networkSizeEstimateRecent + "\u00a0nodes");
+                               }
+                               overviewList.addChild("li", "nodeUptime:\u00a0" 
+ nodeUptimeString);
+                               overviewList.addChild("li", 
"routingMissDistance:\u00a0" + fix4.format(routingMissDistance));
+                               overviewList.addChild("li", 
"backedOffPercent:\u00a0" + fix1.format(backedOffPercent));
+                               overviewList.addChild("li", 
"pInstantReject:\u00a0" + fix1.format(stats.pRejectIncomingInstantly()));
+                               nextTableCell = overviewTableRow.addChild("td");
+                       }
+
+                       // Activity box
+                       int numARKFetchers = node.getNumARKFetchers();
+
+                       HTMLNode activityInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                       activityInfobox.addChild("div", "class", 
"infobox-header", l10n("activityTitle"));
+                       HTMLNode activityInfoboxContent = 
activityInfobox.addChild("div", "class", "infobox-content");
+                       HTMLNode activityList = 
StatisticsToadlet.drawActivity(activityInfoboxContent, node);
+                       if (advancedModeEnabled && activityList != null) {
+                               if (numARKFetchers > 0) {
+                                       activityList.addChild("li", 
"ARK\u00a0Fetch\u00a0Requests:\u00a0" + numARKFetchers);
+                               }
+                               StatisticsToadlet.drawBandwidth(activityList, 
node, nodeUptimeSeconds);
+                       }
+
+                       nextTableCell = advancedModeEnabled ? 
overviewTableRow.addChild("td") : overviewTableRow.addChild("td", "class", 
"last");
+
+                       // Peer statistics box
+                       HTMLNode peerStatsInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                       peerStatsInfobox.addChild("div", "class", 
"infobox-header", l10nStats("peerStatsTitle"));
+                       HTMLNode peerStatsContent = 
peerStatsInfobox.addChild("div", "class", "infobox-content");
+                       HTMLNode peerStatsList = 
peerStatsContent.addChild("ul");
+                       if (numberOfConnected > 0) {
+                               HTMLNode peerStatsConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsConnectedListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_connected", 
l10n("connected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("connectedShort"));
+                               peerStatsConnectedListItem.addChild("span", 
":\u00a0" + numberOfConnected);
+                       }
+                       if (numberOfRoutingBackedOff > 0) {
+                               HTMLNode peerStatsRoutingBackedOffListItem = 
peerStatsList.addChild("li").addChild("span");
+                               
peerStatsRoutingBackedOffListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_backed_off", (advancedModeEnabled ? 
l10n("backedOff") : l10n("busy")), "border-bottom: 1px dotted; cursor: help;" 
}, advancedModeEnabled ? l10n("backedOffShort") : l10n("busyShort"));
+                               
peerStatsRoutingBackedOffListItem.addChild("span", ":\u00a0" + 
numberOfRoutingBackedOff);
+                       }
+                       if (numberOfTooNew > 0) {
+                               HTMLNode peerStatsTooNewListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsTooNewListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_new", 
l10n("tooNew"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooNewShort"));
+                               peerStatsTooNewListItem.addChild("span", 
":\u00a0" + numberOfTooNew);
+                       }
+                       if (numberOfTooOld > 0) {
+                               HTMLNode peerStatsTooOldListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsTooOldListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_old", 
l10n("tooOld"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooOldShort"));
+                               peerStatsTooOldListItem.addChild("span", 
":\u00a0" + numberOfTooOld);
+                       }
+                       if (numberOfDisconnected > 0) {
+                               HTMLNode peerStatsDisconnectedListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsDisconnectedListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_disconnected", 
l10n("notConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("notConnectedShort"));
+                               peerStatsDisconnectedListItem.addChild("span", 
":\u00a0" + numberOfDisconnected);
+                       }
+                       if (numberOfNeverConnected > 0) {
+                               HTMLNode peerStatsNeverConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
+                               
peerStatsNeverConnectedListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_never_connected", 
l10n("neverConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("neverConnectedShort"));
+                               
peerStatsNeverConnectedListItem.addChild("span", ":\u00a0" + 
numberOfNeverConnected);
+                       }
+                       if (numberOfDisabled > 0) {
+                               HTMLNode peerStatsDisabledListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsDisabledListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_disabled", 
l10n("disabled"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("disabledShort"));
+                               peerStatsDisabledListItem.addChild("span", 
":\u00a0" + numberOfDisabled);
+                       }
+                       if (numberOfBursting > 0) {
+                               HTMLNode peerStatsBurstingListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsBurstingListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_bursting", 
l10n("bursting"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("burstingShort"));
+                               peerStatsBurstingListItem.addChild("span", 
":\u00a0" + numberOfBursting);
+                       }
+                       if (numberOfListening > 0) {
+                               HTMLNode peerStatsListeningListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListeningListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_listening", 
l10n("listening"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listeningShort"));
+                               peerStatsListeningListItem.addChild("span", 
":\u00a0" + numberOfListening);
+                       }
+                       if (numberOfListenOnly > 0) {
+                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_listen_only", 
l10n("listenOnly"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listenOnlyShort"));
+                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfListenOnly);
+                       }
+                       if (numberOfClockProblem > 0) {
+                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { 
"peer_clock_problem", l10n("clockProblem"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("clockProblemShort"));
+                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfClockProblem);
+                       }
+                       if (numberOfConnError > 0) {
+                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { 
"peer_clock_problem", l10n("connError"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("connErrorShort"));
+                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfConnError);
+                       }
+
+                       // Peer routing backoff reason box
+                       if(advancedModeEnabled) {
+                               nextTableCell = overviewTableRow.addChild("td", 
"class", "last");
+                               HTMLNode backoffReasonInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                               backoffReasonInfobox.addChild("div", "class", 
"infobox-header", "Peer backoff reasons");
+                               HTMLNode backoffReasonContent = 
backoffReasonInfobox.addChild("div", "class", "infobox-content");
+                               String [] routingBackoffReasons = 
peers.getPeerNodeRoutingBackoffReasons();
+                               if(routingBackoffReasons.length == 0) {
+                                       backoffReasonContent.addChild("#", 
"Good, your node is not backed off from any peers!");
+                               } else {
+                                       HTMLNode reasonList = 
backoffReasonContent.addChild("ul");
+                                       for(int 
i=0;i<routingBackoffReasons.length;i++) {
+                                               int reasonCount = 
peers.getPeerNodeRoutingBackoffReasonSize(routingBackoffReasons[i]);
+                                               if(reasonCount > 0) {
+                                                       
reasonList.addChild("li", routingBackoffReasons[i] + '\u00a0' + reasonCount);
+                                               }
+                                       }
+                               }
+                       }
+                       // END OVERVIEW TABLE
+                       
+                       boolean enablePeerActions = showPeerActionsBox();
+                       
+                       // BEGIN PEER TABLE
+                       if(fProxyJavascriptEnabled) {
+                               StringBuffer jsBuf = new StringBuffer();
+                               // FIXME: There's probably some icky Javascript 
in here (this is the first thing that worked for me); feel free to fix up to 
Javascript guru standards
+                               jsBuf.append( "  function peerNoteChange() {\n" 
);
+                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
+                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
+                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
+                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
+                               jsBuf.append( "      } else {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
+                               jsBuf.append( "      }\n" );
+                               jsBuf.append( "    }\n" );
+                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
+                               //jsBuf.append( "    document.getElementById( 
\"peersForm\" ).submit();\n" );
+                               jsBuf.append( "    document.getElementById( 
\"peersForm\" ).doAction.click();\n" );
+                               jsBuf.append( "  }\n" );
+                               jsBuf.append( "  function peerNoteBlur() {\n" );
+                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
+                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
+                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
+                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
+                               jsBuf.append( "      } else {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
+                               jsBuf.append( "      }\n" );
+                               jsBuf.append( "    }\n" );
+                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
+                               jsBuf.append( "  }\n" );
+                               contentNode.addChild("script", "type", 
"text/javascript").addChild("%", jsBuf.toString());
+                       }
+                       HTMLNode peerTableInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
+                       HTMLNode peerTableInfoboxHeader = 
peerTableInfobox.addChild("div", "class", "infobox-header");
+                       peerTableInfoboxHeader.addChild("#", 
getPeerListTitle());
+                       if (advancedModeEnabled) {
+                               if (!path.endsWith("displaymessagetypes.html")) 
{
+                                       peerTableInfoboxHeader.addChild("#", " 
");
+                                       peerTableInfoboxHeader.addChild("a", 
"href", "displaymessagetypes.html", "(more detailed)");
+                               }
+                       }
+                       HTMLNode peerTableInfoboxContent = 
peerTableInfobox.addChild("div", "class", "infobox-content");
+
+                       if (peerNodeStatuses.length == 0) {
+                               
L10n.addL10nSubstitution(peerTableInfoboxContent, 
"DarknetConnectionsToadlet.noPeersWithHomepageLink", 
+                                               new String[] { "link", "/link" 
}, new String[] { "<a href=\"/\">", "</a>" });
+                       } else {
+                               HTMLNode peerForm = null;
+                               HTMLNode peerTable;
+                               if(enablePeerActions) {
+                                       peerForm = 
ctx.addFormChild(peerTableInfoboxContent, ".", "peersForm");
+                                       peerTable = peerForm.addChild("table", 
"class", "darknet_connections");
+                               } else {
+                                       peerTable = 
peerTableInfoboxContent.addChild("table", "class", "darknet_connections");
+                               }
+                               HTMLNode peerTableHeaderRow = 
peerTable.addChild("tr");
+                               if(enablePeerActions)
+                                       peerTableHeaderRow.addChild("th");
+                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "status")).addChild("#", l10n("statusTitle"));
+                               if(hasNameColumn())
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"name")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("nameClickToMessage"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("nameTitle"));
+                               if (advancedModeEnabled) {
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"address")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("ipAddress"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("ipAddressTitle"));
+                               }
+                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "version")).addChild("#", l10n("versionTitle"));
+                               if (advancedModeEnabled) {
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"location")).addChild("#", "Location");
+                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Other node busy? Display: Percentage of time the 
node is overloaded, Current wait time remaining (0=not overloaded)/total/last 
overload reason", "border-bottom: 1px dotted; cursor: help;" }, "Backoff");
+
+                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Probability of the node rejecting a request due to 
overload or causing a timeout.", "border-bottom: 1px dotted; cursor: help;" }, 
"Overload Probability");
+                               }
+                               
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { l10n("idleTime"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("idleTimeTitle"));
+                               if(hasPrivateNoteColumn())
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"privnote")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("privateNote"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("privateNoteTitle"));
+
+                               if(advancedModeEnabled) {
+                                       peerTableHeaderRow.addChild("th", 
"%\u00a0Time Routable");
+                                       peerTableHeaderRow.addChild("th", 
"Total\u00a0Traffic\u00a0(in/out)");
+                                       peerTableHeaderRow.addChild("th", 
"Congestion\u00a0Control");
+                                       peerTableHeaderRow.addChild("th", 
"Time\u00a0Delta");
+                               }
+                               
+                               for (int peerIndex = 0, peerCount = 
peerNodeStatuses.length; peerIndex < peerCount; peerIndex++) {
+                                       
+                                       PeerNodeStatus peerNodeStatus = 
peerNodeStatuses[peerIndex];
+                                       drawRow(peerTable, peerNodeStatus, 
advancedModeEnabled, fProxyJavascriptEnabled, now, path, enablePeerActions);
+                                       
+                               }
+
+                               if(peerForm != null) {
+                                       drawPeerActionSelectBox(peerForm, 
advancedModeEnabled);
+                               }
+                       }
+                       // END PEER TABLE
+               }
+
+               drawAddPeerBox(contentNode, ctx);
+               
+               // our reference
+               if(shouldDrawNoderefBox(advancedModeEnabled))
+                       drawNoderefBox(contentNode, ctx);
+               
+               // our ports
+               HTMLNode portInfobox = contentNode.addChild("div", "class", 
"infobox infobox-normal");
+               portInfobox.addChild("div", "class", "infobox-header", 
l10n("nodePortsTitle"));
+               HTMLNode portInfoboxContent = portInfobox.addChild("div", 
"class", "infobox-content");
+               HTMLNode portInfoList = portInfoboxContent.addChild("ul");
+               SimpleFieldSet fproxyConfig = 
node.config.get("fproxy").exportFieldSet(true);
+               SimpleFieldSet fcpConfig = 
node.config.get("fcp").exportFieldSet(true);
+               SimpleFieldSet tmciConfig = 
node.config.get("console").exportFieldSet(true);
+               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.darknetFnpPort", new String[] { 
"port" }, new String[] { Integer.toString(node.getFNPPort()) }));
+               int opennetPort = node.getOpennetFNPPort();
+               if(opennetPort > 0)
+                       portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.opennetFnpPort", new String[] { 
"port" }, new String[] { Integer.toString(opennetPort) }));
+               try {
+                       if(fproxyConfig.getBoolean("enabled", false)) {
+                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fproxyPort", new String[] { "port" }, 
new String[] { Integer.toString(fproxyConfig.getInt("port")) }));
+                       } else {
+                               portInfoList.addChild("li", 
l10n("fproxyDisabled"));
+                       }
+                       if(fcpConfig.getBoolean("enabled", false)) {
+                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fcpPort", new String[] { "port" }, 
new String[] { Integer.toString(fcpConfig.getInt("port")) }));
+                       } else {
+                               portInfoList.addChild("li", 
l10n("fcpDisabled"));
+                       }
+                       if(tmciConfig.getBoolean("enabled", false)) {
+                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.tmciPort", new String[] { "port" }, 
new String[] { Integer.toString(tmciConfig.getInt("port")) }));
+                       } else {
+                               portInfoList.addChild("li", 
l10n("tmciDisabled"));
+                       }
+               } catch (FSParseException e) {
+                       // ignore
+               }
+               
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
+       }
+
+       protected abstract boolean acceptRefPosts();
+       
+       /** Where to redirect to if there is an error */
+       protected abstract String defaultRedirectLocation();
+       
+       public void handlePost(URI uri, final HTTPRequest request, 
ToadletContext ctx) throws ToadletContextClosedException, IOException, 
RedirectException {
+               boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
+               
+               if(!acceptRefPosts()) {
+                       super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
+                       return;
+               }
+               
+               if(!ctx.isAllowedFullAccess()) {
+                       super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
+                       return;
+               }
+               
+               String pass = request.getPartAsString("formPassword", 32);
+               if((pass == null) || !pass.equals(core.formPassword)) {
+                       MultiValueTable headers = new MultiValueTable();
+                       headers.put("Location", defaultRedirectLocation());
+                       ctx.sendReplyHeaders(302, "Found", headers, null, 0);
+                       if(logMINOR) Logger.minor(this, "No password ("+pass+" 
should be "+core.formPassword+ ')');
+                       return;
+               }
+               
+               if (request.isPartSet("add")) {
+                       // add a new node
+                       String urltext = request.getPartAsString("url", 100);
+                       urltext = urltext.trim();
+                       String reftext = request.getPartAsString("ref", 2000);
+                       reftext = reftext.trim();
+                       if (reftext.length() < 200) {
+                               reftext = request.getPartAsString("reffile", 
2000);
+                               reftext = reftext.trim();
+                       }
+                       String privateComment = null;
+                       if(!isOpennet())
+                               privateComment = 
request.getPartAsString("peerPrivateNote", 250).trim();
+                       
+                       StringBuffer ref = new StringBuffer(1024);
+                       if (urltext.length() > 0) {
+                               // fetch reference from a URL
+                               BufferedReader in = null;
+                               try {
+                                       URL url = new URL(urltext);
+                                       URLConnection uc = url.openConnection();
+                                       // FIXME get charset encoding from 
uc.getContentType()
+                                       in = new BufferedReader(new 
InputStreamReader(uc.getInputStream()));
+                                       String line;
+                                       while ( (line = in.readLine()) != null) 
{
+                                               ref.append( line ).append('\n');
+                                       }
+                               } catch (IOException e) {
+                                       this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), 
L10n.getString("DarknetConnectionsToadlet.cantFetchNoderefURL", new String[] { 
"url" }, new String[] { urltext }));
+                                       return;
+                               } finally {
+                                       if( in != null ){
+                                               in.close();
+                                       }
+                               }
+                       } else if (reftext.length() > 0) {
+                               // read from post data or file upload
+                               // this slightly scary looking regexp chops any 
extra characters off the beginning or ends of lines and removes extra line 
breaks
+                               ref = new 
StringBuffer(reftext.replaceAll(".*?((?:[\\w,\\.]+\\=[^\r\n]+?)|(?:End))[ 
\\t]*(?:\\r?\\n)+", "$1\n"));
+                       } else {
+                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), l10n("noRefOrURL"));
+                               request.freeParts();
+                               return;
+                       }
+                       ref = new StringBuffer(ref.toString().trim());
+
+                       request.freeParts();
+                       // we have a node reference in ref
+                       SimpleFieldSet fs;
+                       
+                       try {
+                               fs = new SimpleFieldSet(ref.toString(), false, 
true);
+                               if(!fs.getEndMarker().endsWith("End")) {
+                                       sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"),
+                                                       
L10n.getString("DarknetConnectionsToadlet.cantParseWrongEnding", new String[] { 
"end" }, new String[] { fs.getEndMarker() }));
+                                       return;
+                               }
+                               fs.setEndMarker("End"); // It's always End ; 
the regex above doesn't always grok this
+                       } catch (IOException e) {
+                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), 
+                                               
L10n.getString("DarknetConnectionsToadlet.cantParseTryAgain", new String[] { 
"error" }, new String[] { e.toString() }));
+                               return;
+                       } catch (Throwable t) {
+                               this.sendErrorPage(ctx, 
l10n("failedToAddNodeInternalErrorTitle"), 
l10n("failedToAddNodeInternalError"), t);
+                               return;
+                       }
+                       PeerNode pn;
+                       try {
+                               if(isOpennet()) {
+                                       pn = node.createNewOpennetNode(fs);
+                               } else {
+                                       pn = node.createNewDarknetNode(fs);
+                                       
((DarknetPeerNode)pn).setPrivateDarknetCommentNote(privateComment);
+                               }
+                       } catch (FSParseException e1) {
+                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"),
+                                               
L10n.getString("DarknetConnectionsToadlet.cantParseTryAgain", new String[] { 
"error" }, new String[] { e1.toString() }));
+                               return;
+                       } catch (PeerParseException e1) {
+                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), 
+                                               
L10n.getString("DarknetConnectionsToadlet.cantParseTryAgain", new String[] { 
"error" }, new String[] { e1.toString() }));
+                               return;
+                       } catch (ReferenceSignatureVerificationException e1){
+                               HTMLNode node = new HTMLNode("div");
+                               node.addChild("#", 
L10n.getString("DarknetConnectionsToadlet.invalidSignature", new String[] { 
"error" }, new String[] { e1.toString() }));
+                               node.addChild("br");
+                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), node);
+                               return;
+                       } catch (Throwable t) {
+                               this.sendErrorPage(ctx, 
l10n("failedToAddNodeInternalErrorTitle"), 
l10n("failedToAddNodeInternalError"), t);
+                               return;
+                       }
+                       if(Arrays.equals(pn.getIdentity(), 
node.getDarknetIdentity())) {
+                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), l10n("triedToAddSelf"));
+                               return;
+                       }
+                       if(!this.node.addPeerConnection(pn)) {
+                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), l10n("alreadyInReferences"));
+                               return;
+                       }
+                       
+                       MultiValueTable headers = new MultiValueTable();
+                       headers.put("Location", defaultRedirectLocation());
+                       ctx.sendReplyHeaders(302, "Found", headers, null, 0);
+                       return;
+               } else handleAltPost(uri, request, ctx, logMINOR);
+               
+               
+       }
+
+       /** Adding a darknet node or an opennet node? */
+       protected abstract boolean isOpennet();
+
+       /**
+        * Rest of handlePost() method - supplied by subclass.
+        * @throws IOException 
+        * @throws ToadletContextClosedException 
+        * @throws RedirectException 
+        */
+       protected void handleAltPost(URI uri, HTTPRequest request, 
ToadletContext ctx, boolean logMINOR) throws ToadletContextClosedException, 
IOException, RedirectException {
+               // Do nothing - we only support adding nodes
+               handleGet(uri, new HTTPRequestImpl(uri), ctx);
+       }
+
+       /**
+        * What should the heading (before "(more detailed)") be on the peers 
table?
+        */
+       protected abstract String getPeerListTitle();
+
+       /** Should there be a checkbox for each peer, and 
drawPeerActionSelectBox() be called directly
+        * after drawing the peers list? */
+       protected abstract boolean showPeerActionsBox();
+
+       /** If showPeerActionsBox() is true, this will be called directly after 
drawing the peers table.
+        * A form has been added, and checkboxes added for each peer. This 
function should draw the rest
+        * of the form - any additional controls and one or more submit buttons.
+        */
+       protected abstract void drawPeerActionSelectBox(HTMLNode peerForm, 
boolean advancedModeEnabled);
+       
+       protected abstract boolean shouldDrawNoderefBox(boolean 
advancedModeEnabled);
+
+       private void drawNoderefBox(HTMLNode contentNode, ToadletContext ctx) {
+               HTMLNode referenceInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
+               HTMLNode headerReferenceInfobox = 
referenceInfobox.addChild("div", "class", "infobox-header");
+               // FIXME better way to deal with this sort of thing???
+               L10n.addL10nSubstitution(headerReferenceInfobox, 
"DarknetConnectionsToadlet.myReferenceHeader",
+                               new String[] { "linkref", "/linkref", 
"linktext", "/linktext" },
+                               new String[] { "<a href=\"myref.fref\">", 
"</a>", "<a href=\"myref.txt\">", "</a>" });
+               HTMLNode warningSentence = 
headerReferenceInfobox.addChild("pre");
+               L10n.addL10nSubstitution(warningSentence, 
"DarknetConnectionsToadlet.referenceCopyWarning",
+                               new String[] { "bold", "/bold" },
+                               new String[] { "<b>", "</b>" });
+               referenceInfobox.addChild("div", "class", 
"infobox-content").addChild("pre", "id", "reference", getNoderef().toString() + 
'\n');
+       }
+
+       protected abstract String getPageTitle(String titleCountString, String 
myName);
+
+       /** Draw the add a peer box. This comes immediately after the main 
peers table and before the noderef box.
+        * Implementors may skip it by not doing anything in this method. */
+       protected void drawAddPeerBox(HTMLNode contentNode, ToadletContext ctx) 
{
+               // BEGIN PEER ADDITION BOX
+               HTMLNode peerAdditionInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
+               peerAdditionInfobox.addChild("div", "class", "infobox-header", 
l10n("addPeerTitle"));
+               HTMLNode peerAdditionContent = 
peerAdditionInfobox.addChild("div", "class", "infobox-content");
+               HTMLNode peerAdditionForm = 
ctx.addFormChild(peerAdditionContent, ".", "addPeerForm");
+               peerAdditionForm.addChild("#", l10n("pasteReference"));
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("textarea", new String[] { "id", 
"name", "rows", "cols" }, new String[] { "reftext", "ref", "8", "74" });
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("#", (l10n("urlReference") + ' '));
+               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "refurl", "text", "url" });
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("#", (l10n("fileReference") + ' '));
+               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "reffile", "file", "reffile" });
+               peerAdditionForm.addChild("br");
+               if(!isOpennet())
+                       peerAdditionForm.addChild("#", 
(l10n("enterDescription") + ' '));
+               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name", "size", "maxlength", "value" }, new String[] { "peerPrivateNote", 
"text", "peerPrivateNote", "16", "250", "" });
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "add", l10n("add") });
+       }
+
+       protected Comparator comparator(String sortBy, boolean reversed) {
+               return new ComparatorByStatus(sortBy, reversed);
+       }
+
+       abstract protected PeerNodeStatus[] getPeerNodeStatuses();
+
+       abstract protected SimpleFieldSet getNoderef();
+
+       private void drawRow(HTMLNode peerTable, PeerNodeStatus peerNodeStatus, 
boolean advancedModeEnabled, boolean fProxyJavascriptEnabled, long now, String 
path, boolean enablePeerActions) {
+               HTMLNode peerRow = peerTable.addChild("tr");
+
+               if(enablePeerActions) {
+                       // check box column
+                       peerRow.addChild("td", "class", 
"peer-marker").addChild("input", new String[] { "type", "name" }, new String[] 
{ "checkbox", "node_" + peerNodeStatus.hashCode() });
+               }
+
+               // status column
+               String statusString = peerNodeStatus.getStatusName();
+               if (!advancedModeEnabled && (peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF)) {
+                       statusString = "BUSY";
+               }
+               peerRow.addChild("td", "class", "peer-status").addChild("span", 
"class", peerNodeStatus.getStatusCSSName(), statusString + 
(peerNodeStatus.isFetchingARK() ? "*" : ""));
+
+               drawNameColumn(peerRow, peerNodeStatus);
+               
+               // address column
+               if (advancedModeEnabled) {
+                       String pingTime = "";
+                       if (peerNodeStatus.isConnected()) {
+                               pingTime = " (" + (int) 
peerNodeStatus.getAveragePingTime() + "ms)";
+                       }
+                       peerRow.addChild("td", "class", 
"peer-address").addChild("#", ((peerNodeStatus.getPeerAddress() != null) ? 
(peerNodeStatus.getPeerAddress() + ':' + peerNodeStatus.getPeerPort()) : 
(l10n("unknownAddress"))) + pingTime);
+               }
+
+               // version column
+               if (peerNodeStatus.getStatusValue() != 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED && 
(peerNodeStatus.isPublicInvalidVersion() || 
peerNodeStatus.isPublicReverseInvalidVersion())) {  // Don't draw attention to 
a version problem if NEVER CONNECTED
+                       peerRow.addChild("td", "class", 
"peer-version").addChild("span", "class", "peer_version_problem", 
peerNodeStatus.getSimpleVersion());
+               } else {
+                       peerRow.addChild("td", "class", 
"peer-version").addChild("#", peerNodeStatus.getSimpleVersion());
+               }
+
+               // location column
+               if (advancedModeEnabled) {
+                       peerRow.addChild("td", "class", "peer-location", 
String.valueOf(peerNodeStatus.getLocation()));
+               }
+
+               if (advancedModeEnabled) {
+                       // backoff column
+                       HTMLNode backoffCell = peerRow.addChild("td", "class", 
"peer-backoff");
+                       backoffCell.addChild("#", 
fix1.format(peerNodeStatus.getBackedOffPercent()));
+                       int backoff = (int) 
(Math.max(peerNodeStatus.getRoutingBackedOffUntil() - now, 0));
+                       // Don't list the backoff as zero before it's actually 
zero
+                       if ((backoff > 0) && (backoff < 1000)) {
+                               backoff = 1000;
+                       }
+                       backoffCell.addChild("#", ' ' + String.valueOf(backoff 
/ 1000) + '/' + String.valueOf(peerNodeStatus.getRoutingBackoffLength() / 
1000));
+                       backoffCell.addChild("#", 
(peerNodeStatus.getLastBackoffReason() == null) ? "" : ('/' + 
(peerNodeStatus.getLastBackoffReason())));
+
+                       // overload probability column
+                       HTMLNode pRejectCell = peerRow.addChild("td", "class", 
"peer-backoff"); // FIXME
+                       pRejectCell.addChild("#", 
fix1.format(peerNodeStatus.getPReject()));
+               }
+
+               // idle column
+               long idle = peerNodeStatus.getTimeLastRoutable();
+               if (peerNodeStatus.isRoutable()) {
+                       idle = peerNodeStatus.getTimeLastConnectionCompleted();
+               } else if (peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED) {
+                       idle = peerNodeStatus.getPeerAddedTime();
+               }
+               if(!peerNodeStatus.isConnected() && (now - idle) > (2 * 7 * 24 
* 60 * 60 * (long) 1000)) { // 2 weeks
+                       peerRow.addChild("td", "class", 
"peer-idle").addChild("span", "class", "peer_idle_old", idleToString(now, 
idle));
+               } else {
+                       peerRow.addChild("td", "class", "peer-idle", 
idleToString(now, idle));
+               }
+
+               if(hasPrivateNoteColumn())
+                       drawPrivateNoteColumn(peerRow, peerNodeStatus, 
fProxyJavascriptEnabled);
+
+               if(advancedModeEnabled) {
+                       // percent of time connected column
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", 
fix1.format(peerNodeStatus.getPercentTimeRoutableConnection()));
+                       // total traffic column
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", SizeUtil.formatSize(peerNodeStatus.getTotalInputBytes())+" / 
"+SizeUtil.formatSize(peerNodeStatus.getTotalOutputBytes()));
+                       // congestion control
+                       PacketThrottle t = peerNodeStatus.getThrottle();
+                       String val;
+                       if(t == null)
+                               val = "none";
+                       else
+                               val = (int)((1000.0 / t.getDelay()) * 
1024.0)+"B/sec delay "+
+                                       t.getDelay()+"ms (RTT 
"+t.getRoundTripTime()+"ms window "+t.getWindowSize();
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", val);
+                       // time delta
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", TimeUtil.formatTime(peerNodeStatus.getClockDelta()));
+               }
+               
+               if (path.endsWith("displaymessagetypes.html")) {
+                       drawMessageTypes(peerTable, peerNodeStatus);
+               }
+       }
+
+       /** Is there a name column? */
+       abstract protected boolean hasNameColumn();
+       
+       /**
+        * Draw the name column, if there is one. This will be directly after 
the status column.
+        */
+       abstract protected void drawNameColumn(HTMLNode peerRow, PeerNodeStatus 
peerNodeStatus);
+
+       /**
+        * Is there a private note column?
+        */
+       abstract protected boolean hasPrivateNoteColumn();
+
+       /**
+        * Draw the private note column.
+        */
+       abstract protected void drawPrivateNoteColumn(HTMLNode peerRow, 
PeerNodeStatus peerNodeStatus, boolean fProxyJavascriptEnabled);
+       
+       private void drawMessageTypes(HTMLNode peerTable, PeerNodeStatus 
peerNodeStatus) {
+               HTMLNode messageCountRow = peerTable.addChild("tr", "class", 
"message-status");
+               messageCountRow.addChild("td", "colspan", "2");
+               HTMLNode messageCountCell = messageCountRow.addChild("td", 
"colspan", "9");  // = total table row width - 2 from above colspan
+               HTMLNode messageCountTable = messageCountCell.addChild("table", 
"class", "message-count");
+               HTMLNode countHeaderRow = messageCountTable.addChild("tr");
+               countHeaderRow.addChild("th", "Message");
+               countHeaderRow.addChild("th", "Incoming");
+               countHeaderRow.addChild("th", "Outgoing");
+               List messageNames = new ArrayList();
+               Map messageCounts = new HashMap();
+               for (Iterator incomingMessages = 
peerNodeStatus.getLocalMessagesReceived().keySet().iterator(); 
incomingMessages.hasNext(); ) {
+                       String messageName = (String) incomingMessages.next();
+                       messageNames.add(messageName);
+                       Long messageCount = (Long) 
peerNodeStatus.getLocalMessagesReceived().get(messageName);
+                       messageCounts.put(messageName, new Long[] { 
messageCount, new Long(0) });
+               }
+               for (Iterator outgoingMessages = 
peerNodeStatus.getLocalMessagesSent().keySet().iterator(); 
outgoingMessages.hasNext(); ) {
+                       String messageName = (String) outgoingMessages.next();
+                       if (!messageNames.contains(messageName)) {
+                               messageNames.add(messageName);
+                       }
+                       Long messageCount = (Long) 
peerNodeStatus.getLocalMessagesSent().get(messageName);
+                       Long[] existingCounts = (Long[]) 
messageCounts.get(messageName);
+                       if (existingCounts == null) {
+                               messageCounts.put(messageName, new Long[] { new 
Long(0), messageCount });
+                       } else {
+                               existingCounts[1] = messageCount;
+                       }
+               }
+               Collections.sort(messageNames, new Comparator() {
+                       public int compare(Object first, Object second) {
+                               return ((String) 
first).compareToIgnoreCase((String) second);
+                       }
+               });
+               for (Iterator messageNamesIterator = messageNames.iterator(); 
messageNamesIterator.hasNext(); ) {
+                       String messageName = (String) 
messageNamesIterator.next();
+                       Long[] messageCount = (Long[]) 
messageCounts.get(messageName);
+                       HTMLNode messageRow = messageCountTable.addChild("tr");
+                       messageRow.addChild("td", messageName);
+                       messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[0]));
+                       messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[1]));
+               }
+       }
+
+       private String idleToString(long now, long idle) {
+               if (idle <= 0) {
+                       return " ";
+               }
+               long idleMilliseconds = now - idle;
+               return TimeUtil.formatTime(idleMilliseconds);
+       }
+       
+       private static String l10n(String string) {
+               return L10n.getString("DarknetConnectionsToadlet."+string);
+       }
+       
+       private static String l10nStats(String string) {
+               return L10n.getString("StatisticsToadlet."+string);
+       }
+
+       private String sortString(boolean isReversed, String type) {
+               return (isReversed ? ("?sortBy="+type) : 
("?sortBy="+type+"&reversed"));
+       }
+
+
+}

Modified: 
branches/freenet-jfk/src/freenet/clients/http/DarknetConnectionsToadlet.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/clients/http/DarknetConnectionsToadlet.java    
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/clients/http/DarknetConnectionsToadlet.java    
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,735 +1,156 @@
 package freenet.clients.http;

-import java.io.BufferedReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
 import java.net.URI;
-import java.net.URL;
-import java.net.URLConnection;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;

 import freenet.client.HighLevelSimpleClient;
-import freenet.io.comm.PeerParseException;
-import freenet.io.comm.ReferenceSignatureVerificationException;
-import freenet.io.xfer.PacketThrottle;
 import freenet.l10n.L10n;
-import freenet.node.FSParseException;
+import freenet.node.DarknetPeerNode;
+import freenet.node.DarknetPeerNodeStatus;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
-import freenet.node.NodeStats;
 import freenet.node.PeerManager;
-import freenet.node.PeerNode;
 import freenet.node.PeerNodeStatus;
-import freenet.node.Version;
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.MultiValueTable;
 import freenet.support.SimpleFieldSet;
-import freenet.support.SizeUtil;
-import freenet.support.TimeUtil;
 import freenet.support.api.HTTPRequest;

-public class DarknetConnectionsToadlet extends Toadlet {
+public class DarknetConnectionsToadlet extends ConnectionsToadlet {

-       private final Node node;
-       private final NodeClientCore core;
-       private final NodeStats stats;
-       private final PeerManager peers;
-       private boolean isReversed = false;
-       
-       protected DarknetConnectionsToadlet(Node n, NodeClientCore core, 
HighLevelSimpleClient client) {
-               super(client);
-               this.node = n;
-               this.core = core;
-               this.stats = n.nodeStats;
-               this.peers = n.peers;
+       DarknetConnectionsToadlet(Node n, NodeClientCore core, 
HighLevelSimpleClient client) {
+               super(n, core, client);
        }
-
+       
        public String supportedMethods() {
                return "GET, POST";
        }

-       public void handleGet(URI uri, final HTTPRequest request, 
ToadletContext ctx) throws ToadletContextClosedException, IOException, 
RedirectException {
-               String path = uri.getPath();
-               if(path.endsWith("myref.fref")) {
-                       SimpleFieldSet fs = node.exportPublicFieldSet();
-                       StringWriter sw = new StringWriter();
-                       fs.writeTo(sw);
-                       MultiValueTable extraHeaders = new MultiValueTable();
-                       // Force download to disk
-                       extraHeaders.put("Content-Disposition", "attachment; 
filename=myref.fref");
-                       this.writeReply(ctx, 200, 
"application/x-freenet-reference", "OK", extraHeaders, sw.toString());
-                       return;
-               }
+       private static String l10n(String string) {
+               return L10n.getString("DarknetConnectionsToadlet."+string);
+       }
+       
+       protected class DarknetComparator extends ComparatorByStatus {

-               if(path.endsWith("myref.txt")) {
-                       SimpleFieldSet fs = node.exportPublicFieldSet();
-                       StringWriter sw = new StringWriter();
-                       fs.writeTo(sw);
-                       this.writeReply(ctx, 200, "text/plain", "OK", 
sw.toString());
-                       return;
+               DarknetComparator(String sortBy, boolean reversed) {
+                       super(sortBy, reversed);
                }
+       
+               protected int customCompare(PeerNodeStatus firstNode, 
PeerNodeStatus secondNode, String sortBy) {
+                       if(sortBy.equals("name")) {
+                               return 
((DarknetPeerNodeStatus)firstNode).getName().compareToIgnoreCase(((DarknetPeerNodeStatus)secondNode).getName());
+                       }else if(sortBy.equals("privnote")){
+                               return 
((DarknetPeerNodeStatus)firstNode).getPrivateDarknetCommentNote().compareToIgnoreCase(((DarknetPeerNodeStatus)secondNode).getPrivateDarknetCommentNote());
+                       } else
+                               return super.customCompare(firstNode, 
secondNode, sortBy);
+               }

-               if(!ctx.isAllowedFullAccess()) {
-                       super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
-                       return;
+               /** Default comparison, after taking into account status */
+               protected int lastResortCompare(PeerNodeStatus firstNode, 
PeerNodeStatus secondNode) {
+                       return 
((DarknetPeerNodeStatus)firstNode).getName().compareToIgnoreCase(((DarknetPeerNodeStatus)secondNode).getName());
                }
+
+       }
+       
+       protected Comparator comparator(String sortBy, boolean reversed) {
+               return new DarknetComparator(sortBy, reversed);
+       }

-               final boolean advancedModeEnabled = 
node.isAdvancedModeEnabled();
-               final boolean fProxyJavascriptEnabled = 
node.isFProxyJavascriptEnabled();
-               
-               /* gather connection statistics */
-               PeerNodeStatus[] peerNodeStatuses = peers.getPeerNodeStatuses();
-               Arrays.sort(peerNodeStatuses, new Comparator() {
-                       public int compare(Object first, Object second) {
-                               int result = 0;
-                               boolean isSet = true;
-                               PeerNodeStatus firstNode = (PeerNodeStatus) 
first;
-                               PeerNodeStatus secondNode = (PeerNodeStatus) 
second;
-                               
-                               if(request.isParameterSet("sortBy")){
-                                       final String sortBy = 
request.getParam("sortBy"); 
+       protected boolean hasNameColumn() {
+               return true;
+       }
+       
+       protected void drawNameColumn(HTMLNode peerRow, PeerNodeStatus 
peerNodeStatus) {
+               // name column
+               peerRow.addChild("td", "class", "peer-name").addChild("a", 
"href", "/send_n2ntm/?peernode_hashcode=" + peerNodeStatus.hashCode(), 
((DarknetPeerNodeStatus)peerNodeStatus).getName());
+       }

-                                       if(sortBy.equals("name")){
-                                               result = 
firstNode.getName().compareToIgnoreCase(secondNode.getName());
-                                       }else if(sortBy.equals("address")){
-                                               result = 
firstNode.getPeerAddress().compareToIgnoreCase(secondNode.getPeerAddress());
-                                       }else if(sortBy.equals("location")){
-                                               result = 
(firstNode.getLocation() - secondNode.getLocation()) < 0 ? -1 : 1; // Shouldn't 
be equal anyway
-                                       }else if(sortBy.equals("version")){
-                                               result = 
Version.getArbitraryBuildNumber(firstNode.getVersion()) - 
Version.getArbitraryBuildNumber(secondNode.getVersion());
-                                       }else if(sortBy.equals("privnote")){
-                                               result = 
firstNode.getPrivateDarknetCommentNote().compareToIgnoreCase(secondNode.getPrivateDarknetCommentNote());
-                                       }else
-                                               isSet=false;
-                               }else
-                                       isSet=false;
-                               
-                               if(!isSet){
-                                       int statusDifference = 
firstNode.getStatusValue() - secondNode.getStatusValue();
-                                       if (statusDifference != 0) 
-                                               result = (statusDifference < 0 
? -1 : 1);
-                                       else
-                                               result = 
firstNode.getName().compareToIgnoreCase(secondNode.getName());
-                               }
+       protected boolean hasPrivateNoteColumn() {
+               return true;
+       }

-                               if(result == 0){
-                                       return 0;
-                               }else if(request.isParameterSet("reversed")){
-                                       isReversed = true;
-                                       return result > 0 ? -1 : 1;
-                               }else{
-                                       isReversed = false;
-                                       return result < 0 ? -1 : 1;
-                               }
-                       }
-               });
-               
-               int numberOfConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CONNECTED);
-               int numberOfRoutingBackedOff = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF);
-               int numberOfTooNew = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_NEW);
-               int numberOfTooOld = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_OLD);
-               int numberOfDisconnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISCONNECTED);
-               int numberOfNeverConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED);
-               int numberOfDisabled = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISABLED);
-               int numberOfBursting = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_BURSTING);
-               int numberOfListening = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTENING);
-               int numberOfListenOnly = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTEN_ONLY);
-               
-               int numberOfSimpleConnected = numberOfConnected + 
numberOfRoutingBackedOff;
-               int numberOfNotConnected = numberOfTooNew + numberOfTooOld + 
numberOfDisconnected + numberOfNeverConnected + numberOfDisabled + 
numberOfBursting + numberOfListening + numberOfListenOnly;
-               String titleCountString = null;
-               if(advancedModeEnabled) {
-                       titleCountString = "(" + numberOfConnected + '/' + 
numberOfRoutingBackedOff + '/' + numberOfTooNew + '/' + numberOfTooOld + '/' + 
numberOfNotConnected + ')';
+       protected void drawPrivateNoteColumn(HTMLNode peerRow, PeerNodeStatus 
peerNodeStatus, boolean fProxyJavascriptEnabled) {
+               // private darknet node comment note column
+               DarknetPeerNodeStatus status = (DarknetPeerNodeStatus) 
peerNodeStatus;
+               if(fProxyJavascriptEnabled) {
+                       peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "onBlur", "onChange", "value" }, new String[] { 
"text", "peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
"peerNoteBlur();", "peerNoteChange();", status.getPrivateDarknetCommentNote() 
});
                } else {
-                       titleCountString = (numberOfNotConnected + 
numberOfSimpleConnected)>0 ? String.valueOf(numberOfSimpleConnected) : "";
+                       peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "value" }, new String[] { "text", 
"peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
status.getPrivateDarknetCommentNote() });
                }
-               
-               HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(L10n.getString("DarknetConnectionsToadlet.fullTitle",
 new String[] { "counts", "name" }, new String[] { titleCountString, 
node.getMyName() } ), ctx);
-               HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
-               
-               // FIXME! We need some nice images
-               long now = System.currentTimeMillis();
-       
-               if(ctx.isAllowedFullAccess())
-                       contentNode.addChild(core.alerts.createSummary());
-               
-               if(peerNodeStatuses.length>0){
+       }

-                       /* node status values */
-                       long nodeUptimeSeconds = (now - node.startupTime) / 
1000;
-                       int bwlimitDelayTime = (int) 
stats.getBwlimitDelayTime();
-                       int nodeAveragePingTime = (int) 
stats.getNodeAveragePingTime();
-                       int networkSizeEstimateSession = 
stats.getNetworkSizeEstimate(-1);
-                       int networkSizeEstimateRecent = 0;
-                       if(nodeUptimeSeconds > (48*60*60)) {  // 48 hours
-                               networkSizeEstimateRecent = 
stats.getNetworkSizeEstimate(now - (48*60*60*1000));  // 48 hours
-                       }
-                       DecimalFormat fix4 = new DecimalFormat("0.0000");
-                       double routingMissDistance =  
stats.routingMissDistance.currentValue();
-                       DecimalFormat fix1 = new DecimalFormat("##0.0%");
-                       double backedOffPercent =  
stats.backedOffPercent.currentValue();
-                       String nodeUptimeString = 
TimeUtil.formatTime(nodeUptimeSeconds * 1000);  // *1000 to convert to 
milliseconds
+       protected SimpleFieldSet getNoderef() {
+               return node.exportDarknetPublicFieldSet();
+       }

-                       // BEGIN OVERVIEW TABLE
-                       HTMLNode overviewTable = contentNode.addChild("table", 
"class", "column");
-                       HTMLNode overviewTableRow = 
overviewTable.addChild("tr");
-                       HTMLNode nextTableCell = 
overviewTableRow.addChild("td", "class", "first");
+       protected PeerNodeStatus[] getPeerNodeStatuses() {
+               return node.peers.getDarknetPeerNodeStatuses();
+       }

-                       /* node status overview box */
-                       if(advancedModeEnabled) {
-                               HTMLNode overviewInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                               overviewInfobox.addChild("div", "class", 
"infobox-header", "Node status overview");
-                               HTMLNode overviewInfoboxContent = 
overviewInfobox.addChild("div", "class", "infobox-content");
-                               HTMLNode overviewList = 
overviewInfoboxContent.addChild("ul");
-                               overviewList.addChild("li", 
"bwlimitDelayTime:\u00a0" + bwlimitDelayTime + "ms");
-                               overviewList.addChild("li", 
"nodeAveragePingTime:\u00a0" + nodeAveragePingTime + "ms");
-                               overviewList.addChild("li", 
"networkSizeEstimateSession:\u00a0" + networkSizeEstimateSession + 
"\u00a0nodes");
-                               if(nodeUptimeSeconds > (48*60*60)) {  // 48 
hours
-                                       overviewList.addChild("li", 
"networkSizeEstimateRecent:\u00a0" + networkSizeEstimateRecent + "\u00a0nodes");
-                               }
-                               overviewList.addChild("li", "nodeUptime:\u00a0" 
+ nodeUptimeString);
-                               overviewList.addChild("li", 
"routingMissDistance:\u00a0" + fix4.format(routingMissDistance));
-                               overviewList.addChild("li", 
"backedOffPercent:\u00a0" + fix1.format(backedOffPercent));
-                               overviewList.addChild("li", 
"pInstantReject:\u00a0" + fix1.format(stats.pRejectIncomingInstantly()));
-                               nextTableCell = overviewTableRow.addChild("td");
-                       }
+       protected String getPageTitle(String titleCountString, String myName) {
+               return L10n.getString("DarknetConnectionsToadlet.fullTitle", 
new String[] { "counts", "name" }, new String[] { titleCountString, 
node.getMyName() } );
+       }
+       
+       protected boolean shouldDrawNoderefBox(boolean advancedModeEnabled) {
+               return true;
+       }

-                       // Activity box
-                       int numARKFetchers = node.getNumARKFetchers();
+       protected boolean showPeerActionsBox() {
+               return true;
+       }

-                       HTMLNode activityInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                       activityInfobox.addChild("div", "class", 
"infobox-header", l10n("activityTitle"));
-                       HTMLNode activityInfoboxContent = 
activityInfobox.addChild("div", "class", "infobox-content");
-                       HTMLNode activityList = 
StatisticsToadlet.drawActivity(activityInfoboxContent, node);
-                       if (advancedModeEnabled && activityList != null) {
-                               if (numARKFetchers > 0) {
-                                       activityList.addChild("li", 
"ARK\u00a0Fetch\u00a0Requests:\u00a0" + numARKFetchers);
-                               }
-                               StatisticsToadlet.drawBandwidth(activityList, 
node, nodeUptimeSeconds);
-                       }
-
-                       nextTableCell = advancedModeEnabled ? 
overviewTableRow.addChild("td") : overviewTableRow.addChild("td", "class", 
"last");
-
-                       // Peer statistics box
-                       HTMLNode peerStatsInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                       peerStatsInfobox.addChild("div", "class", 
"infobox-header", l10nStats("peerStatsTitle"));
-                       HTMLNode peerStatsContent = 
peerStatsInfobox.addChild("div", "class", "infobox-content");
-                       HTMLNode peerStatsList = 
peerStatsContent.addChild("ul");
-                       if (numberOfConnected > 0) {
-                               HTMLNode peerStatsConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsConnectedListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_connected", 
l10n("connected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("connectedShort"));
-                               peerStatsConnectedListItem.addChild("span", 
":\u00a0" + numberOfConnected);
-                       }
-                       if (numberOfRoutingBackedOff > 0) {
-                               HTMLNode peerStatsRoutingBackedOffListItem = 
peerStatsList.addChild("li").addChild("span");
-                               
peerStatsRoutingBackedOffListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_backed_off", (advancedModeEnabled ? 
l10n("backedOff") : l10n("busy")), "border-bottom: 1px dotted; cursor: help;" 
}, advancedModeEnabled ? l10n("backedOffShort") : l10n("busyShort"));
-                               
peerStatsRoutingBackedOffListItem.addChild("span", ":\u00a0" + 
numberOfRoutingBackedOff);
-                       }
-                       if (numberOfTooNew > 0) {
-                               HTMLNode peerStatsTooNewListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsTooNewListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_new", 
l10n("tooNew"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooNewShort"));
-                               peerStatsTooNewListItem.addChild("span", 
":\u00a0" + numberOfTooNew);
-                       }
-                       if (numberOfTooOld > 0) {
-                               HTMLNode peerStatsTooOldListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsTooOldListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_old", 
l10n("tooOld"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooOldShort"));
-                               peerStatsTooOldListItem.addChild("span", 
":\u00a0" + numberOfTooOld);
-                       }
-                       if (numberOfDisconnected > 0) {
-                               HTMLNode peerStatsDisconnectedListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsDisconnectedListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_disconnected", 
l10n("notConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("notConnectedShort"));
-                               peerStatsDisconnectedListItem.addChild("span", 
":\u00a0" + numberOfDisconnected);
-                       }
-                       if (numberOfNeverConnected > 0) {
-                               HTMLNode peerStatsNeverConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
-                               
peerStatsNeverConnectedListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_never_connected", 
l10n("neverConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("neverConnectedShort"));
-                               
peerStatsNeverConnectedListItem.addChild("span", ":\u00a0" + 
numberOfNeverConnected);
-                       }
-                       if (numberOfDisabled > 0) {
-                               HTMLNode peerStatsDisabledListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsDisabledListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_disabled", 
l10n("disabled"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("disabledShort"));
-                               peerStatsDisabledListItem.addChild("span", 
":\u00a0" + numberOfDisabled);
-                       }
-                       if (numberOfBursting > 0) {
-                               HTMLNode peerStatsBurstingListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsBurstingListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_bursting", 
l10n("bursting"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("burstingShort"));
-                               peerStatsBurstingListItem.addChild("span", 
":\u00a0" + numberOfBursting);
-                       }
-                       if (numberOfListening > 0) {
-                               HTMLNode peerStatsListeningListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsListeningListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_listening", 
l10n("listening"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listeningShort"));
-                               peerStatsListeningListItem.addChild("span", 
":\u00a0" + numberOfListening);
-                       }
-                       if (numberOfListenOnly > 0) {
-                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_listen_only", 
l10n("listenOnly"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listenOnlyShort"));
-                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfListenOnly);
-                       }
-
-                       // Peer routing backoff reason box
-                       if(advancedModeEnabled) {
-                               nextTableCell = overviewTableRow.addChild("td", 
"class", "last");
-                               HTMLNode backoffReasonInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                               backoffReasonInfobox.addChild("div", "class", 
"infobox-header", "Peer backoff reasons");
-                               HTMLNode backoffReasonContent = 
backoffReasonInfobox.addChild("div", "class", "infobox-content");
-                               String [] routingBackoffReasons = 
peers.getPeerNodeRoutingBackoffReasons();
-                               if(routingBackoffReasons.length == 0) {
-                                       backoffReasonContent.addChild("#", 
"Good, your node is not backed off from any peers!");
-                               } else {
-                                       HTMLNode reasonList = 
backoffReasonContent.addChild("ul");
-                                       for(int 
i=0;i<routingBackoffReasons.length;i++) {
-                                               int reasonCount = 
peers.getPeerNodeRoutingBackoffReasonSize(routingBackoffReasons[i]);
-                                               if(reasonCount > 0) {
-                                                       
reasonList.addChild("li", routingBackoffReasons[i] + '\u00a0' + reasonCount);
-                                               }
-                                       }
-                               }
-                       }
-                       // END OVERVIEW TABLE
-
-                       // BEGIN PEER TABLE
-                       if(fProxyJavascriptEnabled) {
-                               StringBuffer jsBuf = new StringBuffer();
-                               // FIXME: There's probably some icky Javascript 
in here (this is the first thing that worked for me); feel free to fix up to 
Javascript guru standards
-                               jsBuf.append( "  function peerNoteChange() {\n" 
);
-                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
-                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
-                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
-                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
-                               jsBuf.append( "      } else {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
-                               jsBuf.append( "      }\n" );
-                               jsBuf.append( "    }\n" );
-                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
-                               //jsBuf.append( "    document.getElementById( 
\"peersForm\" ).submit();\n" );
-                               jsBuf.append( "    document.getElementById( 
\"peersForm\" ).doAction.click();\n" );
-                               jsBuf.append( "  }\n" );
-                               jsBuf.append( "  function peerNoteBlur() {\n" );
-                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
-                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
-                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
-                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
-                               jsBuf.append( "      } else {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
-                               jsBuf.append( "      }\n" );
-                               jsBuf.append( "    }\n" );
-                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
-                               jsBuf.append( "  }\n" );
-                               contentNode.addChild("script", "type", 
"text/javascript").addChild("%", jsBuf.toString());
-                       }
-                       HTMLNode peerTableInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
-                       HTMLNode peerTableInfoboxHeader = 
peerTableInfobox.addChild("div", "class", "infobox-header");
-                       peerTableInfoboxHeader.addChild("#", l10n("myFriends"));
-                       if (advancedModeEnabled) {
-                               if (!path.endsWith("displaymessagetypes.html")) 
{
-                                       peerTableInfoboxHeader.addChild("#", " 
");
-                                       peerTableInfoboxHeader.addChild("a", 
"href", "displaymessagetypes.html", "(more detailed)");
-                               }
-                       }
-                       HTMLNode peerTableInfoboxContent = 
peerTableInfobox.addChild("div", "class", "infobox-content");
-
-                       if (peerNodeStatuses.length == 0) {
-                               
L10n.addL10nSubstitution(peerTableInfoboxContent, 
"DarknetConnectionsToadlet.noPeersWithHomepageLink", 
-                                               new String[] { "link", "/link" 
}, new String[] { "<a href=\"/\">", "</a>" });
-                       } else {
-                               HTMLNode peerForm = 
ctx.addFormChild(peerTableInfoboxContent, ".", "peersForm");
-                               HTMLNode peerTable = peerForm.addChild("table", 
"class", "darknet_connections");
-                               HTMLNode peerTableHeaderRow = 
peerTable.addChild("tr");
-                               peerTableHeaderRow.addChild("th");
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "status")).addChild("#", l10n("statusTitle"));
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "name")).addChild("span", new String[] { 
"title", "style" }, new String[] { l10n("nameClickToMessage"), "border-bottom: 
1px dotted; cursor: help;" }, l10n("nameTitle"));
-                               if (advancedModeEnabled) {
-                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"address")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("ipAddress"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("ipAddressTitle"));
-                               }
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "version")).addChild("#", l10n("versionTitle"));
-                               if (advancedModeEnabled) {
-                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"location")).addChild("#", "Location");
-                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Other node busy? Display: Percentage of time the 
node is overloaded, Current wait time remaining (0=not overloaded)/total/last 
overload reason", "border-bottom: 1px dotted; cursor: help;" }, "Backoff");
-
-                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Probability of the node rejecting a request due to 
overload or causing a timeout.", "border-bottom: 1px dotted; cursor: help;" }, 
"Overload Probability");
-                               }
-                               
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { l10n("idleTime"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("idleTimeTitle"));
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "privnote")).addChild("span", new String[] { 
"title", "style" }, new String[] { l10n("privateNote"), "border-bottom: 1px 
dotted; cursor: help;" }, l10n("privateNoteTitle"));
-
-                               if(advancedModeEnabled) {
-                                       peerTableHeaderRow.addChild("th", 
"%\u00a0Time Routable");
-                                       peerTableHeaderRow.addChild("th", 
"Total\u00a0Traffic\u00a0(in/out)");
-                                       peerTableHeaderRow.addChild("th", 
"Congestion\u00a0Control");
-                               }
-                               
-                               for (int peerIndex = 0, peerCount = 
peerNodeStatuses.length; peerIndex < peerCount; peerIndex++) {
-                                       PeerNodeStatus peerNodeStatus = 
peerNodeStatuses[peerIndex];
-                                       HTMLNode peerRow = 
peerTable.addChild("tr");
-
-                                       // check box column
-                                       peerRow.addChild("td", "class", 
"peer-marker").addChild("input", new String[] { "type", "name" }, new String[] 
{ "checkbox", "node_" + peerNodeStatus.hashCode() });
-
-                                       // status column
-                                       String statusString = 
peerNodeStatus.getStatusName();
-                                       if (!advancedModeEnabled && 
(peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF)) {
-                                               statusString = "BUSY";
-                                       }
-                                       peerRow.addChild("td", "class", 
"peer-status").addChild("span", "class", peerNodeStatus.getStatusCSSName(), 
statusString + (peerNodeStatus.isFetchingARK() ? "*" : ""));
-
-                                       // name column
-                                       peerRow.addChild("td", "class", 
"peer-name").addChild("a", "href", "/send_n2ntm/?peernode_hashcode=" + 
peerNodeStatus.hashCode(), peerNodeStatus.getName());
-
-                                       // address column
-                                       if (advancedModeEnabled) {
-                                               String pingTime = "";
-                                               if 
(peerNodeStatus.isConnected()) {
-                                                       pingTime = " (" + (int) 
peerNodeStatus.getAveragePingTime() + "ms)";
-                                               }
-                                               peerRow.addChild("td", "class", 
"peer-address").addChild("#", ((peerNodeStatus.getPeerAddress() != null) ? 
(peerNodeStatus.getPeerAddress() + ':' + peerNodeStatus.getPeerPort()) : 
(l10n("unknownAddress"))) + pingTime);
-                                       }
-
-                                       // version column
-                                       if (peerNodeStatus.getStatusValue() != 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED && 
(peerNodeStatus.isPublicInvalidVersion() || 
peerNodeStatus.isPublicReverseInvalidVersion())) {  // Don't draw attention to 
a version problem if NEVER CONNECTED
-                                               peerRow.addChild("td", "class", 
"peer-version").addChild("span", "class", "peer_version_problem", 
peerNodeStatus.getSimpleVersion());
-                                       } else {
-                                               peerRow.addChild("td", "class", 
"peer-version").addChild("#", peerNodeStatus.getSimpleVersion());
-                                       }
-
-                                       // location column
-                                       if (advancedModeEnabled) {
-                                               peerRow.addChild("td", "class", 
"peer-location", String.valueOf(peerNodeStatus.getLocation()));
-                                       }
-
-                                       if (advancedModeEnabled) {
-                                               // backoff column
-                                               HTMLNode backoffCell = 
peerRow.addChild("td", "class", "peer-backoff");
-                                               backoffCell.addChild("#", 
fix1.format(peerNodeStatus.getBackedOffPercent()));
-                                               int backoff = (int) 
(Math.max(peerNodeStatus.getRoutingBackedOffUntil() - now, 0));
-                                               // Don't list the backoff as 
zero before it's actually zero
-                                               if ((backoff > 0) && (backoff < 
1000)) {
-                                                       backoff = 1000;
-                                               }
-                                               backoffCell.addChild("#", ' ' + 
String.valueOf(backoff / 1000) + '/' + 
String.valueOf(peerNodeStatus.getRoutingBackoffLength() / 1000));
-                                               backoffCell.addChild("#", 
(peerNodeStatus.getLastBackoffReason() == null) ? "" : ('/' + 
(peerNodeStatus.getLastBackoffReason())));
-
-                                               // overload probability column
-                                               HTMLNode pRejectCell = 
peerRow.addChild("td", "class", "peer-backoff"); // FIXME
-                                               pRejectCell.addChild("#", 
fix1.format(peerNodeStatus.getPReject()));
-                                       }
-
-                                       // idle column
-                                       long idle = 
peerNodeStatus.getTimeLastRoutable();
-                                       if (peerNodeStatus.isRoutable()) {
-                                               idle = 
peerNodeStatus.getTimeLastConnectionCompleted();
-                                       } else if 
(peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED) {
-                                               idle = 
peerNodeStatus.getPeerAddedTime();
-                                       }
-                                       if(!peerNodeStatus.isConnected() && 
(now - idle) > (2 * 7 * 24 * 60 * 60 * (long) 1000)) { // 2 weeks
-                                               peerRow.addChild("td", "class", 
"peer-idle").addChild("span", "class", "peer_idle_old", idleToString(now, 
idle));
-                                       } else {
-                                               peerRow.addChild("td", "class", 
"peer-idle", idleToString(now, idle));
-                                       }
-
-                                       // private darknet node comment note 
column
-                                       if(fProxyJavascriptEnabled) {
-                                               peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "onBlur", "onChange", "value" }, new String[] { 
"text", "peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
"peerNoteBlur();", "peerNoteChange();", 
peerNodeStatus.getPrivateDarknetCommentNote() });
-                                       } else {
-                                               peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "value" }, new String[] { "text", 
"peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
peerNodeStatus.getPrivateDarknetCommentNote() });
-                                       }
-
-                                       if(advancedModeEnabled) {
-                                               // percent of time connected 
column
-                                               peerRow.addChild("td", "class", 
"peer-idle" /* FIXME */).addChild("#", 
fix1.format(peerNodeStatus.getPercentTimeRoutableConnection()));
-                                               // total traffic column
-                                               peerRow.addChild("td", "class", 
"peer-idle" /* FIXME */).addChild("#", 
SizeUtil.formatSize(peerNodeStatus.getTotalInputBytes())+" / 
"+SizeUtil.formatSize(peerNodeStatus.getTotalOutputBytes()));
-                                               // congestion control
-                                               PacketThrottle t = 
peerNodeStatus.getThrottle();
-                                               String val;
-                                               if(t == null)
-                                                       val = "none";
-                                               else
-                                                       val = (int)((1000.0 / 
t.getDelay()) * 1024.0)+"B/sec delay "+
-                                                               
t.getDelay()+"ms (RTT "+t.getRoundTripTime()+"ms window "+t.getWindowSize();
-                                               peerRow.addChild("td", "class", 
"peer-idle" /* FIXME */).addChild("#", val);
-                                       }
-                                       
-                                       if 
(path.endsWith("displaymessagetypes.html")) {
-                                               HTMLNode messageCountRow = 
peerTable.addChild("tr", "class", "message-status");
-                                               messageCountRow.addChild("td", 
"colspan", "2");
-                                               HTMLNode messageCountCell = 
messageCountRow.addChild("td", "colspan", String.valueOf(advancedModeEnabled ? 
9 : 5));  // = total table row width - 2 from above colspan
-                                               HTMLNode messageCountTable = 
messageCountCell.addChild("table", "class", "message-count");
-                                               HTMLNode countHeaderRow = 
messageCountTable.addChild("tr");
-                                               countHeaderRow.addChild("th", 
"Message");
-                                               countHeaderRow.addChild("th", 
"Incoming");
-                                               countHeaderRow.addChild("th", 
"Outgoing");
-                                               List messageNames = new 
ArrayList();
-                                               Map messageCounts = new 
HashMap();
-                                               for (Iterator incomingMessages 
= peerNodeStatus.getLocalMessagesReceived().keySet().iterator(); 
incomingMessages.hasNext(); ) {
-                                                       String messageName = 
(String) incomingMessages.next();
-                                                       
messageNames.add(messageName);
-                                                       Long messageCount = 
(Long) peerNodeStatus.getLocalMessagesReceived().get(messageName);
-                                                       
messageCounts.put(messageName, new Long[] { messageCount, new Long(0) });
-                                               }
-                                               for (Iterator outgoingMessages 
= peerNodeStatus.getLocalMessagesSent().keySet().iterator(); 
outgoingMessages.hasNext(); ) {
-                                                       String messageName = 
(String) outgoingMessages.next();
-                                                       if 
(!messageNames.contains(messageName)) {
-                                                               
messageNames.add(messageName);
-                                                       }
-                                                       Long messageCount = 
(Long) peerNodeStatus.getLocalMessagesSent().get(messageName);
-                                                       Long[] existingCounts = 
(Long[]) messageCounts.get(messageName);
-                                                       if (existingCounts == 
null) {
-                                                               
messageCounts.put(messageName, new Long[] { new Long(0), messageCount });
-                                                       } else {
-                                                               
existingCounts[1] = messageCount;
-                                                       }
-                                               }
-                                               Collections.sort(messageNames, 
new Comparator() {
-                                                       public int 
compare(Object first, Object second) {
-                                                               return 
((String) first).compareToIgnoreCase((String) second);
-                                                       }
-                                               });
-                                               for (Iterator 
messageNamesIterator = messageNames.iterator(); messageNamesIterator.hasNext(); 
) {
-                                                       String messageName = 
(String) messageNamesIterator.next();
-                                                       Long[] messageCount = 
(Long[]) messageCounts.get(messageName);
-                                                       HTMLNode messageRow = 
messageCountTable.addChild("tr");
-                                                       
messageRow.addChild("td", messageName);
-                                                       
messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[0]));
-                                                       
messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[1]));
-                                               }
-                                       }
-                               }
-
-                               HTMLNode actionSelect = 
peerForm.addChild("select", new String[] { "id", "name" }, new String[] { 
"action", "action" });
-                               actionSelect.addChild("option", "value", "", 
l10n("selectAction"));
-                               actionSelect.addChild("option", "value", 
"send_n2ntm", l10n("sendMessageToPeers"));
-                               actionSelect.addChild("option", "value", 
"update_notes", l10n("updateChangedPrivnotes"));
-                               if(advancedModeEnabled) {
-                                       actionSelect.addChild("option", 
"value", "enable", "Enable selected peers");
-                                       actionSelect.addChild("option", 
"value", "disable", "Disable selected peers");
-                                       actionSelect.addChild("option", 
"value", "set_burst_only", "On selected peers, set BurstOnly (only set this if 
you have a static IP and are not NATed and neither is the peer)");
-                                       actionSelect.addChild("option", 
"value", "clear_burst_only", "On selected peers, clear BurstOnly");
-                                       actionSelect.addChild("option", 
"value", "set_listen_only", "On selected peers, set ListenOnly (not 
recommended)");
-                                       actionSelect.addChild("option", 
"value", "clear_listen_only", "On selected peers, clear ListenOnly");
-                                       actionSelect.addChild("option", 
"value", "set_allow_local", "On selected peers, set allowLocalAddresses (useful 
if you are connecting to another node on the same LAN)");
-                                       actionSelect.addChild("option", 
"value", "clear_allow_local", "On selected peers, clear allowLocalAddresses");
-                                       actionSelect.addChild("option", 
"value", "set_ignore_source_port", "On selected peers, set ignoreSourcePort 
(try this if behind an evil corporate firewall; otherwise not recommended)");
-                                       actionSelect.addChild("option", 
"value", "clear_ignore_source_port", "On selected peers, clear 
ignoreSourcePort");
-                               }
-                               actionSelect.addChild("option", "value", "", 
l10n("separator"));
-                               actionSelect.addChild("option", "value", 
"remove", l10n("removePeers"));
-                               peerForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "doAction", l10n("go") });
-                               
-                       }
-                       // END PEER TABLE
+       protected void drawPeerActionSelectBox(HTMLNode peerForm, boolean 
advancedModeEnabled) {
+               HTMLNode actionSelect = peerForm.addChild("select", new 
String[] { "id", "name" }, new String[] { "action", "action" });
+               actionSelect.addChild("option", "value", "", 
l10n("selectAction"));
+               actionSelect.addChild("option", "value", "send_n2ntm", 
l10n("sendMessageToPeers"));
+               actionSelect.addChild("option", "value", "update_notes", 
l10n("updateChangedPrivnotes"));
+               if(advancedModeEnabled) {
+                       actionSelect.addChild("option", "value", "enable", 
"Enable selected peers");
+                       actionSelect.addChild("option", "value", "disable", 
"Disable selected peers");
+                       actionSelect.addChild("option", "value", 
"set_burst_only", "On selected peers, set BurstOnly (only set this if you have 
a static IP and are not NATed and neither is the peer)");
+                       actionSelect.addChild("option", "value", 
"clear_burst_only", "On selected peers, clear BurstOnly");
+                       actionSelect.addChild("option", "value", 
"set_listen_only", "On selected peers, set ListenOnly (not recommended)");
+                       actionSelect.addChild("option", "value", 
"clear_listen_only", "On selected peers, clear ListenOnly");
+                       actionSelect.addChild("option", "value", 
"set_allow_local", "On selected peers, set allowLocalAddresses (useful if you 
are connecting to another node on the same LAN)");
+                       actionSelect.addChild("option", "value", 
"clear_allow_local", "On selected peers, clear allowLocalAddresses");
+                       actionSelect.addChild("option", "value", 
"set_ignore_source_port", "On selected peers, set ignoreSourcePort (try this if 
behind an evil corporate firewall; otherwise not recommended)");
+                       actionSelect.addChild("option", "value", 
"clear_ignore_source_port", "On selected peers, clear ignoreSourcePort");
                }
+               actionSelect.addChild("option", "value", "", l10n("separator"));
+               actionSelect.addChild("option", "value", "remove", 
l10n("removePeers"));
+               peerForm.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "submit", "doAction", l10n("go") });
+       }

-               // BEGIN PEER ADDITION BOX
-               HTMLNode peerAdditionInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
-               peerAdditionInfobox.addChild("div", "class", "infobox-header", 
l10n("addPeerTitle"));
-               HTMLNode peerAdditionContent = 
peerAdditionInfobox.addChild("div", "class", "infobox-content");
-               HTMLNode peerAdditionForm = 
ctx.addFormChild(peerAdditionContent, ".", "addPeerForm");
-               peerAdditionForm.addChild("#", l10n("pasteReference"));
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("textarea", new String[] { "id", 
"name", "rows", "cols" }, new String[] { "reftext", "ref", "8", "74" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("#", (l10n("urlReference") + ' '));
-               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "refurl", "text", "url" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("#", (l10n("fileReference") + ' '));
-               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "reffile", "file", "reffile" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("#", (l10n("enterDescription") + ' 
'));
-               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name", "size", "maxlength", "value" }, new String[] { "peerPrivateNote", 
"text", "peerPrivateNote", "16", "250", "" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "add", l10n("add") });
-               
-               // our reference
-               HTMLNode referenceInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
-               HTMLNode headerReferenceInfobox = 
referenceInfobox.addChild("div", "class", "infobox-header");
-               // FIXME better way to deal with this sort of thing???
-               L10n.addL10nSubstitution(headerReferenceInfobox, 
"DarknetConnectionsToadlet.myReferenceHeader",
-                               new String[] { "linkref", "/linkref", 
"linktext", "/linktext" },
-                               new String[] { "<a href=\"myref.fref\">", 
"</a>", "<a href=\"myref.txt\">", "</a>" });
-               HTMLNode warningSentence = 
headerReferenceInfobox.addChild("pre");
-               L10n.addL10nSubstitution(warningSentence, 
"DarknetConnectionsToadlet.referenceCopyWarning",
-                               new String[] { "bold", "/bold" },
-                               new String[] { "<b>", "</b>" });
-               referenceInfobox.addChild("div", "class", 
"infobox-content").addChild("pre", "id", "reference", 
node.exportPublicFieldSet().toString());
-               
-               // our ports
-               HTMLNode portInfobox = contentNode.addChild("div", "class", 
"infobox infobox-normal");
-               portInfobox.addChild("div", "class", "infobox-header", 
l10n("nodePortsTitle"));
-               HTMLNode portInfoboxContent = portInfobox.addChild("div", 
"class", "infobox-content");
-               HTMLNode portInfoList = portInfoboxContent.addChild("ul");
-               SimpleFieldSet fproxyConfig = 
node.config.get("fproxy").exportFieldSet(true);
-               SimpleFieldSet fcpConfig = 
node.config.get("fcp").exportFieldSet(true);
-               SimpleFieldSet tmciConfig = 
node.config.get("console").exportFieldSet(true);
-               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fnpPort", new String[] { "port" }, 
new String[] { Integer.toString(node.getFNPPort()) }));
-               try {
-                       if(fproxyConfig.getBoolean("enabled", false)) {
-                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fproxyPort", new String[] { "port" }, 
new String[] { Integer.toString(fproxyConfig.getInt("port")) }));
-                       } else {
-                               portInfoList.addChild("li", 
l10n("fproxyDisabled"));
-                       }
-                       if(fcpConfig.getBoolean("enabled", false)) {
-                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fcpPort", new String[] { "port" }, 
new String[] { Integer.toString(fcpConfig.getInt("port")) }));
-                       } else {
-                               portInfoList.addChild("li", 
l10n("fcpDisabled"));
-                       }
-                       if(tmciConfig.getBoolean("enabled", false)) {
-                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.tmciPort", new String[] { "port" }, 
new String[] { Integer.toString(tmciConfig.getInt("port")) }));
-                       } else {
-                               portInfoList.addChild("li", 
l10n("tmciDisabled"));
-                       }
-               } catch (FSParseException e) {
-                       // ignore
-               }
-               
-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+       protected String getPeerListTitle() {
+               return l10n("myFriends");
        }
-       
-       private static String l10n(String string) {
-               return L10n.getString("DarknetConnectionsToadlet."+string);
+
+       protected boolean acceptRefPosts() {
+               return true;
        }
-       
-       private static String l10nStats(String string) {
-               return L10n.getString("StatisticsToadlet."+string);
-       }

-       private String sortString(boolean isReversed, String type) {
-               return (isReversed ? ("?sortBy="+type) : 
("?sortBy="+type+"&reversed"));
+       protected String defaultRedirectLocation() {
+               return "/friends/"; // FIXME
        }

-       public void handlePost(URI uri, final HTTPRequest request, 
ToadletContext ctx) throws ToadletContextClosedException, IOException, 
RedirectException {
-               boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
-               
-               if(!ctx.isAllowedFullAccess()) {
-                       super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
-                       return;
-               }
-               
-               String pass = request.getPartAsString("formPassword", 32);
-               if((pass == null) || !pass.equals(core.formPassword)) {
-                       MultiValueTable headers = new MultiValueTable();
-                       headers.put("Location", "/friends/");
-                       ctx.sendReplyHeaders(302, "Found", headers, null, 0);
-                       if(logMINOR) Logger.minor(this, "No password ("+pass+" 
should be "+core.formPassword+ ')');
-                       return;
-               }
-               
-               if (request.isPartSet("add")) {
-                       // add a new node
-                       String urltext = request.getPartAsString("url", 100);
-                       urltext = urltext.trim();
-                       String reftext = request.getPartAsString("ref", 2000);
-                       reftext = reftext.trim();
-                       if (reftext.length() < 200) {
-                               reftext = request.getPartAsString("reffile", 
2000);
-                               reftext = reftext.trim();
-                       }
-                       String privateComment = 
request.getPartAsString("peerPrivateNote", 250).trim();
-                       
-                       StringBuffer ref = new StringBuffer(1024);
-                       if (urltext.length() > 0) {
-                               // fetch reference from a URL
-                               BufferedReader in = null;
-                               try {
-                                       URL url = new URL(urltext);
-                                       URLConnection uc = url.openConnection();
-                                       in = new BufferedReader(new 
InputStreamReader(uc.getInputStream()));
-                                       String line;
-                                       while ( (line = in.readLine()) != null) 
{
-                                               ref.append( line ).append('\n');
-                                       }
-                               } catch (IOException e) {
-                                       this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), 
L10n.getString("DarknetConnectionsToadlet.cantFetchNoderefURL", new String[] { 
"url" }, new String[] { urltext }));
-                                       return;
-                               } finally {
-                                       if( in != null ){
-                                               in.close();
-                                       }
-                               }
-                       } else if (reftext.length() > 0) {
-                               // read from post data or file upload
-                               // this slightly scary looking regexp chops any 
extra characters off the beginning or ends of lines and removes extra line 
breaks
-                               ref = new 
StringBuffer(reftext.replaceAll(".*?((?:[\\w,\\.]+\\=[^\r\n]+?)|(?:End))[ 
\\t]*(?:\\r?\\n)+", "$1\n"));
-                       } else {
-                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), l10n("noRefOrURL"));
-                               request.freeParts();
-                               return;
-                       }
-                       ref = new StringBuffer(ref.toString().trim());
-
-                       request.freeParts();
-                       // we have a node reference in ref
-                       SimpleFieldSet fs;
-                       
-                       try {
-                               fs = new SimpleFieldSet(ref.toString(), false, 
true);
-                               if(!fs.getEndMarker().endsWith("End")) {
-                                       sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"),
-                                                       
L10n.getString("DarknetConnectionsToadlet.cantParseWrongEnding", new String[] { 
"end" }, new String[] { fs.getEndMarker() }));
-                                       return;
-                               }
-                               fs.setEndMarker("End"); // It's always End ; 
the regex above doesn't always grok this
-                       } catch (IOException e) {
-                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), 
-                                               
L10n.getString("DarknetConnectionsToadlet.cantParseTryAgain", new String[] { 
"error" }, new String[] { e.toString() }));
-                               return;
-                       } catch (Throwable t) {
-                               this.sendErrorPage(ctx, 
l10n("failedToAddNodeInternalErrorTitle"), 
l10n("failedToAddNodeInternalError"), t);
-                               return;
-                       }
-                       PeerNode pn;
-                       try {
-                               pn = new PeerNode(fs, node, node.peers, false);
-                               pn.setPrivateDarknetCommentNote(privateComment);
-                       } catch (FSParseException e1) {
-                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"),
-                                               
L10n.getString("DarknetConnectionsToadlet.cantParseTryAgain", new String[] { 
"error" }, new String[] { e1.toString() }));
-                               return;
-                       } catch (PeerParseException e1) {
-                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), 
-                                               
L10n.getString("DarknetConnectionsToadlet.cantParseTryAgain", new String[] { 
"error" }, new String[] { e1.toString() }));
-                               return;
-                       } catch (ReferenceSignatureVerificationException e1){
-                               HTMLNode node = new HTMLNode("div");
-                               node.addChild("#", 
L10n.getString("DarknetConnectionsToadlet.invalidSignature", new String[] { 
"error" }, new String[] { e1.toString() }));
-                               node.addChild("br");
-                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), node);
-                               return;
-                       } catch (Throwable t) {
-                               this.sendErrorPage(ctx, 
l10n("failedToAddNodeInternalErrorTitle"), 
l10n("failedToAddNodeInternalError"), t);
-                               return;
-                       }
-                       if(pn.getIdentityHash()==node.getIdentityHash()) {
-                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), l10n("triedToAddSelf"));
-                               return;
-                       }
-                       if(!this.node.addDarknetConnection(pn)) {
-                               this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"), l10n("alreadyInReferences"));
-                               return;
-                       }
-                       
-                       MultiValueTable headers = new MultiValueTable();
-                       headers.put("Location", "/friends/");
-                       ctx.sendReplyHeaders(302, "Found", headers, null, 0);
-                       return;
-               } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("send_n2ntm")) {
+       /**
+        * Implement other post actions than adding nodes.
+        * @throws IOException 
+        * @throws ToadletContextClosedException 
+        * @throws RedirectException 
+        */
+       protected void handleAltPost(URI uri, HTTPRequest request, 
ToadletContext ctx, boolean logMINOR) throws ToadletContextClosedException, 
IOException, RedirectException {
+               if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("send_n2ntm")) {
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("sendMessageTitle"), ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        HashMap peers = new HashMap();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
-                                       PeerNode pn = peerNodes[i];
+                                       DarknetPeerNode pn = peerNodes[i];
                                        String peer_name = pn.getName();
                                        String peer_hash = "" + pn.hashCode();
                                        if(!peers.containsKey(peer_hash)) {
@@ -738,12 +159,12 @@
                                }
                        }
                        N2NTMToadlet.createN2NTMSendForm( pageNode, 
contentNode, ctx, peers);
-                       this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                        return;
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("update_notes")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("peerPrivateNote_"+peerNodes[i].hashCode())) {
                                        
if(!request.getPartAsString("peerPrivateNote_"+peerNodes[i].hashCode(),250).equals(peerNodes[i].getPrivateDarknetCommentNote()))
 {
@@ -758,7 +179,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("enable")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].enablePeer();
@@ -771,7 +192,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("disable")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].disablePeer();
@@ -784,7 +205,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_burst_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setBurstOnly(true);
@@ -797,7 +218,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_burst_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setBurstOnly(false);
@@ -810,7 +231,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_ignore_source_port")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setIgnoreSourcePort(true);
@@ -823,7 +244,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_ignore_source_port")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setIgnoreSourcePort(false);
@@ -836,7 +257,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_listen_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setListenOnly(true);
@@ -849,7 +270,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_listen_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setListenOnly(false);
@@ -862,7 +283,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_allow_local")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        
peerNodes[i].setAllowLocalAddresses(true);
@@ -875,7 +296,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_allow_local")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        
peerNodes[i].setAllowLocalAddresses(false);
@@ -888,15 +309,15 @@
                } else if (request.isPartSet("remove") || 
(request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("remove"))) {                   
                        if(logMINOR) Logger.minor(this, "Remove node");

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {       
                                        
if((peerNodes[i].timeLastConnectionCompleted() < (System.currentTimeMillis() - 
1000*60*60*24*7) /* one week */) ||  (peerNodes[i].peerNodeStatus == 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED) || request.isPartSet("forceit")){
-                                               
this.node.removeDarknetConnection(peerNodes[i]);
+                                               
this.node.removePeerConnection(peerNodes[i]);
                                                if(logMINOR) Logger.minor(this, 
"Removed node: node_"+peerNodes[i].hashCode());
                                        }else{
                                                if(logMINOR) Logger.minor(this, 
"Refusing to remove : node_"+peerNodes[i].hashCode()+" (trying to prevent 
network churn) : let's display the warning message.");
-                                               HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("Please confirm"), ctx);
+                                               HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("confirmRemoveNodeTitle"), ctx);
                                                HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
                                                HTMLNode infobox = 
contentNode.addChild(ctx.getPageMaker().getInfobox("infobox-warning", 
l10n("confirmRemoveNodeWarningTitle")));
                                                HTMLNode content = 
ctx.getPageMaker().getContentNode(infobox);
@@ -908,7 +329,7 @@
                                                removeForm.addChild("input", 
new String[] { "type", "name", "value" }, new String[] { "submit", "remove", 
l10n("remove") });
                                                removeForm.addChild("input", 
new String[] { "type", "name", "value" }, new String[] { "hidden", "forceit", 
l10n("forceRemove") });

-                                               writeReply(ctx, 200, 
"text/html", "OK", pageNode.generate());
+                                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                                return; // FIXME: maybe it 
breaks multi-node removing
                                        }                               
                                } else {
@@ -919,16 +340,41 @@
                        headers.put("Location", "/friends/");
                        ctx.sendReplyHeaders(302, "Found", headers, null, 0);
                        return;
+               } else if (request.isPartSet("acceptTransfer")) {
+                       // FIXME this is ugly, should probably move both this 
code and the PeerNode code somewhere.
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
+                       for(int i = 0; i < peerNodes.length; i++) {
+                               if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
+                                       long id = 
Long.parseLong(request.getPartAsString("id", 32)); // FIXME handle 
NumberFormatException
+                                       peerNodes[i].acceptTransfer(id);
+                                       break;
+                               }
+                       }
+                       MultiValueTable headers = new MultiValueTable();
+                       headers.put("Location", "/friends/");
+                       ctx.sendReplyHeaders(302, "Found", headers, null, 0);
+                       return;
+               } else if (request.isPartSet("rejectTransfer")) {
+                       // FIXME this is ugly, should probably move both this 
code and the PeerNode code somewhere.
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
+                       for(int i = 0; i < peerNodes.length; i++) {
+                               if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
+                                       long id = 
Long.parseLong(request.getPartAsString("id", 32)); // FIXME handle 
NumberFormatException
+                                       peerNodes[i].rejectTransfer(id);
+                                       break;
+                               }
+                       }
+                       MultiValueTable headers = new MultiValueTable();
+                       headers.put("Location", "/friends/");
+                       ctx.sendReplyHeaders(302, "Found", headers, null, 0);
+                       return;
                } else {
                        this.handleGet(uri, new HTTPRequestImpl(uri), ctx);
                }
        }
-       
-       private String idleToString(long now, long idle) {
-               if (idle <= 0) {
-                       return " ";
-               }
-               long idleMilliseconds = now - idle;
-               return TimeUtil.formatTime(idleMilliseconds);
+
+       protected boolean isOpennet() {
+               return false;
        }
+
 }

Modified: branches/freenet-jfk/src/freenet/clients/http/FProxyToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/FProxyToadlet.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/FProxyToadlet.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -241,6 +241,7 @@
                if(checkForString(buf, "<rss")) return true;
                if(checkForString(buf, "<feed")) return true;
                if(checkForString(buf, "<rdf:RDF")) return true;
+               is.close();
                return false;
        }

@@ -318,13 +319,18 @@
                        }
                        return;
                }else if(ks.equals("/robots.txt") && ctx.doRobots()){
-                       this.writeReply(ctx, 200, "text/plain", "Ok", 
"User-agent: *\nDisallow: /");
+                       this.writeTextReply(ctx, 200, "Ok", "User-agent: 
*\nDisallow: /");
                        return;
                }else if(ks.startsWith("/darknet/")) { //TODO: remove when 
obsolete
                        MultiValueTable headers = new MultiValueTable();
                        headers.put("Location", "/friends/");
                        ctx.sendReplyHeaders(301, "Permanent Redirect", 
headers, null, 0);
                        return;
+               }else if(ks.startsWith("/opennet/")) { //TODO: remove when 
obsolete
+                       MultiValueTable headers = new MultiValueTable();
+                       headers.put("Location", "/strangers/");
+                       ctx.sendReplyHeaders(301, "Permanent Redirect", 
headers, null, 0);
+                       return;
                }

                if(ks.startsWith("/"))
@@ -349,7 +355,7 @@
                        errorContent.addChild("br");
                        addHomepageLink(errorContent);

-                       this.writeReply(ctx, 400, "text/html", 
l10n("invalidKeyTitle"), pageNode.generate());
+                       this.writeHTMLReply(ctx, 400, l10n("invalidKeyTitle"), 
pageNode.generate());
                        return;
                }
                String requestedMimeType = httprequest.getParam("type", null);
@@ -372,9 +378,7 @@
                        String msg = e.getMessage();
                        if(Logger.shouldLog(Logger.MINOR, this))
                                Logger.minor(this, "Failed to fetch "+uri+" : 
"+e);
-                       if(e.mode == FetchException.NOT_ENOUGH_PATH_COMPONENTS) 
{
-                               this.writePermanentRedirect(ctx, 
l10n("notEnoughMetaStrings"), '/' + key.toString() + '/' + override);
-                       } else if(e.newURI != null) {
+                       if(e.newURI != null) {
                                this.writePermanentRedirect(ctx, msg, '/' 
+e.newURI.toString() + override);
                        } else if(e.mode == FetchException.TOO_BIG) {
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("fileInformationTitle"), ctx);
@@ -412,7 +416,7 @@
                                }
                                optionList.addChild("li").addChild("a", new 
String[] { "href", "title" }, new String[] { "/", "FProxy home page" }, 
l10n("abortToHomepage"));

-                               writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        } else {
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(FetchException.getShortMessage(e.mode), ctx);
                                HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
@@ -460,8 +464,8 @@
                                option = optionList.addChild("li");
                                
option.addChild(ctx.getPageMaker().createBackLink(ctx, l10n("goBackToPrev")));

-                               this.writeReply(ctx, 500 /* close enough - 
FIXME probably should depend on status code */,
-                                               "text/html", 
FetchException.getShortMessage(e.mode), pageNode.generate());
+                               this.writeHTMLReply(ctx, 500 /* close enough - 
FIXME probably should depend on status code */,
+                                               "Internal Error", 
pageNode.generate());
                        }
                } catch (SocketException e) {
                        // Probably irrelevant
@@ -562,34 +566,35 @@
                return f;
        }

-       public static void maybeCreateFProxyEtc(NodeClientCore core, Node node, 
Config config, SubConfig fproxyConfig) throws IOException, 
InvalidConfigValueException {
+       public static SimpleToadletServer maybeCreateFProxyEtc(NodeClientCore 
core, Node node, Config config, SubConfig fproxyConfig) throws IOException, 
InvalidConfigValueException {

+               SimpleToadletServer server = null;
+               
                // FIXME how to change these on the fly when the interface 
language is changed?

                try {

-                       SimpleToadletServer server = new 
SimpleToadletServer(fproxyConfig, core);
+                       server = new SimpleToadletServer(fproxyConfig, core);

                        HighLevelSimpleClient client = 
core.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS, true);

-                       core.setToadletContainer(server);
                        random = new byte[32];
                        core.random.nextBytes(random);
                        FProxyToadlet fproxy = new FProxyToadlet(client, core);
                        core.setFProxy(fproxy);
-                       server.register(fproxy, "/", false, 
l10n("welcomeTitle"), l10n("welcome"), false);
+                       server.register(fproxy, "/", false, 
"FProxyToadlet.welcomeTitle", "FProxyToadlet.welcome", false, null);

-                       PproxyToadlet pproxy = new PproxyToadlet(client, 
node.pluginManager, core);
-                       server.register(pproxy, "/plugins/", true, 
l10n("pluginsTitle"), l10n("plugins"), true);
+                       PproxyToadlet pproxy = new PproxyToadlet(client, node, 
core);
+                       server.register(pproxy, "/plugins/", true, 
"FProxyToadlet.pluginsTitle", "FProxyToadlet.plugins", true, null);

-                       WelcomeToadlet welcometoadlet = new 
WelcomeToadlet(client, node);
+                       WelcomeToadlet welcometoadlet = new 
WelcomeToadlet(client, core, node);
                        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, 
l10n("configTitle"), l10n("config"), true);
+                       server.register(configtoadlet, "/config/", true, 
"FProxyToadlet.configTitle", "FProxyToadlet.config", true, null);

                        StaticToadlet statictoadlet = new StaticToadlet(client);
                        server.register(statictoadlet, "/static/", true, false);
@@ -599,16 +604,20 @@

                        DarknetConnectionsToadlet friendsToadlet = new 
DarknetConnectionsToadlet(node, core, client);
 //                     server.register(friendsToadlet, "/darknet/", true, 
l10n("friendsTitle"), l10n("friends"), true);
-                       server.register(friendsToadlet, "/friends/", true, 
l10n("friendsTitle"), l10n("friends"), true);
+                       server.register(friendsToadlet, "/friends/", true, 
"FProxyToadlet.friendsTitle", "FProxyToadlet.friends", true, null);

+                       OpennetConnectionsToadlet opennetToadlet = new 
OpennetConnectionsToadlet(node, core, client);
+//                     server.register(opennetToadlet, "/opennet/", true, 
l10n("opennetTitle"), l10n("opennet"), true, opennetToadlet);
+                       server.register(opennetToadlet, "/strangers/", true, 
"FProxyToadlet.opennetTitle", "FProxyToadlet.opennet", true, opennetToadlet);
+                       
                        N2NTMToadlet n2ntmToadlet = new N2NTMToadlet(node, 
core, client);
                        server.register(n2ntmToadlet, "/send_n2ntm/", true, 
true);

                        QueueToadlet queueToadlet = new QueueToadlet(core, 
core.getFCPServer(), client);
-                       server.register(queueToadlet, "/queue/", true, 
l10n("queueTitle"), l10n("queue"), false);
+                       server.register(queueToadlet, "/queue/", true, 
"FProxyToadlet.queueTitle", "FProxyToadlet.queue", false, null);

                        StatisticsToadlet statisticsToadlet = new 
StatisticsToadlet(node, core, client);
-                       server.register(statisticsToadlet, "/stats/", true, 
l10n("statsTitle"), l10n("stats"), true);
+                       server.register(statisticsToadlet, "/stats/", true, 
"FProxyToadlet.statsTitle", "FProxyToadlet.stats", true, null);

                        LocalFileInsertToadlet localFileInsertToadlet = new 
LocalFileInsertToadlet(core, client);
                        server.register(localFileInsertToadlet, "/files/", 
true, false);
@@ -620,14 +629,11 @@
                        server.register(browsertTestToadlet, "/test/", true, 
false);

                        TranslationToadlet translationToadlet = new 
TranslationToadlet(client, core);
-                       server.register(translationToadlet, 
TranslationToadlet.TOADLET_URL, true, l10n("translationTitle"), 
l10n("translation"), true);
+                       server.register(translationToadlet, 
TranslationToadlet.TOADLET_URL, true, true);

-                       FirstTimeWizardToadlet firstTimeWizardToadlet = new 
FirstTimeWizardToadlet(client, node);
+                       FirstTimeWizardToadlet firstTimeWizardToadlet = new 
FirstTimeWizardToadlet(client, node, core);
                        server.register(firstTimeWizardToadlet, 
FirstTimeWizardToadlet.TOADLET_URL, true, false);

-                       // Now start the server.
-                       server.start();
-                       
                }catch (BindException e){
                        Logger.error(core,"Failed to start FProxy port already 
bound: isn't Freenet already running ?");
                        System.err.println("Failed to start FProxy port already 
bound: isn't Freenet already running ?");
@@ -637,6 +643,8 @@
                }

                fproxyConfig.finishedInitialization();
+               
+               return server; // caller must start server
        }

        /**

Modified: 
branches/freenet-jfk/src/freenet/clients/http/FirstTimeWizardToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/FirstTimeWizardToadlet.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/FirstTimeWizardToadlet.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -4,6 +4,7 @@
 package freenet.clients.http;

 import java.io.IOException;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.URI;
@@ -16,6 +17,7 @@
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
 import freenet.support.Base64;
+import freenet.support.Fields;
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.api.HTTPRequest;
@@ -32,9 +34,9 @@
        private final Config config;


-       FirstTimeWizardToadlet(HighLevelSimpleClient client, Node node) {
+       FirstTimeWizardToadlet(HighLevelSimpleClient client, Node node, 
NodeClientCore core) {
                super(client);
-               this.core = node.clientCore;
+               this.core = core;
                this.config = node.config;
        }

@@ -52,6 +54,26 @@
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step1Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

+                       HTMLNode opennetInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
+                       HTMLNode opennetInfoboxHeader = 
opennetInfobox.addChild("div", "class", "infobox-header");
+                       HTMLNode opennetInfoboxContent = 
opennetInfobox.addChild("div", "class", "infobox-content");
+                       
+                       opennetInfoboxHeader.addChild("#", 
l10n("connectToStrangers"));
+                       opennetInfoboxContent.addChild("#", 
l10n("connectToStrangersLong"));
+                       HTMLNode opennetForm = 
ctx.addFormChild(opennetInfoboxContent, ".", "opennetForm");
+                       
+                       HTMLNode opennetDiv = opennetForm.addChild("div", 
"class", "opennetDiv");
+                       opennetDiv.addChild("#", l10n("enableOpennet"));
+                       opennetDiv.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "radio", "enableOpennet", "true" }, 
L10n.getString("Toadlet.yes"));
+                       opennetDiv.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "radio", "enableOpennet", "false" }, 
L10n.getString("Toadlet.no"));
+                       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) {
+                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step2Title"), false, ctx);
+                       HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
+                       
                        HTMLNode nnameInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
                        HTMLNode nnameInfoboxHeader = 
nnameInfobox.addChild("div", "class", "infobox-header");
                        HTMLNode nnameInfoboxContent = 
nnameInfobox.addChild("div", "class", "infobox-content");
@@ -61,12 +83,12 @@
                        HTMLNode nnameForm = 
ctx.addFormChild(nnameInfoboxContent, ".", "nnameForm");
                        nnameForm.addChild("input", "name", "nname");

-                       nnameForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "nnameF", 
L10n.getString("Toadlet.clickHere")});
+                       nnameForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "nnameF", 
L10n.getString("FirstTimeWizardToadlet.continue")});
                        nnameForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               } else if(currentStep == 2) {
-                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step2Title"), false, ctx);
+               } else if(currentStep == 3) {
+                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step3Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

                        HTMLNode bandwidthInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
@@ -78,20 +100,20 @@
                        HTMLNode bandwidthForm = 
ctx.addFormChild(bandwidthInfoboxContent, ".", "bwForm");
                        HTMLNode result = bandwidthForm.addChild("select", 
"name", "bw");

-                       result.addChild("option", new String[] { "value", 
"selected" }, new String[] { "15", "selected" }, "I don't know");
+                       result.addChild("option", new String[] { "value", 
"selected" }, new String[] { "15K", "selected" }, "I don't know");
                        result.addChild("option", "value", "8K", "lower speed");
-                       result.addChild("option", "value", "12K", "1024+/128 
kbps");
+                       result.addChild("option", "value", "12K", "512+/128 
kbps");
                        result.addChild("option", "value", "24K", "1024+/256 
kbps");
                        result.addChild("option", "value", "48K", "1024+/512 
kbps");
                        result.addChild("option", "value", "96K", "1024+/1024 
kbps");
                        result.addChild("option", "value", "1000K", "higher 
speed");

-                       bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "bwF", 
L10n.getString("Toadlet.clickHere")});
+                       bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "bwF", 
L10n.getString("FirstTimeWizardToadlet.continue")});
                        bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               } else if(currentStep == 3) {
-                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step3Title"), false, ctx);
+               } else if(currentStep == 4) {
+                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step4Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

                        HTMLNode bandwidthInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
@@ -113,12 +135,12 @@
                        result.addChild("option", "value", "50G", "50GiB");
                        result.addChild("option", "value", "100G", "100GiB");

-                       bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "dsF", 
L10n.getString("Toadlet.clickHere")});
+                       bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "dsF", 
L10n.getString("FirstTimeWizardToadlet.continue")});
                        bandwidthForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               } else if(currentStep == 4) {
-                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step4Title"), false, ctx);
+               } else if(currentStep == 5) {
+                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step5Title"), false, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

                        HTMLNode networkInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
@@ -156,12 +178,12 @@
                        }
                        ctx.addFormChild(networkInfoboxContent, ".", 
"networkForm").addChild(networkForm);

-                       networkForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "networkF", 
L10n.getString("Toadlet.clickHere")});
+                       networkForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "networkF", 
L10n.getString("FirstTimeWizardToadlet.continue")});
                        networkForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
-               }else if(currentStep == 5) {
-                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step5Title"), true, ctx);
+               }else if(currentStep == 6) {
+                       HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("step6Title"), true, ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);

                        HTMLNode congratzInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
@@ -171,7 +193,7 @@
                        congratzInfoboxHeader.addChild("#", l10n("congratz"));
                        congratzInfoboxContent.addChild("#", 
l10n("congratzLong"));

-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
                }

@@ -185,12 +207,12 @@

                HTMLNode firstParagraph = welcomeInfoboxContent.addChild("p");
                firstParagraph.addChild("#", l10n("welcomeInfoboxContent1") + ' 
');
-               firstParagraph.addChild("a", "href", "?step=1").addChild("#", 
L10n.getString("Toadlet.clickHere"));
+               firstParagraph.addChild("a", "href", "?step=1").addChild("#", 
L10n.getString("FirstTimeWizardToadlet.continue"));

                HTMLNode secondParagraph = welcomeInfoboxContent.addChild("p");
                secondParagraph.addChild("a", "href", "/").addChild("#", 
l10n("skipWizard"));

-               this.writeReply(ctx, 200, "text/html; charset=utf-8", "OK", 
pageNode.generate());
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

        public void handlePost(URI uri, HTTPRequest request, ToadletContext 
ctx) throws ToadletContextClosedException, IOException {
@@ -208,7 +230,27 @@
                        return;
                }

-               if(request.isPartSet("nnameF")) {
+               
+               if(request.isPartSet("enableOpennet")) {
+                       String isOpennetEnabled = 
request.getPartAsString("enableOpennet", 255);
+                       boolean enable;
+                       try {
+                               enable = Fields.stringToBool(isOpennetEnabled);
+                       } catch (NumberFormatException e) {
+                               Logger.error(this, "Invalid opennetEnabled: 
"+isOpennetEnabled);
+                               super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step=1");
+                               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");
+                               return;
+                       }
+                       super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step=2");
+                       return;
+               } else if(request.isPartSet("nnameF")) {
                        String selectedNName = request.getPartAsString("nname", 
255);

                        try {
@@ -217,7 +259,7 @@
                        } catch (InvalidConfigValueException e) {
                                Logger.error(this, "Should not happen, please 
report!" + e);
                        }
-                       super.writeTemporaryRedirect(ctx, "step2", 
TOADLET_URL+"?step=2");
+                       super.writeTemporaryRedirect(ctx, "step3", 
TOADLET_URL+"?step=3");
                        return;
                } else if(request.isPartSet("bwF")) {
                        String selectedUploadSpeed 
=request.getPartAsString("bw", 6);
@@ -228,7 +270,7 @@
                        } catch (InvalidConfigValueException e) {
                                Logger.error(this, "Should not happen, please 
report!" + e);
                        }
-                       super.writeTemporaryRedirect(ctx, "step3", 
TOADLET_URL+"?step=3");
+                       super.writeTemporaryRedirect(ctx, "step4", 
TOADLET_URL+"?step=4");
                        return;
                } else if(request.isPartSet("dsF")) {
                        String selectedStoreSize =request.getPartAsString("ds", 
6);
@@ -239,13 +281,14 @@
                        } catch (InvalidConfigValueException e) {
                                Logger.error(this, "Should not happen, please 
report!" + e);
                        }
-                       super.writeTemporaryRedirect(ctx, "step3", 
TOADLET_URL+"?step=4");
+                       super.writeTemporaryRedirect(ctx, "step5", 
TOADLET_URL+"?step=5");
                        return;
                } else if(request.isPartSet("networkF")) {
                        StringBuffer sb = new StringBuffer();
                        // prevent the user from locking himself out
-                       sb.append("127.0.0.1,0:0:0:0:0:0:0:1");
+                       sb.append("127.0.0.1");
                        short ifCount = 0;
+                       boolean hasIPV6 = false;

                        Enumeration interfaces = 
NetworkInterface.getNetworkInterfaces();
                        while(interfaces.hasMoreElements()) {
@@ -255,6 +298,9 @@
                                Enumeration ipAddresses = 
currentIF.getInetAddresses();
                                while(ipAddresses.hasMoreElements()) {
                                        InetAddress currentInetAddress = 
(InetAddress) ipAddresses.nextElement();
+                                       if(currentInetAddress instanceof 
Inet6Address)
+                                               hasIPV6 = true;
+                                       
                                        if((currentInetAddress == null) || 
(currentInetAddress.isLoopbackAddress())) continue;

                                        String isIFSelected 
=request.getPartAsString(Base64.encode(currentInetAddress.getAddress()), 255);
@@ -266,6 +312,9 @@
                                }
                        }

+                       if(hasIPV6)
+                               sb.append(",0:0:0:0:0:0:0:1");
+                       
                        if(ifCount > 0) {
                                try {
                                        // Java doesn't provide a way to get 
the netmask : workaround and bind only to trusted if
@@ -284,7 +333,7 @@
                                }
                        }

-                       super.writeTemporaryRedirect(ctx, "step4", 
TOADLET_URL+"?step=5");
+                       super.writeTemporaryRedirect(ctx, "step6", 
TOADLET_URL+"?step=6");
                        return;
                }


Modified: branches/freenet-jfk/src/freenet/clients/http/HTTPRequestImpl.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/HTTPRequestImpl.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/HTTPRequestImpl.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -375,10 +375,11 @@
         * params, whereas if it is multipart/form-data it will be separated 
into buckets.
         */
        private void parseMultiPartData() throws IOException {
+               boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(data == null) return;
                String ctype = (String) this.headers.get("content-type");
                if (ctype == null) return;
-               if(Logger.shouldLog(Logger.MINOR, this))
+               if(logMINOR)
                        Logger.minor(this, "Uploaded content-type: "+ctype);
                String[] ctypeparts = ctype.split(";");
                
if(ctypeparts[0].equalsIgnoreCase("application/x-www-form-urlencoded")) {
@@ -408,6 +409,9 @@

                boundary = "--"+boundary;

+               if(logMINOR)
+                       Logger.minor(this, "Boundary is: "+boundary);
+               
                InputStream is = this.data.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(is, 32768);
                LineReadingInputStream lis = new LineReadingInputStream(bis);
@@ -487,8 +491,12 @@
                                        // offset bytes matched, but no more
                                        // write the bytes that matched, then 
the non-matching byte
                                        bbos.write(bbound, 0, offset);
-                                       bbos.write((int) b & 0xff);
                                        offset = 0;
+                                       if(b == bbound[0]) {
+                                               offset = 1;
+                                       } else {
+                                               bbos.write((int) b & 0xff);
+                                       }
                                } else {
                                        bbos.write((int) b & 0xff);
                                }
@@ -497,7 +505,7 @@
                        bbos.close();

                        parts.put(name, filedata);
-                       if(Logger.shouldLog(Logger.MINOR, this))
+                       if(logMINOR)
                                Logger.minor(this, "Name = "+name+" length = 
"+filedata.size()+" filename = "+filename);
                        if (filename != null) {
                                uploadedFiles.put(name, new 
HTTPUploadedFileImpl(filename, contentType, filedata));

Copied: branches/freenet-jfk/src/freenet/clients/http/LinkEnabledCallback.java 
(from rev 14796, 
trunk/freenet/src/freenet/clients/http/LinkEnabledCallback.java)
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/LinkEnabledCallback.java      
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/clients/http/LinkEnabledCallback.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,8 @@
+package freenet.clients.http;
+
+public interface LinkEnabledCallback {
+
+       /** Whether to show the link? */
+       boolean isEnabled();
+
+}

Modified: 
branches/freenet-jfk/src/freenet/clients/http/LocalFileInsertToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/LocalFileInsertToadlet.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/LocalFileInsertToadlet.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -140,7 +140,7 @@
                        ulNode.addChild("li", l10n("checkPathReadable"));
                }

-               writeReply(toadletContext, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+               writeHTMLReply(toadletContext, 200, "OK", pageNode.generate());
        }

        private String l10n(String key, String pattern, String value) {

Modified: branches/freenet-jfk/src/freenet/clients/http/N2NTMToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/N2NTMToadlet.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/N2NTMToadlet.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -10,36 +10,25 @@
 import java.util.Iterator;

 import freenet.client.HighLevelSimpleClient;
-import freenet.io.comm.DMT;
-import freenet.io.comm.Message;
-import freenet.io.comm.NotConnectedException;
-import freenet.io.comm.UdpSocketManager;
 import freenet.l10n.L10n;
+import freenet.node.DarknetPeerNode;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
 import freenet.node.PeerManager;
-import freenet.node.PeerNode;
-import freenet.support.Base64;
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.MultiValueTable;
-import freenet.support.SimpleFieldSet;
 import freenet.support.api.HTTPRequest;

 public class N2NTMToadlet extends Toadlet {
-
        private Node node;
-
        private NodeClientCore core;

-       private UdpSocketManager usm;
-
        protected N2NTMToadlet(Node n, NodeClientCore core,
                        HighLevelSimpleClient client) {
                super(client);
                this.node = n;
                this.core = core;
-               this.usm = n.getUSM();
        }

        public String supportedMethods() {
@@ -72,7 +61,7 @@
                                // ignore here, handle below
                        }
                        if (input_hashcode != -1) {
-                               PeerNode[] peerNodes = 
node.getDarknetConnections();
+                               DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                                for (int i = 0; i < peerNodes.length; i++) {
                                        int peer_hashcode = 
peerNodes[i].hashCode();
                                        if (peer_hashcode == input_hashcode) {
@@ -85,14 +74,14 @@
                                
contentNode.addChild(createPeerInfobox("infobox-error",
                                                l10n("peerNotFoundTitle"), 
l10n("peerNotFoundWithHash",
                                                                "hash", 
input_hashcode_string)));
-                               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode
+                               this.writeHTMLReply(ctx, 200, "OK", pageNode
                                                .generate());
                                return;
                        }
                        HashMap peers = new HashMap();
                        peers.put(input_hashcode_string, peernode_name);
                        createN2NTMSendForm(pageNode, contentNode, ctx, peers);
-                       this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
                }
                MultiValueTable headers = new MultiValueTable();
@@ -146,7 +135,7 @@
                        String message = request.getPartAsString("message", 5 * 
1024);
                        message = message.trim();
                        if (message.length() > 1024) {
-                               this.writeReply(ctx, 400, "text/plain", 
l10n("tooLongTitle"),
+                               this.writeTextReply(ctx, 400, "Bad request",
                                                l10n("tooLong"));
                                return;
                        }
@@ -155,15 +144,15 @@
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
                        HTMLNode peerTableInfobox = contentNode.addChild("div", 
"class",
                                        "infobox infobox-normal");
-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        String fnam = request.getPartAsString("filename", 1024);
                        File filename = null;
-                       if(fnam != null) {
+                       if(fnam != null && fnam.length() > 0) {
                                filename = new File(fnam);
                                if(!(filename.exists() && filename.canRead())) {
                                        peerTableInfobox.addChild("#", 
l10n("noSuchFileOrCannotRead"));
                                        
Toadlet.addHomepageLink(peerTableInfobox);
-                                       this.writeReply(ctx, 400, "text/html", 
"OK", pageNode.generate());
+                                       this.writeHTMLReply(ctx, 400, "OK", 
pageNode.generate());
                                        return;
                                }
                        }
@@ -174,7 +163,7 @@
                        peerTableHeaderRow.addChild("th", l10n("sendStatus"));
                        for (int i = 0; i < peerNodes.length; i++) {
                                if (request.isPartSet("node_" + 
peerNodes[i].hashCode())) {
-                                       PeerNode pn = peerNodes[i];
+                                       DarknetPeerNode pn = peerNodes[i];

                                        int status;

@@ -184,7 +173,7 @@
                                                } catch (IOException e) {
                                                        
peerTableInfobox.addChild("#", l10n("noSuchFileOrCannotRead"));
                                                        
Toadlet.addHomepageLink(peerTableInfobox);
-                                                       this.writeReply(ctx, 
400, "text/html", "OK", pageNode.generate());
+                                                       
this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                                                        return;
                                                }
                                        } else {
@@ -234,7 +223,7 @@
                        list.addChild("li").addChild("a", new String[] { 
"href", "title" },
                                        new String[] { "/friends/", 
l10n("returnToFriends") },
                                        l10n("friends"));
-                       this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
                }
                MultiValueTable headers = new MultiValueTable();

Copied: 
branches/freenet-jfk/src/freenet/clients/http/OpennetConnectionsToadlet.java 
(from rev 14796, 
trunk/freenet/src/freenet/clients/http/OpennetConnectionsToadlet.java)
===================================================================
--- 
branches/freenet-jfk/src/freenet/clients/http/OpennetConnectionsToadlet.java    
                            (rev 0)
+++ 
branches/freenet-jfk/src/freenet/clients/http/OpennetConnectionsToadlet.java    
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,83 @@
+package freenet.clients.http;
+
+import freenet.client.HighLevelSimpleClient;
+import freenet.l10n.L10n;
+import freenet.node.Node;
+import freenet.node.NodeClientCore;
+import freenet.node.PeerNodeStatus;
+import freenet.support.HTMLNode;
+import freenet.support.SimpleFieldSet;
+
+public class OpennetConnectionsToadlet extends ConnectionsToadlet implements 
LinkEnabledCallback {
+
+       protected OpennetConnectionsToadlet(Node n, NodeClientCore core, 
HighLevelSimpleClient client) {
+               super(n, core, client);
+       }
+
+       protected void drawNameColumn(HTMLNode peerRow,
+                       PeerNodeStatus peerNodeStatus) {
+               // Do nothing - no names on opennet
+       }
+
+       protected void drawPrivateNoteColumn(HTMLNode peerRow,
+                       PeerNodeStatus peerNodeStatus, boolean 
fProxyJavascriptEnabled) {
+               // Do nothing - no private notes either (no such thing as 
negative trust in cyberspace)
+       }
+
+       protected boolean hasNameColumn() {
+               return false;
+       }
+
+       protected boolean hasPrivateNoteColumn() {
+               return false;
+       }
+
+       protected SimpleFieldSet getNoderef() {
+               return node.exportOpennetPublicFieldSet();
+       }
+
+       protected PeerNodeStatus[] getPeerNodeStatuses() {
+               return node.peers.getOpennetPeerNodeStatuses();
+       }
+
+       public boolean isEnabled() {
+               return node.isOpennetEnabled();
+       }
+
+       protected String getPageTitle(String titleCountString, String myName) {
+               return L10n.getString("OpennetConnectionsToadlet.fullTitle", 
new String[] { "counts", "name" }, new String[] { titleCountString, 
node.getMyName() } );
+       }
+
+       protected boolean shouldDrawNoderefBox(boolean advancedModeEnabled) {
+               // Developers may want to see the noderef.
+               // Users as well until the announcement protocol is implemented
+               return true;
+       }
+
+       protected boolean showPeerActionsBox() {
+               // No per-peer actions supported on opennet - there's no point, 
they'll only reconnect,
+               // possibly as a different identity. And we don't want to be 
able to send N2NTM spam either.
+               return false;
+       }
+
+       protected void drawPeerActionSelectBox(HTMLNode peerForm, boolean 
advancedModeEnabled) {
+               // Do nothing, see showPeerActionsBox().
+       }
+
+       protected String getPeerListTitle() {
+               return 
L10n.getString("OpennetConnectionsToadlet.peersListTitle");
+       }
+
+       protected boolean acceptRefPosts() {
+               return true;
+       }
+
+       protected String defaultRedirectLocation() {
+               return "/opennet/";
+       }
+
+       protected boolean isOpennet() {
+               return true;
+       }
+
+}

Modified: branches/freenet-jfk/src/freenet/clients/http/PageMaker.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/PageMaker.java        
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/PageMaker.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -32,6 +32,7 @@
        private final Map navigationLinkTitles = new HashMap();
        private final Map navigationLinks = new HashMap();
        private final Map contentNodes = new HashMap();
+       private final Map /* <String, LinkEnabledCallback> */ 
navigationLinkCallbacks = new HashMap();

        /** Cache for themes read from the JAR file. */
        private List jarThemesCache = null;
@@ -56,12 +57,14 @@
                }
        }

-       public void addNavigationLink(String path, String name, String title, 
boolean fullOnly) {
+       public void addNavigationLink(String path, String name, String title, 
boolean fullOnly, LinkEnabledCallback cb) {
                navigationLinkTexts.add(name);
                if(!fullOnly)
                        navigationLinkTextsNonFull.add(name);
                navigationLinkTitles.put(name, title);
                navigationLinks.put(name, path);
+               if(cb != null)
+                       navigationLinkCallbacks.put(name, cb);
        }

        public void removeNavigationLink(String name) {
@@ -109,10 +112,12 @@
                        HTMLNode navbarUl = navbarDiv.addChild("ul", "id", 
"navlist");
                        for (Iterator navigationLinkIterator = fullAccess ? 
navigationLinkTexts.iterator() : navigationLinkTextsNonFull.iterator(); 
navigationLinkIterator.hasNext();) {
                                String navigationLink = (String) 
navigationLinkIterator.next();
+                               LinkEnabledCallback cb = (LinkEnabledCallback) 
navigationLinkCallbacks.get(navigationLink);
+                               if(cb != null && !cb.isEnabled()) continue;
                                String navigationTitle = (String) 
navigationLinkTitles.get(navigationLink);
                                String navigationPath = (String) 
navigationLinks.get(navigationLink);
                                HTMLNode listItem = navbarUl.addChild("li");
-                               listItem.addChild("a", new String[] { "href", 
"title" }, new String[] { navigationPath, navigationTitle }, navigationLink);
+                               listItem.addChild("a", new String[] { "href", 
"title" }, new String[] { navigationPath, L10n.getString(navigationTitle) }, 
L10n.getString(navigationLink));
                        }
                }
                HTMLNode contentDiv = pageDiv.addChild("div", "id", "content");

Modified: branches/freenet-jfk/src/freenet/clients/http/PluginToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/PluginToadlet.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/PluginToadlet.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -73,11 +73,11 @@
                                if (plugin instanceof HttpPlugin) {
                                        ((HttpPlugin) 
plugin).handleGet(httpRequest, ctx);
                                } else {
-                                       writeReply(ctx, 220, "text/html; 
charset=utf-8", "OK", createBox(ctx, l10n("noWebInterfaceTitle"), 
l10n("noWebInterface")).toString());
+                                       writeHTMLReply(ctx, 220, "OK", 
createBox(ctx, l10n("noWebInterfaceTitle"), l10n("noWebInterface")).toString());
                                }
                                return;
                        }
-                       writeReply(ctx, 220, "text/html; charset=utf-8", "OK", 
createBox(ctx, l10n("pluginNotFoundTitle"), l10n("pluginNotFound")).toString());
+                       writeHTMLReply(ctx, 220, "OK", createBox(ctx, 
l10n("pluginNotFoundTitle"), l10n("pluginNotFound")).toString());
                        return;
                }

@@ -96,10 +96,10 @@
                if ("list".equals(action)) {
                        replyBuffer.append(listPlugins(ctx));
                } else {
-                       writeReply(ctx, 220, "text/html; charset=utf-8", "OK", 
createBox(ctx, l10n("unsupportedMethodTitle"), 
l10n("unsupportedMethod")).toString());
+                       writeHTMLReply(ctx, 220, "OK", createBox(ctx, 
l10n("unsupportedMethodTitle"), l10n("unsupportedMethod")).toString());
                        return;
                }
-               writeReply(ctx, 220, "text/html; charset=utf-8", "OK", 
replyBuffer.toString());
+               writeHTMLReply(ctx, 220, "OK", replyBuffer.toString());
        }

        private String l10n(String key) {
@@ -119,11 +119,11 @@
                                if (plugin instanceof HttpPlugin) {
                                        ((HttpPlugin) 
plugin).handlePost(httpRequest, ctx);
                                } else {
-                                       writeReply(ctx, 220, "text/html; 
charset=utf-8", "OK", createBox(ctx, l10n("noWebInterfaceTitle"), 
l10n("noWebInterface")).toString());
+                                       writeHTMLReply(ctx, 220, "OK", 
createBox(ctx, l10n("noWebInterfaceTitle"), l10n("noWebInterface")).toString());
                                }
                                return;
                        }
-                       writeReply(ctx, 220, "text/html; charset=utf-8", "OK", 
createBox(ctx, l10n("pluginNotFoundTitle") , 
l10n("pluginNotFound")).toString());
+                       writeHTMLReply(ctx, 220, "OK", createBox(ctx, 
l10n("pluginNotFoundTitle") , l10n("pluginNotFound")).toString());
                        return;
                }

@@ -149,6 +149,7 @@
                StringBuffer replyBuffer = new StringBuffer();
                if ("add".equals(action)) {
                        pluginName = httpRequest.getPartAsString("pluginName", 
MAX_PLUGIN_NAME_LENGTH);
+
                        boolean added = false;
                        try {
                                pluginManager.addPlugin(pluginName, true);
@@ -176,7 +177,7 @@
                        writePermanentRedirect(ctx, l10n("pluginList"), 
"?action=list");
                        return;
                }
-               writeReply(ctx, 220, "text/html; charset=utf-8", "OK", 
replyBuffer.toString());
+               writeHTMLReply(ctx, 220, "OK", replyBuffer.toString());
        }

        /**
@@ -227,7 +228,7 @@
                        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("input", new String[] { "type", "value" }, new String[] { 
"submit", l10n("visit") });
+                               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");
                        }

Modified: branches/freenet-jfk/src/freenet/clients/http/PproxyToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/PproxyToadlet.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/PproxyToadlet.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -7,6 +7,7 @@

 import freenet.client.HighLevelSimpleClient;
 import freenet.l10n.L10n;
+import freenet.node.Node;
 import freenet.node.NodeClientCore;
 import freenet.pluginmanager.AccessDeniedPluginHTTPException;
 import freenet.pluginmanager.DownloadPluginHTTPException;
@@ -22,12 +23,12 @@

 public class PproxyToadlet extends Toadlet {
        private static final int MAX_PLUGIN_NAME_LENGTH = 1024;
-       private final PluginManager pm;
+       private final Node node;
        private final NodeClientCore core;

-       public PproxyToadlet(HighLevelSimpleClient client, PluginManager pm, 
NodeClientCore core) {
+       public PproxyToadlet(HighLevelSimpleClient client, Node node, 
NodeClientCore core) {
                super(client);
-               this.pm = pm;
+               this.node = node;
                this.core = core;
        }

@@ -61,6 +62,8 @@

                if(Logger.shouldLog(Logger.MINOR, this)) Logger.minor(this, 
"Pproxy received POST on "+path);

+               PluginManager pm = node.pluginManager;
+               
                if(path.length()>0)
                {
                        try
@@ -77,28 +80,28 @@
                                        plugin = path.substring(0, to);
                                }

-                               writeReply(ctx, 200, "text/html", "OK", 
pm.handleHTTPPost(plugin, request));
+                               writeHTMLReply(ctx, 200, "OK", 
pm.handleHTTPPost(plugin, request));
                        }
                        catch (RedirectPluginHTTPException e) {
                                writeTemporaryRedirect(ctx, e.message, 
e.newLocation);
                        }
                        catch (NotFoundPluginHTTPException e) {
-                               sendErrorPage(ctx, e.code, e.message, 
e.location);
+                               sendErrorPage(ctx, 
NotFoundPluginHTTPException.code, e.message, e.location);
                        }
                        catch (AccessDeniedPluginHTTPException e) {
-                               sendErrorPage(ctx, e.code, e.message, 
e.location);
+                               sendErrorPage(ctx, 
AccessDeniedPluginHTTPException.code, e.message, e.location);
                        }
                        catch (DownloadPluginHTTPException e) {
                                // FIXME: maybe it ought to be defined like 
sendErrorPage : in toadlets

                                MultiValueTable head = new MultiValueTable();
                                head.put("Content-Disposition", "attachment; 
filename=\"" + e.filename + '"');
-                               ctx.sendReplyHeaders(e.code, "Found", head, 
e.mimeType, e.data.length);
+                               
ctx.sendReplyHeaders(DownloadPluginHTTPException.CODE, "Found", head, 
e.mimeType, e.data.length);
                                ctx.writeData(e.data);
                        }
                        catch(PluginHTTPException e)
                        {
-                               sendErrorPage(ctx, e.code, e.message, 
e.location);
+                               sendErrorPage(ctx, PluginHTTPException.code, 
e.message, e.location);
                        }
                        catch(Throwable t)
                        {
@@ -130,7 +133,7 @@
                                infoboxContent.addChild("#", 
l10n("pluginUnloadedWithName", "name", request.getPartAsString("remove", 
MAX_PLUGIN_NAME_LENGTH)));
                                infoboxContent.addChild("br");
                                infoboxContent.addChild("a", "href", 
"/plugins/", l10n("returnToPluginPage"));
-                               writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        }if (request.getPartAsString("unload", 
MAX_PLUGIN_NAME_LENGTH).length() > 0) {
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("plugins"), ctx);
@@ -144,7 +147,7 @@
                                unloadForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
                                unloadForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "hidden", "unloadconfirm", 
request.getPartAsString("unload", MAX_PLUGIN_NAME_LENGTH) });
                                unloadForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "confirm", l10n("unload") 
});
-                               writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        }else if (request.getPartAsString("reload", 
MAX_PLUGIN_NAME_LENGTH).length() > 0) {
                                String fn = null;
@@ -196,11 +199,13 @@
                if(path.startsWith("/")) path = path.substring(1);
                if(path.startsWith("plugins/")) path = 
path.substring("plugins/".length());

+               PluginManager pm = node.pluginManager;
+               
                if(Logger.shouldLog(Logger.MINOR, this))
                        Logger.minor(this, "Pproxy fetching "+path);
                try {
                        if (path.equals("")) {
-                               this.showPluginList(ctx, request);
+                               this.showPluginList(ctx, request, pm);
                        } else {
                                // split path into plugin class name and 'data' 
path for plugin
                                int to = path.indexOf("/");
@@ -213,7 +218,7 @@

                                // Plugin may need to know where it was 
accessed from, so it can e.g. produce relative URLs.
                                //writeReply(ctx, 200, "text/html", "OK", 
mkPage("plugin", pm.handleHTTPGet(plugin, data)));
-                               writeReply(ctx, 200, "text/html", "OK", 
pm.handleHTTPGet(plugin, request));                             
+                               writeHTMLReply(ctx, 200, "OK", 
pm.handleHTTPGet(plugin, request));                              
                        }

                        //FetchResult result = fetch(key);
@@ -221,24 +226,24 @@
                } catch (RedirectPluginHTTPException e) {
                        writeTemporaryRedirect(ctx, e.message, e.newLocation);
                } catch (NotFoundPluginHTTPException e) {
-                       sendErrorPage(ctx, e.code, e.message, e.location);
+                       sendErrorPage(ctx, NotFoundPluginHTTPException.code, 
e.message, e.location);
                } catch (AccessDeniedPluginHTTPException e) {
-                       sendErrorPage(ctx, e.code, e.message, e.location);
+                       sendErrorPage(ctx, 
AccessDeniedPluginHTTPException.code, e.message, e.location);
                } catch (DownloadPluginHTTPException e) {
                        // FIXME: maybe it ought to be defined like 
sendErrorPage : in toadlets

                        MultiValueTable head = new MultiValueTable();
                        head.put("Content-Disposition", "attachment; 
filename=\"" + e.filename + '"');
-                       ctx.sendReplyHeaders(e.code, "Found", head, e.mimeType, 
e.data.length);
+                       ctx.sendReplyHeaders(DownloadPluginHTTPException.CODE, 
"Found", head, e.mimeType, e.data.length);
                        ctx.writeData(e.data);
                } catch(PluginHTTPException e) {
-                       sendErrorPage(ctx, e.code, e.message, e.location);
+                       sendErrorPage(ctx, PluginHTTPException.code, e.message, 
e.location);
                } catch (Throwable t) {
                        writeInternalError(t, ctx);
                }
        }

-       private void showPluginList(ToadletContext ctx, HTTPRequest request) 
throws ToadletContextClosedException, IOException {
+       private void showPluginList(ToadletContext ctx, HTTPRequest request, 
PluginManager pm) throws ToadletContextClosedException, IOException {
                if(!ctx.isAllowedFullAccess()) {
                        super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
                        return;
@@ -289,7 +294,7 @@
                        loadDiv.addChild("#", (l10n("loadPluginLabel") + ' '));
                        loadDiv.addChild("input", new String[] { "type", 
"name", "size" }, new String[] { "text", "load", "40" });
                        loadDiv.addChild("input", new String[] { "type", 
"value" }, new String[] { "submit", "Load" });
-                       writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                } 
        }
 }

Modified: branches/freenet-jfk/src/freenet/clients/http/QueueToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/QueueToadlet.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/QueueToadlet.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -106,6 +106,7 @@
                                                                        new 
String[]{ "id", "message" },
                                                                        new 
String[]{ identifier, e.getMessage()}
                                                        ));
+                                       return;
                                }
                                writePermanentRedirect(ctx, "Done", "/queue/");
                                return;
@@ -207,7 +208,10 @@
                                        insertURI = new FreenetURI("CHK@");
                                } else if ("ksk".equals(keyType)) {
                                        try {
-                                               insertURI = new 
FreenetURI(request.getPartAsString("key", 128));
+                                               String u = 
request.getPartAsString("key", 128);
+                                               insertURI = new FreenetURI(u);
+                                               if(logMINOR)
+                                                       Logger.minor(this, 
"Inserting key: "+insertURI+" ("+u+")");
                                        } catch (MalformedURLException mue1) {
                                                
writeError(L10n.getString("QueueToadlet.errorInvalidURI"), 
L10n.getString("QueueToadlet.errorInvalidURIToU"), ctx);
                                                return;
@@ -293,12 +297,12 @@
                                                                        
HTMLNode alertContent = ctx.getPageMaker().getContentNode(alertNode);
                                                                        
alertContent.addChild("#", 
L10n.getString("QueueToadlet.warningUnsafeContentExplanation"));
                                                                        
HTMLNode optionListNode = alertContent.addChild("ul");
-                                                                       
HTMLNode optionForm = ctx.addFormChild(optionListNode, "/queue/", 
"queueDownloadNotFilteredConfirmForm");
+                                                                       
HTMLNode optionForm = ctx.addFormChild(optionListNode, "/queue/", 
"queueDownloadNotFilteredConfirmForm-" + identifier.hashCode());
                                                                        
optionForm.addChild("input", new String[] { "type", "name", "value" }, new 
String[] { "hidden", "identifier", identifier });
                                                                        
optionForm.addChild("input", new String[] { "type", "name", "value" }, new 
String[] { "hidden", "forceDownload", 
String.valueOf(System.currentTimeMillis()) });
                                                                        
optionForm.addChild("input", new String[] { "type", "name", "value" }, new 
String[] { "submit", "get", "Download anyway" });
                                                                        
optionForm.addChild("input", new String[] { "type", "name", "value" }, new 
String[] { "submit", "return", "Return to queue page" });
-                                                                       
writeReply(ctx, 200, "text/html; charset=utf-8", "OK", pageNode.generate());
+                                                                       
writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                                                                        return;
                                                                }
                                                        }
@@ -326,7 +330,7 @@
                HTMLNode infoboxContent = pageMaker.getContentNode(infobox);
                infoboxContent.addChild("#", message);
                infoboxContent.addChild("div").addChildren(new HTMLNode[] { new 
HTMLNode("#", "Return to "), new HTMLNode("a", "href", "/queue/", "queue 
page"), new HTMLNode("#", ".") });
-               writeReply(context, 400, "text/html; charset=utf-8", "Error", 
pageNode.generate());
+               writeHTMLReply(context, 400, "Bad request", 
pageNode.generate());
        }

        public void handleGet(URI uri, final HTTPRequest request, 
ToadletContext ctx) 
@@ -396,7 +400,7 @@
                        HTMLNode infoboxContent = 
pageMaker.getContentNode(infobox);
                        infoboxContent.addChild("#", 
L10n.getString("QueueToadlet.noTaskOnGlobalQueue"));
                        contentNode.addChild(createInsertBox(pageMaker, ctx));
-                       writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                        return;
                }

@@ -578,7 +582,7 @@
                boolean advancedModeEnabled = core.isAdvancedModeEnabled();

                if (!completedDownloadToTemp.isEmpty()) {
-                       contentNode.addChild("a", "name", 
"completedDownloadToTemp");
+                       contentNode.addChild("a", "id", 
"completedDownloadToTemp");
                        HTMLNode completedDownloadsTempInfobox = 
contentNode.addChild(pageMaker.getInfobox("completed_requests", 
L10n.getString("QueueToadlet.completedDinTempDirectory", new String[]{ "size" 
}, new String[]{ String.valueOf(completedDownloadToTemp.size()) })));
                        HTMLNode completedDownloadsToTempContent = 
pageMaker.getContentNode(completedDownloadsTempInfobox);
                        if (advancedModeEnabled) {
@@ -589,7 +593,7 @@
                }

                if (!completedDownloadToDisk.isEmpty()) {
-                       contentNode.addChild("a", "name", 
"completedDownloadToDisk");
+                       contentNode.addChild("a", "id", 
"completedDownloadToDisk");
                        HTMLNode completedToDiskInfobox = 
contentNode.addChild(pageMaker.getInfobox("completed_requests", 
L10n.getString("QueueToadlet.completedDinDownloadDirectory", new String[]{ 
"size" }, new String[]{ String.valueOf(completedDownloadToDisk.size()) })));
                        HTMLNode completedToDiskInfoboxContent = 
pageMaker.getContentNode(completedToDiskInfobox);
                        if (advancedModeEnabled) {
@@ -600,7 +604,7 @@
                }

                if (!completedUpload.isEmpty()) {
-                       contentNode.addChild("a", "name", "completedUpload");
+                       contentNode.addChild("a", "id", "completedUpload");
                        HTMLNode completedUploadInfobox = 
contentNode.addChild(pageMaker.getInfobox("completed_requests", 
L10n.getString("QueueToadlet.completedU", new String[]{ "size" }, new String[]{ 
String.valueOf(completedUpload.size()) })));
                        HTMLNode completedUploadInfoboxContent = 
pageMaker.getContentNode(completedUploadInfobox);
                        if (advancedModeEnabled) {
@@ -611,7 +615,7 @@
                }

                if (!completedDirUpload.isEmpty()) {
-                       contentNode.addChild("a", "name", "completedDirUpload");
+                       contentNode.addChild("a", "id", "completedDirUpload");
                        HTMLNode completedUploadDirInfobox = 
contentNode.addChild(pageMaker.getInfobox("completed_requests", 
L10n.getString("QueueToadlet.completedUDirectory", new String[]{ "size" }, new 
String[]{ String.valueOf(completedDirUpload.size()) })));
                        HTMLNode completedUploadDirContent = 
pageMaker.getContentNode(completedUploadDirInfobox);
                        if (advancedModeEnabled) {
@@ -622,7 +626,7 @@
                }

                if (!failedDownload.isEmpty()) {
-                       contentNode.addChild("a", "name", "failedDownload");
+                       contentNode.addChild("a", "id", "failedDownload");
                        HTMLNode failedInfobox = 
contentNode.addChild(pageMaker.getInfobox("failed_requests", 
L10n.getString("QueueToadlet.failedD", new String[]{ "size" }, new String[]{ 
String.valueOf(failedDownload.size()) })));
                        HTMLNode failedContent = 
pageMaker.getContentNode(failedInfobox);
                        if (advancedModeEnabled) {
@@ -633,7 +637,7 @@
                }

                if (!failedUpload.isEmpty()) {
-                       contentNode.addChild("a", "name", "failedUpload");
+                       contentNode.addChild("a", "id", "failedUpload");
                        HTMLNode failedInfobox = 
contentNode.addChild(pageMaker.getInfobox("failed_requests", 
L10n.getString("QueueToadlet.failedU", new String[]{ "size" }, new String[]{ 
String.valueOf(failedUpload.size()) })));
                        HTMLNode failedContent = 
pageMaker.getContentNode(failedInfobox);
                        if (advancedModeEnabled) {
@@ -644,7 +648,7 @@
                }

                if (!failedDirUpload.isEmpty()) {
-                       contentNode.addChild("a", "name", "failedDirUpload");
+                       contentNode.addChild("a", "id", "failedDirUpload");
                        HTMLNode failedInfobox = 
contentNode.addChild(pageMaker.getInfobox("failed_requests", 
L10n.getString("QueueToadlet.failedU", new String[]{ "size" }, new String[]{ 
String.valueOf(failedDirUpload.size()) })));
                        HTMLNode failedContent = 
pageMaker.getContentNode(failedInfobox);
                        if (advancedModeEnabled) {
@@ -655,7 +659,7 @@
                }

                if (!uncompletedDownload.isEmpty()) {
-                       contentNode.addChild("a", "name", 
"uncompletedDownload");
+                       contentNode.addChild("a", "id", "uncompletedDownload");
                        HTMLNode uncompletedInfobox = 
contentNode.addChild(pageMaker.getInfobox("requests_in_progress", 
L10n.getString("QueueToadlet.wipD", new String[]{ "size" }, new String[]{ 
String.valueOf(uncompletedDownload.size()) })));
                        HTMLNode uncompletedContent = 
pageMaker.getContentNode(uncompletedInfobox);
                        if (advancedModeEnabled) {
@@ -666,7 +670,7 @@
                }

                if (!uncompletedUpload.isEmpty()) {
-                       contentNode.addChild("a", "name", "uncompletedUpload");
+                       contentNode.addChild("a", "id", "uncompletedUpload");
                        HTMLNode uncompletedInfobox = 
contentNode.addChild(pageMaker.getInfobox("requests_in_progress", 
L10n.getString("QueueToadlet.wipU", new String[]{ "size" }, new String[]{ 
String.valueOf(uncompletedUpload.size()) })));
                        HTMLNode uncompletedContent = 
pageMaker.getContentNode(uncompletedInfobox);
                        if (advancedModeEnabled) {
@@ -677,7 +681,7 @@
                }

                if (!uncompletedDirUpload.isEmpty()) {
-                       contentNode.addChild("a", "name", 
"uncompletedDirUpload");
+                       contentNode.addChild("a", "id", "uncompletedDirUpload");
                        HTMLNode uncompletedInfobox = 
contentNode.addChild(pageMaker.getInfobox("requests_in_progress", 
L10n.getString("QueueToadlet.wipDU", new String[]{ "size" }, new String[]{ 
String.valueOf(uncompletedDirUpload.size()) })));
                        HTMLNode uncompletedContent = 
pageMaker.getContentNode(uncompletedInfobox);
                        if (advancedModeEnabled) {
@@ -688,7 +692,7 @@
                }

                MultiValueTable pageHeaders = new MultiValueTable();
-               this.writeReply(ctx, 200, "text/html", "OK", pageHeaders, 
pageNode.generate());
+               writeHTMLReply(ctx, 200, "OK", pageHeaders, 
pageNode.generate());
        }


@@ -761,7 +765,7 @@
        private HTMLNode createPriorityCell(PageMaker pageMaker, String 
identifier, short priorityClass, ToadletContext ctx, String[] priorityClasses) {

                HTMLNode priorityCell = new HTMLNode("td", "class", 
"request-priority nowrap");
-               HTMLNode priorityForm = ctx.addFormChild(priorityCell, 
"/queue/", "queueChangePriorityCell");
+               HTMLNode priorityForm = ctx.addFormChild(priorityCell, 
"/queue/", "queueChangePriorityCell-" + identifier.hashCode());
                priorityForm.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "hidden", "identifier", identifier });
                HTMLNode prioritySelect = priorityForm.addChild("select", 
"name", "priority");
                for (int p = 0; p < RequestStarter.NUMBER_OF_PRIORITY_CLASSES; 
p++) {
@@ -777,14 +781,14 @@

        private HTMLNode createDeleteCell(PageMaker pageMaker, String 
identifier, ClientRequest clientRequest, ToadletContext ctx) {
                HTMLNode deleteNode = new HTMLNode("td", "class", 
"request-delete");
-               HTMLNode deleteForm = ctx.addFormChild(deleteNode, "/queue/", 
"queueDeleteForm");
+               HTMLNode deleteForm = ctx.addFormChild(deleteNode, "/queue/", 
"queueDeleteForm-" + identifier.hashCode());
                deleteForm.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "hidden", "identifier", identifier });
                deleteForm.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "submit", "remove_request", 
L10n.getString("QueueToadlet.remove") });

                // If it's failed, offer to restart it

                if(clientRequest.hasFinished() && !clientRequest.hasSucceeded() 
&& clientRequest.canRestart()) {
-                       HTMLNode retryForm = ctx.addFormChild(deleteNode, 
"/queue/", "queueRestartForm");
+                       HTMLNode retryForm = ctx.addFormChild(deleteNode, 
"/queue/", "queueRestartForm-" + identifier.hashCode());
                        String restartName = L10n.getString(clientRequest 
instanceof ClientGet && ((ClientGet)clientRequest).hasPermRedirect() ? 
"QueueToadlet.follow" : "QueueToadlet.restart");
                        retryForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "hidden", "identifier", identifier });
                        retryForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "restart_request", restartName });

Modified: branches/freenet-jfk/src/freenet/clients/http/SimpleToadletServer.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/SimpleToadletServer.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/SimpleToadletServer.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -29,6 +29,7 @@
 import freenet.io.NetworkInterface;
 import freenet.l10n.L10n;
 import freenet.node.NodeClientCore;
+import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.OOMHandler;
 import freenet.support.StringArray;
@@ -339,9 +340,9 @@

                this.advancedModeEnabled = 
fproxyConfig.getBoolean("advancedModeEnabled");              
                toadlets = new LinkedList();
-               core.setToadletContainer(this); // even if not enabled, because 
of config

-               this.networkInterface = NetworkInterface.create(port, 
this.bindTo, fproxyConfig.getString("allowedHosts"));
+               this.networkInterface = NetworkInterface.create(port, 
this.bindTo, fproxyConfig.getString("allowedHosts"), core.getExecutor());
+               
                if(!enabled) {
                        Logger.normal(core, "Not starting FProxy as it's 
disabled");
                        System.out.println("Not starting FProxy as it's 
disabled");
@@ -349,6 +350,7 @@
                        myThread = new Thread(this, "SimpleToadletServer");
                        myThread.setDaemon(true);
                }
+
        }

        public boolean doRobots() {
@@ -364,16 +366,16 @@
        }

        public void register(Toadlet t, String urlPrefix, boolean atFront, 
boolean fullOnly) {
-               register(t, urlPrefix, atFront, null, null, fullOnly);
+               register(t, urlPrefix, atFront, null, null, fullOnly, null);
        }

-       public void register(Toadlet t, String urlPrefix, boolean atFront, 
String name, String title, boolean fullOnly) {
+       public void register(Toadlet t, String urlPrefix, boolean atFront, 
String name, String title, boolean fullOnly, LinkEnabledCallback cb) {
                ToadletElement te = new ToadletElement(t, urlPrefix);
                if(atFront) toadlets.addFirst(te);
                else toadlets.addLast(te);
                t.container = this;
                if (name != null) {
-                       pageMaker.addNavigationLink(urlPrefix, name, title, 
fullOnly);
+                       pageMaker.addNavigationLink(urlPrefix, name, title, 
fullOnly, cb);
                }
        }

@@ -419,9 +421,7 @@
                }

                void start() {
-                       Thread t = new Thread(this, 
"SimpleToadletServer$SocketHandler");
-                       t.setDaemon(true);
-                       t.start();
+                       core.getExecutor().execute(this, 
"SimpleToadletServer$SocketHandler@"+hashCode());
                }

                public void run() {
@@ -482,4 +482,14 @@
                return L10n.getString("SimpleToadletServer."+key);
        }

+       public HTMLNode addFormChild(HTMLNode parentNode, String target, String 
id) {
+               HTMLNode formNode =
+                       parentNode.addChild("form", new String[] { "action", 
"method", "enctype", "id",  "accept-charset" }, 
+                                       new String[] { target, "post", 
"multipart/form-data", id, "utf-8"} ).addChild("div");
+               formNode.addChild("input", new String[] { "type", "name", 
"value" }, 
+                               new String[] { "hidden", "formPassword", 
getFormPassword() });
+               
+               return formNode;
+       }
+
 }

Modified: branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java        
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -16,6 +16,7 @@
 import freenet.config.SubConfig;
 import freenet.io.comm.IOStatisticCollector;
 import freenet.l10n.L10n;
+import freenet.node.Location;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
 import freenet.node.NodeStarter;
@@ -121,7 +122,7 @@
                                if (statusDifference != 0) {
                                        return statusDifference;
                                }
-                               return 
firstNode.getName().compareToIgnoreCase(secondNode.getName());
+                               return 0;
                        }
                });

@@ -288,7 +289,7 @@
                        addNodeCircle(nodeCircleTable);
                }

-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

        private void drawRejectReasonsBox(HTMLNode nextTableCell) {
@@ -410,7 +411,7 @@
                                        "\u00a0(" + ((storeHits*100) / 
(storeAccesses)) + "%)");

                storeSizeList.addChild("li", 
-                               "Avg. access rate:\u00a0" + 
thousendPoint.format(overallAccesses/nodeUptimeSeconds) + "/s");
+                               "Avg. access rate:\u00a0" + 
thousendPoint.format(overallAccesses/nodeUptimeSeconds) + "/sec");

        }

@@ -870,7 +871,7 @@
                for (int peerIndex = 0; peerIndex < peerCount; peerIndex++) {
                        peerNodeStatus = peerNodeStatuses[peerIndex];
                        peerLocation = peerNodeStatus.getLocation();
-                       peerDistance = PeerManager.distance( myLocation, 
peerLocation );
+                       peerDistance = Location.distance( myLocation, 
peerLocation );
                        histogramIndex = (int) (Math.floor(peerDistance * 
HISTOGRAM_LENGTH * 2));
                        if (peerNodeStatus.isConnected()) {
                                histogramConnected[histogramIndex]++;

Modified: branches/freenet-jfk/src/freenet/clients/http/SymlinkerToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/SymlinkerToadlet.java 
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/SymlinkerToadlet.java 
2007-08-21 20:26:59 UTC (rev 14828)
@@ -118,7 +118,7 @@

                // TODO redirect to errorpage
                if ((foundtarget == null) || (foundkey == null)) {
-                       writeReply(ctx, 404, "text/html", 
L10n.getString("StaticToadlet.pathNotFoundTitle"), 
+                       writeTextReply(ctx, 404, "Not found", 
                                        
L10n.getString("StaticToadlet.pathNotFound"));
                        return;
                }
@@ -130,7 +130,7 @@
                                 path, uri.getQuery(), uri.getFragment());
                } catch (URISyntaxException e) {
                        // TODO Handle error somehow
-                       writeReply(ctx, 200, "text/html", "OK", e.getMessage());
+                       writeHTMLReply(ctx, 200, "OK", e.getMessage());
                        return;
                }


Modified: branches/freenet-jfk/src/freenet/clients/http/Toadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/Toadlet.java  2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/Toadlet.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -147,6 +147,22 @@
                writeReply(ctx, code, mimeType, desc, null, reply);
        }

+       protected void writeHTMLReply(ToadletContext ctx, int code, String 
desc, String reply) throws ToadletContextClosedException, IOException {
+               writeReply(ctx, code, "text/html; charset=utf-8", desc, null, 
reply);
+       }
+       
+       protected void writeTextReply(ToadletContext ctx, int code, String 
desc, String reply) throws ToadletContextClosedException, IOException {
+               writeReply(ctx, code, "text/plain; charset=utf-8", desc, null, 
reply);
+       }
+       
+       protected void writeHTMLReply(ToadletContext ctx, int code, String 
desc, MultiValueTable headers, String reply) throws 
ToadletContextClosedException, IOException {
+               writeReply(ctx, code, "text/html; charset=utf-8", desc, 
headers, reply);
+       }
+       
+       protected void writeTextReply(ToadletContext ctx, int code, String 
desc, MultiValueTable headers, String reply) throws 
ToadletContextClosedException, IOException {
+               writeReply(ctx, code, "text/plain; charset=utf-8", desc, 
headers, reply);
+       }
+       
        protected void writeReply(ToadletContext context, int code, String 
mimeType, String desc, MultiValueTable headers, String reply) throws 
ToadletContextClosedException, IOException {
                byte[] buffer = reply.getBytes("UTF-8");
                writeReply(context, code, mimeType, desc, headers, buffer, 0, 
buffer.length);
@@ -219,7 +235,7 @@
                infoboxContent.addChild("a", "href", ".", 
l10n("returnToPrevPage"));
                addHomepageLink(infoboxContent);

-               writeReply(ctx, code, "text/html; charset=UTF-8", desc, 
pageNode.generate());
+               writeHTMLReply(ctx, code, desc, pageNode.generate());
        }

        /**
@@ -250,7 +266,7 @@
                infoboxContent.addChild("a", "href", ".", 
l10n("returnToPrevPage"));
                addHomepageLink(infoboxContent);

-               writeReply(ctx, 500, "text/html; charset=UTF-8", desc, 
pageNode.generate());
+               writeHTMLReply(ctx, 500, desc, pageNode.generate());
        }

        protected void writeInternalError(Throwable t, ToadletContext ctx) 
throws ToadletContextClosedException, IOException {
@@ -262,7 +278,7 @@
                t.printStackTrace(pw);
                pw.flush();
                msg = msg + sw.toString() + "</pre></body></html>";
-               writeReply(ctx, 500, "text/html", "Internal Error", msg);
+               writeHTMLReply(ctx, 500, "Internal Error", msg);
        }

        protected static void addHomepageLink(HTMLNode content) {

Modified: branches/freenet-jfk/src/freenet/clients/http/ToadletContainer.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/ToadletContainer.java 
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/ToadletContainer.java 
2007-08-21 20:26:59 UTC (rev 14828)
@@ -6,6 +6,8 @@
 import java.net.InetAddress;
 import java.net.URI;

+import freenet.support.HTMLNode;
+
 /** Interface for toadlet containers. Toadlets should register here. */
 public interface ToadletContainer {

@@ -36,4 +38,6 @@

        /** Whether to tell spiders to go away */
        public boolean doRobots();
+
+       public HTMLNode addFormChild(HTMLNode parentNode, String target, String 
name);
 }

Modified: branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,5 +1,6 @@
 package freenet.clients.http;

+import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -214,7 +215,7 @@
         */
        public static void handle(Socket sock, ToadletContainer container, 
BucketFactory bf, PageMaker pageMaker) {
                try {
-                       InputStream is = sock.getInputStream();
+                       InputStream is = new 
BufferedInputStream(sock.getInputStream(), 4096);

                        LineReadingInputStream lis = new 
LineReadingInputStream(is);

@@ -372,6 +373,8 @@
                } catch (ToadletContextClosedException e) {
                        Logger.error(ToadletContextImpl.class, 
"ToadletContextClosedException while handling connection!");
                        return;
+               } catch (Throwable t) {
+                       Logger.error(ToadletContextImpl.class, "Caught error: 
"+t+" handling socket", t);
                }
        }

@@ -425,13 +428,7 @@
        }

        public HTMLNode addFormChild(HTMLNode parentNode, String target, String 
name) {
-               HTMLNode formNode =
-                       parentNode.addChild("form", new String[] { "action", 
"method", "enctype", "id", "name", "accept-charset" }, 
-                                       new String[] { target, "post", 
"multipart/form-data", name, name, "utf-8"} );
-               formNode.addChild("input", new String[] { "type", "name", 
"value" }, 
-                               new String[] { "hidden", "formPassword", 
container.getFormPassword() });
-               
-               return formNode;
+               return container.addFormChild(parentNode, target, name);
        }

        public boolean isAllowedFullAccess() {

Modified: branches/freenet-jfk/src/freenet/clients/http/TranslationToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/TranslationToadlet.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/TranslationToadlet.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -83,7 +83,7 @@
                        footer.addChild("%", "&nbsp;&nbsp;");
                        footer.addChild("a", "href", TOADLET_URL + 
(showEverything ? "" : "?toTranslateOnly")).addChild("#", 
l10n("returnToTranslations"));

-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;                         
                } else if (request.isParameterSet("translate")) {
                        String key = request.getParam("translate");
@@ -126,7 +126,7 @@
                                updateForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "hidden", "toTranslateOnly", key });

                        updateForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
                } else if (request.isParameterSet("remove")) {
                        String key = request.getParam("remove");
@@ -145,7 +145,7 @@
                        removeForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "remove_confirmed", l10n("remove") 
});
                        removeForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });

-                       this.writeReply(ctx, 200, "text/html; charset=utf-8", 
"OK", pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                        return;
                }

@@ -188,7 +188,7 @@
                        }
                }

-               this.writeReply(ctx, 200, "text/html; charset=utf-8", "OK", 
pageNode.generate());
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

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

Modified: branches/freenet-jfk/src/freenet/clients/http/TrivialToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/TrivialToadlet.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/TrivialToadlet.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -19,7 +19,7 @@
                String reply = "<html><head><title>You requested "+encFetched+
                        "</title></head><body>You fetched <a 
href=\""+encFetched+"\">"+
                        encFetched+"</a>.</body></html>";
-               this.writeReply(ctx, 200, "text/html", "OK", reply);
+               this.writeHTMLReply(ctx, 200, "OK", reply);
        }

        public String supportedMethods() {

Modified: branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -47,10 +47,10 @@
        final Node node;
        final BookmarkManager bookmarkManager;

-       WelcomeToadlet(HighLevelSimpleClient client, Node node) {
+       WelcomeToadlet(HighLevelSimpleClient client, NodeClientCore core, Node 
node) {
                super(client);
                this.node = node;
-               this.core = node.clientCore;
+               this.core = core;
                this.bookmarkManager = core.bookmarkManager;
                try {
                        manageBookmarksURI = new 
URI("/welcome/?managebookmarks");
@@ -109,7 +109,7 @@
                        HTMLNode content = 
ctx.getPageMaker().getContentNode(infobox);
                        content.addChild("p").addChild("#", l10n("updating"));
                        content.addChild("p").addChild("#", l10n("thanks"));
-                       writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                        Logger.normal(this, "Node is updating/restarting");
                        node.getNodeUpdater().arm();
                }else if 
(request.getPartAsString(GenericReadFilterCallback.magicHTTPEscapeString, 
MAX_URL_LENGTH).length()>0){
@@ -132,7 +132,7 @@
                        HTMLNode updateForm = ctx.addFormChild(content, "/", 
"updateConfirmForm");
                        updateForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel")});
                        updateForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "updateconfirm", l10n("update") });
-                       writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                }else if(request.isPartSet("getThreadDump")) {
                        if(noPassword) {
                                redirectToRoot(ctx);
@@ -149,7 +149,7 @@
                                HTMLNode infobox = 
contentNode.addChild(ctx.getPageMaker().getInfobox("infobox-error",l10n("threadDumpSubTitle")));
                                
ctx.getPageMaker().getContentNode(infobox).addChild("#", 
l10n("threadDumpNotUsingWrapper"));
                        }
-                       this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                }else if(request.isPartSet("getJEStatsDump")) {
                        if(noPassword) {
                                redirectToRoot(ctx);
@@ -164,7 +164,7 @@
                        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>  END 
DATABASE STATS  <<<<<<<<<<<<<<<<<<<<<<<");

                        
ctx.getPageMaker().getContentNode(infobox).addChild("#", 
l10n("writtenDatabaseStats"));
-                       this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                }else if(request.isPartSet("disable")){
                        if(noPassword) {
                                redirectToRoot(ctx);
@@ -183,10 +183,10 @@
                                                Logger.normal(this,"Disabling 
the userAlert "+alert.hashCode());
                                                alert.isValid(false);
                                        }
-
-                                       writePermanentRedirect(ctx, 
l10n("disabledAlert"), "/");
                                }
                        }
+                       writePermanentRedirect(ctx, l10n("disabledAlert"), "/");
+                       return;
                } else 
if(request.isPartSet("boardname")&&(request.isPartSet("filename")||request.isPartSet("message")))
 {
                        // Inserting into a frost board FIN
                        // boardname
@@ -239,7 +239,7 @@

                                postForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
                                postForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "finconfirm", l10n("post") 
});
-                               writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        }

@@ -282,7 +282,7 @@
                        content.addChild("br");
                        addHomepageLink(content);

-                       writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                        request.freeParts();
                }else 
if(request.isPartSet("key")&&request.isPartSet("filename")){
                        if(noPassword) {
@@ -335,7 +335,7 @@
                        content.addChild("br");
                        addHomepageLink(content);

-                       writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                       writeHTMLReply(ctx, 200, "OK", pageNode.generate());
                        request.freeParts();
                        bucket.free();
                }else if (request.isPartSet("shutdownconfirm")) {
@@ -413,7 +413,7 @@
                                while((read = reader.read(buffer)) != -1)
                                        sw.write(buffer, 0, read);

-                               this.writeReply(ctx, 200, "text/plain", "OK", 
sw.toString());
+                               this.writeHTMLReply(ctx, 200, "OK", 
sw.toString());
                                return;
                        } else if (request.isParameterSet("terminated")) {
                                if((!request.isParameterSet("formPassword")) || 
!request.getParam("formPassword").equals(core.formPassword)) {
@@ -426,7 +426,7 @@
                                HTMLNode infobox = 
contentNode.addChild(ctx.getPageMaker().getInfobox("infobox-information", 
l10n("shutdownDone")));
                                HTMLNode infoboxContent = 
ctx.getPageMaker().getContentNode(infobox);
                                infoboxContent.addChild("#", l10n("thanks"));
-                               this.writeReply(ctx, 200, "text/html; 
charset=utf-8", "OK", pageNode.generate());
+                               this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        } else if (request.isParameterSet("restarted")) {
                                if((!request.isParameterSet("formPassword")) || 
!request.getParam("formPassword").equals(core.formPassword)) {
@@ -439,7 +439,7 @@
                                HTMLNode infobox = 
contentNode.addChild(ctx.getPageMaker().getInfobox("infobox-information", 
l10n("restartingTitle")));
                                HTMLNode infoboxContent = 
ctx.getPageMaker().getContentNode(infobox);
                                infoboxContent.addChild("#", 
l10n("restarting"));
-                               writeReply(ctx, 200, "text/html; 
charset=utf-8", "OK", pageNode.generate());
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                Logger.normal(this, "Node is restarting");
                                return;
                         } else if (request.getParam("newbookmark").length() > 
0) {
@@ -457,7 +457,7 @@
                                 addForm.addChild("input", new String[] 
{"type", "name", "value"}, new String[] {"hidden", "bookmark", "/"});
                                addForm.addChild("input", new String[] {"type", 
"name", "value"}, new String[] {"hidden", "action", "addItem"});
                                addForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "addbookmark", 
L10n.getString("BookmarkEditorToadlet.addBookmark") });
-                               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        } else if 
(request.getParam(GenericReadFilterCallback.magicHTTPEscapeString).length() > 
0) {
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode( l10n("confirmExternalLinkTitle"), ctx);
@@ -471,7 +471,7 @@
                                externalLinkForm.addChild("input", new String[] 
{ "type", "name", "value" }, new String[] { "hidden", 
GenericReadFilterCallback.magicHTTPEscapeString, target });
                                externalLinkForm.addChild("input", new String[] 
{ "type", "name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
                                externalLinkForm.addChild("input", new String[] 
{ "type", "name", "value" }, new String[] { "submit", "Go", 
l10n("goToExternalLink") });
-                               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               this.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        } else if (request.isParameterSet("exit")) {
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("shutdownConfirmTitle"), ctx);
@@ -482,7 +482,7 @@
                                HTMLNode shutdownForm = 
ctx.addFormChild(content.addChild("p"), "/", "confirmShutdownForm");
                                shutdownForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
                                shutdownForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "shutdownconfirm", 
l10n("shutdown") });
-                               writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        }else if (request.isParameterSet("restart")) {
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("restartConfirmTitle"), ctx);
@@ -493,7 +493,7 @@
                                HTMLNode restartForm = 
ctx.addFormChild(content.addChild("p"), "/", "confirmRestartForm");
                                restartForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "cancel", 
L10n.getString("Toadlet.cancel") });
                                restartForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "restartconfirm", 
l10n("restart") });
-                               writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+                               writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                return;
                        }
                }
@@ -526,8 +526,8 @@
                HTMLNode fetchKeyBox = 
contentNode.addChild(ctx.getPageMaker().getInfobox("infobox-normal", 
l10n("fetchKeyLabel")));
                HTMLNode fetchKeyContent = 
ctx.getPageMaker().getContentNode(fetchKeyBox);
                fetchKeyContent.addAttribute("id", "keyfetchbox");
-               HTMLNode fetchKeyForm = fetchKeyContent.addChild("form", new 
String[] { "action", "method" }, new String[] { "/", "get" });
-               fetchKeyForm.addChild("#", "Key: ");
+               HTMLNode fetchKeyForm = fetchKeyContent.addChild("form", new 
String[] { "action", "method" }, new String[] { "/", "get" }).addChild("div");
+               fetchKeyForm.addChild("#", l10n("keyRequestLabel")+' ');
                fetchKeyForm.addChild("input", new String[] { "type", "size", 
"name" }, new String[] { "text", "80", "key" });
                fetchKeyForm.addChild("input", new String[] { "type", "value" 
}, new String[] { "submit", l10n("fetch") });

@@ -563,11 +563,11 @@
                }
                versionContent.addChild("br");
                if(ctx.isAllowedFullAccess()){
-                       HTMLNode shutdownForm = versionContent.addChild("form", 
new String[] { "action", "method" }, new String[] { ".", "get" });
+                       HTMLNode shutdownForm = versionContent.addChild("form", 
new String[] { "action", "method" }, new String[] { ".", "get" 
}).addChild("div");
                        shutdownForm.addChild("input", new String[] { "type", 
"name" }, new String[] { "hidden", "exit" });
                        shutdownForm.addChild("input", new String[] { "type", 
"value" }, new String[] { "submit", l10n("shutdownNode") });
                        if(node.isUsingWrapper()){
-                               HTMLNode restartForm = 
versionContent.addChild("form", new String[] { "action", "method" }, new 
String[] { ".", "get" });
+                               HTMLNode restartForm = 
versionContent.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") });
                        }
@@ -584,7 +584,7 @@
                        activityList.addChild("li", l10n("arkFetchCount", 
"total", Integer.toString(node.getNumARKFetchers())));
                }

-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+               this.writeHTMLReply(ctx, 200, "OK", pageNode.generate());
        }

        public String supportedMethods() {

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategories.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategories.java  
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategories.java  
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,17 +1,10 @@
 package freenet.clients.http.bookmark;

 import java.util.Vector;
-import java.util.Iterator;

-public final class BookmarkCategories // implements Iterator
-{
+public final class BookmarkCategories {
+       private final Vector categories = new Vector();

-       Vector categories;
-
-       public BookmarkCategories() {
-               categories = new Vector();
-       }
-
        public BookmarkCategory get(int i) {
                return (BookmarkCategory) categories.get(i);
        }
@@ -28,8 +21,4 @@
        public int size() {
                return categories.size();
        }
-
-       public Iterator iterator() {
-               return categories.iterator();
-       }
 }

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategory.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategory.java    
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategory.java    
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,22 +1,18 @@
 package freenet.clients.http.bookmark;

 import java.util.Vector;
-import java.util.Iterator;

 import freenet.support.StringArray;

-public class BookmarkCategory extends Bookmark // implements Iterator
-{
+public class BookmarkCategory extends Bookmark {

-       private final Vector bookmarks;
+       private final Vector bookmarks = new Vector();

        public BookmarkCategory(String name) {
-               bookmarks = new Vector();
                setName(name);
        }

        public BookmarkCategory(String name, String desc) {
-               bookmarks = new Vector();
                setName(name);
                setDesc(desc);
        }
@@ -127,9 +123,4 @@
                for (int i = 0; i < size(); i++)
                        subCategories.get(i).setPrivate(bool);
        }
-
-       public Iterator iterator() {
-               return bookmarks.iterator();
-       }
-
 }

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItem.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItem.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItem.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -17,13 +17,10 @@
 public class BookmarkItem extends Bookmark {

        private FreenetURI key;
-
        private boolean updated;
-
        private final BookmarkUpdatedUserAlert alert;
+       private final UserAlertManager alerts;

-       private UserAlertManager alerts;
-
        public BookmarkItem(FreenetURI k, String n, UserAlertManager uam)
                        throws MalformedURLException {
                this.key = k;
@@ -135,11 +132,7 @@
        }

        public String getName() {
-               if (name.equals("")) {
-                       return l10n("unnamedBookmark");
-               } else {
-                       return name;
-               }
+               return ("".equals(name) ? l10n("unnamedBookmark") : name);
        }

        public void setPrivate(boolean bool) {
@@ -147,7 +140,7 @@
        }

        public String toString() {
-               return this.name + "=" + this.key.toString();
+               return this.name + '=' + this.key.toString();
        }

        public synchronized void setEdition(long ed, NodeClientCore node) {

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItems.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItems.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItems.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,17 +1,10 @@
 package freenet.clients.http.bookmark;

 import java.util.Vector;
-import java.util.Iterator;

-public class BookmarkItems // implements Iterator
-{
+public class BookmarkItems {
+       private final Vector items = new Vector();

-       Vector items;
-
-       public BookmarkItems() {
-               items = new Vector();
-       }
-
        public BookmarkItem get(int i) {
                return (BookmarkItem) items.get(i);
        }
@@ -28,8 +21,4 @@
        public int size() {
                return items.size();
        }
-
-       public Iterator iterator() {
-               return (items).iterator();
-       }
 }

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkManager.java 
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkManager.java 
2007-08-21 20:26:59 UTC (rev 14828)
@@ -20,106 +20,65 @@
 public class BookmarkManager {

        private final NodeClientCore node;
+       private final USKUpdatedCallback uskCB = new USKUpdatedCallback();
+       private final StringArrCallback configCB = new BookmarkCallback();
+       private static final BookmarkCategory MAIN_CATEGORY = new 
BookmarkCategory("/");;
+       private final HashMap bookmarks = new HashMap();

-       private USKUpdatedCallback uskcb;
-
-       private boolean started;
-
-       private BookmarkCategory mainCategory;
-
-       private HashMap bookmarks;
-
-       private SubConfig sc;
-
        public BookmarkManager(NodeClientCore n, SubConfig sc) {
-
-               bookmarks = new HashMap();
-               mainCategory = new BookmarkCategory("/");
-               bookmarks.put("/", mainCategory);
-
-               this.uskcb = new USKUpdatedCallback();
+               bookmarks.put("/", MAIN_CATEGORY);
                this.node = n;
-               this.sc = sc;

                try {
-
                        BookmarkCategory defaultRoot = new 
BookmarkCategory("/");
-
-                       BookmarkCategory indexes = (BookmarkCategory) 
defaultRoot
-                                       .addBookmark(new 
BookmarkCategory("Indexes"));
-                       indexes
-                                       .addBookmark(new BookmarkItem(
-                                                       new FreenetURI(
-                                                                       "USK at 
7H66rhYmxIFgMyw5Dl11JazXGHPhp7dSN7WMa1pbtEo,jQHUQUPTkeRcjmjgrc7t5cDRdDkK3uKkrSzuw5CO9uk,AQACAAE/ENTRY.POINT/25/"),
+                       BookmarkCategory indexes = (BookmarkCategory) 
defaultRoot.addBookmark(new BookmarkCategory("Indexes"));
+                       indexes.addBookmark(new BookmarkItem(
+                                       new FreenetURI(
+                                                       "USK at 
7H66rhYmxIFgMyw5Dl11JazXGHPhp7dSN7WMa1pbtEo,jQHUQUPTkeRcjmjgrc7t5cDRdDkK3uKkrSzuw5CO9uk,AQACAAE/ENTRY.POINT/25/"),
                                                        "Entry point (freesites 
with descriptions but no categories)",
                                                        node.alerts));

-                       indexes
-                                       .addBookmark(new BookmarkItem(
-                                                       new FreenetURI(
-                                                                       "USK at 
zQyF2O1o8B4y40w7Twz8y2I9haW3d2DTlxjTHPu7zc8,h2mhQNNE9aQvF~2yKAmKV1uorr7141-QOroBf5hrlbw,AQACAAE/AnotherIndex/3/"),
+                       indexes.addBookmark(new BookmarkItem(
+                                       new FreenetURI(
+                                                       "USK at 
zQyF2O1o8B4y40w7Twz8y2I9haW3d2DTlxjTHPu7zc8,h2mhQNNE9aQvF~2yKAmKV1uorr7141-QOroBf5hrlbw,AQACAAE/AnotherIndex/3/"),
                                                        "Another Index 
(freesites with categories but no descriptions)",
                                                        node.alerts));

-                       BookmarkCategory flog = (BookmarkCategory) defaultRoot
-                                       .addBookmark(new 
BookmarkCategory("Freenet devel's flogs"));
-                       flog
-                                       .addBookmark(new BookmarkItem(
-                                                       new FreenetURI(
-                                                                       "USK at 
yGvITGZzrY1vUZK-4AaYLgcjZ7ysRqNTMfdcO8gS-LY,-ab5bJVD3Lp-LXEQqBAhJpMKrKJ19RnNaZMIkusU79s,AQACAAE/toad/2/"),
+                       BookmarkCategory flog = (BookmarkCategory) 
defaultRoot.addBookmark(new BookmarkCategory("Freenet devel's flogs"));
+                       flog.addBookmark(new BookmarkItem(
+                                       new FreenetURI(
+                                                       "USK at 
yGvITGZzrY1vUZK-4AaYLgcjZ7ysRqNTMfdcO8gS-LY,-ab5bJVD3Lp-LXEQqBAhJpMKrKJ19RnNaZMIkusU79s,AQACAAE/toad/2/"),
                                                        "Toad", node.alerts));
-                       flog
-                                       .addBookmark(new BookmarkItem(
-                                                       new FreenetURI(
-                                                                       "USK at 
hM9XRwjXIzU8xTSBXNZvTn2KuvTSRFnVn4EER9FQnpM,gsth24O7ud4gL4NwNuYJDUqfaWASOG2zxZY~ChtgPxc,AQACAAE/Flog/4/"),
+                       flog.addBookmark(new BookmarkItem(
+                                       new FreenetURI(
+                                                       "USK at 
hM9XRwjXIzU8xTSBXNZvTn2KuvTSRFnVn4EER9FQnpM,gsth24O7ud4gL4NwNuYJDUqfaWASOG2zxZY~ChtgPxc,AQACAAE/Flog/4/"),
                                                        "Nextgen$", 
node.alerts));

-                       BookmarkCategory apps = (BookmarkCategory) defaultRoot
-                                       .addBookmark(new BookmarkCategory(
-                                                       "Freenet related 
software"));
-                       apps
-                                       .addBookmark(new BookmarkItem(
-                                                       new FreenetURI(
-                                                                       "USK at 
QRZAI1nSm~dAY2hTdzVWXmEhkaI~dso0OadnppBR7kE,wq5rHGBI7kpChBe4yRmgBChIGDug7Xa5SG9vYGXdxR0,AQACAAE/frost/1"),
+                       BookmarkCategory apps = (BookmarkCategory) 
defaultRoot.addBookmark(new BookmarkCategory("Freenet related software"));
+                       apps.addBookmark(new BookmarkItem(
+                                       new FreenetURI(
+                                                       "USK at 
QRZAI1nSm~dAY2hTdzVWXmEhkaI~dso0OadnppBR7kE,wq5rHGBI7kpChBe4yRmgBChIGDug7Xa5SG9vYGXdxR0,AQACAAE/frost/1"),
                                                        "Frost", node.alerts));

-                       sc.register("bookmarks", defaultRoot.toStrings(), 0, 
true, false,
-                                       "BookmarkManager.list", 
"BookmarkManager.listLong",
-                                       makeCB());
-
-                       if (!importOldBookmarks())
-                               makeCB().set(
-                                               
(sc.getStringArr("bookmarks").length == 0 ? defaultRoot
-                                                               .toStrings() : 
sc.getStringArr("bookmarks")));
-
+                       sc.register("bookmarks", defaultRoot.toStrings(), 0, 
true, false,"BookmarkManager.list", "BookmarkManager.listLong", configCB);
+                       
+                       configCB.set(sc.getStringArr("bookmarks"));
                } catch (MalformedURLException mue) {
-                       // just ignore that one
                } catch (InvalidConfigValueException icve) {
-                       // TODO
                        icve.printStackTrace();
                }
-
-               synchronized (this) {
-                       started = true;
-               }
        }

        public class BookmarkCallback implements StringArrCallback {
-               private final Pattern pattern = Pattern
-                               .compile("/(.*/)([^/]*)=([A-Z]{3}@.*).*");
+               private final Pattern pattern = 
Pattern.compile("/(.*/)([^/]*)=([A-Z]{3}@.*).*");

                public String[] get() {
-
                        synchronized (BookmarkManager.this) {
-
-                               return mainCategory.toStrings();
-
+                               return MAIN_CATEGORY.toStrings();
                        }
                }

                public void set(String[] newVals) throws 
InvalidConfigValueException {
-                       clear();
-
                        FreenetURI key;
                        for (int i = 0; i < newVals.length; i++) {
                                try {
@@ -143,10 +102,9 @@

        private class USKUpdatedCallback implements USKCallback {
                public void onFoundEdition(long edition, USK key) {
-                       BookmarkItems items = mainCategory.getAllItems();
+                       BookmarkItems items = MAIN_CATEGORY.getAllItems();
                        for (int i = 0; i < items.size(); i++) {
-
-                               if (!items.get(i).getKeyType().equals("USK"))
+                               if (!"USK".equals(items.get(i).getKeyType()))
                                        continue;

                                try {
@@ -164,39 +122,12 @@
                }
        }

-       private boolean importOldBookmarks() {
-               String[] strs = sc.getStringArr("bookmarks");
-
-               final Pattern pattern = Pattern.compile("([A-Z]{3}@.*)=(.*)");
-               for (int i = 0; i < strs.length; i++) {
-                       Matcher matcher = pattern.matcher(strs[i]);
-                       if (matcher.matches() && matcher.groupCount() == 2) {
-                               if (getCategoryByPath("/Imported/") == null)
-                                       addBookmark("/", new 
BookmarkCategory("Imported"), false);
-                               try {
-                                       addBookmark("/Imported/", new 
BookmarkItem(new FreenetURI(
-                                                       matcher.group(1)), 
matcher.group(2), node.alerts),
-                                                       false);
-                               } catch (MalformedURLException mue) {
-                               }
-                       } else
-                               return false;
-               }
-
-               node.storeConfig();
-               return true;
-       }
-
        public String l10n(String key) {
                return L10n.getString("BookmarkManager."+key);
        }

-       public BookmarkCallback makeCB() {
-               return new BookmarkCallback();
-       }
-
        public BookmarkCategory getMainCategory() {
-               return mainCategory;
+               return MAIN_CATEGORY;
        }

        public String parentPath(String path) {
@@ -226,45 +157,40 @@
                return null;
        }

-       public void addBookmark(String parentPath, Bookmark bookmark, boolean 
store)
-                       throws NullPointerException {
+       public void addBookmark(String parentPath, Bookmark bookmark, boolean 
store) {
                BookmarkCategory parent = getCategoryByPath(parentPath);
-               if (parent == null)
-                       throw new NullPointerException();
-               else {
-                       parent.addBookmark(bookmark);
-                       putPaths(parentPath + bookmark.getName()
-                                       + ((bookmark instanceof 
BookmarkCategory) ? "/" : ""),
-                                       bookmark);
+               parent.addBookmark(bookmark);
+               putPaths(parentPath + bookmark.getName()
+                               + ((bookmark instanceof BookmarkCategory) ? "/" 
: ""),
+                               bookmark);

-                       if (bookmark instanceof BookmarkItem && ((BookmarkItem) 
bookmark).getKeyType().equals("USK")) {
-                               try {
-                                       USK u = ((BookmarkItem) 
bookmark).getUSK();
-                                       this.node.uskManager.subscribe(u, 
this.uskcb, true, this);
-                               } catch (MalformedURLException mue) {
-                               }
+               if (bookmark instanceof BookmarkItem && ((BookmarkItem) 
bookmark).getKeyType().equals("USK")) {
+                       try {
+                               USK u = ((BookmarkItem) bookmark).getUSK();
+                               this.node.uskManager.subscribe(u, this.uskCB, 
true, this);
+                       } catch (MalformedURLException mue) {
                        }
                }
                if (store)
                        node.storeConfig();
        }

-       // TODO
        public void renameBookmark(String path, String newName) {
                Bookmark bookmark = getBookmarkByPath(path);
+               
+               String oldName = bookmark.getName();
+               String oldPath = '/' + oldName + '/';
+               String newPath = oldPath.substring(0, oldPath.indexOf(oldName)) 
+ newName + '/';
+
                bookmark.setName(newName);
-               if (bookmark instanceof BookmarkCategory) {
-                       try {
-                               makeCB().set(makeCB().get());
+               
+               bookmarks.remove(path);
+               bookmarks.put(newPath, bookmark);

-                       } catch (InvalidConfigValueException icve) {
-                       }
-               }
-
+               node.storeConfig();
        }

-       public void moveBookmark(String bookmarkPath, String newParentPath,
-                       boolean store) {
+       public void moveBookmark(String bookmarkPath, String newParentPath, 
boolean store) {
                Bookmark b = getBookmarkByPath(bookmarkPath);
                addBookmark(newParentPath, b, false);

@@ -273,7 +199,6 @@

                if (store)
                        node.storeConfig();
-
        }

        public void removeBookmark(String path, boolean store) {
@@ -286,15 +211,15 @@
                        for (int i = 0; i < cat.size(); i++) {
                                removeBookmark(
                                                path
-                                                               + 
cat.get(i).getName()
-                                                               + ((cat.get(i) 
instanceof BookmarkCategory) ? "/"
-                                                                               
: ""), false);
+                                               + cat.get(i).getName()
+                                               + ((cat.get(i) instanceof 
BookmarkCategory) ? "/"
+                                                               : ""), false);
                        }
                } else {
                        if (((BookmarkItem) 
bookmark).getKeyType().equals("USK")) {
                                try {
                                        USK u = ((BookmarkItem) 
bookmark).getUSK();
-                                       this.node.uskManager.unsubscribe(u, 
this.uskcb, true);
+                                       this.node.uskManager.unsubscribe(u, 
this.uskCB, true);
                                } catch (MalformedURLException mue) {
                                }
                        }
@@ -306,7 +231,6 @@

                if (store)
                        node.storeConfig();
-
        }

        public void moveBookmarkUp(String path, boolean store) {
@@ -366,17 +290,13 @@
        }

        public void clear() {
-
                removeBookmark("/", false);
                bookmarks.clear();
-
-               mainCategory = new BookmarkCategory("/");
-               bookmarks.put("/", mainCategory);
-
+               bookmarks.put("/", MAIN_CATEGORY);
        }

        public FreenetURI[] getBookmarkURIs() {
-               BookmarkItems items = mainCategory.getAllItems();
+               BookmarkItems items = MAIN_CATEGORY.getAllItems();
                FreenetURI[] uris = new FreenetURI[items.size()];
                for (int i = 0; i < items.size(); i++) {
                        uris[i] = items.get(i).getURI();
@@ -384,36 +304,4 @@

                return uris;
        }
-
-       /*
-        * public void addBookmark(Bookmark b, boolean store) {
-        * this.bookmarks.add(b); if (b.getKeyType().equals("USK")) { try { USK 
u =
-        * b.getUSK(); this.node.uskManager.subscribe(u, this.uskcb, true, 
this); }
-        * catch (MalformedURLException mue) {
-        *  } } if(store && started) node.storeConfig(); }
-        * 
-        * public void removeBookmark(Bookmark b, boolean store) { if
-        * (b.getKeyType().equals("USK")) { try { USK u = b.getUSK();
-        * this.node.uskManager.unsubscribe(u, this.uskcb, true); } catch
-        * (MalformedURLException mue) {
-        *  } } this.bookmarks.remove(b); if(store && started) 
node.storeConfig(); }
-        * 
-        * public void moveBookmarkDown (Bookmark b, boolean store) { int i =
-        * this.bookmarks.indexOf(b); if (i == -1) return;
-        * 
-        * Bookmark bk = (Bookmark)this.bookmarks.get(i); 
this.bookmarks.remove(i);
-        * this.bookmarks.add((i+1)%(this.bookmarks.size()+1), bk);
-        * 
-        * if(store && started) node.storeConfig(); }
-        * 
-        * public void moveBookmarkUp (Bookmark b, boolean store) { int i =
-        * this.bookmarks.indexOf(b); if (i == -1) return;
-        * 
-        * Bookmark bk = (Bookmark)this.bookmarks.get(i); 
this.bookmarks.remove(i);
-        * if (--i < 0) i = this.bookmarks.size(); this.bookmarks.add(i, bk);
-        * 
-        * if(store && started) node.storeConfig(); }
-        * 
-        * public int getSize() { return this.bookmarks.size(); }
-        */
 }

Modified: 
branches/freenet-jfk/src/freenet/config/FreenetFilePersistentConfig.java
===================================================================
--- branches/freenet-jfk/src/freenet/config/FreenetFilePersistentConfig.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/config/FreenetFilePersistentConfig.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -48,7 +48,7 @@
        public void store() {
                synchronized(this) {
                        if(!finishedInit) {
-                               Logger.error(this, "Initialization not 
finished, refusing to write config", new Exception("error"));
+                               Logger.minor(this, "Initialization not 
finished, refusing to write config", new Exception("error"));
                                return;
                        }
                }

Modified: branches/freenet-jfk/src/freenet/config/PersistentConfig.java
===================================================================
--- branches/freenet-jfk/src/freenet/config/PersistentConfig.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/config/PersistentConfig.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -39,12 +39,16 @@
                return 
exportFieldSet(Config.CONFIG_REQUEST_TYPE_CURRENT_SETTINGS, withDefaults);
        }

-       public synchronized SimpleFieldSet exportFieldSet(int 
configRequestType, boolean withDefaults) {
+       public SimpleFieldSet exportFieldSet(int configRequestType, boolean 
withDefaults) {
                SimpleFieldSet fs = new SimpleFieldSet(true);
-               Iterator configsIterator = configsByPrefix.keySet().iterator();
+               SubConfig[] configs;
+               synchronized(this) {
+                       // FIXME maybe keep a cache of this?
+                       configs = (SubConfig[]) 
configsByPrefix.values().toArray(new SubConfig[configsByPrefix.size()]);
+               }
                SubConfig current;
-               while (configsIterator.hasNext()) {
-                       current = (SubConfig) 
configsByPrefix.get(configsIterator.next());
+               for(int i=0;i<configs.length;i++) {
+                       current = configs[i];
                        SimpleFieldSet scfs = 
current.exportFieldSet(configRequestType, withDefaults);
                        fs.tput(current.prefix, scfs);
                }

Modified: branches/freenet-jfk/src/freenet/config/SubConfig.java
===================================================================
--- branches/freenet-jfk/src/freenet/config/SubConfig.java      2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/config/SubConfig.java      2007-08-21 
20:26:59 UTC (rev 14828)
@@ -7,6 +7,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import java.util.Map.Entry;

 import freenet.l10n.L10n;
 import freenet.support.Logger;
@@ -199,13 +200,16 @@

        public SimpleFieldSet exportFieldSet(int configRequestType, boolean 
withDefaults) {
                SimpleFieldSet fs = new SimpleFieldSet(true);
-               Set entrySet = map.entrySet();
-               Iterator i = entrySet.iterator();
+               // 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()]);
+               }
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR)
                        Logger.minor(this, "Prefix="+prefix);
-               while(i.hasNext()) {
-                       Map.Entry entry = (Map.Entry) i.next();
+               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)
@@ -261,6 +265,11 @@
                o.setValue(value);
        }

+       public void set(String name, boolean value) throws 
InvalidConfigValueException {
+               BooleanOption o = (BooleanOption) map.get(name);
+               o.set(value);
+       }
+
        /**
         * If the option's value is equal to the provided old default, then set 
it to the
         * new default. Used to deal with changes to important options where 
this is not
@@ -303,9 +312,14 @@
        }

        public String getRawOption(String name) {
-               if(config instanceof PersistentConfig)
-                       return ((PersistentConfig) 
config).origConfigFileContents.get(prefix + SimpleFieldSet.MULTI_LEVEL_CHAR + 
name);
-               else return null;
+               if(config instanceof PersistentConfig) {
+                       PersistentConfig pc = (PersistentConfig) config;
+                       if(pc.finishedInit)
+                               throw new 
IllegalStateException("getRawOption("+name+") on "+this+" but persistent config 
has been finishedInit() already!");
+                       SimpleFieldSet fs = pc.origConfigFileContents;
+                       if(fs == null) return null;
+                       return fs.get(prefix + SimpleFieldSet.MULTI_LEVEL_CHAR 
+ name);
+               } else return null;
        }

 }

Modified: branches/freenet-jfk/src/freenet/crypt/DSAPrivateKey.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/DSAPrivateKey.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/DSAPrivateKey.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -35,7 +35,7 @@
         BigInteger tempX;
         do {
             tempX = new NativeBigInteger(256, r);
-        } while (tempX.compareTo(g.getQ()) > -1);
+        } while (tempX.compareTo(g.getQ()) > -1 || 
tempX.equals(BigInteger.ZERO));
         this.x = tempX;
     }


Modified: branches/freenet-jfk/src/freenet/crypt/DiffieHellman.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/DiffieHellman.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/DiffieHellman.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -44,7 +44,7 @@
        private static class PrecalcBufferFill extends Thread {

                public PrecalcBufferFill() {
-                       setName("Diffie-Helman-Precalc");
+                       setName("Diffie-Hellman-Precalc");
                        setDaemon(true);
                }


Modified: branches/freenet-jfk/src/freenet/crypt/KEProtocol.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/KEProtocol.java      2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/KEProtocol.java      2007-08-21 
20:26:59 UTC (rev 14828)
@@ -9,7 +9,7 @@

 /**
  * Defines the interface that must be implemented by key-exchange protocols
- * such as RSA and Diffie-Helman
+ * such as RSA and Diffie-Hellman
  */
 public abstract class KEProtocol {
     protected RandomSource randomSource;

Modified: branches/freenet-jfk/src/freenet/crypt/PCFBMode.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/PCFBMode.java        2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/PCFBMode.java        2007-08-21 
20:26:59 UTC (rev 14828)
@@ -11,6 +11,11 @@
  * Control mechanism for the Periodic Cipher Feed Back mode.  This is
  * a CFB variant used apparently by a number of programs, including PGP. 
  * Thanks to Hal for suggesting it.
+ * 
+ * http://www.streamsec.com/pcfb1.pdf
+ * 
+ * NOTE: This is identical to CFB if block size = key size. As of Freenet 0.7, 
+ * we use it with block size = key size.
  *
  * @author Scott
  */

Modified: branches/freenet-jfk/src/freenet/crypt/SHA256.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/SHA256.java  2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/SHA256.java  2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -48,6 +48,7 @@
 import org.tanukisoftware.wrapper.WrapperManager;

 import freenet.node.Node;
+import freenet.node.NodeInitException;
 import freenet.support.HexUtil;
 import freenet.support.Logger;

@@ -372,7 +373,7 @@
                System.err.println("Check your JVM settings especially the 
JCE!"+e2);
                e2.printStackTrace();
                }
-               WrapperManager.stop(Node.EXIT_CRAPPY_JVM);
+               WrapperManager.stop(NodeInitException.EXIT_CRAPPY_JVM);
                throw new RuntimeException();
        }

@@ -386,8 +387,6 @@
                if(!(algo.equals("SHA-256") || algo.equals("SHA256")))
                        throw new IllegalArgumentException("Should be SHA-256 
but is "+algo);
                md256.reset();
-               if(Logger.shouldLog(Logger.DEBUG, SHA256.class))
-                       Logger.debug(SHA256.class, "Returning message digest 
"+md256, new Exception());
                digests.add(md256);
        }


Modified: branches/freenet-jfk/src/freenet/crypt/Yarrow.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/Yarrow.java  2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/Yarrow.java  2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -3,6 +3,7 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.crypt;

+import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -55,7 +56,7 @@
        private static final int Pg = 10;
        private final SecureRandom sr;

-       private final File seedfile; //A file to which seed data should be 
dumped periodically
+       public final File seedfile; //A file to which seed data should be 
dumped periodically

        public Yarrow() {
                this("prng.seed", "SHA1", "Rijndael",true);
@@ -121,6 +122,7 @@
                        }
                }

+               boolean isSystemEntropyAvailable = true;
                // Read some bits from /dev/urandom
                try {
                    fis = new FileInputStream("/dev/urandom");
@@ -131,6 +133,9 @@
                    consumeBytes(buf);
                } catch (Throwable t) {
                    Logger.normal(this, "Can't read /dev/urandom: "+t, t);
+                   // We can't read it; let's skip /dev/random and seed from 
SecureRandom.generateSeed()
+                   canBlock = true;
+                   isSystemEntropyAvailable = false;
                } finally {
                    try {
                        if(fis != null)
@@ -139,7 +144,7 @@
                            dis.close();
                    } catch (IOException e) {}
                }
-               if(canBlock) {
+               if(canBlock && isSystemEntropyAvailable) {
                    // Read some bits from /dev/random
                    try {
                        fis = new FileInputStream("/dev/random");
@@ -214,40 +219,70 @@
         */
        private void read_seed(File filename) {
                try {
-                       DataInputStream dis =
-                               new DataInputStream(new 
FileInputStream(filename));
-                       EntropySource seedFile = new EntropySource();
+                       FileInputStream fis = null;
+                       BufferedInputStream bis = null;
+                       DataInputStream dis = null;
+                       
                        try {
-                               for (int i = 0; i < 32; i++)
-                                       acceptEntropy(seedFile, dis.readLong(), 
64);
-                       } catch (EOFException f) {
+                               fis = new FileInputStream(filename);
+                               bis = new BufferedInputStream(fis);
+                               dis = new DataInputStream(bis);
+                               
+                               EntropySource seedFile = new EntropySource();
+                               try {
+                                       for (int i = 0; i < 32; i++)
+                                               acceptEntropy(seedFile, 
dis.readLong(), 64);
+                               } catch (EOFException f) {}
+
+                       } catch (IOException e) {
+                               Logger.error(this, "IOE trying to read the 
seedfile from disk : " + e.getMessage());
+                       } finally {
+                               if(dis != null) dis.close();
+                               if(bis != null) bis.close();
+                               if(fis != null) fis.close();
                        }
-                       dis.close();
-               } catch (Exception e) {
-               }
+               } catch (Exception e) {}
                fast_pool_reseed();
        }

        private long timeLastWroteSeed = -1;

-       private void write_seed(File filename) {
-               synchronized(this) {
-                       long now = System.currentTimeMillis();
-                       if(now - timeLastWroteSeed <= 60*1000) {
-                               return;
-                       } else
-                               timeLastWroteSeed = now;
+       public void write_seed(File filename) {
+               write_seed(filename, false);
+       }
+       
+       public void write_seed(File filename, boolean force) {
+               if(!force) {
+                       synchronized(this) {
+                               long now = System.currentTimeMillis();
+                               if(now - timeLastWroteSeed <= 60*60*1000 /* 
once per hour */) {
+                                       return;
+                               } else
+                                       timeLastWroteSeed = now;
+                       }
                }

                try {
-                       DataOutputStream dos =
-                               new DataOutputStream(new 
BufferedOutputStream(new FileOutputStream(filename)));
-                       for (int i = 0; i < 32; i++)
-                               dos.writeLong(nextLong());
-                       dos.close();
-               } catch (Exception e) {
-               }
-               
+                       FileOutputStream fos = null;
+                       BufferedOutputStream bos = null;
+                       DataOutputStream dos = null;
+
+                       try {
+                               fos = new FileOutputStream(filename);
+                               bos = new BufferedOutputStream(fos);
+                               dos = new DataOutputStream(bos);
+
+                               for (int i = 0; i < 32; i++)
+                                       dos.writeLong(nextLong());
+
+                       }catch (IOException e) {
+                               Logger.error(this, "IOE while saving the seed 
file! : "+e.getMessage());
+                       } finally {
+                               if(dos != null) dos.close();
+                               if(bos != null) bos.close();
+                               if(fos != null) fos.close();
+                       }
+               } catch (Exception e) {}
        }

        /**

Modified: branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Algorithm.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Algorithm.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Algorithm.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -25,7 +25,7 @@
  *
  * License is apparently available from 
http://www.cryptix.org/docs/license.html
  */
-public final class Rijndael_Algorithm // implicit no-argument constructor
+final class Rijndael_Algorithm // implicit no-argument constructor
 {
 //     Debugging methods and variables
 //     
...........................................................................
@@ -293,17 +293,6 @@
 //     
...........................................................................

        /**
-        * Convenience method to expand a user-supplied key material into a
-        * session key, assuming Rijndael's default block size (128-bit).
-        *
-        * @param k The 128/192/256-bit user-key to use.
-        * @exception  InvalidKeyException  If the key is invalid.
-        */
-       private static final Object makeKey (byte[] k) throws 
InvalidKeyException {
-               return makeKey(k, BLOCK_SIZE);
-       }
-
-       /**
         * Convenience method to encrypt exactly one block of plaintext, 
assuming
         * Rijndael's default block size (128-bit).
         *

Modified: 
branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Properties.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Properties.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael_Properties.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -26,7 +26,7 @@
  * @author  Jill Baker
  * @author  Raif S. Naffah
  */
-public class Rijndael_Properties // implicit no-argument constructor
+class Rijndael_Properties // implicit no-argument constructor
 {
 //     Constants and variables with relevant static code
 //     
...........................................................................

Modified: branches/freenet-jfk/src/freenet/io/Inet6AddressMatcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/Inet6AddressMatcher.java        
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/Inet6AddressMatcher.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -54,6 +54,7 @@
                                        bits = Math.max(bits - 8, 0);
                                }
                        }
+                       if(Arrays.equals(netmask, FULL_MASK)) netmask = 
FULL_MASK;
                } else {
                        address = convertToBytes(pattern);
                        netmask = FULL_MASK;

Modified: branches/freenet-jfk/src/freenet/io/NetworkInterface.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/NetworkInterface.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/NetworkInterface.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -29,6 +29,7 @@
 import java.util.StringTokenizer;

 import freenet.io.AddressIdentifier.AddressType;
+import freenet.support.Executor;
 import freenet.support.Logger;

 /**
@@ -60,9 +61,11 @@

        /** The number of running acceptors. */
        private int runningAcceptors = 0;
+       
+       private final Executor executor;

-       public static NetworkInterface create(int port, String bindTo, String 
allowedHosts) throws IOException {
-               NetworkInterface iface = new NetworkInterface(port, 
allowedHosts);
+       public static NetworkInterface create(int port, String bindTo, String 
allowedHosts, Executor executor) throws IOException {
+               NetworkInterface iface = new NetworkInterface(port, 
allowedHosts, executor);
                try {
                        iface.setBindTo(bindTo);
                } catch (IOException e) {
@@ -86,9 +89,10 @@
         * @param allowedHosts
         *            A comma-separated list of allowed addresses
         */
-       private NetworkInterface(int port, String allowedHosts) throws 
IOException {
+       private NetworkInterface(int port, String allowedHosts, Executor 
executor) throws IOException {
                this.port = port;
                this.allowedHosts = new AllowedHosts(allowedHosts);
+               this.executor = executor;
        }

        /**
@@ -128,19 +132,12 @@
                        acceptors.add(acceptor);
                }
                setSoTimeout(timeout);
-               List tempThreads = new ArrayList();
                synchronized (syncObject) {
                        Iterator acceptors = this.acceptors.iterator();
                        while (acceptors.hasNext()) {
-                               Thread t = new Thread((Acceptor) 
acceptors.next(), "Network Interface Acceptor");
-                               t.setDaemon(true);
-                               tempThreads.add(t);
+                               executor.execute((Acceptor) acceptors.next(), 
"Network Interface Acceptor");
                        }
                }
-               for (Iterator i = tempThreads.iterator(); i.hasNext();) {
-                       ((Thread) i.next()).start();
-                       runningAcceptors++;
-               }
        }

        public void setAllowedHosts(String allowedHosts) {

Modified: branches/freenet-jfk/src/freenet/io/comm/DMT.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/DMT.java   2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/DMT.java   2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -78,6 +78,7 @@
        public static final String TYPE = "type";
        public static final String PAYLOAD = "payload";
        public static final String COUNTER = "counter";
+       public static final String LINEAR_COUNTER = "linearCounter";
        public static final String RETURN_LOCATION = "returnLocation";
        public static final String BLOCK_HEADERS = "blockHeaders";
        public static final String DATA_INSERT_REJECTED_REASON = 
"dataInsertRejectedReason";
@@ -96,7 +97,26 @@
        public static final String MY_UID = "myUID";
        public static final String PEER_LOCATIONS = "peerLocations";
        public static final String PEER_UIDS = "peerUIDs";
-
+       public static final String BEST_LOCATIONS_NOT_VISITED = 
"bestLocationsNotVisited";
+       public static final String MAIN_JAR_KEY = "mainJarKey";
+       public static final String EXTRA_JAR_KEY = "extraJarKey";
+       public static final String REVOCATION_KEY = "revocationKey";
+       public static final String HAVE_REVOCATION_KEY = "haveRevocationKey";
+       public static final String MAIN_JAR_VERSION = "mainJarVersion";
+       public static final String EXTRA_JAR_VERSION = "extJarVersion";
+       public static final String REVOCATION_KEY_TIME_LAST_TRIED = 
"revocationKeyTimeLastTried";
+       public static final String REVOCATION_KEY_DNF_COUNT = 
"revocationKeyDNFCount";
+       public static final String REVOCATION_KEY_FILE_LENGTH = 
"revocationKeyFileLength";
+       public static final String MAIN_JAR_FILE_LENGTH = "mainJarFileLength";
+       public static final String EXTRA_JAR_FILE_LENGTH = "extraJarFileLength";
+       public static final String PING_TIME = "pingTime";
+       public static final String BWLIMIT_DELAY_TIME = "bwlimitDelayTime";
+       public static final String TIME = "time";
+       public static final String FORK_COUNT = "forkCount";
+       public static final String TIME_LEFT = "timeLeft";
+       public static final String PREV_UID = "prevUID";
+       public static final String OPENNET_NODEREF = "opennetNoderef";
+       
        //Diagnostic
        public static final MessageType ping = new MessageType("ping") {{
                addField(SEND_TIME, Long.class);
@@ -505,6 +525,18 @@
                return msg;
        }

+       public static final MessageType FNPRecentlyFailed = new 
MessageType("FNPRecentlyFailed") {{
+               addField(UID, Long.class);
+               addField(TIME_LEFT, Integer.class);
+       }};
+       
+       public static final Message createFNPRecentlyFailed(long id, int 
timeLeft) {
+               Message msg = new Message(FNPRecentlyFailed);
+               msg.set(UID, id);
+               msg.set(TIME_LEFT, timeLeft);
+               return msg;
+       }
+       
        public static final MessageType FNPCHKDataFound = new 
MessageType("FNPCHKDataFound") {{
                addField(UID, Long.class);
                addField(BLOCK_HEADERS, ShortBuffer.class);
@@ -685,6 +717,58 @@
                return msg;
        }

+       // Opennet completions (not sent to darknet nodes)
+       
+       /** Sent when a request to an opennet node is completed, but the data 
source does not want to 
+        * path fold */
+       public static MessageType FNPOpennetCompletedAck = new 
MessageType("FNPOpennetCompletedAck") {{
+               addField(UID, Long.class);
+       }};
+       
+       public static Message createFNPOpennetCompletedAck(long uid) {
+               Message msg = new Message(FNPOpennetCompletedAck);
+               msg.set(UID, uid);
+               return msg;
+       }
+       
+       /** Sent when a request completes and the data source does want to path 
fold */
+       public static MessageType FNPOpennetConnectDestination = new 
MessageType("FNPOpennetConnectDestination") {{
+               addField(UID, Long.class);
+               addField(OPENNET_NODEREF, ShortBuffer.class);
+       }};
+       
+       public static Message createFNPOpennetConnectDestination(long uid, 
ShortBuffer buf) {
+               Message msg = new Message(FNPOpennetConnectDestination);
+               msg.set(UID, uid);
+               msg.set(OPENNET_NODEREF, buf);
+               return msg;
+       }
+       
+       /** Path folding response */
+       public static MessageType FNPOpennetConnectReply = new 
MessageType("FNPOpennetConnectReply") {{
+               addField(UID, Long.class);
+               addField(OPENNET_NODEREF, ShortBuffer.class);
+       }};
+       
+       public static Message createFNPOpennetConnectReply(long uid, 
ShortBuffer buf) {
+               Message msg = new Message(FNPOpennetConnectReply);
+               msg.set(UID, uid);
+               msg.set(OPENNET_NODEREF, buf);
+               return msg;
+       }
+       
+       // Key offers (ULPRs)
+       
+       public static MessageType FNPOfferKey = new MessageType("FNPOfferKey") 
{{
+               addField(KEY, Key.class);
+       }};
+       
+       public static Message createFNPOfferKey(Key key) {
+               Message msg = new Message(FNPOfferKey);
+               msg.set(KEY, key);
+               return msg;
+       }
+       
        public static final MessageType FNPPing = new MessageType("FNPPing") {{
                addField(PING_SEQNO, Integer.class);
        }};
@@ -732,10 +816,11 @@
                addField(BEST_LOCATION, Double.class);
                addField(HTL, Short.class);
                addField(COUNTER, Short.class);
+               addField(LINEAR_COUNTER, Short.class);
        }};

        public static final Message createFNPProbeRequest(long uid, double 
target, double nearest, 
-                       double best, short htl, short counter) {
+                       double best, short htl, short counter, short 
linearCounter) {
                Message msg = new Message(FNPProbeRequest);
                msg.set(UID, uid);
                msg.set(TARGET_LOCATION, target);
@@ -743,6 +828,7 @@
                msg.set(BEST_LOCATION, best);
                msg.set(HTL, htl);
                msg.set(COUNTER, counter);
+               msg.set(LINEAR_COUNTER, linearCounter);
                return msg;
        }

@@ -757,9 +843,13 @@
                addField(MY_UID, Long.class);
                addField(PEER_LOCATIONS, ShortBuffer.class);
                addField(PEER_UIDS, ShortBuffer.class);
+               addField(FORK_COUNT, Short.class);
+               addField(LINEAR_COUNTER, Short.class);
+               addField(REASON, String.class);
+               addField(PREV_UID, Long.class);
        }};

-       public static Message createFNPProbeTrace(long uid, double target, 
double nearest, double best, short htl, short counter, double myLoc, long 
swapIdentifier, double[] peerLocs, long[] peerUIDs) {
+       public static Message createFNPProbeTrace(long uid, double target, 
double nearest, double best, short htl, short counter, double myLoc, long 
swapIdentifier, double[] peerLocs, long[] peerUIDs, short forkCount, short 
linearCounter, String reason, long prevUID) {
                Message msg = new Message(FNPProbeTrace);
                msg.set(UID, uid);
                msg.set(TARGET_LOCATION, target);
@@ -771,6 +861,10 @@
                msg.set(MY_UID, swapIdentifier);
                msg.set(PEER_LOCATIONS, new 
ShortBuffer(Fields.doublesToBytes(peerLocs)));
                msg.set(PEER_UIDS, new 
ShortBuffer(Fields.longsToBytes(peerUIDs)));
+               msg.set(FORK_COUNT, forkCount);
+               msg.set(LINEAR_COUNTER, linearCounter);
+               msg.set(REASON, reason);
+               msg.set(PREV_UID, prevUID);
                return msg;
        }

@@ -780,16 +874,18 @@
                addField(NEAREST_LOCATION, Double.class);
                addField(BEST_LOCATION, Double.class);
                addField(COUNTER, Short.class);
+               addField(LINEAR_COUNTER, Short.class);
        }};

        public static final Message createFNPProbeReply(long uid, double 
target, double nearest, 
-                       double best, short counter) {
+                       double best, short counter, short linearCounter) {
                Message msg = new Message(FNPProbeReply);
                msg.set(UID, uid);
                msg.set(TARGET_LOCATION, target);
                msg.set(NEAREST_LOCATION, nearest);
                msg.set(BEST_LOCATION, best);
                msg.set(COUNTER, counter);
+               msg.set(LINEAR_COUNTER, linearCounter);
                return msg;
        }

@@ -801,10 +897,11 @@
                addField(HTL, Short.class);
                addField(COUNTER, Short.class);
                addField(REASON, Short.class);
+               addField(LINEAR_COUNTER, Short.class);
        }};

        public static final Message createFNPProbeRejected(long uid, double 
target, double nearest, 
-                       double best, short counter, short htl, short reason) {
+                       double best, short counter, short htl, short reason, 
short linearCounter) {
                Message msg = new Message(FNPProbeRejected);
                msg.set(UID, uid);
                msg.set(TARGET_LOCATION, target);
@@ -813,6 +910,7 @@
                msg.set(HTL, htl);
                msg.set(COUNTER, counter);
                msg.set(REASON, reason);
+               msg.set(LINEAR_COUNTER, linearCounter);
                return msg;
        }

@@ -936,6 +1034,16 @@
                return msg;
        }

+       public static final MessageType FNPTime = new MessageType("FNPTime") {{
+               addField(TIME, Long.class);
+       }};
+       
+       public static final Message createFNPTime(long time) {
+               Message msg = new Message(FNPTime);
+               msg.set(TIME, time);
+               return msg;
+       }
+       
        public static final MessageType FNPVoid = new MessageType("FNPVoid") {{
        }};

@@ -944,6 +1052,130 @@
                return msg;
        }

+       // Update over mandatory. Not strictly part of FNP. Only goes between 
nodes at the link
+       // level, and will be sent, and parsed, even if the node is out of 
date. Should be stable 
+       // long-term.
+       
+       // Sent on connect
+       public static final MessageType UOMAnnounce = new 
MessageType("UOMAnnounce") {{
+               addField(MAIN_JAR_KEY, String.class);
+               addField(EXTRA_JAR_KEY, String.class);
+               addField(REVOCATION_KEY, String.class);
+               addField(HAVE_REVOCATION_KEY, Boolean.class);
+               addField(MAIN_JAR_VERSION, Long.class);
+               addField(EXTRA_JAR_VERSION, Long.class);
+               // Last time (ms ago) we had 3 DNFs in a row on the revocation 
checker.
+               addField(REVOCATION_KEY_TIME_LAST_TRIED, Long.class);
+               // Number of DNFs so far this time.
+               addField(REVOCATION_KEY_DNF_COUNT, Integer.class);
+               // For convenience, may change
+               addField(REVOCATION_KEY_FILE_LENGTH, Long.class);
+               addField(MAIN_JAR_FILE_LENGTH, Long.class);
+               addField(EXTRA_JAR_FILE_LENGTH, Long.class);
+               addField(PING_TIME, Integer.class);
+               addField(BWLIMIT_DELAY_TIME, Integer.class);
+       }};
+
+       public static final Message createUOMAnnounce(String mainKey, String 
extraKey, String revocationKey,
+                       boolean haveRevocation, long mainJarVersion, long 
extraJarVersion, long timeLastTriedRevocationFetch,
+                       int revocationDNFCount, long revocationKeyLength, long 
mainJarLength, long extraJarLength, int pingTime, int bwlimitDelayTime) {
+               Message msg = new Message(UOMAnnounce);
+               
+               msg.set(MAIN_JAR_KEY, mainKey);
+               msg.set(EXTRA_JAR_KEY, extraKey);
+               msg.set(REVOCATION_KEY, revocationKey);
+               msg.set(HAVE_REVOCATION_KEY, haveRevocation);
+               msg.set(MAIN_JAR_VERSION, mainJarVersion);
+               msg.set(EXTRA_JAR_VERSION, extraJarVersion);
+               msg.set(REVOCATION_KEY_TIME_LAST_TRIED, 
timeLastTriedRevocationFetch);
+               msg.set(REVOCATION_KEY_DNF_COUNT, revocationDNFCount);
+               msg.set(REVOCATION_KEY_FILE_LENGTH, revocationKeyLength);
+               msg.set(MAIN_JAR_FILE_LENGTH, mainJarLength);
+               msg.set(EXTRA_JAR_FILE_LENGTH, extraJarLength);
+               msg.set(PING_TIME, pingTime);
+               msg.set(BWLIMIT_DELAY_TIME, bwlimitDelayTime);
+               
+               return msg;
+       }
+       
+       public static final MessageType UOMRequestRevocation = new 
MessageType("UOMRequestRevocation") {{
+               addField(UID, Long.class);
+       }};
+       
+       public static final Message createUOMRequestRevocation(long uid) {
+               Message msg = new Message(UOMRequestRevocation);
+               msg.set(UID, uid);
+               return msg;
+       }
+       
+       public static final MessageType UOMRequestMain = new 
MessageType("UOMRequestMain") {{
+               addField(UID, Long.class);
+       }};
+       
+       public static final Message createUOMRequestMain(long uid) {
+               Message msg = new Message(UOMRequestMain);
+               msg.set(UID, uid);
+               return msg;
+       }
+       
+       public static final MessageType UOMRequestExtra = new 
MessageType("UOMRequestExtra") {{
+               addField(UID, Long.class);
+       }};
+       
+       public static final Message createUOMRequestExtra(long uid) {
+               Message msg = new Message(UOMRequestExtra);
+               msg.set(UID, uid);
+               return msg;
+       }
+       
+       public static final MessageType UOMSendingRevocation = new 
MessageType("UOMSendingRevocation") {{
+               addField(UID, Long.class);
+               // Probably excessive, but lengths are always long's, and 
wasting a few bytes here
+               // doesn't matter in the least, as it's very rarely called.
+               addField(FILE_LENGTH, Long.class);
+               addField(REVOCATION_KEY, String.class);
+       }};
+       
+       public static final Message createUOMSendingRevocation(long uid, long 
length, String key) {
+               Message msg = new Message(UOMSendingRevocation);
+               msg.set(UID, uid);
+               msg.set(FILE_LENGTH, length);
+               msg.set(REVOCATION_KEY, key);
+               return msg;
+       }
+       
+       public static final MessageType UOMSendingMain = new 
MessageType("UOMSendingMain") {{
+               addField(UID, Long.class);
+               addField(FILE_LENGTH, Long.class);
+               addField(MAIN_JAR_KEY, String.class);
+               addField(MAIN_JAR_VERSION, Integer.class);
+       }};
+       
+       public static final Message createUOMSendingMain(long uid, long length, 
String key, int version) {
+               Message msg = new Message(UOMSendingMain);
+               msg.set(UID, uid);
+               msg.set(FILE_LENGTH, length);
+               msg.set(MAIN_JAR_KEY, key);
+               msg.set(MAIN_JAR_VERSION, version);
+               return msg;
+       }
+       
+       public static final MessageType UOMSendingExtra = new 
MessageType("UOMSendingExtra") {{
+               addField(UID, Long.class);
+               addField(FILE_LENGTH, Long.class);
+               addField(EXTRA_JAR_KEY, String.class);
+               addField(EXTRA_JAR_VERSION, Integer.class);
+       }};
+       
+       public static final Message createUOMSendingExtra(long uid, long 
length, String key, int version) {
+               Message msg = new Message(UOMSendingExtra);
+               msg.set(UID, uid);
+               msg.set(FILE_LENGTH, length);
+               msg.set(EXTRA_JAR_KEY, key);
+               msg.set(EXTRA_JAR_VERSION, version);
+               return msg;
+       }
+       
        // Secondary messages (debug messages attached to primary messages)

        public static final MessageType FNPSwapNodeUIDs = new 
MessageType("FNPSwapNodeUIDs") {{
@@ -956,6 +1188,36 @@
                return msg;
        }

+       public static final Message createFNPSwapLocations(long[] uids) {
+               Message msg = new Message(FNPSwapNodeUIDs);
+               msg.set(NODE_UIDS, new ShortBuffer(Fields.longsToBytes(uids)));
+               return msg;
+       }
+       
+       // More permanent secondary messages (should perhaps be replaced by new 
main messages when stable)
+       
+       public static final MessageType FNPBestRoutesNotTaken = new 
MessageType("FNPBestRoutesNotTaken") {{
+               // Maybe this should be some sort of typed array?
+               // It's just a bunch of double's anyway.
+               addField(BEST_LOCATIONS_NOT_VISITED, ShortBuffer.class);
+       }};
+       
+       public static final Message createFNPBestRoutesNotTaken(byte[] locs) {
+               Message msg = new Message(FNPBestRoutesNotTaken);
+               msg.set(BEST_LOCATIONS_NOT_VISITED, new ShortBuffer(locs));
+               return msg;
+       }
+       
+       public static final Message createFNPBestRoutesNotTaken(double[] locs) {
+               return createFNPBestRoutesNotTaken(Fields.doublesToBytes(locs));
+       }
+       
+       public static Message createFNPBestRoutesNotTaken(Double[] doubles) {
+               double[] locs = new double[doubles.length];
+               for(int i=0;i<locs.length;i++) locs[i] = 
doubles[i].doubleValue();
+               return createFNPBestRoutesNotTaken(locs);
+       }
+
        public static void init() { }

 }

Modified: branches/freenet-jfk/src/freenet/io/comm/FreenetInetAddress.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/FreenetInetAddress.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/FreenetInetAddress.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -248,6 +248,13 @@
                        return _address.getHostAddress();
                }
        }
+       
+       public String toStringPrefNumeric() {
+               if(_address != null)
+                       return _address.getHostAddress();
+               else
+                       return hostname;
+       }

        public void writeToDataOutputStream(DataOutputStream dos, boolean 
oldForm) throws IOException {
                InetAddress addr = this.getAddress();

Modified: branches/freenet-jfk/src/freenet/io/comm/IOStatisticCollector.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/IOStatisticCollector.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/IOStatisticCollector.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -16,6 +16,7 @@
        private long lastrotate;

        private static IOStatisticCollector _currentSC;
+       private static boolean logDEBUG;
        private long totalbytesin;
        private long totalbytesout;
        private final LinkedHashMap targets;
@@ -28,6 +29,7 @@
                // This should only happen once
                //SNMPAgent.create();
                //SNMPStarter.initialize();
+               logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
        }

        private static IOStatisticCollector getSC() {
@@ -59,8 +61,8 @@
                synchronized(this) {
                        totalbytesout += (outbytes>0)?outbytes:0;
                        totalbytesin += (inbytes>0)?inbytes:0;
-                       if(Logger.shouldLog(Logger.MINOR, 
IOStatisticCollector.class))
-                               Logger.minor(IOStatisticCollector.class, 
"Add("+key+ ',' +inbytes+ ',' +outbytes+" -> "+totalbytesin+" : 
"+totalbytesout);
+                       if(logDEBUG)
+                               Logger.debug(IOStatisticCollector.class, 
"Add("+key+ ',' +inbytes+ ',' +outbytes+" -> "+totalbytesin+" : 
"+totalbytesout);
                }
        }


Copied: branches/freenet-jfk/src/freenet/io/comm/MessageCore.java (from rev 
14796, trunk/freenet/src/freenet/io/comm/MessageCore.java)
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/MessageCore.java                   
        (rev 0)
+++ branches/freenet-jfk/src/freenet/io/comm/MessageCore.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,502 @@
+/*
+ * Dijjer - A Peer to Peer HTTP Cache
+ * Copyright (C) 2004,2005 Change.Tv, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package freenet.io.comm;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Vector;
+
+import freenet.node.PeerNode;
+import freenet.node.Ticker;
+import freenet.support.Logger;
+import freenet.support.TimeUtil;
+
+public class MessageCore {
+
+       public static final String VERSION = "$Id: MessageCore.java,v 1.22 
2005/08/25 17:28:19 amphibian Exp $";
+       private static boolean logMINOR; 
+       private Dispatcher _dispatcher;
+       /** _filters serves as lock for both */
+       private final LinkedList _filters = new LinkedList();
+       private final LinkedList _unclaimed = new LinkedList();
+       private static final int MAX_UNMATCHED_FIFO_SIZE = 50000;
+       private static final long MAX_UNCLAIMED_FIFO_ITEM_LIFETIME = 
10*60*1000;  // 10 minutes; maybe this should be per message type??
+       // Every second, remove all timed out filters
+       private static final int FILTER_REMOVE_TIME = 1000;
+       private long startedTime;
+       
+       public synchronized long getStartedTime() {
+               return startedTime;
+       }
+
+       public MessageCore() {
+               _timedOutFilters = new Vector(32);
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       }
+
+       /**
+        * Decode a packet from data and a peer.
+        * Can be called by IncomingPacketFilter's.
+     * @param data
+     * @param offset
+     * @param length
+     * @param peer
+     */
+    public Message decodeSingleMessage(byte[] data, int offset, int length, 
PeerContext peer, int overhead) {
+        try {
+            return Message.decodeMessageFromPacket(data, offset, length, peer, 
overhead);
+        } catch (Throwable t) {
+            Logger.error(this, "Could not decode packet: "+t, t);
+            return null;
+        }
+    }
+
+    /** Only used by removeTimedOutFilters() - if future code uses this 
elsewhere, we need to
+     * reconsider its locking. */
+    private final Vector _timedOutFilters;
+    
+    public void start(final Ticker ticker) {
+       synchronized(this) {
+               startedTime = System.currentTimeMillis();
+       }
+       ticker.queueTimedJob(new Runnable() {
+
+                       public void run() {
+                               try {
+                                       removeTimedOutFilters();
+                               } catch (Throwable t) {
+                                       Logger.error(this, "Failed to remove 
timed out filters: "+t, t);
+                               } finally {
+                                       ticker.queueTimedJob(this, 
FILTER_REMOVE_TIME);
+                               }
+                               // TODO Auto-generated method stub
+                               
+                       }
+               
+       }, FILTER_REMOVE_TIME);
+    }
+    
+    /**
+     * Remove timed out filters.
+     */
+       void removeTimedOutFilters() {
+               long tStart = System.currentTimeMillis();
+               synchronized (_filters) {
+                       for (ListIterator i = _filters.listIterator(); 
i.hasNext();) {
+                               MessageFilter f = (MessageFilter) i.next();
+                               if (f.timedOut()) {
+                                       i.remove();
+                                       _timedOutFilters.add(f);
+                               } else { // Because _filters are in order of 
timeout, we
+                                       // can abort the iteration as soon as 
we find one that
+                                       // doesn't timeout
+                                       break;
+                               }
+                       }
+               }
+               
+               for(int i=0;i<_timedOutFilters.size();i++) {
+                       MessageFilter f = (MessageFilter) 
_timedOutFilters.get(i);
+                       f.setMessage(null);
+                       f.onTimedOut();
+               }
+               _timedOutFilters.clear();
+               
+               long tEnd = System.currentTimeMillis();
+               if(tEnd - tStart > 50) {
+                       if(tEnd - tStart > 3000)
+                               Logger.error(this, "removeTimedOutFilters took 
"+(tEnd-tStart)+"ms");
+                       else
+                               if(logMINOR) Logger.minor(this, 
"removeTimedOutFilters took "+(tEnd-tStart)+"ms");
+               }
+       }
+
+       /**
+        * Dispatch a message to a waiting filter, or feed it to the
+        * Dispatcher if none are found.
+        * @param m The Message to dispatch.
+        */
+       public void checkFilters(Message m, PacketSocketHandler from) {
+               long tStart = System.currentTimeMillis();
+               if(logMINOR) Logger.minor(this, "checkFilters: "+m+" from 
"+m.getSource());
+               if ((m.getSource()) instanceof PeerNode)
+               {
+                       
((PeerNode)m.getSource()).addToLocalNodeReceivedMessagesFromStatistic(m);
+               }
+               boolean matched = false;
+               if ((!(m.getSpec().equals(DMT.packetTransmit))) && logMINOR) {
+                       if ((m.getSpec().equals(DMT.ping) || 
m.getSpec().equals(DMT.pong)) && Logger.shouldLog(Logger.DEBUG, this)) {
+                               Logger.debug(this, "" + 
(System.currentTimeMillis() % 60000) + ' ' + from + " <- "
+                                               + m.getSource() + " : " + m);
+                       } else {
+                               if(logMINOR) Logger.minor(this, "" + 
(System.currentTimeMillis() % 60000) + ' ' + from + " <- "
+                                               + m.getSource() + " : " + m);
+                       }
+               }
+               MessageFilter match = null;
+               synchronized (_filters) {
+                       for (ListIterator i = _filters.listIterator(); 
i.hasNext();) {
+                               MessageFilter f = (MessageFilter) i.next();
+                               if (f.match(m)) {
+                                       matched = true;
+                                       i.remove();
+                                       match = f;
+                                       if(logMINOR) Logger.minor(this, 
"Matched: "+f);
+                                       break; // Only one match permitted per 
message
+                               }
+                       }
+               }
+               if(match != null) {
+                       match.setMessage(m);
+                       match.onMatched();
+               }
+               // Feed unmatched messages to the dispatcher
+               if ((!matched) && (_dispatcher != null)) {
+                   try {
+                       if(logMINOR) Logger.minor(this, "Feeding to dispatcher: 
"+m);
+                       matched = _dispatcher.handleMessage(m);
+                   } catch (Throwable t) {
+                       Logger.error(this, "Dispatcher threw "+t, t);
+                   }
+               }
+               // Keep the last few _unclaimed messages around in case the 
intended receiver isn't receiving yet
+               if (!matched) {
+                       if(logMINOR) Logger.minor(this, "Unclaimed: "+m);
+                   /** Check filters and then add to _unmatched is ATOMIC
+                    * It has to be atomic, because otherwise we can get a
+                    * race condition that results in timeouts on MFs.
+                    * 
+                    * Specifically:
+                    * - Thread A receives packet
+                    * - Thread A checks filters. It doesn't match any.
+                    * - Thread A feeds to Dispatcher.
+                    * - Thread B creates filter.
+                    * - Thread B checks _unmatched.
+                    * - Thread B adds filter.
+                    * - Thread B sleeps.
+                    * - Thread A returns from Dispatcher. Which didn't match.
+                    * - Thread A adds to _unmatched.
+                    * 
+                    * OOPS!
+                    * The only way to fix this is to have checking the
+                    * filters and unmatched be a single atomic operation.
+                    * Another race is possible if we merely recheck the
+                    * filters after we return from dispatcher, for example.
+                    */
+                       synchronized (_filters) {
+                               if(logMINOR) Logger.minor(this, "Rechecking 
filters and adding message");
+                               for (ListIterator i = _filters.listIterator(); 
i.hasNext();) {
+                                       MessageFilter f = (MessageFilter) 
i.next();
+                                       if (f.match(m)) {
+                                               matched = true;
+                                               match = f;
+                                               i.remove();
+                                               if(logMINOR) Logger.minor(this, 
"Matched: "+f);
+                                               break; // Only one match 
permitted per message
+                                       }
+                               }
+                               if(!matched) {
+                                   while (_unclaimed.size() > 
MAX_UNMATCHED_FIFO_SIZE) {
+                                       Message removed = 
(Message)_unclaimed.removeFirst();
+                                       long messageLifeTime = 
System.currentTimeMillis() - removed.localInstantiationTime;
+                                       if ((removed.getSource()) instanceof 
PeerNode) {
+                                           Logger.normal(this, "Dropping 
unclaimed from "+removed.getSource().getPeer()+", lived 
"+TimeUtil.formatTime(messageLifeTime, 2, true)+" (quantity)"+": "+removed);
+                                       } else {
+                                           Logger.normal(this, "Dropping 
unclaimed, lived "+TimeUtil.formatTime(messageLifeTime, 2, true)+" 
(quantity)"+": "+removed);
+                                       }
+                                   }
+                                   _unclaimed.addLast(m);
+                                   if(logMINOR) Logger.minor(this, "Done");
+                               }
+                       }
+                       if(match != null) {
+                               match.setMessage(m);
+                               match.onMatched();
+                       }
+               }
+               long tEnd = System.currentTimeMillis();
+               if(tEnd - tStart > 50) {
+                       if(tEnd - tStart > 3000)
+                               Logger.error(this, "checkFilters took 
"+(tEnd-tStart)+"ms with unclaimedFIFOSize of "+_unclaimed.size()+" for 
matched: "+matched);
+                       else
+                               if(logMINOR) Logger.minor(this, "checkFilters 
took "+(tEnd-tStart)+"ms with unclaimedFIFOSize of "+_unclaimed.size()+" for 
matched: "+matched);
+               }
+       }
+       
+       /** IncomingPacketFilter should call this when a node is disconnected. 
*/
+       public void onDisconnect(PeerContext ctx) {
+               Vector droppedFilters = null; // rare operation, we can waste 
objects for better locking
+           synchronized(_filters) {
+                       ListIterator i = _filters.listIterator();
+                       while (i.hasNext()) {
+                           MessageFilter f = (MessageFilter) i.next();
+                           if(f.matchesDroppedConnection(ctx)) {
+                               if(droppedFilters == null)
+                                       droppedFilters = new Vector();
+                               droppedFilters.add(f);
+                               i.remove();
+                           }
+                       }
+           }
+           if(droppedFilters != null) {
+               for(int i=0;i<droppedFilters.size();i++) {
+                       MessageFilter mf = (MessageFilter) 
droppedFilters.get(i);
+                       mf.onDroppedConnection(ctx);
+               }
+           }
+       }
+       
+       /** IncomingPacketFilter should call this when a node connects with a 
new boot ID */
+       public void onRestart(PeerContext ctx) {
+               Vector droppedFilters = null; // rare operation, we can waste 
objects for better locking
+           synchronized(_filters) {
+                       ListIterator i = _filters.listIterator();
+                       while (i.hasNext()) {
+                           MessageFilter f = (MessageFilter) i.next();
+                           if(f.matchesRestartedConnection(ctx)) {
+                               if(droppedFilters == null)
+                                       droppedFilters = new Vector();
+                               droppedFilters.add(f);
+                               i.remove();
+                           }
+                       }
+           }
+           if(droppedFilters != null) {
+               for(int i=0;i<droppedFilters.size();i++) {
+                       MessageFilter mf = (MessageFilter) 
droppedFilters.get(i);
+                       mf.onRestartedConnection(ctx);
+               }
+           }
+       }
+
+       public void addAsyncFilter(MessageFilter filter, 
AsyncMessageFilterCallback callback) throws DisconnectedException {
+               filter.setAsyncCallback(callback);
+               filter.onStartWaiting();
+               boolean logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
+               if(logDEBUG) Logger.debug(this, "Adding async filter "+filter+" 
for "+callback);
+               Message ret = null;
+               if(filter._source != null && (!filter._source.isConnected()) &&
+                       filter.matchesDroppedConnection(filter._source))
+                   throw new DisconnectedException();
+               // Check to see whether the filter matches any of the recently 
_unclaimed messages
+               // Drop any _unclaimed messages that the filter doesn't match 
that are also older than MAX_UNCLAIMED_FIFO_ITEM_LIFETIME
+               long now = System.currentTimeMillis();
+               long messageDropTime = now - MAX_UNCLAIMED_FIFO_ITEM_LIFETIME;
+               long messageLifeTime = 0;
+               synchronized (_filters) {
+                       if(logMINOR) Logger.minor(this, "Checking _unclaimed");
+                       for (ListIterator i = _unclaimed.listIterator(); 
i.hasNext();) {
+                               Message m = (Message) i.next();
+                               if (filter.match(m)) {
+                                       i.remove();
+                                       ret = m;
+                                       if(logMINOR) Logger.debug(this, 
"Matching from _unclaimed");
+                                       break;
+                               } else if (m.localInstantiationTime < 
messageDropTime) {
+                                       i.remove();
+                                       messageLifeTime = now - 
m.localInstantiationTime;
+                                       if ((m.getSource()) instanceof 
PeerNode) {
+                                               Logger.normal(this, "Dropping 
unclaimed from "+m.getSource().getPeer()+", lived 
"+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": "+m);
+                                       } else {
+                                               Logger.normal(this, "Dropping 
unclaimed, lived "+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": 
"+m);
+                                       }
+                               }
+                       }
+                       if (ret == null) {
+                               if(logMINOR) Logger.minor(this, "Not in 
_unclaimed");
+                           // Insert filter into filter list in order of 
timeout
+                               ListIterator i = _filters.listIterator();
+                               while (true) {
+                                       if (!i.hasNext()) {
+                                               i.add(filter);
+                                               if(logMINOR) Logger.minor(this, 
"Added at end");
+                                               break;
+                                       }
+                                       MessageFilter mf = (MessageFilter) 
i.next();
+                                       if (mf.getTimeout() > 
filter.getTimeout()) {
+                                               i.previous();
+                                               i.add(filter);
+                                               if(logMINOR) Logger.minor(this, 
"Added in middle - mf timeout="+mf.getTimeout()+" - my 
timeout="+filter.getTimeout());
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               if(ret != null) {
+                       filter.onMatched();
+                       filter.clearMatched();
+               }
+       }
+
+       /**
+        * Wait for a filter to trigger, or timeout. Blocks until either the 
trigger is activated, or it times
+        * out, or the peer is disconnected.
+        * @param filter The filter to wait for.
+        * @param ctr Byte counter to add bytes from the message to.
+        * @return Either a message, or null if the filter timed out.
+        * @throws DisconnectedException If the single peer being waited for 
disconnects.
+        */
+       public Message waitFor(MessageFilter filter, ByteCounter ctr) throws 
DisconnectedException {
+               boolean logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
+               if(logDEBUG) Logger.debug(this, "Waiting for "+filter);
+               long startTime = System.currentTimeMillis();
+               filter.onStartWaiting();
+               Message ret = null;
+               if(filter._source != null && (!filter._source.isConnected()) &&
+                       filter.matchesDroppedConnection(filter._source))
+                   throw new DisconnectedException();
+               // Check to see whether the filter matches any of the recently 
_unclaimed messages
+               // Drop any _unclaimed messages that the filter doesn't match 
that are also older than MAX_UNCLAIMED_FIFO_ITEM_LIFETIME
+               long now = System.currentTimeMillis();
+               long messageDropTime = now - MAX_UNCLAIMED_FIFO_ITEM_LIFETIME;
+               long messageLifeTime = 0;
+               synchronized (_filters) {
+                       if(logMINOR) Logger.minor(this, "Checking _unclaimed");
+                       for (ListIterator i = _unclaimed.listIterator(); 
i.hasNext();) {
+                               Message m = (Message) i.next();
+                               if (filter.match(m)) {
+                                       i.remove();
+                                       ret = m;
+                                       if(logMINOR) Logger.debug(this, 
"Matching from _unclaimed");
+                                       break;
+                               } else if (m.localInstantiationTime < 
messageDropTime) {
+                                       i.remove();
+                                       messageLifeTime = now - 
m.localInstantiationTime;
+                                       if ((m.getSource()) instanceof 
PeerNode) {
+                                               Logger.normal(this, "Dropping 
unclaimed from "+m.getSource().getPeer()+", lived 
"+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": "+m);
+                                       } else {
+                                               Logger.normal(this, "Dropping 
unclaimed, lived "+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": 
"+m);
+                                       }
+                               }
+                       }
+                       if (ret == null) {
+                               if(logMINOR) Logger.minor(this, "Not in 
_unclaimed");
+                           // Insert filter into filter list in order of 
timeout
+                               ListIterator i = _filters.listIterator();
+                               while (true) {
+                                       if (!i.hasNext()) {
+                                               i.add(filter);
+                                               if(logMINOR) Logger.minor(this, 
"Added at end");
+                                               break;
+                                       }
+                                       MessageFilter mf = (MessageFilter) 
i.next();
+                                       if (mf.getTimeout() > 
filter.getTimeout()) {
+                                               i.previous();
+                                               i.add(filter);
+                                               if(logMINOR) Logger.minor(this, 
"Added in middle - mf timeout="+mf.getTimeout()+" - my 
timeout="+filter.getTimeout());
+                                               break;
+                                       }
+                               }
+                       }
+               }
+               long tEnd = System.currentTimeMillis();
+               if(tEnd - now > 50) {
+                       if(tEnd - now > 3000)
+                               Logger.error(this, "waitFor _unclaimed 
iteration took "+(tEnd-now)+"ms with unclaimedFIFOSize of "+_unclaimed.size()+" 
for ret of "+ret);
+                       else
+                               if(logMINOR) Logger.minor(this, "waitFor 
_unclaimed iteration took "+(tEnd-now)+"ms with unclaimedFIFOSize of 
"+_unclaimed.size()+" for ret of "+ret);
+               }
+               // Unlock to wait on filter
+               // Waiting on the filter won't release the outer lock
+               // So we have to release it here
+               if(ret == null) {       
+                       if(logMINOR) Logger.minor(this, "Waiting...");
+                       synchronized (filter) {
+                               try {
+                                       // Precaution against filter getting 
matched between being added to _filters and
+                                       // here - bug discovered by Mason
+                                   boolean fmatched = false;
+                                   while(!(fmatched = (filter.matched() || 
(filter.droppedConnection() != null)))) {
+                                       long wait = 
filter.getTimeout()-System.currentTimeMillis();
+                                       if(wait > 0)
+                                           filter.wait(wait);
+                                       else break;
+                                       }
+                                   if(filter.droppedConnection() != null)
+                                       throw new DisconnectedException();
+                                   if(logMINOR) Logger.minor(this, "Matched: 
"+fmatched);
+                               } catch (InterruptedException e) {
+                               }
+                               ret = filter.getMessage();
+                               filter.clearMatched();
+                       }
+                       if(logDEBUG) Logger.debug(this, "Returning "+ret+" from 
"+filter);
+               } else {
+                       // Matched an unclaimed packet
+                       filter.onMatched();
+                       filter.clearMatched();
+               }
+               // Probably get rid...
+//             if (Dijjer.getDijjer().getDumpMessageWaitTimes() != null) {
+//                     
Dijjer.getDijjer().getDumpMessageWaitTimes().println(filter.toString() + "\t" + 
filter.getInitialTimeout() + "\t"
+//                                     + (System.currentTimeMillis() - 
startTime));
+//                     Dijjer.getDijjer().getDumpMessageWaitTimes().flush();
+//             }
+               long endTime = System.currentTimeMillis();
+               if(logDEBUG) Logger.debug(this, "Returning in 
"+(endTime-startTime)+"ms");
+               if((ctr != null) && (ret != null))
+                       ctr.receivedBytes(ret._receivedByteCount);
+               return ret;
+       }
+
+       /**
+        * Send a Message to a PeerContext.
+        * @throws NotConnectedException If we are not currently connected to 
the node.
+        */
+       public void send(PeerContext destination, Message m, ByteCounter ctr) 
throws NotConnectedException {
+           if(m.getSpec().isInternalOnly()) {
+               Logger.error(this, "Trying to send internal-only message "+m+" 
of spec "+m.getSpec(), new Exception("debug"));
+               return;
+           }
+               destination.sendAsync(m, null, 0, ctr);
+       }
+
+       public void setDispatcher(Dispatcher d) {
+               _dispatcher = d;
+       }
+
+       /**
+        * @return the number of received messages that are currently unclaimed
+        */
+       public int getUnclaimedFIFOSize() {
+               synchronized (_filters){
+                       return _unclaimed.size();
+               }
+       }
+       
+       public Map getUnclaimedFIFOMessageCounts() {
+               Map messageCounts = new HashMap();
+               synchronized(_filters) {
+                       for (ListIterator i = _unclaimed.listIterator(); 
i.hasNext();) {
+                               Message m = (Message) i.next();
+                               String messageName = m.getSpec().getName();
+                               Integer messageCount = (Integer) 
messageCounts.get(messageName);
+                               if (messageCount == null) {
+                                       messageCounts.put(messageName, new 
Integer(1) );
+                               } else {
+                                       messageCount = new 
Integer(messageCount.intValue() + 1);
+                                       messageCounts.put(messageName, 
messageCount );
+                               }
+                       }
+               }
+               return messageCounts;
+       }
+}

Modified: branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java 2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -29,7 +29,7 @@
  * To change the template for this generated type comment go to Window - 
Preferences - Java - Code Generation - Code and
  * Comments
  */
-public class MessageFilter {
+public final class MessageFilter {

     public static final String VERSION = "$Id: MessageFilter.java,v 1.7 
2005/08/25 17:28:19 amphibian Exp $";

@@ -41,6 +41,9 @@
     private Vector _fieldList = new Vector(1,1);
     PeerContext _source;
     private long _timeout;
+    /** If true, timeouts are relative to the start of waiting, if false, they 
are relative to
+     * the time of calling setTimeout() */
+    private boolean _timeoutFromWait;
     private int _initialTimeout;
     private MessageFilter _or;
     private Message _message;
@@ -52,13 +55,34 @@
         setTimeout(DEFAULT_TIMEOUT);
         _matchesDroppedConnections = true; // on by default
         _matchesRestartedConnections = true; // also on by default
+        _timeoutFromWait = true;
     }

     public static MessageFilter create() {
         return new MessageFilter();
     }

+    void onStartWaiting() {
+       synchronized(this) {
+               if(_initialTimeout > 0 && _timeoutFromWait)
+                       _timeout = System.currentTimeMillis() + _initialTimeout;
+       }
+       if(_or != null)
+               _or.onStartWaiting();
+    }
+    
     /**
+     * Set whether the timeout is relative to the creation of the filter, or 
the start of
+     * waitFor().
+     * @param b If true, the timeout is relative to the time at which 
setTimeout() was called,
+     * if false, it's relative to the start of waitFor().
+     */
+    public MessageFilter setTimeoutRelativeToCreation(boolean b) {
+       _timeoutFromWait = !b;
+       return this;
+    }
+    
+    /**
      * This filter will expire after the specificed amount of time. Note also 
that where two or more filters match the
      * same message, the one with the nearer expiry time will get priority
      *
@@ -118,7 +142,7 @@
        }

        public MessageFilter or(MessageFilter or) {
-               if((or != null) && (_or != null)) {
+               if((or != null) && (_or != null) && or != _or) {
                        // FIXME maybe throw? this is almost certainly a bug, 
and a nasty one too!
                        Logger.error(this, "or() replacement: "+_or+" -> "+or, 
new Exception("error"));
                }
@@ -142,6 +166,7 @@
        }

        public boolean match(Message m) {
+               if(timedOut()) return false;
                if ((_or != null) && (_or.match(m))) {
                        _matched = true;
                        return true;
@@ -171,11 +196,15 @@
                return _matched;
        }

+       /**
+        * Which connection dropped or was restarted?
+        */
        public PeerContext droppedConnection() {
            return _droppedConnection;
        }

        public boolean timedOut() {
+               if(_matched) return false;
                if(_callback != null && _callback.shouldTimeout())
                        _timeout = -1; // timeout immediately
                return _timeout < System.currentTimeMillis();
@@ -225,6 +254,7 @@
      * @param ctx
      */
     public synchronized void onDroppedConnection(PeerContext ctx) {
+       _droppedConnection = ctx;
                notifyAll();
     }

@@ -234,6 +264,7 @@
      * @param ctx
      */
     public synchronized void onRestartedConnection(PeerContext ctx) {
+       _droppedConnection = ctx;
                notifyAll();
     }


Modified: branches/freenet-jfk/src/freenet/io/comm/MessageType.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/MessageType.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/MessageType.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -92,7 +92,9 @@
                if (!(o instanceof MessageType)) {
                        return false;
                }
-               return ((MessageType) o)._name.equals(_name);
+               // We can only register one MessageType for each name.
+               // So we can do == here.
+               return ((MessageType) o)._name == _name;
        }

        public int hashCode() {

Copied: branches/freenet-jfk/src/freenet/io/comm/PacketSocketHandler.java (from 
rev 14796, trunk/freenet/src/freenet/io/comm/PacketSocketHandler.java)
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/PacketSocketHandler.java           
                (rev 0)
+++ branches/freenet-jfk/src/freenet/io/comm/PacketSocketHandler.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,32 @@
+/* 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.io.comm;
+
+import freenet.io.comm.Peer.LocalAddressException;
+
+/**
+ * Base class for UdpSocketHandler and any other datagram-based transports.
+ */
+public interface PacketSocketHandler extends SocketHandler {
+
+       /** The maximum size of a packet, not including transport layer headers 
*/
+       int getMaxPacketSize();
+
+       /**
+        * Send a block of encoded bytes to a peer. This is called by
+        * send, and by IncomingPacketFilter.processOutgoing(..).
+     * @param blockToSend The data block to send.
+     * @param destination The peer to send it to.
+     */
+    public void sendPacket(byte[] blockToSend, Peer destination, boolean 
allowLocalAddresses) throws LocalAddressException;
+
+    /**
+     * Get the size of the transport layer headers, for byte accounting 
purposes.
+     */
+       int getHeadersLength();
+
+       /** Set the decryption filter to which incoming packets will be fed */
+       public void setLowLevelFilter(IncomingPacketFilter f);
+
+}

Modified: branches/freenet-jfk/src/freenet/io/comm/Peer.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/Peer.java  2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/Peer.java  2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -92,6 +92,7 @@

     public Peer(FreenetInetAddress addr, int port) {
        this.addr = addr;
+       if(addr == null) throw new NullPointerException();
        this._port = port;
        }

@@ -194,4 +195,11 @@
        public boolean isRealInternetAddress(boolean lookup, boolean 
defaultVal) {
                return addr.isRealInternetAddress(lookup, defaultVal);
        }
+
+       /**
+        * Get the address:port string, but prefer numeric IPs - don't return 
the name.
+        */
+       public String toStringPrefNumeric() {
+               return addr.toStringPrefNumeric()+':'+_port;
+       }
 }
\ No newline at end of file

Modified: branches/freenet-jfk/src/freenet/io/comm/PeerContext.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/PeerContext.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/PeerContext.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -4,6 +4,7 @@
 package freenet.io.comm;

 import freenet.io.xfer.PacketThrottle;
+import freenet.node.OutgoingPacketMangler;

 /**
  * @author amphibian
@@ -36,4 +37,10 @@
        /** Get the PacketThrottle for the node's current address for the 
standard packet size (if the 
         * address changes then we get a new throttle). */ 
        public PacketThrottle getThrottle();
+
+       /** Get the SocketHandler which handles incoming packets from this node 
*/
+       SocketHandler getSocketHandler();
+       
+       /** Get the OutgoingPacketMangler which encrypts outgoing packets to 
this node */
+       OutgoingPacketMangler getOutgoingMangler();
 }

Modified: branches/freenet-jfk/src/freenet/io/comm/RetrievalException.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/RetrievalException.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/RetrievalException.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -39,6 +39,7 @@
     public static final int ALREADY_CACHED = 6;
     public static final int SENDER_DISCONNECTED = 7;
     public static final int NO_DATAINSERT = 8;
+    public static final int CANCELLED_BY_RECEIVER = 9;

        int _reason;
        String _cause;

Copied: branches/freenet-jfk/src/freenet/io/comm/SocketHandler.java (from rev 
14796, trunk/freenet/src/freenet/io/comm/SocketHandler.java)
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/SocketHandler.java                 
        (rev 0)
+++ branches/freenet-jfk/src/freenet/io/comm/SocketHandler.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,14 @@
+/* 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.io.comm;
+
+/**
+ * Base class for all transports. We have a single object of this type for 
both incoming and
+ * outgoing packets, but multiple instances for different instances of the 
transport e.g. on
+ * different ports, with different crypto backends etc.
+ * @author toad
+ */
+public interface SocketHandler {
+
+}

Copied: branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java (from 
rev 14796, trunk/freenet/src/freenet/io/comm/UdpSocketHandler.java)
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java              
                (rev 0)
+++ branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,375 @@
+package freenet.io.comm;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.util.Random;
+
+import org.tanukisoftware.wrapper.WrapperManager;
+
+import freenet.io.comm.Peer.LocalAddressException;
+import freenet.node.LoggingConfigHandler;
+import freenet.node.Node;
+import freenet.node.NodeInitException;
+import freenet.support.FileLoggerHook;
+import freenet.support.Logger;
+import freenet.support.OOMHandler;
+
+public class UdpSocketHandler extends Thread implements PacketSocketHandler {
+
+       private final DatagramSocket _sock;
+       private final InetAddress _bindTo;
+       private IncomingPacketFilter lowLevelFilter;
+       /** RNG for debugging, used with _dropProbability.
+        * NOT CRYPTO SAFE. DO NOT USE FOR THINGS THAT NEED CRYPTO SAFE RNG!
+        */
+       private Random dropRandom;
+       /** If >0, 1 in _dropProbability chance of dropping a packet; for 
debugging */
+       private int _dropProbability;
+       // Icky layer violation, but we need to know the Node to work around 
the EvilJVMBug.
+       private final Node node;
+       private static boolean logMINOR; 
+       private volatile int lastTimeInSeconds;
+       private boolean _isDone;
+       private boolean _active = true;
+       
+       public UdpSocketHandler(int listenPort, InetAddress bindto, Node node) 
throws SocketException {
+               super("MessageCore packet receiver thread on port " + 
listenPort);
+               this.node = node;
+               _bindTo = bindto;
+                   // Keep the Updater code in, just commented out, for now
+                   // We may want to be able to do on-line updates.
+//                     if (Updater.hasResource()) {
+//                             _sock = (DatagramSocket) Updater.getResource();
+//                     } else {
+               _sock = new DatagramSocket(listenPort, bindto);
+               int sz = _sock.getReceiveBufferSize();
+               if(sz < 32768)
+                       _sock.setReceiveBufferSize(32768);
+               try {
+                       // Exit reasonably quickly
+                       _sock.setSoTimeout(1000);
+               } catch (SocketException e) {
+                       throw new RuntimeException(e);
+               }
+//                     }
+               // Only used for debugging, no need to seed from Yarrow
+               dropRandom = new Random();
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       }
+
+       /** Must be called, or we will NPE in run() */
+       public void setLowLevelFilter(IncomingPacketFilter f) {
+           lowLevelFilter = f;
+       }
+       
+       public InetAddress getBindTo() {
+               return _bindTo;
+       }
+       
+       public void run() { // Listen for packets
+               try {
+                       runLoop();
+               } catch (Throwable t) {
+                       // Impossible? It keeps on exiting. We get the below,
+                       // but not this...
+                       try {
+                               System.err.print(t.getClass().getName());
+                               System.err.println();
+                       } catch (Throwable tt) {};
+                       try {
+                               System.err.print(t.getMessage());
+                               System.err.println();
+                       } catch (Throwable tt) {};
+                       try {
+                               System.gc();
+                               System.runFinalization();
+                               System.gc();
+                               System.runFinalization();
+                       } catch (Throwable tt) {}
+                       try {
+                               Runtime r = Runtime.getRuntime();
+                               System.err.print(r.freeMemory());
+                               System.err.println();
+                               System.err.print(r.totalMemory());
+                               System.err.println();
+                       } catch (Throwable tt) {};
+                       try {
+                               t.printStackTrace();
+                       } catch (Throwable tt) {};
+               } finally {
+                       System.err.println("run() exiting for UdpSocketHandler 
on port "+_sock.getLocalPort());
+                       Logger.error(this, "run() exiting for UdpSocketHandler 
on port "+_sock.getLocalPort());
+                       synchronized (this) {
+                               _isDone = true;
+                               notifyAll();
+                       }
+               }
+       }
+
+       private void runLoop() {
+               byte[] buf = new byte[MAX_RECEIVE_SIZE];
+               DatagramPacket packet = new DatagramPacket(buf, buf.length);
+               while (/*_active*/true) {
+                       synchronized(this) {
+                               if(!_active) return; // Finished
+                       }
+                       try {
+                               lastTimeInSeconds = (int) 
(System.currentTimeMillis() / 1000);
+                               realRun(packet);
+            } catch (OutOfMemoryError e) {
+                               OOMHandler.handleOOM(e);
+                               System.err.println("Will retry above failed 
operation...");
+                       } catch (Throwable t) {
+                               System.err.println("Caught "+t);
+                               t.printStackTrace(System.err);
+                               Logger.error(this, "Caught " + t, t);
+                       }
+               }
+       }
+       
+       private void realRun(DatagramPacket packet) {
+               // Single receiving thread
+               boolean gotPacket = getPacket(packet);
+               if (gotPacket) {
+                       long startTime = System.currentTimeMillis();
+                       Peer peer = new Peer(packet.getAddress(), 
packet.getPort());
+                       long endTime = System.currentTimeMillis();
+                       if(endTime - startTime > 50) {
+                               if(endTime-startTime > 3000)
+                                       Logger.error(this, "packet creation 
took "+(endTime-startTime)+"ms");
+                               else
+                                       if(logMINOR) Logger.minor(this, "packet 
creation took "+(endTime-startTime)+"ms");
+                       }
+                       byte[] data = packet.getData();
+                       int offset = packet.getOffset();
+                       int length = packet.getLength();
+                       try {
+                               if(logMINOR) Logger.minor(this, "Processing 
packet of length "+length+" from "+peer);
+                               startTime = System.currentTimeMillis();
+                               lowLevelFilter.process(data, offset, length, 
peer);
+                               endTime = System.currentTimeMillis();
+                               if(endTime - startTime > 50) {
+                                       if(endTime-startTime > 3000)
+                                               Logger.error(this, "processing 
packet took "+(endTime-startTime)+"ms");
+                                       else
+                                               if(logMINOR) Logger.minor(this, 
"processing packet took "+(endTime-startTime)+"ms");
+                               }
+                               if(logMINOR) Logger.minor(this,
+                                               "Successfully handled packet 
length " + length);
+                       } catch (Throwable t) {
+                               Logger.error(this, "Caught " + t + " from "
+                                               + lowLevelFilter, t);
+                       }
+               } else if(logMINOR) Logger.minor(this, "Null packet");
+       }
+       
+    // FIXME necessary to deal with bugs around build 1000; arguably necessary 
to deal with large node names in connection setup
+    // Revert to 1500?
+    private static final int MAX_RECEIVE_SIZE = 2048;
+    
+    private boolean getPacket(DatagramPacket packet) {
+               try {
+                       _sock.receive(packet);
+                       // TODO: keep?
+                       IOStatisticCollector.addInfo(packet.getAddress() + ":" 
+ packet.getPort(),
+                                       packet.getLength(), 0);
+               } catch (SocketTimeoutException e1) {
+                       return false;
+               } catch (IOException e2) {
+                       throw new RuntimeException(e2);
+               }
+               if(logMINOR) Logger.minor(this, "Received packet");
+               return true;
+       }
+
+       /**
+        * Send a block of encoded bytes to a peer. This is called by
+        * send, and by IncomingPacketFilter.processOutgoing(..).
+     * @param blockToSend The data block to send.
+     * @param destination The peer to send it to.
+     */
+    public void sendPacket(byte[] blockToSend, Peer destination, boolean 
allowLocalAddresses) throws LocalAddressException {
+       assert(blockToSend != null);
+               // there should be no DNS needed here, but go ahead if we can, 
but complain doing it
+               if( destination.getAddress(false, allowLocalAddresses) == null 
) {
+                       Logger.error(this, "Tried sending to destination 
without pre-looked up IP address(needs a real Peer.getHostname()): null:" + 
destination.getPort(), new Exception("error"));
+                       if( destination.getAddress(true, allowLocalAddresses) 
== null ) {
+                               Logger.error(this, "Tried sending to bad 
destination address: null:" + destination.getPort(), new Exception("error"));
+                               return;
+                       }
+               }
+               if (_dropProbability > 0) {
+                       if (dropRandom.nextInt() % _dropProbability == 0) {
+                               if(logMINOR) Logger.minor(this, "DROPPED: " + 
_sock.getLocalPort() + " -> " + destination.getPort());
+                               return;
+                       }
+               }
+               InetAddress address = destination.getAddress(false, 
allowLocalAddresses);
+               assert(address != null);
+               int port = destination.getPort();
+               DatagramPacket packet = new DatagramPacket(blockToSend, 
blockToSend.length);
+               packet.setAddress(address);
+               packet.setPort(port);
+               
+               // TODO: keep?
+               // packet.length() is simply the size of the buffer, it knows 
nothing of UDP headers
+               IOStatisticCollector.addInfo(address + ":" + port, 0, 
blockToSend.length + UDP_HEADERS_LENGTH); 
+               
+               try {
+                       _sock.send(packet);
+               } catch (IOException e) {
+                       if(packet.getAddress() instanceof Inet6Address)
+                               Logger.normal(this, "Error while sending packet 
to IPv6 address: "+destination+": "+e, e);
+                       else
+                               Logger.error(this, "Error while sending packet 
to " + destination+": "+e, e);
+               }
+    }
+
+       // CompuServe use 1400 MTU; AOL claim 1450; DFN at home use 1448.
+       // http://info.aol.co.uk/broadband/faqHomeNetworking.adp
+       // 
http://www.compuserve.de/cso/hilfe/linux/hilfekategorien/installation/contentview.jsp?conid=385700
+       // http://www.studenten-ins-netz.net/inhalt/service_faq.html
+       // officially GRE is 1476 and PPPoE is 1492.
+       // unofficially, PPPoE is often 1472 (seen in the wild). Also PPPoATM 
is sometimes 1472.
+    static final int MAX_ALLOWED_MTU = 1400;
+    // FIXME this is different for IPv6 (check all uses of constant when 
fixing)
+    public static final int UDP_HEADERS_LENGTH = 28;
+    
+    /**
+     * @return The maximum packet size supported by this SocketManager, not 
including transport (UDP/IP) headers.
+     */
+    public int getMaxPacketSize() { //FIXME: what about passing a peerNode 
though and doing it on a per-peer basis?
+       final int minAdvertisedMTU = node.ipDetector.getMinimumDetectedMTU();
+       
+       // We don't want the MTU detection thingy to prevent us to send 
PacketTransmits!
+       if(minAdvertisedMTU < 1100){
+               Logger.error(this, "It shouldn't happen : we disabled the MTU 
detection algorithm because the advertised MTU is smallish !! 
("+node.ipDetector.getMinimumDetectedMTU()+')'); 
+               return MAX_ALLOWED_MTU - UDP_HEADERS_LENGTH;
+       } else
+               return Math.min(MAX_ALLOWED_MTU, minAdvertisedMTU) - 
UDP_HEADERS_LENGTH;
+       // UDP/IP header is 28 bytes.
+    }
+
+       public void start() {
+               start(false);
+       }
+       
+       public void start(boolean disableHangChecker) {
+               lastTimeInSeconds = (int) (System.currentTimeMillis() / 1000);
+               setDaemon(true);
+               setPriority(Thread.MAX_PRIORITY);
+               super.start();
+               if(!disableHangChecker) {
+                       Thread checker = new Thread(new USMChecker(), 
"MessageCore$USMChecker");
+                       checker.setDaemon(true);
+                       checker.setPriority(Thread.MAX_PRIORITY);
+                       checker.start();
+               }
+       }
+       
+       public class USMChecker implements Runnable {
+               public void run() {
+                       while(true) {
+                               if(_isDone) return; // don't synchronize 
because don't want to deadlock - this is our recovery mechanism
+                               logMINOR = Logger.shouldLog(Logger.MINOR, 
UdpSocketHandler.this);
+                               try {
+                                       Thread.sleep(10*1000);
+                               } catch (InterruptedException e) {
+                                       // Ignore
+                               }
+                               if(UdpSocketHandler.this.isAlive()) {
+                                       if(logMINOR) Logger.minor(this, "PING 
on "+UdpSocketHandler.this);
+                                       long time = System.currentTimeMillis();
+                                       int timeSecs = (int) (time / 1000);
+                                       if(timeSecs - lastTimeInSeconds > 3*60) 
{
+                                               
+                                               // USM has hung.
+                                               // Probably caused by the 
EvilJVMBug (see PacketSender).
+                                               // We'd better restart... :(
+                                               
+                                               LoggingConfigHandler lch = 
Node.logConfigHandler;
+                                               FileLoggerHook flh = lch == 
null ? null : lch.getFileLoggerHook();
+                                               boolean hasRedirected = flh == 
null ? false : flh.hasRedirectedStdOutErrNoLock();
+                                               
+                                               if(!hasRedirected)
+                                                       
System.err.println("Restarting node: MessageCore froze for 3 minutes!");
+                                               
+                                               try {
+                                                       
if(node.isUsingWrapper()){
+                                                               
WrapperManager.requestThreadDump();
+                                                               
WrapperManager.restart();
+                                                       }else{
+                                                               
if(!hasRedirected)
+                                                                       
System.err.println("Exiting on deadlock, but not running in the wrapper! Please 
restart the node manually.");
+                                                               
+                                                               // No wrapper : 
we don't want to let it harm the network!
+                                                               node.exit("USM 
deadlock");
+                                                       }
+                                               } catch (Throwable t) {
+                                                       if(!hasRedirected) {
+                                                               
System.err.println("Error : can't restart the node : consider installing the 
wrapper. PLEASE REPORT THAT ERROR TO devl at freenetproject.org");
+                                                               
t.printStackTrace();
+                                                       }
+                                                       node.exit("USM deadlock 
and error");
+                                               }
+                                       }
+                               } else {
+                                       if(_isDone) return;
+                                       // Final check
+                                       synchronized(this) {
+                                               if(_isDone) return;
+                                       }
+                                       Logger.error(this, "MAIN LOOP 
TERMINATED");
+                                       System.err.println("MAIN LOOP 
TERMINATED!");
+                                       
node.exit(NodeInitException.EXIT_MAIN_LOOP_LOST);
+                               }
+                       }
+               }
+       }
+
+    public void close(boolean exit) {
+       Logger.normal(this, "Closing.", new Exception("error"));
+               synchronized (this) {
+                       _active = false;
+                       while (!_isDone) {
+                               try {
+                                       wait(2000);
+                               } catch (InterruptedException e) {
+                                       e.printStackTrace();
+                               }
+                       }
+               }
+               if (exit) {
+                       _sock.close();
+               } else {
+                   Logger.fatal(this, 10, "Not implemented: close(false)");
+                       //Updater.saveResource(_sock);
+               }
+       }
+    
+       public int getDropProbability() {
+               return _dropProbability;
+       }
+       
+       public void setDropProbability(int dropProbability) {
+               _dropProbability = dropProbability;
+       }
+
+    public int getPortNumber() {
+        return _sock.getLocalPort();
+    }
+
+       public String toString() {
+               return _sock.getLocalAddress() + ":" + _sock.getLocalPort();
+       }
+
+       public int getHeadersLength() {
+               return UDP_HEADERS_LENGTH;
+       }
+
+}

Deleted: branches/freenet-jfk/src/freenet/io/comm/UdpSocketManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/UdpSocketManager.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/comm/UdpSocketManager.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,851 +0,0 @@
-/*
- * Dijjer - A Peer to Peer HTTP Cache
- * Copyright (C) 2004,2005 Change.Tv, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-package freenet.io.comm;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-import org.tanukisoftware.wrapper.WrapperManager;
-
-import freenet.io.comm.Peer.LocalAddressException;
-import freenet.node.LoggingConfigHandler;
-import freenet.node.Node;
-import freenet.node.PeerNode;
-import freenet.support.FileLoggerHook;
-import freenet.support.Logger;
-import freenet.support.OOMHandler;
-import freenet.support.TimeUtil;
-
-public class UdpSocketManager extends Thread {
-
-       public static final String VERSION = "$Id: UdpSocketManager.java,v 1.22 
2005/08/25 17:28:19 amphibian Exp $";
-       private static boolean logMINOR; 
-       private Dispatcher _dispatcher;
-       private final DatagramSocket _sock;
-       /** _filters serves as lock for both */
-       private final LinkedList _filters = new LinkedList();
-       private final LinkedList _unclaimed = new LinkedList();
-       private int _dropProbability;
-       private IncomingPacketFilter lowLevelFilter;
-       /** RNG for debugging, used with _dropProbability.
-        * NOT CRYPTO SAFE. DO NOT USE FOR THINGS THAT NEED CRYPTO SAFE RNG!
-        */
-       private Random dropRandom;
-       private boolean _isDone;
-       private static UdpSocketManager _usm;
-       private static final int MAX_UNMATCHED_FIFO_SIZE = 50000;
-       private static final long MAX_UNCLAIMED_FIFO_ITEM_LIFETIME = 
60*60*1000;  // 1 hour
-       private volatile int lastTimeInSeconds;
-       private final InetAddress _bindTo;
-
-       // Icky layer violation, but we need to know the Node to work around 
the EvilJVMBug.
-       private final Node node;
-       
-       public void start() {
-               start(false);
-       }
-       
-       public void start(boolean disableHangChecker) {
-               lastTimeInSeconds = (int) (System.currentTimeMillis() / 1000);
-               setDaemon(true);
-               setPriority(Thread.MAX_PRIORITY);
-               super.start();
-               if(!disableHangChecker) {
-                       Thread checker = new Thread(new USMChecker(), 
"UdpSocketManager$USMChecker");
-                       checker.setDaemon(true);
-                       checker.setPriority(Thread.MAX_PRIORITY);
-                       checker.start();
-               }
-       }
-       
-       public class USMChecker implements Runnable {
-               public void run() {
-                       while(true) {
-                               logMINOR = Logger.shouldLog(Logger.MINOR, 
UdpSocketManager.this);
-                               try {
-                                       Thread.sleep(10*1000);
-                               } catch (InterruptedException e) {
-                                       // Ignore
-                               }
-                               if(UdpSocketManager.this.isAlive()) {
-                                       if(logMINOR) Logger.minor(this, "PING 
on "+UdpSocketManager.this);
-                                       long time = System.currentTimeMillis();
-                                       int timeSecs = (int) (time / 1000);
-                                       if(timeSecs - lastTimeInSeconds > 3*60) 
{
-                                               
-                                               // USM has hung.
-                                               // Probably caused by the 
EvilJVMBug (see PacketSender).
-                                               // We'd better restart... :(
-                                               
-                                               LoggingConfigHandler lch = 
Node.logConfigHandler;
-                                               FileLoggerHook flh = lch == 
null ? null : lch.getFileLoggerHook();
-                                               boolean hasRedirected = flh == 
null ? false : flh.hasRedirectedStdOutErrNoLock();
-                                               
-                                               if(!hasRedirected)
-                                                       
System.err.println("Restarting node: UdpSocketManager froze for 3 minutes!");
-                                               
-                                               try {
-                                                       
if(node.isUsingWrapper()){
-                                                               
WrapperManager.requestThreadDump();
-                                                               
WrapperManager.restart();
-                                                       }else{
-                                                               
if(!hasRedirected)
-                                                                       
System.err.println("Exiting on deadlock, but not running in the wrapper! Please 
restart the node manually.");
-                                                               
-                                                               // No wrapper : 
we don't want to let it harm the network!
-                                                               node.exit("USM 
deadlock");
-                                                       }
-                                               } catch (Throwable t) {
-                                                       if(!hasRedirected) {
-                                                               
System.err.println("Error : can't restart the node : consider installing the 
wrapper. PLEASE REPORT THAT ERROR TO devl at freenetproject.org");
-                                                               
t.printStackTrace();
-                                                       }
-                                                       node.exit("USM deadlock 
and error");
-                                               }
-                                       }
-                               } else {
-                                       Logger.error(this, "MAIN LOOP 
TERMINATED");
-                                       System.err.println("MAIN LOOP 
TERMINATED!");
-                                       node.exit(Node.EXIT_MAIN_LOOP_LOST);
-                               }
-                       }
-               }
-       }
-
-       public UdpSocketManager(int listenPort, InetAddress bindto, Node node) 
throws SocketException {
-               super("UdpSocketManager packet receiver thread on port " + 
listenPort);
-               this.node = node;
-               _bindTo = bindto;
-                   // Keep the Updater code in, just commented out, for now
-                   // We may want to be able to do on-line updates.
-//                     if (Updater.hasResource()) {
-//                             _sock = (DatagramSocket) Updater.getResource();
-//                     } else {
-               _sock = new DatagramSocket(listenPort, bindto);
-               int sz = _sock.getReceiveBufferSize();
-               if(sz < 32768)
-                       _sock.setReceiveBufferSize(32768);
-               try {
-                       // We make it timeout every 100ms so that we can check 
for
-                       // _filters which have timed out, this
-                       // is ugly but our only option without resorting to 
java.nio
-                       // because there is no way to forcefully
-                       // interrupt a socket wait operation
-                       _sock.setSoTimeout(100);
-               } catch (SocketException e) {
-                       throw new RuntimeException(e);
-               }
-//                     }
-               // Only used for debugging, no need to seed from Yarrow
-               dropRandom = new Random();
-               _timedOutFilters = new Vector(32);
-               logMINOR = Logger.shouldLog(Logger.MINOR, this);
-       }
-
-       public InetAddress getBindTo() {
-               return _bindTo;
-       }
-       
-       public void run() { // Listen for packets
-               try {
-                       runLoop();
-               } catch (Throwable t) {
-                       // Impossible? It keeps on exiting. We get the below,
-                       // but not this...
-                       try {
-                               System.err.print(t.getClass().getName());
-                               System.err.println();
-                       } catch (Throwable tt) {};
-                       try {
-                               System.err.print(t.getMessage());
-                               System.err.println();
-                       } catch (Throwable tt) {};
-                       try {
-                               System.gc();
-                               System.runFinalization();
-                               System.gc();
-                               System.runFinalization();
-                       } catch (Throwable tt) {}
-                       try {
-                               Runtime r = Runtime.getRuntime();
-                               System.err.print(r.freeMemory());
-                               System.err.println();
-                               System.err.print(r.totalMemory());
-                               System.err.println();
-                       } catch (Throwable tt) {};
-                       try {
-                               t.printStackTrace();
-                       } catch (Throwable tt) {};
-               } finally {
-                       System.err.println("run() exiting");
-                       Logger.error(this, "run() exiting");
-                       synchronized (this) {
-                               _isDone = true;
-                               notifyAll();
-                       }
-               }
-       }
-
-       private void runLoop() {
-               byte[] buf = new byte[MAX_RECEIVE_SIZE];
-               DatagramPacket packet = new DatagramPacket(buf, buf.length);
-               while (/*_active*/true) {
-                       try {
-                               lastTimeInSeconds = (int) 
(System.currentTimeMillis() / 1000);
-                               realRun(packet);
-            } catch (OutOfMemoryError e) {
-                               OOMHandler.handleOOM(e);
-                               System.err.println("Will retry above failed 
operation...");
-                       } catch (Throwable t) {
-                               System.err.println("Caught "+t);
-                               t.printStackTrace(System.err);
-                               Logger.error(this, "Caught " + t, t);
-                       }
-               }
-       }
-       
-       private void realRun(DatagramPacket packet) {
-               // Single receiving thread
-               boolean gotPacket = getPacket(packet);
-               // Check for timedout _filters
-               removeTimedOutFilters();
-               if (gotPacket) {
-                       long startTime = System.currentTimeMillis();
-                       Peer peer = new Peer(packet.getAddress(), 
packet.getPort());
-                       long endTime = System.currentTimeMillis();
-                       if(endTime - startTime > 50) {
-                               if(endTime-startTime > 3000)
-                                       Logger.error(this, "packet creation 
took "+(endTime-startTime)+"ms");
-                               else
-                                       if(logMINOR) Logger.minor(this, "packet 
creation took "+(endTime-startTime)+"ms");
-                       }
-                       byte[] data = packet.getData();
-                       int offset = packet.getOffset();
-                       int length = packet.getLength();
-                       try {
-                               if(logMINOR) Logger.minor(this, "Processing 
packet of length "+length+" from "+peer);
-                               startTime = System.currentTimeMillis();
-                               lowLevelFilter.process(data, offset, length, 
peer);
-                               endTime = System.currentTimeMillis();
-                               if(endTime - startTime > 50) {
-                                       if(endTime-startTime > 3000)
-                                               Logger.error(this, "processing 
packet took "+(endTime-startTime)+"ms");
-                                       else
-                                               if(logMINOR) Logger.minor(this, 
"processing packet took "+(endTime-startTime)+"ms");
-                               }
-                               if(logMINOR) Logger.minor(this,
-                                               "Successfully handled packet 
length " + length);
-                       } catch (Throwable t) {
-                               Logger.error(this, "Caught " + t + " from "
-                                               + lowLevelFilter, t);
-                       }
-               } else if(logMINOR) Logger.minor(this, "Null packet");
-       }
-       
-       /**
-        * Decode a packet from data and a peer.
-        * Can be called by IncomingPacketFilter's.
-     * @param data
-     * @param offset
-     * @param length
-     * @param peer
-     */
-    public Message decodeSingleMessage(byte[] data, int offset, int length, 
PeerContext peer, int overhead) {
-        try {
-            return Message.decodeMessageFromPacket(data, offset, length, peer, 
overhead);
-        } catch (Throwable t) {
-            Logger.error(this, "Could not decode packet: "+t, t);
-            return null;
-        }
-    }
-
-    // FIXME necessary to deal with bugs around build 1000; arguably necessary 
to deal with large node names in connection setup
-    // Revert to 1500?
-    private static final int MAX_RECEIVE_SIZE = 2048;
-    
-    private boolean getPacket(DatagramPacket packet) {
-               try {
-                       _sock.receive(packet);
-                       // TODO: keep?
-                       IOStatisticCollector.addInfo(packet.getAddress() + ":" 
+ packet.getPort(),
-                                       packet.getLength(), 0);
-               } catch (SocketTimeoutException e1) {
-                       return false;
-               } catch (IOException e2) {
-                       throw new RuntimeException(e2);
-               }
-               if(logMINOR) Logger.minor(this, "Received packet");
-               return true;
-       }
-
-    /** Only used by removeTimedOutFilters() - if future code uses this 
elsewhere, we need to
-     * reconsider its locking. */
-    private final Vector _timedOutFilters;
-    
-    /**
-     * Remove timed out filters.
-     * Only called by realRun(), so it can move timed out filters to the 
_timedOutFilters array,
-     * and then tell them that they are timed out without holding locks.
-     *
-     */
-       private void removeTimedOutFilters() {
-               long tStart = System.currentTimeMillis();
-               synchronized (_filters) {
-                       for (ListIterator i = _filters.listIterator(); 
i.hasNext();) {
-                               MessageFilter f = (MessageFilter) i.next();
-                               if (f.timedOut()) {
-                                       i.remove();
-                                       _timedOutFilters.add(f);
-                               } else { // Because _filters are in order of 
timeout, we
-                                       // can abort the iteration as soon as 
we find one that
-                                       // doesn't timeout
-                                       break;
-                               }
-                       }
-               }
-               
-               for(int i=0;i<_timedOutFilters.size();i++) {
-                       MessageFilter f = (MessageFilter) 
_timedOutFilters.get(i);
-                       f.setMessage(null);
-                       f.onTimedOut();
-               }
-               _timedOutFilters.clear();
-               
-               long tEnd = System.currentTimeMillis();
-               if(tEnd - tStart > 50) {
-                       if(tEnd - tStart > 3000)
-                               Logger.error(this, "removeTimedOutFilters took 
"+(tEnd-tStart)+"ms");
-                       else
-                               if(logMINOR) Logger.minor(this, 
"removeTimedOutFilters took "+(tEnd-tStart)+"ms");
-               }
-       }
-
-       /**
-        * Dispatch a message to a waiting filter, or feed it to the
-        * Dispatcher if none are found.
-        * @param m The Message to dispatch.
-        */
-       public void checkFilters(Message m) {
-               long tStart = System.currentTimeMillis();
-               if(logMINOR) Logger.minor(this, "checkFilters: "+m+" from 
"+m.getSource());
-               if ((m.getSource()) instanceof PeerNode)
-               {
-                       
((PeerNode)m.getSource()).addToLocalNodeReceivedMessagesFromStatistic(m);
-               }
-               boolean matched = false;
-               if ((!(m.getSpec().equals(DMT.packetTransmit))) && logMINOR) {
-                       if ((m.getSpec().equals(DMT.ping) || 
m.getSpec().equals(DMT.pong)) && Logger.shouldLog(Logger.DEBUG, this)) {
-                               Logger.debug(this, "" + 
(System.currentTimeMillis() % 60000) + ' ' + _sock.getLocalPort() + " <- "
-                                               + m.getSource() + " : " + m);
-                       } else {
-                               if(logMINOR) Logger.minor(this, "" + 
(System.currentTimeMillis() % 60000) + ' ' + _sock.getLocalPort() + " <- "
-                                               + m.getSource() + " : " + m);
-                       }
-               }
-               MessageFilter match = null;
-               synchronized (_filters) {
-                       for (ListIterator i = _filters.listIterator(); 
i.hasNext();) {
-                               MessageFilter f = (MessageFilter) i.next();
-                               if (f.match(m)) {
-                                       matched = true;
-                                       i.remove();
-                                       match = f;
-                                       if(logMINOR) Logger.minor(this, 
"Matched: "+f);
-                                       break; // Only one match permitted per 
message
-                               }
-                       }
-               }
-               if(match != null) {
-                       match.setMessage(m);
-                       match.onMatched();
-               }
-               // Feed unmatched messages to the dispatcher
-               if ((!matched) && (_dispatcher != null)) {
-                   try {
-                       if(logMINOR) Logger.minor(this, "Feeding to dispatcher: 
"+m);
-                       matched = _dispatcher.handleMessage(m);
-                   } catch (Throwable t) {
-                       Logger.error(this, "Dispatcher threw "+t, t);
-                   }
-               }
-               // Keep the last few _unclaimed messages around in case the 
intended receiver isn't receiving yet
-               if (!matched) {
-                       if(logMINOR) Logger.minor(this, "Unclaimed: "+m);
-                   /** Check filters and then add to _unmatched is ATOMIC
-                    * It has to be atomic, because otherwise we can get a
-                    * race condition that results in timeouts on MFs.
-                    * 
-                    * Specifically:
-                    * - Thread A receives packet
-                    * - Thread A checks filters. It doesn't match any.
-                    * - Thread A feeds to Dispatcher.
-                    * - Thread B creates filter.
-                    * - Thread B checks _unmatched.
-                    * - Thread B adds filter.
-                    * - Thread B sleeps.
-                    * - Thread A returns from Dispatcher. Which didn't match.
-                    * - Thread A adds to _unmatched.
-                    * 
-                    * OOPS!
-                    * The only way to fix this is to have checking the
-                    * filters and unmatched be a single atomic operation.
-                    * Another race is possible if we merely recheck the
-                    * filters after we return from dispatcher, for example.
-                    */
-                       synchronized (_filters) {
-                               if(logMINOR) Logger.minor(this, "Rechecking 
filters and adding message");
-                               for (ListIterator i = _filters.listIterator(); 
i.hasNext();) {
-                                       MessageFilter f = (MessageFilter) 
i.next();
-                                       if (f.match(m)) {
-                                               matched = true;
-                                               match = f;
-                                               i.remove();
-                                               if(logMINOR) Logger.minor(this, 
"Matched: "+f);
-                                               break; // Only one match 
permitted per message
-                                       }
-                               }
-                               if(!matched) {
-                                   while (_unclaimed.size() > 
MAX_UNMATCHED_FIFO_SIZE) {
-                                       Message removed = 
(Message)_unclaimed.removeFirst();
-                                       long messageLifeTime = 
System.currentTimeMillis() - removed.localInstantiationTime;
-                                       if ((removed.getSource()) instanceof 
PeerNode) {
-                                           Logger.normal(this, "Dropping 
unclaimed from "+removed.getSource().getPeer()+", lived 
"+TimeUtil.formatTime(messageLifeTime, 2, true)+" (quantity)"+": "+removed);
-                                       } else {
-                                           Logger.normal(this, "Dropping 
unclaimed, lived "+TimeUtil.formatTime(messageLifeTime, 2, true)+" 
(quantity)"+": "+removed);
-                                       }
-                                   }
-                                   _unclaimed.addLast(m);
-                                   if(logMINOR) Logger.minor(this, "Done");
-                               }
-                       }
-                       if(match != null) {
-                               match.setMessage(m);
-                               match.onMatched();
-                       }
-               }
-               long tEnd = System.currentTimeMillis();
-               if(tEnd - tStart > 50) {
-                       if(tEnd - tStart > 3000)
-                               Logger.error(this, "checkFilters took 
"+(tEnd-tStart)+"ms with unclaimedFIFOSize of "+_unclaimed.size()+" for 
matched: "+matched);
-                       else
-                               if(logMINOR) Logger.minor(this, "checkFilters 
took "+(tEnd-tStart)+"ms with unclaimedFIFOSize of "+_unclaimed.size()+" for 
matched: "+matched);
-               }
-       }
-       
-       /** IncomingPacketFilter should call this when a node is disconnected. 
*/
-       public void onDisconnect(PeerContext ctx) {
-               Vector droppedFilters = null; // rare operation, we can waste 
objects for better locking
-           synchronized(_filters) {
-                       ListIterator i = _filters.listIterator();
-                       while (i.hasNext()) {
-                           MessageFilter f = (MessageFilter) i.next();
-                           if(f.matchesDroppedConnection(ctx)) {
-                               if(droppedFilters == null)
-                                       droppedFilters = new Vector();
-                               droppedFilters.add(f);
-                               i.remove();
-                           }
-                       }
-           }
-           if(droppedFilters != null) {
-               for(int i=0;i<droppedFilters.size();i++) {
-                       MessageFilter mf = (MessageFilter) 
droppedFilters.get(i);
-                       mf.onDroppedConnection(ctx);
-               }
-           }
-       }
-       
-       /** IncomingPacketFilter should call this when a node connects with a 
new boot ID */
-       public void onRestart(PeerContext ctx) {
-               Vector droppedFilters = null; // rare operation, we can waste 
objects for better locking
-           synchronized(_filters) {
-                       ListIterator i = _filters.listIterator();
-                       while (i.hasNext()) {
-                           MessageFilter f = (MessageFilter) i.next();
-                           if(f.matchesRestartedConnection(ctx)) {
-                               if(droppedFilters == null)
-                                       droppedFilters = new Vector();
-                               droppedFilters.add(f);
-                               i.remove();
-                           }
-                       }
-           }
-           if(droppedFilters != null) {
-               for(int i=0;i<droppedFilters.size();i++) {
-                       MessageFilter mf = (MessageFilter) 
droppedFilters.get(i);
-                       mf.onRestartedConnection(ctx);
-               }
-           }
-       }
-
-       public void addAsyncFilter(MessageFilter filter, 
AsyncMessageFilterCallback callback) throws DisconnectedException {
-               filter.setAsyncCallback(callback);
-               boolean logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
-               if(logDEBUG) Logger.debug(this, "Adding async filter "+filter+" 
for "+callback);
-               Message ret = null;
-               if((lowLevelFilter != null) && (filter._source != null) && 
-                       filter.matchesDroppedConnection(filter._source) &&
-                       lowLevelFilter.isDisconnected(filter._source))
-                   throw new DisconnectedException();
-               // Check to see whether the filter matches any of the recently 
_unclaimed messages
-               // Drop any _unclaimed messages that the filter doesn't match 
that are also older than MAX_UNCLAIMED_FIFO_ITEM_LIFETIME
-               long now = System.currentTimeMillis();
-               long messageDropTime = now - MAX_UNCLAIMED_FIFO_ITEM_LIFETIME;
-               long messageLifeTime = 0;
-               synchronized (_filters) {
-                       if(logMINOR) Logger.minor(this, "Checking _unclaimed");
-                       for (ListIterator i = _unclaimed.listIterator(); 
i.hasNext();) {
-                               Message m = (Message) i.next();
-                               if (filter.match(m)) {
-                                       i.remove();
-                                       ret = m;
-                                       if(logMINOR) Logger.debug(this, 
"Matching from _unclaimed");
-                                       break;
-                               } else if (m.localInstantiationTime < 
messageDropTime) {
-                                       i.remove();
-                                       messageLifeTime = now - 
m.localInstantiationTime;
-                                       if ((m.getSource()) instanceof 
PeerNode) {
-                                               Logger.normal(this, "Dropping 
unclaimed from "+m.getSource().getPeer()+", lived 
"+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": "+m);
-                                       } else {
-                                               Logger.normal(this, "Dropping 
unclaimed, lived "+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": 
"+m);
-                                       }
-                               }
-                       }
-                       if (ret == null) {
-                               if(logMINOR) Logger.minor(this, "Not in 
_unclaimed");
-                           // Insert filter into filter list in order of 
timeout
-                               ListIterator i = _filters.listIterator();
-                               while (true) {
-                                       if (!i.hasNext()) {
-                                               i.add(filter);
-                                               if(logMINOR) Logger.minor(this, 
"Added at end");
-                                               break;
-                                       }
-                                       MessageFilter mf = (MessageFilter) 
i.next();
-                                       if (mf.getTimeout() > 
filter.getTimeout()) {
-                                               i.previous();
-                                               i.add(filter);
-                                               if(logMINOR) Logger.minor(this, 
"Added in middle - mf timeout="+mf.getTimeout()+" - my 
timeout="+filter.getTimeout());
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               if(ret != null) {
-                       filter.onMatched();
-                       filter.clearMatched();
-               }
-       }
-
-       /**
-        * Wait for a filter to trigger, or timeout. Blocks until either the 
trigger is activated, or it times
-        * out, or the peer is disconnected.
-        * @param filter The filter to wait for.
-        * @param ctr Byte counter to add bytes from the message to.
-        * @return Either a message, or null if the filter timed out.
-        * @throws DisconnectedException If the single peer being waited for 
disconnects.
-        */
-       public Message waitFor(MessageFilter filter, ByteCounter ctr) throws 
DisconnectedException {
-               boolean logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
-               if(logDEBUG) Logger.debug(this, "Waiting for "+filter);
-               long startTime = System.currentTimeMillis();
-               Message ret = null;
-               if((lowLevelFilter != null) && (filter._source != null) && 
-                       filter.matchesDroppedConnection(filter._source) &&
-                       lowLevelFilter.isDisconnected(filter._source))
-                   throw new DisconnectedException();
-               // Check to see whether the filter matches any of the recently 
_unclaimed messages
-               // Drop any _unclaimed messages that the filter doesn't match 
that are also older than MAX_UNCLAIMED_FIFO_ITEM_LIFETIME
-               long now = System.currentTimeMillis();
-               long messageDropTime = now - MAX_UNCLAIMED_FIFO_ITEM_LIFETIME;
-               long messageLifeTime = 0;
-               synchronized (_filters) {
-                       if(logMINOR) Logger.minor(this, "Checking _unclaimed");
-                       for (ListIterator i = _unclaimed.listIterator(); 
i.hasNext();) {
-                               Message m = (Message) i.next();
-                               if (filter.match(m)) {
-                                       i.remove();
-                                       ret = m;
-                                       if(logMINOR) Logger.debug(this, 
"Matching from _unclaimed");
-                                       break;
-                               } else if (m.localInstantiationTime < 
messageDropTime) {
-                                       i.remove();
-                                       messageLifeTime = now - 
m.localInstantiationTime;
-                                       if ((m.getSource()) instanceof 
PeerNode) {
-                                               Logger.normal(this, "Dropping 
unclaimed from "+m.getSource().getPeer()+", lived 
"+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": "+m);
-                                       } else {
-                                               Logger.normal(this, "Dropping 
unclaimed, lived "+TimeUtil.formatTime(messageLifeTime, 2, true)+" (age)"+": 
"+m);
-                                       }
-                               }
-                       }
-                       if (ret == null) {
-                               if(logMINOR) Logger.minor(this, "Not in 
_unclaimed");
-                           // Insert filter into filter list in order of 
timeout
-                               ListIterator i = _filters.listIterator();
-                               while (true) {
-                                       if (!i.hasNext()) {
-                                               i.add(filter);
-                                               if(logMINOR) Logger.minor(this, 
"Added at end");
-                                               break;
-                                       }
-                                       MessageFilter mf = (MessageFilter) 
i.next();
-                                       if (mf.getTimeout() > 
filter.getTimeout()) {
-                                               i.previous();
-                                               i.add(filter);
-                                               if(logMINOR) Logger.minor(this, 
"Added in middle - mf timeout="+mf.getTimeout()+" - my 
timeout="+filter.getTimeout());
-                                               break;
-                                       }
-                               }
-                       }
-               }
-               long tEnd = System.currentTimeMillis();
-               if(tEnd - now > 50) {
-                       if(tEnd - now > 3000)
-                               Logger.error(this, "waitFor _unclaimed 
iteration took "+(tEnd-now)+"ms with unclaimedFIFOSize of "+_unclaimed.size()+" 
for ret of "+ret);
-                       else
-                               if(logMINOR) Logger.minor(this, "waitFor 
_unclaimed iteration took "+(tEnd-now)+"ms with unclaimedFIFOSize of 
"+_unclaimed.size()+" for ret of "+ret);
-               }
-               // Unlock to wait on filter
-               // Waiting on the filter won't release the outer lock
-               // So we have to release it here
-               if(ret == null) {       
-                       if(logMINOR) Logger.minor(this, "Waiting...");
-                       synchronized (filter) {
-                               try {
-                                       // Precaution against filter getting 
matched between being added to _filters and
-                                       // here - bug discovered by Mason
-                                   boolean fmatched = false;
-                                   while(!(fmatched = (filter.matched() || 
(filter.droppedConnection() != null)))) {
-                                       long wait = 
filter.getTimeout()-System.currentTimeMillis();
-                                       if(wait > 0)
-                                           filter.wait(wait);
-                                       else break;
-                                       }
-                                   if(filter.droppedConnection() != null)
-                                       throw new DisconnectedException();
-                                   if(logMINOR) Logger.minor(this, "Matched: 
"+fmatched);
-                               } catch (InterruptedException e) {
-                               }
-                               ret = filter.getMessage();
-                               filter.clearMatched();
-                       }
-                       if(logDEBUG) Logger.debug(this, "Returning "+ret+" from 
"+filter);
-               } else {
-                       // Matched an unclaimed packet
-                       filter.onMatched();
-                       filter.clearMatched();
-               }
-               // Probably get rid...
-//             if (Dijjer.getDijjer().getDumpMessageWaitTimes() != null) {
-//                     
Dijjer.getDijjer().getDumpMessageWaitTimes().println(filter.toString() + "\t" + 
filter.getInitialTimeout() + "\t"
-//                                     + (System.currentTimeMillis() - 
startTime));
-//                     Dijjer.getDijjer().getDumpMessageWaitTimes().flush();
-//             }
-               long endTime = System.currentTimeMillis();
-               if(logDEBUG) Logger.debug(this, "Returning in 
"+(endTime-startTime)+"ms");
-               if((ctr != null) && (ret != null))
-                       ctr.receivedBytes(ret._receivedByteCount);
-               return ret;
-       }
-
-       /**
-        * Send a Message to a PeerContext.
-        * @throws NotConnectedException If we are not currently connected to 
the node.
-        */
-       public void send(PeerContext destination, Message m, ByteCounter ctr) 
throws NotConnectedException {
-           if(m.getSpec().isInternalOnly()) {
-               Logger.error(this, "Trying to send internal-only message "+m+" 
of spec "+m.getSpec(), new Exception("debug"));
-               return;
-           }
-               if ((m.getSpec().equals(DMT.ping) || 
m.getSpec().equals(DMT.pong)) && logMINOR) {
-                       if(Logger.shouldLog(Logger.DEBUG, this))
-                               Logger.debug(this, "" + 
(System.currentTimeMillis() % 60000) + ' ' + _sock.getPort() + " -> " + 
destination
-                                               + " : " + m);
-               } else {
-                       if(logMINOR) Logger.minor(this, "" + 
(System.currentTimeMillis() % 60000) + ' ' + _sock.getPort() + " -> " + 
destination
-                                       + " : " + m);
-               }
-//             byte[] blockToSend = m.encodeToPacket(lowLevelFilter, 
destination);
-//             if(lowLevelFilter != null) {
-//                     try {
-//                             lowLevelFilter.processOutgoing(blockToSend, 0, 
blockToSend.length, destination);
-//                             return;
-//                     } catch (IncomingPacketFilterException t) {
-//                             Logger.error(this, "Caught "+t+" sending "+m+" 
to "+destination, t);
-//                             destination.forceDisconnect();
-//                             throw new NotConnectedException("Error 
"+t.toString()+" forced disconnect");
-//                     }
-//             } else {
-//                 sendPacket(blockToSend, destination.getPeer());
-//             }
-               destination.sendAsync(m, null, 0, ctr);
-       }
-
-       /**
-        * Send a block of encoded bytes to a peer. This is called by
-        * send, and by IncomingPacketFilter.processOutgoing(..).
-     * @param blockToSend The data block to send.
-     * @param destination The peer to send it to.
-     */
-    public void sendPacket(byte[] blockToSend, Peer destination, boolean 
allowLocalAddresses) throws LocalAddressException {
-       assert(blockToSend != null);
-               // there should be no DNS needed here, but go ahead if we can, 
but complain doing it
-               if( destination.getAddress(false, allowLocalAddresses) == null 
) {
-                       Logger.error(this, "Tried sending to destination 
without pre-looked up IP address(needs a real Peer.getHostname()): null:" + 
destination.getPort(), new Exception("error"));
-                       if( destination.getAddress(true, allowLocalAddresses) 
== null ) {
-                               Logger.error(this, "Tried sending to bad 
destination address: null:" + destination.getPort(), new Exception("error"));
-                               return;
-                       }
-               }
-               if (_dropProbability > 0) {
-                       if (dropRandom.nextInt() % _dropProbability == 0) {
-                               if(logMINOR) Logger.minor(this, "DROPPED: " + 
_sock.getLocalPort() + " -> " + destination.getPort());
-                               return;
-                       }
-               }
-               InetAddress address = destination.getAddress(false, 
allowLocalAddresses);
-               assert(address != null);
-               int port = destination.getPort();
-               DatagramPacket packet = new DatagramPacket(blockToSend, 
blockToSend.length);
-               packet.setAddress(address);
-               packet.setPort(port);
-               
-               // TODO: keep?
-               // packet.length() is simply the size of the buffer, it knows 
nothing of UDP headers
-               IOStatisticCollector.addInfo(address + ":" + port, 0, 
blockToSend.length + UDP_HEADERS_LENGTH); 
-               
-               try {
-                       _sock.send(packet);
-               } catch (IOException e) {
-                       if(packet.getAddress() instanceof Inet6Address)
-                               Logger.normal(this, "Error while sending packet 
to IPv6 address: "+destination+": "+e, e);
-                       else
-                               Logger.error(this, "Error while sending packet 
to " + destination+": "+e, e);
-               }
-    }
-
-    public void close(boolean exit) {
-       Logger.error(this, "Closing.", new Exception("error"));
-               synchronized (this) {
-                       while (!_isDone) {
-                               try {
-                                       wait(2000);
-                               } catch (InterruptedException e) {
-                                       e.printStackTrace();
-                               }
-                       }
-               }
-               if (exit) {
-                       _sock.close();
-               } else {
-                   Logger.fatal(this, 10, "Not implemented: close(false)");
-                       //Updater.saveResource(_sock);
-               }
-       }
-
-       public void setDispatcher(Dispatcher d) {
-               _dispatcher = d;
-       }
-
-       /** Must be called, or we will NPE */
-       public void setLowLevelFilter(IncomingPacketFilter f) {
-           lowLevelFilter = f;
-       }
-       
-       public String toString() {
-               return _sock.getLocalAddress() + ":" + _sock.getLocalPort();
-       }
-
-       public void setDropProbability(int dropProbability) {
-               _dropProbability = dropProbability;
-       }
-
-       public static UdpSocketManager getUdpSocketManager()
-       {
-               return _usm;
-       }
-
-//     public static void init(int externalListenPort, InetAddress bindto)
-//             throws SocketException
-//     {
-//             _usm = new UdpSocketManager(externalListenPort, bindto);
-//     }
-//
-    public int getPortNumber() {
-        return _sock.getLocalPort();
-    }
-
-    
-       // CompuServe use 1400 MTU; AOL claim 1450; DFN at home use 1448.
-       // http://info.aol.co.uk/broadband/faqHomeNetworking.adp
-       // 
http://www.compuserve.de/cso/hilfe/linux/hilfekategorien/installation/contentview.jsp?conid=385700
-       // http://www.studenten-ins-netz.net/inhalt/service_faq.html
-       // officially GRE is 1476 and PPPoE is 1492.
-       // unofficially, PPPoE is often 1472 (seen in the wild). Also PPPoATM 
is sometimes 1472.
-    static final int MAX_ALLOWED_MTU = 1400;
-    // FIXME this is different for IPv6 (check all uses of constant when 
fixing)
-    public static final int UDP_HEADERS_LENGTH = 28;
-    
-    /**
-     * @return The maximum packet size supported by this SocketManager, not 
including transport (UDP/IP) headers.
-     */
-    public int getMaxPacketSize() { //FIXME: what about passing a peerNode 
though and doing it on a per-peer basis?
-       final int minAdvertisedMTU = node.ipDetector.getMinimumDetectedMTU();
-       
-       // We don't want the MTU detection thingy to prevent us to send 
PacketTransmits!
-       if(minAdvertisedMTU < 1100){
-               Logger.error(this, "It shouldn't happen : we disabled the MTU 
detection algorithm because the advertised MTU is smallish !! 
("+node.ipDetector.getMinimumDetectedMTU()+')'); 
-               return MAX_ALLOWED_MTU - UDP_HEADERS_LENGTH;
-       } else
-               return (minAdvertisedMTU < MAX_ALLOWED_MTU ? minAdvertisedMTU : 
MAX_ALLOWED_MTU) - UDP_HEADERS_LENGTH;
-       // UDP/IP header is 28 bytes.
-    }
-
-       /**
-        * @return the number of received messages that are currently unclaimed
-        */
-       public int getUnclaimedFIFOSize() {
-               synchronized (_filters){
-                       return _unclaimed.size();
-               }
-       }
-       
-       public Map getUnclaimedFIFOMessageCounts() {
-               Map messageCounts = new HashMap();
-               synchronized(_filters) {
-                       for (ListIterator i = _unclaimed.listIterator(); 
i.hasNext();) {
-                               Message m = (Message) i.next();
-                               String messageName = m.getSpec().getName();
-                               Integer messageCount = (Integer) 
messageCounts.get(messageName);
-                               if (messageCount == null) {
-                                       messageCounts.put(messageName, new 
Integer(1) );
-                               } else {
-                                       messageCount = new 
Integer(messageCount.intValue() + 1);
-                                       messageCounts.put(messageName, 
messageCount );
-                               }
-                       }
-               }
-               return messageCounts;
-       }
-
-       public int getDropProbability() {
-               return _dropProbability;
-       }
-}

Modified: branches/freenet-jfk/src/freenet/io/xfer/BlockReceiver.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/xfer/BlockReceiver.java 2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/xfer/BlockReceiver.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -29,7 +29,7 @@
 import freenet.io.comm.NotConnectedException;
 import freenet.io.comm.PeerContext;
 import freenet.io.comm.RetrievalException;
-import freenet.io.comm.UdpSocketManager;
+import freenet.io.comm.MessageCore;
 import freenet.support.BitArray;
 import freenet.support.Buffer;
 import freenet.support.Logger;
@@ -47,12 +47,12 @@
        PartiallyReceivedBlock _prb;
        PeerContext _sender;
        long _uid;
-       UdpSocketManager _usm;
+       MessageCore _usm;
        /** packet : Integer -> reportTime : Long * */
        HashMap _recentlyReportedMissingPackets = new HashMap();
        ByteCounter _ctr;

-       public BlockReceiver(UdpSocketManager usm, PeerContext sender, long 
uid, PartiallyReceivedBlock prb, ByteCounter ctr) {
+       public BlockReceiver(MessageCore usm, PeerContext sender, long uid, 
PartiallyReceivedBlock prb, ByteCounter ctr) {
                _sender = sender;
                _prb = prb;
                _uid = uid;

Modified: branches/freenet-jfk/src/freenet/io/xfer/BlockTransmitter.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/xfer/BlockTransmitter.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/xfer/BlockTransmitter.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -26,16 +26,17 @@
 import freenet.io.comm.DMT;
 import freenet.io.comm.DisconnectedException;
 import freenet.io.comm.Message;
+import freenet.io.comm.MessageCore;
 import freenet.io.comm.MessageFilter;
 import freenet.io.comm.NotConnectedException;
 import freenet.io.comm.PeerContext;
-import freenet.io.comm.UdpSocketManager;
-import freenet.node.FNPPacketMangler;
 import freenet.node.PeerNode;
 import freenet.support.BitArray;
 import freenet.support.DoubleTokenBucket;
+import freenet.support.Executor;
 import freenet.support.Logger;
 import freenet.support.TimeUtil;
+import freenet.support.transport.ip.IPUtil;

 /**
  * @author ian
@@ -45,22 +46,22 @@
        public static final int SEND_TIMEOUT = 60000;
        public static final int PING_EVERY = 8;

-       UdpSocketManager _usm;
-       PeerContext _destination;
-       boolean _sendComplete;
-       long _uid;
-       PartiallyReceivedBlock _prb;
-       LinkedList _unsent;
-       Thread _receiverThread, _senderThread;
-       BitArray _sentPackets;
+       final MessageCore _usm;
+       final PeerContext _destination;
+       private boolean _sendComplete;
+       final long _uid;
+       final PartiallyReceivedBlock _prb;
+       private LinkedList _unsent;
+       private Runnable _senderThread;
+       private BitArray _sentPackets;
        boolean failedByOverload;
        final PacketThrottle throttle;
-       long timeAllSent = -1;
+       private long timeAllSent = -1;
        final DoubleTokenBucket _masterThrottle;
        final ByteCounter _ctr;
        final int PACKET_SIZE;

-       public BlockTransmitter(UdpSocketManager usm, PeerContext destination, 
long uid, PartiallyReceivedBlock source, DoubleTokenBucket masterThrottle, 
ByteCounter ctr) {
+       public BlockTransmitter(MessageCore usm, PeerContext destination, long 
uid, PartiallyReceivedBlock source, DoubleTokenBucket masterThrottle, 
ByteCounter ctr) {
                _usm = usm;
                _destination = destination;
                _uid = uid;
@@ -68,7 +69,7 @@
                _ctr = ctr;
                _masterThrottle = masterThrottle;
                PACKET_SIZE = DMT.packetTransmitSize(_prb._packetSize, 
_prb._packets)
-                       + FNPPacketMangler.FULL_HEADERS_LENGTH_ONE_MESSAGE;
+                       + 
destination.getOutgoingMangler().fullHeadersLengthOneMessage();
                try {
                        _sentPackets = new BitArray(_prb.getNumPackets());
                } catch (AbortedException e) {
@@ -76,7 +77,7 @@
                        // Will throw on running
                }
                throttle = _destination.getThrottle();
-               _senderThread = new Thread("_senderThread for "+_uid+ " to 
"+_destination.getPeer()) {
+               _senderThread = new Runnable() {

                        public void run() {
                                while (!_sendComplete) {
@@ -144,7 +145,8 @@
                                // Get the current inter-packet delay
                                long end = 
throttle.scheduleDelay(startThrottle);

-                               _masterThrottle.blockingGrab(PACKET_SIZE);
+                               
if(IPUtil.isValidAddress(_destination.getPeer().getAddress(), false))
+                                       
_masterThrottle.blockingGrab(PACKET_SIZE);

                                long now = System.currentTimeMillis();

@@ -172,16 +174,13 @@
                                }
                        }
                };
-               _senderThread.setDaemon(true);
        }

        public void sendAborted(int reason, String desc) throws 
NotConnectedException {
                _usm.send(_destination, DMT.createSendAborted(_uid, reason, 
desc), _ctr);
        }

-       public boolean send() {
-               _receiverThread = Thread.currentThread();
-               
+       public boolean send(Executor executor) {
                PartiallyReceivedBlock.PacketReceivedListener myListener;

                try {
@@ -206,7 +205,7 @@
                                        }
                                });
                        }
-                       _senderThread.start();
+                       executor.execute(_senderThread, "BlockTransmitter 
sender for "+_uid);

                        while (true) {
                                if (_prb.isAborted()) {
@@ -230,7 +229,7 @@
                                }
                                if(logMINOR) Logger.minor(this, "Got "+msg);
                                if(!_destination.isConnected()) {
-                                       Logger.normal(this, "Terminating send 
"+_uid+" to "+_destination+" from "+_usm.getPortNumber()+" because node 
disconnected while waiting");
+                                       Logger.normal(this, "Terminating send 
"+_uid+" to "+_destination+" from "+_destination.getSocketHandler()+" because 
node disconnected while waiting");
                                        synchronized(_senderThread) {
                                                _sendComplete = true;
                                                _senderThread.notifyAll();
@@ -247,7 +246,7 @@
                                                        _sendComplete = true;
                                                        
_senderThread.notifyAll();
                                                }
-                                               Logger.error(this, "Terminating 
send "+_uid+" to "+_destination+" from "+_usm.getPortNumber()+" as we haven't 
heard from receiver in "+TimeUtil.formatTime((now - timeAllSent), 2, true)+ 
'.');
+                                               Logger.error(this, "Terminating 
send "+_uid+" to "+_destination+" from "+_destination.getSocketHandler()+" as 
we haven't heard from receiver in "+TimeUtil.formatTime((now - timeAllSent), 2, 
true)+ '.');
                                                return false;
                                        } else {
                                                if(logMINOR) Logger.minor(this, 
"Ignoring timeout: timeAllSent="+timeAllSent+" ("+(System.currentTimeMillis() - 
timeAllSent)+"), getNumSent="+getNumSent()+ '/' +_prb.getNumPackets());
@@ -310,12 +309,10 @@
        /**
         * Send the data, off-thread.
         */
-       public void sendAsync() {
-               Runnable r = new Runnable() {
-                       public void run() { send(); } };
-               Thread t = new Thread(r, "BlockTransmitter:sendAsync() for 
"+this);
-               t.setDaemon(true);
-               t.start();
+       public void sendAsync(final Executor executor) {
+               executor.execute(new Runnable() {
+                       public void run() { send(executor); } },
+                       "BlockTransmitter:sendAsync() for "+this);
        }

        public void waitForComplete() {

Modified: branches/freenet-jfk/src/freenet/io/xfer/BulkReceiver.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/xfer/BulkReceiver.java  2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/xfer/BulkReceiver.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -54,9 +54,9 @@
         * @return True if the whole file was received, false otherwise.
         */
        public boolean receive() {
-               MessageFilter mfSendKilled = 
MessageFilter.create().setSource(peer).setType(DMT.FNPBulkSendAborted) 
.setField(DMT.UID, uid).setTimeout(TIMEOUT);
-               MessageFilter mfPacket = 
MessageFilter.create().setSource(peer).setType(DMT.FNPBulkPacketSend) 
.setField(DMT.UID, uid).setTimeout(TIMEOUT);
                while(true) {
+                       MessageFilter mfSendKilled = 
MessageFilter.create().setSource(peer).setType(DMT.FNPBulkSendAborted) 
.setField(DMT.UID, uid).setTimeout(TIMEOUT);
+                       MessageFilter mfPacket = 
MessageFilter.create().setSource(peer).setType(DMT.FNPBulkPacketSend) 
.setField(DMT.UID, uid).setTimeout(TIMEOUT);
                        if(prb.hasWholeFile()) {
                                try {
                                        
peer.sendAsync(DMT.createFNPBulkReceivedAll(uid), null, 0, null);

Modified: branches/freenet-jfk/src/freenet/io/xfer/BulkTransmitter.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/xfer/BulkTransmitter.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/xfer/BulkTransmitter.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -13,6 +13,7 @@
 import freenet.support.BitArray;
 import freenet.support.DoubleTokenBucket;
 import freenet.support.Logger;
+import freenet.support.transport.ip.IPUtil;

 /**
  * Bulk data transfer (not block). Bulk transfer is designed for files which 
may be much bigger than a 
@@ -40,6 +41,7 @@
        final DoubleTokenBucket masterThrottle;
        private boolean sentCancel;
        private boolean finished;
+       final int packetSize;

        public BulkTransmitter(PartiallyReceivedBulk prb, PeerContext peer, 
long uid, DoubleTokenBucket masterThrottle) throws DisconnectedException {
                this.prb = prb;
@@ -87,6 +89,8 @@
                        cancel();
                        throw e;
                }
+               packetSize = DMT.bulkPacketTransmitSize(prb.blockSize) +
+                       peer.getOutgoingMangler().fullHeadersLengthOneMessage();
        }

        /**
@@ -145,7 +149,6 @@
         * @return True if the file was successfully sent. False otherwise.
         */
        public boolean send() {
-               int packetSize = prb.getPacketSize();
                long lastSentPacket = System.currentTimeMillis();
                while(true) {
                        if(prb.isAborted()) return false;
@@ -191,7 +194,8 @@
                        long now = System.currentTimeMillis();
                        long waitUntil = peer.getThrottle().scheduleDelay(now);

-                       masterThrottle.blockingGrab(packetSize);
+                       if(IPUtil.isValidAddress(peer.getPeer().getAddress(), 
false))
+                               masterThrottle.blockingGrab(packetSize);

                        while((now = System.currentTimeMillis()) < waitUntil) {
                                long sleepTime = waitUntil - now;

Modified: branches/freenet-jfk/src/freenet/io/xfer/PartiallyReceivedBulk.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/xfer/PartiallyReceivedBulk.java 
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/io/xfer/PartiallyReceivedBulk.java 
2007-08-21 20:26:59 UTC (rev 14828)
@@ -5,10 +5,8 @@

 import java.io.IOException;

-import freenet.io.comm.DMT;
+import freenet.io.comm.MessageCore;
 import freenet.io.comm.RetrievalException;
-import freenet.io.comm.UdpSocketManager;
-import freenet.node.FNPPacketMangler;
 import freenet.support.BitArray;
 import freenet.support.Logger;
 import freenet.support.io.RandomAccessThing;
@@ -31,11 +29,10 @@
        private final BitArray blocksReceived;
        final int blocks;
        private BulkTransmitter[] transmitters;
-       final UdpSocketManager usm;
+       final MessageCore usm;
        /** The one and only BulkReceiver */
        private BulkReceiver recv;
        private int blocksReceivedCount;
-       final int packetSize;
        // Abort status
        boolean _aborted;
        int _abortReason;
@@ -49,7 +46,7 @@
         * @param initialState If true, assume all blocks have been received. 
If false, assume no blocks have
         * been received.
         */
-       public PartiallyReceivedBulk(UdpSocketManager usm, long size, int 
blockSize, RandomAccessThing raf, boolean initialState) {
+       public PartiallyReceivedBulk(MessageCore usm, long size, int blockSize, 
RandomAccessThing raf, boolean initialState) {
                this.size = size;
                this.blockSize = blockSize;
                this.raf = raf;
@@ -63,8 +60,6 @@
                        blocksReceived.setAllOnes();
                        blocksReceivedCount = this.blocks;
                }
-               packetSize = DMT.bulkPacketTransmitSize(blockSize) + 
-                       FNPPacketMangler.FULL_HEADERS_LENGTH_ONE_MESSAGE; // 
FIXME generalise
        }

        /**
@@ -99,9 +94,11 @@
         * @param offset The start of the data in the buffer.
         */
        void received(int blockNum, byte[] data, int offset, int length) {
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, "Received block "+blockNum);
                BulkTransmitter[] notifyBTs;
                long fileOffset = (long)blockNum * (long)blockSize;
-               int bs = (int) Math.max(blockSize, size - fileOffset);
+               int bs = (int) Math.min(blockSize, size - fileOffset);
                if(length < bs) {
                        String err = "Data too short! Should be "+bs+" actually 
"+length;
                        Logger.error(this, err+" for "+this);
@@ -127,7 +124,7 @@
                }
        }

-       void abort(int errCode, String why) {
+       public void abort(int errCode, String why) {
                BulkTransmitter[] notifyBTs;
                BulkReceiver notifyBR;
                synchronized(this) {
@@ -144,23 +141,20 @@
                }
                if(notifyBR != null)
                        notifyBR.onAborted();
+               raf.close();
        }

        public synchronized boolean isAborted() {
                return _aborted;
        }

-       public int getPacketSize() {
-               return packetSize;
-       }
-
        public boolean hasWholeFile() {
                return blocksReceivedCount >= blocks;
        }

        public byte[] getBlockData(int blockNum) {
                long fileOffset = (long)blockNum * (long)blockSize;
-               int bs = (int) Math.max(blockSize, size - fileOffset);
+               int bs = (int) Math.min(blockSize, size - fileOffset);
                byte[] data = new byte[bs];
                try {
                        raf.pread(fileOffset, data, 0, bs);
@@ -173,6 +167,11 @@
        }

        public synchronized void remove(BulkTransmitter remove) {
+               boolean found = false;
+               for(int i=0;i<transmitters.length;i++) {
+                       if(transmitters[i] == remove) found = true;
+               }
+               if(!found) return;
                BulkTransmitter[] newTrans = new 
BulkTransmitter[transmitters.length-1];
                int j = 0;
                for(int i=0;i<transmitters.length;i++) {

Modified: branches/freenet-jfk/src/freenet/keys/ClientCHK.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/ClientCHK.java        2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/keys/ClientCHK.java        2007-08-21 
20:26:59 UTC (rev 14828)
@@ -136,6 +136,9 @@
        }

        public NodeCHK getNodeCHK() {
+               // This costs us more or less nothing: we have to keep the 
routingKey anyway.
+               // Therefore, keeping a NodeCHK as well is a net saving, since 
it's frequently
+               // asked for. (A SoftReference would cost more).
                if(nodeKey == null)
                nodeKey = new NodeCHK(routingKey, cryptoAlgorithm);
            return nodeKey;

Modified: branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -34,8 +34,12 @@
                this.key = key;
        }

-       public ClientSSKBlock(SSKBlock block, ClientSSK key) throws 
SSKVerifyException {
-               this(block.data, block.headers, key, false);
+       public static ClientSSKBlock construct(SSKBlock block, ClientSSK key) 
throws SSKVerifyException {
+               // Constructor expects clientkey to have the pubkey.
+               // In the case of binary blobs, the block may have it instead.
+               if(key.getPubKey() == null && block.getPubKey() != null)
+                       key.setPublicKey(block.getPubKey());
+               return new ClientSSKBlock(block.data, block.headers, key, 
false);
        }

        /**

Modified: branches/freenet-jfk/src/freenet/keys/FreenetURI.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/FreenetURI.java       2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/keys/FreenetURI.java       2007-08-21 
20:26:59 UTC (rev 14828)
@@ -728,9 +728,12 @@
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                Logger.minor(this, "Getting preferred filename for "+this);
                Vector names = new Vector();
-               if(keyType != null && (keyType.equals("KSK") || 
keyType.equals("SSK"))) {
+               if(keyType != null && (keyType.equals("KSK") || 
keyType.equals("SSK") || keyType.equals("USK"))) {
                        if(logMINOR) Logger.minor(this, "Adding docName: 
"+docName);
                        names.add(docName);
+                       if(keyType.equals("USK")) {
+                               names.add(Long.toString(suggestedEdition));
+                       }
                }
                if(metaStr != null)
                        for(int i=0;i<metaStr.length;i++) {

Modified: branches/freenet-jfk/src/freenet/keys/Key.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/Key.java      2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/keys/Key.java      2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -249,4 +249,11 @@
     public byte[] getKeyBytes() {
        return routingKey;
     }
+
+       public static ClientKeyBlock createKeyBlock(ClientKey key, KeyBlock 
block) throws KeyVerifyException {
+               if(key instanceof ClientSSK)
+                       return ClientSSKBlock.construct((SSKBlock)block, 
(ClientSSK)key);
+               else //if(key instanceof ClientCHK
+                       return new ClientCHKBlock((CHKBlock)block, 
(ClientCHK)key);
+       }
 }

Modified: branches/freenet-jfk/src/freenet/keys/NodeCHK.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/NodeCHK.java  2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/keys/NodeCHK.java  2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -7,8 +7,6 @@
 import java.io.DataOutput;
 import java.io.DataOutputStream;
 import java.io.IOException;
-import java.util.Arrays;
-
 import freenet.support.Base64;

 /**

Modified: branches/freenet-jfk/src/freenet/keys/TooBigException.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/TooBigException.java  2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/keys/TooBigException.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -4,6 +4,11 @@

 public class TooBigException extends IOException {

+       /**
+        * 
+        */
+       private static final long serialVersionUID = 1L;
+
        public TooBigException(String msg) {
                super(msg);
        }

Modified: branches/freenet-jfk/src/freenet/keys/USK.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/USK.java      2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/keys/USK.java      2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -139,4 +139,27 @@
        public String toString() {
                return super.toString()+ ':' +getURI();
        }
+
+       public FreenetURI turnMySSKIntoUSK(FreenetURI uri) {
+               if(uri.getKeyType().equals("SSK") &&
+                               Arrays.equals(uri.getRoutingKey(), pubKeyHash) 
&&
+                               Arrays.equals(uri.getCryptoKey(), cryptoKey) &&
+                               Arrays.equals(uri.getExtra(), 
ClientSSK.getExtraBytes(cryptoAlgorithm)) &&
+                               uri.getDocName() != null &&
+                               uri.getDocName().startsWith(siteName)) {
+                       String doc = uri.getDocName();
+                       doc = doc.substring(siteName.length());
+                       if(doc.length() < 2 || doc.charAt(0) != '-') return uri;
+                       long edition;
+                       try {
+                               edition = Long.parseLong(doc);
+                       } catch (NumberFormatException e) {
+                               Logger.normal(this, "Trying to turn SSK back 
into USK: "+uri+" doc="+doc+" caught "+e, e);
+                               return uri;
+                       }
+                       if(!doc.equals(Long.toString(edition))) return uri;
+                       return new FreenetURI("USK", siteName, 
uri.getAllMetaStrings(), pubKeyHash, cryptoKey, 
ClientSSK.getExtraBytes(cryptoAlgorithm), edition);
+               }
+               return uri;
+       }
 }

Modified: branches/freenet-jfk/src/freenet/l10n/L10n.java
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/L10n.java     2007-08-21 19:57:05 UTC 
(rev 14827)
+++ branches/freenet-jfk/src/freenet/l10n/L10n.java     2007-08-21 20:26:59 UTC 
(rev 14828)
@@ -37,7 +37,7 @@

        // English has to remain the first one!
        public static final String FALLBACK_DEFAULT = "en";
-       public static final String[] AVAILABLE_LANGUAGES = { "en", "fr", "pl", 
"it", "se", "no", "unlisted" };
+       public static final String[] AVAILABLE_LANGUAGES = { "en", "de", "fr", 
"it", "no", "pl", "se", "unlisted" };
        private final String selectedLanguage;

        private static SimpleFieldSet currentTranslation = null;
@@ -223,8 +223,10 @@
                }
                if(result != null)
                        return result;
-               else
+               else {
+                       Logger.normal(CLASS_NAME, "The translation for " + key 
+ " hasn't been found ("+getSelectedLanguage()+")! please tell the 
maintainer.");
                        return (returnNullIfNotFound ? null : 
getDefaultString(key));
+               }
        }

        /**
@@ -262,11 +264,10 @@
                }

                if(result != null) {
-                       Logger.normal(CLASS_NAME, "The translation for " + key 
+ " hasn't been found! please tell the maintainer.");
                        return result;
                }
-               Logger.error(CLASS_NAME, "The translation for " + key + " 
hasn't been found!");
-               System.err.println("The translation for " + key + " hasn't been 
found!");
+               Logger.error(CLASS_NAME, "The default translation for " + key + 
" hasn't been found!");
+               System.err.println("The default translation for " + key + " 
hasn't been found!");
                new Exception().printStackTrace();
                return key;
        }
@@ -316,6 +317,7 @@
        */
        public static String getSelectedLanguage() {
                synchronized (sync) {
+                       if(currentClass == null) return null;
                        return currentClass.selectedLanguage;   
                }
        }
@@ -339,7 +341,7 @@
                        if(in != null)
                                result = SimpleFieldSet.readFrom(in, false, 
false);
                } catch (Exception e) {
-                       Logger.error("L10n", "Error while loading the l10n file 
from " + name + " :" + e.getMessage());
+                       Logger.error("L10n", "Error while loading the l10n file 
from " + name + " :" + e.getMessage(), e);
                        result = null;
                } finally {
                        if (in != null) try { in.close(); } catch (Throwable 
ignore) {}

Copied: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties (from 
rev 14796, trunk/freenet/src/freenet/l10n/freenet.l10n.de.properties)
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties            
                (rev 0)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,993 @@
+BookmarkEditorToadlet.addBookmark=Lesezeichen hinzuf?gen
+BookmarkEditorToadlet.addCategory=Kategorie hinzuf?gen
+BookmarkEditorToadlet.addNewBookmark=Ein neues Lesezeichen hinzuf?gen
+BookmarkEditorToadlet.addNewCategory=Eine neue Kategorie hinzuf?gen
+BookmarkEditorToadlet.addedNewBookmark=Das neue Lesezeichen wurde erfolgreich 
hinzugef?gt.
+BookmarkEditorToadlet.addedNewBookmarkTitle=Neues Lesezeichen hinzugef?gt
+BookmarkEditorToadlet.bookmarkDoesNotExist=Das Lesezeichen "${bookmark}" 
existiert nicht.
+BookmarkEditorToadlet.cancelCut=Ausschneiden abbrechen
+BookmarkEditorToadlet.changesSaved=Die ?nderungen wurden erfolgreich 
gespeichert.
+BookmarkEditorToadlet.changesSavedTitle=?nderungen gespeichert
+BookmarkEditorToadlet.confirmDelete=L?schen
+BookmarkEditorToadlet.cut=Ausschneiden
+BookmarkEditorToadlet.delete=L?schen
+BookmarkEditorToadlet.deleteBookmark=Lesezeichen l?schen
+BookmarkEditorToadlet.deleteBookmarkConfirm=Sind Sie sich sicher, dass Sie 
${bookmark} l?schen wollen?
+BookmarkEditorToadlet.deleteCategory=Kategorie l?schen
+BookmarkEditorToadlet.deleteCategoryConfirm=Sind Sie sich sicher, dass Sie 
${bookmark} und alle Untergeordneten l?schen wollen?
+BookmarkEditorToadlet.deleteSucceeded=Das Lesezeichen wurde erfolgreich 
gel?scht.
+BookmarkEditorToadlet.deleteSucceededTitle=L?schen erfolgreich
+BookmarkEditorToadlet.edit=?ndern
+BookmarkEditorToadlet.editBookmarkTitle=Lesezeichen ?ndern
+BookmarkEditorToadlet.editCategoryTitle=Kategorie bearbeiten
+BookmarkEditorToadlet.error=Fehler
+BookmarkEditorToadlet.invalidKey=Der Freenet-Schl?ssel ist ung?ltig.
+BookmarkEditorToadlet.invalidKeyTitle=Ung?ltiger Schl?ssel
+BookmarkEditorToadlet.invalidKeyWithReason=Ung?ltiger Freenet-Schl?ssel.
+BookmarkEditorToadlet.keyLabel=Schl?ssel:
+BookmarkEditorToadlet.moveDown=Runter
+BookmarkEditorToadlet.moveUp=Hoch
+BookmarkEditorToadlet.myBookmarksTitle=Meine Lesezeichen
+BookmarkEditorToadlet.nameLabel=Name:
+BookmarkEditorToadlet.paste=Einf?gen
+BookmarkEditorToadlet.pasteOrCancel=Klicken Sie auf ein Einf?gen-Symbol oder 
Abbrechen.
+BookmarkEditorToadlet.pasteTitle=Ausschneiden/Einf?gen
+BookmarkEditorToadlet.save=Speichern
+BookmarkEditorToadlet.title=Lesezeichen-Editor
+BookmarkEditorToadlet.urlDecodeError=URL-Dekodier-Fehler
+BookmarkItem.bookmarkUpdated=Die in den Lesezeichen gespeicherte Seite ${name} 
wurde auf die Ausgabe ${edition} aktualisiert.
+BookmarkItem.bookmarkUpdatedTitle=Lesezeichen aktualisiert: ${name}
+BookmarkItem.bookmarkUpdatedWithLink=Die in den Lesezeichen gespeicherte Seite 
${link}${name}${/link} wurde auf die Ausgabe ${edition} aktualisiert.
+BookmarkItem.deleteBookmarkUpdateNotification=Benachrichtigung l?schen
+BookmarkItem.unnamedBookmark=Unbenanntes Lesezeichen
+BookmarkManager.list=Liste von Lesezeichen
+BookmarkManager.listLong=Eine Liste von Lesezeichen von Freesites
+BookmarkManager.malformedBookmark=Missgebildetes Lesezeichen
+BooleanOption.parseError=Unbekannter Boolescher Wert: ${val} - versuchen Sie 
true oder false
+BuildOldAgeUserAlert.tooOld=Die Software dieses Knotens ist ?lter, als die 
?lteste Version (Build #${lastgood}), die von dem neusten Partner, zu dem 
Kontakt aufgenommen wird, akzeptiert wird.  Bitte aktualisieren Sie ihren 
Knoten so bald wie m?glich, da Sie keinen Kontakt zu Partnern aufnehmen k?nnen, 
welche mit "ZU NEU" gekennzeichnet sind, bis Sie dies tun. (Freenet k?nnte 
ihren Knoten im "Staub der Vergangenheit" zur?cklassen, wenn Sie ihn nicht 
aktualisieren.)
+BuildOldAgeUserAlert.tooOldTitle=Build (Version) zu alt.
+CSSTokenizerFilter.deletedDisallowedString=Nicht erlaubte Zeichenfolge gel?scht
+CSSTokenizerFilter.deletedUnmatchedChar=Unerreichbare Zeichen ignoriert:
+CSSTokenizerFilter.deletedUnofficialIdent=Inoffizieller Bezeichner gel?scht
+CSSTokenizerFilter.deletedUnofficialIdentWithURL=Inoffizieller Bezeichner mit 
url gel?scht
+CSSTokenizerFilter.invalidURLContents=Ung?ltige Inhalte der url()
+CSSTokenizerFilter.supplementalCharsNotSupported=UCS-4-ZEICHEN OBERHALB VON 
0xFFFF WERDEN NICHT UNTERST?TZT!
+CSSTokenizerFilter.unknownAtIdentifierLabel=Unbekannter @Bezeichner:
+ConfigToadlet.appliedFailureExceptions=Ihre ?nderungen der Konfiguration 
wurden, mit folgenden Ausnahmen, ?bernommen:
+ConfigToadlet.appliedFailureTitle=Konfiguration nicht ?bernommen
+ConfigToadlet.appliedSuccess=Die ?nderungen in der Konfiguration wurden 
erfolgreich ?bernommen.
+ConfigToadlet.appliedTitle=Konfiguration wurde ?bernommen
+ConfigToadlet.apply=?bernehmen
+ConfigToadlet.configNavTitle=Konfigurations-Navigation
+ConfigToadlet.contributeTranslation=Zur ?bersetzung beitragen
+ConfigToadlet.defaultIs=Der Standard-Wert f?r diese Einstellung ist: 
"${default}".
+ConfigToadlet.fullTitle=Freenet-Knoten-Konfiguration von ${name}
+ConfigToadlet.possibilitiesTitle=Ihre M?glichkeiten
+ConfigToadlet.reset=Zur?cksetzen
+ConfigToadlet.returnToNodeConfig=Zur?ck zur Knoten-Konfiguration
+ConfigToadlet.shortTitle=Konfiguration
+ConfigToadlet.title=Freenet-Knoten-Konfiguration
+ConfigurablePersister.doesNotExistCannotCreate=Datei existiert nicht und kann 
nicht erzeugt werden
+ConfigurablePersister.existsCannotReadWrite=Datei existiert und kann nicht 
gelesen/geschrieben werden
+ContentDataFilter.unknownCharset=Die Seite, die Sie ansehen wollen, hat einen 
unbekannten Zeichensatz. Das hei?t, dass wir nicht die M?glichkeit haben, die 
Seite zu filtern und es kann sein, dass dies Ihre Anonymit?t gef?hrdet.
+ContentDataFilter.unknownCharsetTitle=Unbekannter Zeichensatz!
+ContentDataFilter.warningUnknownCharsetTitle=Warnung: Unbekannter Zeichensatz 
(${charset})
+ContentFilter.applicationPdfReadAdvice=Adobe(R) PDF-Dokument - SEHR GEF?HRLICH!
+ContentFilter.applicationPdfWriteAdvice=Adobe(R) PDF-Dokument - SEHR 
GEF?HRLICH!
+ContentFilter.imageGifReadAdvice=GIF-Bild - wahrscheinlich nicht gef?hrlich
+ContentFilter.imageGifWriteAdvice=GIF-Bild - wahrscheinlich nicht gef?hrlich, 
aber Sie sollten alle Kommentare l?schen
+ContentFilter.imageIcoReadAdvice=Icon(=Symbol)-Datei - wahrscheinlich nicht 
gef?hrlich
+ContentFilter.imageIcoWriteAdvice=Icon(=Symbol)-Datei - wahrscheinlich nicht 
gef?hrlich (aber kann, aufgrund des Offsets, andere Daten enthalten)
+ContentFilter.imageJpegReadAdvice=JPEG-Bild - wahrscheinlich nicht gef?hrlich
+ContentFilter.imageJpegWriteAdvice=JPEG-Bild - wahrscheinlich nicht 
gef?hrlich, aber kann EXIF-Daten enthalten
+ContentFilter.imagePngReadAdvice=PNG-Bild - wahrscheinlich nicht gef?hrlich
+ContentFilter.imagePngWriteAdvice=PNG-Bild - wahrscheinlich nicht gef?hrlich, 
aber Sie sollten alle Kommentare oder Text-Bl?cke entfernen
+ContentFilter.textCssReadAdvice=CSS (cascading style sheet, wird normalerweise 
mit HTML verwendet) - wahrscheinlich nicht gef?hrlich wenn gefiltert, aber der 
Filter ist kein Whitelist-Filter, also Vorsicht
+ContentFilter.textCssWriteAdvice=CSS (cascading style sheet, wird 
normalerweise mit HTML genutzt) - dies kann wahrscheinlich Meta-Daten 
enthalten, pr?fen Sie dies manuell
+ContentFilter.textHtmlReadAdvice=HTML - nicht gef?hrlich wenn gefiltert
+ContentFilter.textHtmlWriteAdvice=HTML - kann gef?hrliche Meta-Daten usw. 
enthalten; es wird vorgeschlagen dies manuell zu pr?fen
+ContentFilter.textPlainReadAdvice=Flie?text - nicht gef?hrlich, es sei denn 
Ihr Browser ist unf?hig/dumm (z.B. Internet Explorer)
+ContentFilter.textPlainWriteAdvice=Flie?text - nicht gef?hlich, es sei denn, 
er enth?lt kompromittierende (~blo?stellende) Informationen
+DarknetConnectionsToadlet.activityTitle=Derzeitige Aktivit?t
+DarknetConnectionsToadlet.add=Hinzuf?gen
+DarknetConnectionsToadlet.addPeerTitle=Einen weiteren Partner hinzuf?gen
+DarknetConnectionsToadlet.alreadyInReferences=Wir haben die gegebene Referenz 
bereits.
+DarknetConnectionsToadlet.backedOff=Verbunden aber zur?ckgezogen: Diese 
Partner sind verbunden aber wir haben uns vor Ihnen zur?ckgezogen, der Knoten 
leitet deshalb keine Anfragen an sie weiter.
+DarknetConnectionsToadlet.backedOffShort=Zur?ckgezogen
+DarknetConnectionsToadlet.bursting=Nicht verbunden und berstend: Dieser Knoten 
versucht sich, f?r eine kurze Zeit, mit diesen Partnern zu verbinden, weil der 
Benutzer sie auf BurstOnly (nur bersten) gesetzt hat.
+DarknetConnectionsToadlet.burstingShort=Berstend
+DarknetConnectionsToadlet.busy=Besch?ftigt: Diese Partner sind verbunden, aber 
sie sind zu besch?ftigt um unsere Anfragen zu bearbeiten, der Knoten leitet 
deshalb keine Anfragen an sie weiter.
+DarknetConnectionsToadlet.busyShort=Besch?ftigt
+DarknetConnectionsToadlet.cantFetchNoderefURL=Nicht in der Lage die 
Knoten-Referenz von ${url} abzurufen. Bitte versuchen Sie es noch einmal.
+DarknetConnectionsToadlet.cantParseTryAgain=Nicht in der Lage den gegebenen 
Text als Knoten-Referenz zu erkennen: (${error}). Bitte versuchen Sie es noch 
einmal.
+DarknetConnectionsToadlet.cantParseWrongEnding=Nicht in der Lage die 
Knoten-Referenz zu erkennen: Sie sollte mit "End" in einer separaten Zeile 
enden, aber sie endet mit: ${end}
+DarknetConnectionsToadlet.clockProblem=Ihre Uhrzeit und die des Knotens 
unterscheiden sich um mehr als 24 Stunden. Wir haben die Verbindung 
deaktiviert, da dies Probleme mit der Aktualisierung und den Clients 
hervorrufen k?nnte.
+DarknetConnectionsToadlet.clockProblemShort=Uhrzeit-Problem
+DarknetConnectionsToadlet.confirmRemoveNode=Sind Sie sich sicher, dass Sie 
"${name}" entfernen wollen? Es ist nicht zu empfehlen dies zu tun, bevor er 
nicht mindestens eine Woche Ausfallzeit hat, da er nur tempor?r 
ausgefallen/abgeschaltet sein k?nnte und viele Benutzer ihren Knoten nicht rund 
um die Uhr betreiben k?nnen.
+DarknetConnectionsToadlet.confirmRemoveNodeTitle=Bitte best?tigen
+DarknetConnectionsToadlet.confirmRemoveNodeWarningTitle=Entfernen des Knotens
+DarknetConnectionsToadlet.connError=Verbinden fehlgeschlagen (fehlerhafter 
Knoten?)
+DarknetConnectionsToadlet.connErrorShort=Verbindungs-Fehler
+DarknetConnectionsToadlet.connected=Verbunden: Wir sind erfolgreich mit diesen 
Knoten verbunden
+DarknetConnectionsToadlet.connectedShort=Verbunden
+DarknetConnectionsToadlet.darknetFnpPort=Darknet-FNP: ${port}/UDP (wird 
benutzt um sich mit vertrauensw?rdigen Partnern d.h. Freunden zu verbinden; 
leiten Sie diesen Port weiter wenn Sie k?nnen)
+DarknetConnectionsToadlet.disabled=Nicht verbunden und deaktiviert: weil der 
Benutzer den Knoten angewiesen hat sich nicht mit diesen Partnern zu verbinden.
+DarknetConnectionsToadlet.disabledShort=Deaktiviert
+DarknetConnectionsToadlet.enterDescription=Beschreibung eingeben:
+DarknetConnectionsToadlet.failedToAddNodeInternalError=Nicht in der Lage den 
gegebenen Text als Knoten-Referenz zu erkennen. Bitte melden Sie folgendes an 
die Entwickler:
+DarknetConnectionsToadlet.failedToAddNodeInternalErrorTitle=Hinzuf?gen des 
Knotens fehlgeschlagen: Interner Fehler
+DarknetConnectionsToadlet.failedToAddNodeTitle=Hinzuf?gen des Knotens 
fehlgeschlagen
+DarknetConnectionsToadlet.fcpDisabled=FCP ist deaktiviert (f?r Freenet-Clients 
wie z.B. Frost und Thaw)
+DarknetConnectionsToadlet.fcpPort=FCP: ${port}/TCP (f?r Freenet-Clients wie 
z.B. Frost und Thaw)
+DarknetConnectionsToadlet.fileReference=W?hlen Sie hier die Datei aus, die die 
Referenz enth?lt:
+DarknetConnectionsToadlet.forceRemove=Entfernen erzwingen
+DarknetConnectionsToadlet.fproxyDisabled=FProxy ist deaktiviert (diese 
Web-Schnittstelle)
+DarknetConnectionsToadlet.fproxyPort=FProxy: ${port}/TCP (diese 
Web-Schnittstelle)
+DarknetConnectionsToadlet.fullTitle=${counts} Freunde (vertrauensw?rdige 
Partner) von ${name}
+DarknetConnectionsToadlet.go=Los
+DarknetConnectionsToadlet.idleTime=Zeit seit sich der Knoten verband oder 
zuletzt gesehen wurde
+DarknetConnectionsToadlet.idleTimeTitle=Verbunden / Unt?tig
+DarknetConnectionsToadlet.invalidSignature=Nicht in der Lage die Signatur der 
gegebenen Referenz zu verifizieren (${error}).
+DarknetConnectionsToadlet.ipAddress=Die Netzwerk-Adresse des Knotens in der 
Form IP:Port
+DarknetConnectionsToadlet.ipAddressTitle=Adresse
+DarknetConnectionsToadlet.listenOnly=Nicht verbunden und nur h?rend: Dieser 
Knoten wird nicht versuchen sich mit diesen Partnern zu verbinden, weil der 
Benutzer sie auf ListenOnly (nur h?ren) gesetzt hat.
+DarknetConnectionsToadlet.listenOnlyShort=Nur H?ren
+DarknetConnectionsToadlet.listening=Nicht verbunden aber h?rend: Dieser Knoten 
wird nicht oft versuchen sich mit diesen Partnern zu verbinden, weil der 
Benutzer sie auf BurstOnly (nur bersten) gesetzt hat.
+DarknetConnectionsToadlet.listeningShort=H?rend
+DarknetConnectionsToadlet.myFriends=Meine Freunde (von mir hinzugef?gte 
zuverl?ssige Partner)
+DarknetConnectionsToadlet.myReferenceHeader=${linkref}Meine 
Knoten-Referenz${/linkref} (${linktext}als Text${/linktext})
+DarknetConnectionsToadlet.nameClickToMessage=Der Name des Knotens. Klicken Sie 
auf den Namens-Link um dem Knoten eine N2NTM (Knoten-zu-Knoten-Text-Nachricht) 
zu senden
+DarknetConnectionsToadlet.nameTitle=Name
+DarknetConnectionsToadlet.neverConnected=Niemals verbunden: Der Knoten hat 
sich noch nie mit diesen Partnern verbunden.
+DarknetConnectionsToadlet.neverConnectedShort=Niemals verbunden
+DarknetConnectionsToadlet.noPeersWithHomepageLink=Freenet kann nicht arbeiten, 
da Sie noch keine Partner hinzugef?gt haben. Bitte gehen Sie zur 
${link}Knoten-Startseite${/link} und lesen Sie den oberen Informations-Kasten 
um zu erfahren wie das geht.
+DarknetConnectionsToadlet.noRefOrURL=Konnte weder eine Knoten-Referenz noch 
eine URL erkennen. Bitte versuchen Sie es noch einmal.
+DarknetConnectionsToadlet.nodePortsTitle=Vom Knoten verwendete Ports
+DarknetConnectionsToadlet.notConnected=Nicht verbunden: Bis jetzt noch keine 
Verbindung, aber dieser Knoten versucht kontinuierlich sich zu verbinden.
+DarknetConnectionsToadlet.notConnectedShort=Nicht verbunden
+DarknetConnectionsToadlet.opennetFnpPort=Opennet-FNP: ${port}/UDP (wird 
benutzt um sich mit nicht vertrauensw?rdigen Partnern d.h. Fremden zu 
verbinden; leiten Sie diesen Port weiter wenn Sie k?nnen)
+DarknetConnectionsToadlet.pasteReference=Die Referenz hier einf?gen:
+DarknetConnectionsToadlet.privateNote=Eine, diesen Partner betreffende, 
private Notiz
+DarknetConnectionsToadlet.privateNoteTitle=Private Notiz
+DarknetConnectionsToadlet.referenceCopyWarning=Die Knoten-Referenz muss 
${bold}SO WIE SIE IST${/bold} kopiert werden. Eine Ver?nderung macht sie 
${bold}unbrauchbar${/bold}.
+DarknetConnectionsToadlet.remove=Entfernen!
+DarknetConnectionsToadlet.removePeers=Ausgew?hlte Partner entfernen
+DarknetConnectionsToadlet.selectAction=-- Aktion ausw?hlen --
+DarknetConnectionsToadlet.sendMessageTitle=Knoten-zu-Knoten-Text-Nachricht 
(N2NTM) senden
+DarknetConnectionsToadlet.sendMessageToPeers=N2NTM an die ausgew?hlten Partner 
senden
+DarknetConnectionsToadlet.separator=-- -- --
+DarknetConnectionsToadlet.statusTitle=Status
+DarknetConnectionsToadlet.tmciDisabled=TMCI ist deaktiviert (einfache 
Telnet-basierte Kommandozeilen-Schnittstelle)
+DarknetConnectionsToadlet.tmciPort=TMCI: ${port}/TCP (einfache Telnet-basierte 
Kommandozeilen-Schnittstelle)
+DarknetConnectionsToadlet.tooNew=Verbunden aber zu neu: Das minimal geforderte 
Build dieser Partner ist h?her als die Build-Nummer dieses Knotens.
+DarknetConnectionsToadlet.tooNewShort=Zu neu
+DarknetConnectionsToadlet.tooOld=Verbunden aber zu alt: Der von diesem Knoten 
geforderte minimale Build (Version) ist h?her als die Build-Nummer dieser 
Partner. Der Knoten leitet keine Anfragen an diese weiter.
+DarknetConnectionsToadlet.tooOldShort=Zu alt
+DarknetConnectionsToadlet.triedToAddSelf=Sie k?nnen Ihren eigenen Knoten nicht 
zur Liste der (entfernten) Partner hinzuf?gen.
+DarknetConnectionsToadlet.unknownAddress=(unbekannte Adresse)
+DarknetConnectionsToadlet.updateChangedPrivnotes=Ge?nderte private Notizen 
aktualisieren
+DarknetConnectionsToadlet.urlReference=Die URL der Referenz hier eingeben:
+DarknetConnectionsToadlet.versionTitle=Version
+ExtOldAgeUserAlert.extTooOld=Ihre freenet-ext.jar-Datei scheint veraltet zu 
sein: wir raten Ihnen dringend, Sie mit dieser 
http://downloads.freenetproject.org/alpha/freenet-ext.jar zu aktualisieren.
+ExtOldAgeUserAlert.extTooOldTitle=Freenet-ext ist zu alt
+FProxyToadlet.abortToHomepage=Abbrechen und zur FProxy-Startseite zur?ckkehren
+FProxyToadlet.backToFProxy=${link}Klicken Sie hier${/link} um zur 
FProxy-Startseite zu gehen.
+FProxyToadlet.backToReferrer=${link}Klicken Sie hier${/link} um zur 
verweisenden Seite zur?ckzukehren.
+FProxyToadlet.cantBindPort=FProxy kann diesen Port nicht benutzen!
+FProxyToadlet.config=Ihren Knoten konfigurieren
+FProxyToadlet.configTitle=Konfiguration
+FProxyToadlet.dangerousContentTitle=Potenziell gef?hrlicher Inhalt
+FProxyToadlet.dangerousRSS=Freenet hat erkannt, dass die Datei, die Sie 
versucht haben abzurufen, eine RSS-Datei sein k?nnte. RSS kann von Freenet 
nicht richtig gefiltert werden und k?nnte Web-Bugs (Web-Programmfehler) 
(integrierte Bilder usw. welche Ihre IP-Adresse einem b?swilligen 
Seiten-Betreiber preisgeben und deshalb Ihre Anonymit?t verletzen k?nnten) 
enthalten. Firefox 2.0 und Internet Explorer 7.0 werden die Datei als RSS-Datei 
?ffnen, obwohl ihr Inhalts-Typ "${type}" ist.
+FProxyToadlet.dangerousRSSSubtitle=RSS-Feed, k?nnte gef?hrlich sein
+FProxyToadlet.dangerousRSSTitle=Potenziell gef?hrlicher Inhalt (RSS)
+FProxyToadlet.downloadInBackgroundToDisk=Im Hintergrund herunterladen und im 
Downloads-Verzeichnis speichern
+FProxyToadlet.errorIsFatal=Dies ist ein schwerer Fehler. Es ist 
unwahrscheinlich, dass ein neuer Versuch das Problem l?sen wird.
+FProxyToadlet.errorWithReason=Fehler: ${error}
+FProxyToadlet.expectedKeyButGot=Erwartete einen Freenet-Schl?ssel, aber bekam:
+FProxyToadlet.expectedMimeType=Erwarteter MIME-Typ: ${mime}
+FProxyToadlet.explanationTitle=Erkl?rung
+FProxyToadlet.fetchLargeFileAnywayAndDisplay=Trotzdem abrufen und die Datei im 
Browser anzeigen
+FProxyToadlet.fileInformationTitle=Datei-Informationen
+FProxyToadlet.filenameLabel=Dateiname:
+FProxyToadlet.friends=f2f(Freund-zu-Freund)-Verbindungen verwalten
+FProxyToadlet.friendsTitle=Freunde
+FProxyToadlet.goBack=Zur?ck
+FProxyToadlet.goBackToPrev=Zur?ck zur vorherigen Seite
+FProxyToadlet.invalidKeyTitle=Ung?ltiger Schl?ssel
+FProxyToadlet.invalidKeyWithReason=Ung?ltiger Schl?ssel: ${reason}
+FProxyToadlet.largeFile=Gro?e Datei
+FProxyToadlet.largeFileExplanationAndOptions=Der Freenet-Schl?ssel, den Sie 
angefordert haben, verweist auf eine gro?e Datei. Dateien dieser Gr??e, k?nnen 
allgemein nicht direkt an Ihren Browser gesendet werden, da sie f?r Ihren 
Freenet-Knoten beim Abrufen zu lange brauchen. Die folgenden M?glichkeiten sind 
verf?gbar:
+FProxyToadlet.mayChange=(k?nnte sich ?ndern)
+FProxyToadlet.mimeType=MIME-Typ: ${mime}
+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.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).
+FProxyToadlet.openPossRSSForceDisk=${link}Klicken Sie hier${/link} um Ihren 
Browser zu zwingen, die Datei auf einen Datentr?ger herunterzuladen 
(${bold}dies k?nnte auch gef?hrlich sein${/bold} wenn Sie Firefox 2.0.0 
benutzen, 2.0.1 sollte dies beheben).
+FProxyToadlet.openRSSAsRSS=${link}Klicken Sie hier${/link} um die Datei als 
RSS-Datei zu ?ffnen (dies ${bold}ist gef?hrlich${/bold} wenn der 
Seiten-Betreiber b?swillig ist, da Freenet bis jetzt noch nicht wei?, wie man 
RSS filtert).
+FProxyToadlet.openRSSForce=${link}Klicken Sie hier${/link} um die Datei als 
${mime} zu ?ffnen (dies ${bold}kann gef?hrlich sein${/bold} wenn Sie Internet 
Explorer 7 oder Firefox 2 benutzen).
+FProxyToadlet.opennet=Nicht vertrauensw?rdige Verbindungen verwalten
+FProxyToadlet.opennetTitle=Fremde
+FProxyToadlet.options=Ihre M?glichkeiten sind:
+FProxyToadlet.pathNotFound=Der angegebene Pfad existiert nicht.
+FProxyToadlet.pathNotFoundTitle=Pfad nicht gefunden
+FProxyToadlet.plugins=Plugins (Erweiterungen) konfigurieren und verwalten
+FProxyToadlet.pluginsTitle=Plugins
+FProxyToadlet.queue=Wartende/eingereihte Anfragen verwalten
+FProxyToadlet.queueTitle=Warteschlange
+FProxyToadlet.retryNow=Jetzt nochmal versuchen
+FProxyToadlet.sizeLabel=Gr??e:
+FProxyToadlet.sizeUnknown=Gr??e: unbekannt
+FProxyToadlet.stats=Statistiken anzeigen
+FProxyToadlet.statsTitle=Statistiken
+FProxyToadlet.unableToRetrieve=Freenet war nicht in der Lage, diese Datei 
abzurufen.
+FProxyToadlet.unknownMIMEType=MIME-Typ: unbekannt
+FProxyToadlet.welcome=Startseite
+FProxyToadlet.welcomeTitle=Startseite
+FcpServer.allowedHosts=Erlaubte Hosts (lesen Sie die Warnung!)
+FcpServer.allowedHostsFullAccess=Hosts mit vollem Zugriff
+FcpServer.allowedHostsFullAccessLong=IP-Adressen welchen der volle Zugriff auf 
den Knoten erlaubt ist. Clients (Nutzer) auf diesen IPs k?nnen den Knoten 
neustarten, rekonfigurieren usw. Beachten Sie, dass es ALLEN Clients erlaubt 
ist, direkte Datentr?ger-I/O (E/A) zu betreiben!
+FcpServer.allowedHostsLong=IP-Adressen welchen es erlaubt ist sich mit dem 
FCP-Server zu verbinden. Kann eine Komma-getrennte Liste von einzelnen IPs und 
CIDR-maskierten IPs, wie 192.168.0.0/24, sein. WARNUNG! Jeder, der Zugang zum 
FCP hat, kann jede Datei hochladen, auf welche der Knoten Zugriff hat, oder 
Dateien auf den Datentr?ger herunterladen (aber der Knoten wird versuchen 
vorhandene Dateien nicht zu ?berschreiben).
+FcpServer.assumeDownloadDDAIsAllowed=Annehmen, dass das Herunterladen von DDA 
erlaubt ist?
+FcpServer.assumeDownloadDDAIsAllowedLong=Annehmen, dass das Herunterladen von 
DDA erlaubt ist? Wenn nicht (false), m?ssen sie eine TestDDARequest 
(TestDDAAnfrage) ausl?sen, bevor Sie einen DDA-Zugriff ausf?hren.
+FcpServer.assumeUploadDDAIsAllowed=Annehmen, dass das Hochladen von DDA 
erlaubt ist?
+FcpServer.assumeUploadDDAIsAllowedLong=Annehmen, dass das Hochladen von DDA 
erlaubt ist? Wenn nicht (false), m?ssen sie eine TestDDARequest 
(TestDDAAnfrage) ausl?sen, bevor Sie einen DDA-Zugriff ausf?hren.
+FcpServer.bindTo=Zu benutzende IP-Adresse
+FcpServer.bindToLong=IP-Adresse die der FCP-Server benutzen soll.
+FcpServer.cannotStartOrStopOnTheFly=Der FCP-Server kann nicht im laufenden 
Betrieb gestartet oder gestoppt werden
+FcpServer.couldNotChangeBindTo=Konnte die zu benutzende FCP-Adresse nicht 
?ndern: ${error}.
+FcpServer.downloadsFileCanCreateCannotReadOrWrite=Datei erstellt, kann aber 
weder gelesen noch geschrieben werden
+FcpServer.downloadsFileDoesNotExistCannotCreate=Datei existiert nicht und kann 
nicht erstellt werden
+FcpServer.downloadsFileExistsCannotReadOrWrite=Die Datei existiert, aber kann 
weder gelesen noch geschrieben werden
+FcpServer.downloadsFileIsDirectory=Ung?ltiger Datei-Name f?r die 
Download-Liste: Ist ein Verzeichnis
+FcpServer.downloadsFileParentDoesNotExist=Oberverzeichnis existiert nicht
+FcpServer.downloadsFileUnreadable=Datei existiert aber kann nicht gelesen 
(zugegriffen) werden
+FcpServer.enablePersistentDownload=Persistente Downloads aktivieren?
+FcpServer.enablePersistentDownloadLong=Ob f?r FCP-Anfragen Persistence=forever 
(Persistenz/Speicher-Dauer=f?r immer) aktiviert werden darf. Soll hei?en, ob 
Anfragen unterst?tzt werden sollen, welche ?ber Knoten-Neustarts hinaus 
andauern; diese m?ssen auf den Datentr?ger geschrieben werden und dies k?nnte 
f?r manche Leute ein Sicherheits-Problem darstellen.
+FcpServer.filenameToStorePData=Name der Datei in welcher persistente Downloads 
gespeichert werden
+FcpServer.filenameToStorePDataLong=Name der Datei, in der Details zu 
persistenten (dauerhaften) Downloads gespeichert werden.
+FcpServer.intervalBetweenWrites=Zeitraum zwischen dem Schreiben persistenter 
Downloads auf den Datentr?ger.
+FcpServer.intervalBetweenWritesLong=Zeitraum zwischen dem Schreiben 
persistenter (dauerhafter) Downloads auf den Datentr?ger.
+FcpServer.isEnabled=Ist der FCP-Server aktiviert?
+FcpServer.isEnabledLong=Ist der FCP-Server aktiviert?
+FcpServer.portNumber=FCP-Port-Nummer
+FcpServer.portNumberLong=FCP-Port-Nummer.
+FetchException.longError.1=Zu viele Ebenen von Rekursion (Schachtelung) in den 
Archiven
+FetchException.longError.10=Datei nicht im Archiv
+FetchException.longError.11=Zu viele Pfad-Komponenten - kein Manifest(???)? 
Versuchen Sie eine zu entfernen
+FetchException.longError.12=Interner Fehler in den tempor?ren Dateien, 
vielleicht ist der Datentr?ger voll oder es besteht ein Rechte-Problem?
+FetchException.longError.13=Daten nicht gefunden
+FetchException.longError.14=Route nicht gefunden - konnte nicht gen?gend 
Knoten finden um sicher zu gehen, dass die Daten nicht existieren
+FetchException.longError.15=Ein Knoten war ?berlastet oder verursachte eine 
Zeit?berschreitung
+FetchException.longError.16=Zu viele Weiter-/Umleitungen - Schleife?
+FetchException.longError.17=Interner Fehler, wahrscheinlich ein Programmfehler
+FetchException.longError.18=Datei gefunden, aber w?hrend dem Empfang der Daten 
verloren
+FetchException.longError.19=Teil-Datei-Fehler
+FetchException.longError.2=Wei? nicht was mit der Teil-Datei zu tun ist
+FetchException.longError.20=Ung?ltiger URI
+FetchException.longError.21=Zu gro?
+FetchException.longError.22=Metadaten zu gro?
+FetchException.longError.23=Zu viele Bl?cke pro Segment
+FetchException.longError.24=F?gen Sie dem URI mehr Metadaten 
(Pfad-Komponenten) hinzu
+FetchException.longError.25=Abgebrochen
+FetchException.longError.26=Archiv neugestartet
+FetchException.longError.27=Permanente Weiter-/Umleitung: Benutzen Sie den 
neuen URI
+FetchException.longError.28=Nicht genug Daten gefunden; es wurden ein paar 
Daten abgerufen aber die Weiter-/Umleitung k?nnte ins Nirgendwo zeigen
+FetchException.longError.29=Falscher MIME-Typ: Der Schl?ssel war nicht in der 
Liste der erlaubten MIME-Typen, die vom Client zur Verf?gung gestellt wurde.
+FetchException.longError.3=Wei? nicht was mit den Metadaten zu tun ist
+FetchException.longError.30=Die Anfrage wurde von einem Knoten terminiert, 
weil er vor kurzem eine Anfrage nach dem selben Schl?ssel bekam und diese 
Anfrage fehlschlug
+FetchException.longError.4=Analyse der Metadaten fehlgeschlagen
+FetchException.longError.5=Fehler beim Entpacken von Dateien aus einem Archiv
+FetchException.longError.6=Das Dekodieren eines Blocks ist fehlgeschlagen
+FetchException.longError.7=Zu viele Ebenen von geteilten Metadaten
+FetchException.longError.8=Die Anfrage wurde, aufgrund von 
Archiv-Ver?nderungen, zu oft neugestartet
+FetchException.longError.9=Zu viele Weiter-/Umleitungen (zu viel Rekursion)
+FetchException.shortError.1=Zu tiefe Archiv-Rekursion
+FetchException.shortError.10=Nicht im Archiv
+FetchException.shortError.11=Zu viele Pfad-Komponenten
+FetchException.shortError.12="Tempor?re Dateien"-Fehler
+FetchException.shortError.13=Daten nicht gefunden
+FetchException.shortError.14=Route nicht gefunden
+FetchException.shortError.15=Zeit?berschreitung oder ?berlastung
+FetchException.shortError.16=Zu viele Weiter-/Umleitungen
+FetchException.shortError.17=Interner Fehler
+FetchException.shortError.18=Transfer fehlgeschlagen
+FetchException.shortError.19=Teil-Datei-Fehler
+FetchException.shortError.2=Unbekannte Teil-Datei-Metadaten
+FetchException.shortError.20=Ung?ltiger URI
+FetchException.shortError.21=Zu gro?
+FetchException.shortError.22=Metadaten zu gro?
+FetchException.shortError.23=Zu viele Bl?cke pro Segment
+FetchException.shortError.24=Nicht genug Meta-Zeichenfolgen
+FetchException.shortError.25=Abbruch durch den Aufrufer
+FetchException.shortError.26=Archiv neugestartet
+FetchException.shortError.27=Neuer URI
+FetchException.shortError.28=Alle Daten nicht gefunden
+FetchException.shortError.29=Falscher MIME-Typ
+FetchException.shortError.3=Unbekannte Metadaten
+FetchException.shortError.30=Daten nicht gefunden (vor kurzem fehlgeschlagen)
+FetchException.shortError.4=Ung?ltige Metadaten
+FetchException.shortError.5=Archiv versagt
+FetchException.shortError.6=Block-Dekodierungs-Fehler
+FetchException.shortError.7=Zu viele Metadaten-Ebenen
+FetchException.shortError.8=Zu viele Archiv-Neustarts
+FetchException.shortError.9=Zu viel Rekursion
+FileOffer.acceptTransferButton=?bertragung akzeptieren
+FileOffer.askUserTitle=Direkte Datei-?bertragung
+FileOffer.commentLabel=Kommentar:
+FileOffer.failedReceiveHeader=Die ?bertragung der Datei ${filename} von 
${node} ist fehlgeschlagen.
+FileOffer.failedReceiveTitle=Empfang der Datei fehlgeschlagen
+FileOffer.fileLabel=Datei:
+FileOffer.mimeLabel=MIME-Typ:
+FileOffer.offeredFileHeader=Der Knoten ${name} hat eine Datei angeboten:
+FileOffer.rejectTransferButton=?bertragung ablehnen
+FileOffer.senderLabel=Sender:
+FileOffer.sizeLabel=Gr??e:
+FileOffer.succeededReceiveHeader=Die ?bertragung der Datei ${filename} von 
${node} war erfolgreich.
+FileOffer.succeededReceiveTitle=Datei erfolgreich empfangen
+FirstTimeWizardToadlet.bandwidthLimit=Bandbreiten-Limits
+FirstTimeWizardToadlet.bandwidthLimitLong=Bitte w?hlen Sie Ihren 
Internet-Verbindungs-Typ und -Geschwindigkeit aus der unten befindlichen 
Dropdown-Liste.
+FirstTimeWizardToadlet.chooseNodeName=Knoten-Name ben?tigt!
+FirstTimeWizardToadlet.chooseNodeNameLong=Bitte geben Sie einen Knoten-Namen 
in das unten stehende Feld ein. Dieser Name wird f?r niemanden, au?er den 
Partnern mit denen Sie verbunden sind, sichtbar sein. Wir empfehlen Ihnen, 
Ihren wahren Namen oder IRC-Nickname (Spitznamen), m?glicherweise mit 
Kontakt-Informationen, sodass Sie erreichbar sind wenn ein Problem auftritt, 
anzugeben (z.B.: "Max Mustermann <niemand at nirgendwo.de>").
+FirstTimeWizardToadlet.congratz=Willkommen an Bord!
+FirstTimeWizardToadlet.congratzLong=Herzlichen Gl?ckwunsch, die 
Basis-Konfiguration Ihres Freenet-Knotens ist nun abgeschlossen. Sie k?nnen 
alle Parameter, die Sie soeben eingestellt haben, ?ndern indem Sie auf die 
"Konfiguration"s-Seite gehen, diese ist jederzeit vom Men? auf der linken Seite 
der Oberfl?che zu erreichen. Wir w?nschen Ihnen ein angenehmes Freenet-Erlebnis.
+FirstTimeWizardToadlet.connectToStrangers=Mit Fremden verbinden?
+FirstTimeWizardToadlet.connectToStrangersLong=Wenn Sie Freenet sich mit 
Fremden verbinden lassen, wird Freenet f?r Sie weniger sicher sein, jeder kann 
herausfinden, dass Sie Freenet benutzen und jeder b?se Mensch kann sich mit 
Ihrem Knoten verbinden. Wenn Sie dies nicht tun, werden Sie mindestens drei 
andere Freunde (Leute, die Sie schon kennen), die Freenet benutzen, manuell 
kontaktieren und sich mit Ihnen verbinden m?ssen.
+FirstTimeWizardToadlet.continue=Fortfahren
+FirstTimeWizardToadlet.datastoreSize=Gr??e des Datenspeichers
+FirstTimeWizardToadlet.datastoreSizeLong=Bitte w?hlen Sie eine Gr??e f?r Ihren 
Datenspeicher. Der Datenspeicher verh?lt sich wie ein Zwischenspeicher (Cache); 
Daten f?r das Netzwerk zu speichern wird Ihnen zu einem besseren 
Daten-Durchsatz verhelfen, wenn Sie popul?re Dateien anfordern. Je mehr Platz 
Sie zur Verf?gung stellen k?nnen, desto besser ist es f?r die Gemeinschaft und 
desto schneller wird Ihr Knoten.
+FirstTimeWizardToadlet.enableOpennet=Automatisch mit Knoten von nicht 
vertrauensw?rdigen Fremden verbinden?
+FirstTimeWizardToadlet.homepageTitle=Freenet-Einrichtungs-Assistent!
+FirstTimeWizardToadlet.iDoTrust=Trauen Sie Leuten die mit ${interface} (${ip}) 
verbunden sind?
+FirstTimeWizardToadlet.isNetworkTrusted=Ist Ihr lokales Netzwerk 
vertrauensw?rdig?
+FirstTimeWizardToadlet.isNetworkTrustedLong=Ist Ihr lokales Netzwerk 
vertrauensw?rdig? Wenn Sie hier mit Ja antworten, werden alle Dienste, die Ihr 
Knoten anbietet, f?r jeden der vom lokalen Netzwerk auf sie zugreifen will, 
frei zug?nglich sein. Sie werden die M?glichkeit haben, selektive 
Zugangs-Kontrollen auf der Konfigurations-Seite einzurichten, wenn der 
Assistent beendet ist.
+FirstTimeWizardToadlet.noNetworkIF=Keine zus?tzliche Netzwerk-Schnittstelle 
gefunden
+FirstTimeWizardToadlet.noNetworkIFLong=Freenet hat keine zus?tzliche 
Netzwerk-Schnittstelle gefunden. Es wird annehmen, dass Sie sich von Ihrem 
Computer (und nur von Ihrem Computer) mit ihm verbinden.
+FirstTimeWizardToadlet.skipWizard=Ich bin kein Neuling, bitte ?berspringe den 
Assistenten!
+FirstTimeWizardToadlet.step1Title=Freenet-Einrichtungs-Assistent! - Freunde 
und Fremde
+FirstTimeWizardToadlet.step2Title=Freenet-Einrichtungs-Assistent! - W?hlen Sie 
einen Knoten-Namen
+FirstTimeWizardToadlet.step3Title=Freenet-Einrichtungs-Assistent! - 
Bandbreiten-Limits
+FirstTimeWizardToadlet.step4Title=Freenet-Einrichtungs-Assistent! - Gr??e des 
Datenspeichers
+FirstTimeWizardToadlet.step5Title=Freenet-Einrichtungs-Assistent! - 
Netzwerk-Konfiguration
+FirstTimeWizardToadlet.step6Title=Freenet-Einrichtungs-Assistent! - Herzlichen 
Gl?ckwunsch, Ihr Knoten ist nun konfiguriert
+FirstTimeWizardToadlet.welcomeInfoboxContent1=Willkommen beim 
Freenet-Einrichtungs-Assistent. Dieses Werkzeug erlaubt es Ihnen, Ihren Knoten 
schnell und einfach f?r den ersten Betrieb einzurichten. Bitte
+FirstTimeWizardToadlet.welcomeInfoboxTitle=Willkommen zum 
Freenet-Einrichtungs-Assistent!
+GIFFilter.invalidHeader=Die Datei enth?lt keinen g?ltigen GIF-Header 
(Dateikopf).
+GIFFilter.invalidHeaderTitle=Ung?ltiger Header
+GIFFilter.notGif=Die Datei, die Sie versucht haben abzurufen, ist keine 
GIF-Datei. Es k?nnte sich um ein anderes Datei-Format handeln und Ihr Browser 
k?nnte etwas gef?hrliches damit tun, deshalb haben wir sie blockiert.
+GIFFilter.tooShort=Die Datei ist zu klein um eine GIF-Datei zu sein.
+GIFFilter.tooShortTitle=Zu klein
+GenericReadFilterCallback.couldNotParseAbsoluteFreenetURI=Konnte nicht als 
absolute Freenet-URI erkannt werden
+GenericReadFilterCallback.couldNotParseFormURIWithError=Der Filter konnte die 
Form des URI nicht analysieren: ${error}
+GenericReadFilterCallback.couldNotParseRelativeFreenetURI=Konnte nicht als 
relative Freenet-URI erkannt werden
+GenericReadFilterCallback.couldNotParseURIWithError=Der Filter konnte den URI 
nicht analysieren: ${error}
+GenericReadFilterCallback.invalidFormURI=Ung?ltige Form des URI: zeigt auf 
externe Ressource
+GenericReadFilterCallback.invalidFormURIAttemptToEscape=Versuch, aus der 
Verzeichnis-Struktur auszubrechen
+GenericReadFilterCallback.malformedAbsoluteURL=Falsch aufgebaute URL 
(absolut): ${error}
+GenericReadFilterCallback.malformedRelativeURL=Falsch aufgebaute URL 
(relativ): ${error}
+GenericReadFilterCallback.protocolNotEscaped=Kein escape-Protokoll: ${protocol}
+HTMLFilter.couldNotParseStyle=Der Eingabe-Stil konnte nicht abgeglichen werden
+HTMLFilter.deletedUnknownStyle=unbekannter Stil gel?scht
+HTMLFilter.failedToParseLabel=Der HTML-Filter konnte die Seite nicht 
analysieren
+HTMLFilter.tooManyNestedStyleOrScriptTags=Zu viele verschachtelte Stil- oder 
Script-Tags (Markierungen) - mehrdeutig oder ung?ltige Analyse
+HTMLFilter.tooManyNestedStyleOrScriptTagsLong=Zu viele verschachtelte 
</style>-Tags (Markierungen) - mehrdeutig oder ung?ltige Analyse, es kann nicht 
zuverl?ssig gefiltert werden, deshalb werden die inneren Tags entfernt - es 
kann sein, dass der Browser Unfug anzeigt
+HTMLFilter.unknownTag=unbekannter Tag (Markierung) ${tag}
+IPDetectorPluginManager.direct=Sie scheinen direkt mit dem Internet verbunden 
zu sein. Herzlichen Gl?ckwunsch, Sie sollten in der Lage sein, sich mit jedem 
anderen Freenet-Knoten zu verbinden.
+IPDetectorPluginManager.directTitle=Direkte Internet-Verbindung erkannt
+IPDetectorPluginManager.fullCone=Ihre Internet-Verbindung scheint sich hinter 
einem "full cone"-NAT (Router) zu befinden. Herzlichen Gl?ckwunsch, Ihr Knoten 
sollte in der Lage sein, sich mit jedem anderen Knoten zu verbinden.
+IPDetectorPluginManager.fullConeTitle="Full cone"-NAT erkannt
+IPDetectorPluginManager.noConnectivity=Ihre Internet-Verbindung scheint UDP 
nicht zu unterst?tzen. Wenn diese Erkennung richtig ist, wird Freenet auf Ihrem 
Computer derzeit wahrscheinlich nicht funktionieren.
+IPDetectorPluginManager.noConnectivityTitle=Keine UDP-Konnektivit?t
+IPDetectorPluginManager.portRestricted=Ihre Internet-Verbindung scheint sich 
hinter einem "port-restricted cone"-NAT (Router) zu befinden. Sie werden in der 
Lage sein sich mit den meisten anderen Benutzern zu verbinden, aber nicht mit 
denen, die sich hinter einem symmetrischen NAT befinden.
+IPDetectorPluginManager.portRestrictedTitle="Port restricted cone"-NAT erkannt
+IPDetectorPluginManager.restricted=Ihre Internet-Verbindung scheint sich 
hinter einem "restricted cone"-NAT (Router) zu befinden. Sie sollten in der 
Lage sein, sich mit den meisten anderen Benutzern zu verbinden.
+IPDetectorPluginManager.restrictedTitle="Restricted cone"-NAT erkannt
+IPDetectorPluginManager.suggestForwardPort=Sie m?chten vielleicht den Port 
(UDP-Port Nummer ${port}) manuell weiterleiten (siehe 
http://wiki.freenetproject.org/deFirewallAndRouterIssues ).
+IPDetectorPluginManager.suggestForwardPortWithLink=Sie m?chten vielleicht den 
Port (UDP-Port Nummer ${port}) ${link}manuell weiterleiten${/link} (es kann 
sein, dass Sie dies schon getan haben, Freenet kann dies nicht sicher erkennen).
+IPDetectorPluginManager.symmetric=Ihre Internet-Verbindung scheint sich hinter 
einem symmetrischen NAT (Router) oder einer Firewall zu befinden. Sie werden 
wahrscheinlich nur in der Lage sein, sich mit Benutzern zu verbinden, die 
direkt oder ?ber "restricted cone"-NATs mit dem Internet verbunden sind.
+IPDetectorPluginManager.symmetricTitle=Symmetrische Firewall erkannt
+IPUndetectedUserAlert.detecting=Freenet versucht gerade Ihre externe 
IP-Adresse zu ermitteln. Wenn dies mehr als ein paar Minuten dauert, l?uft 
etwas schief...
+IPUndetectedUserAlert.detectingWithConfigLink=Freenet versucht gerade Ihre 
externe IP-Adresse zu ermitteln. Wenn dies mehr als ein paar Minuten dauert, 
dann l?uft etwas schief und Sie k?nnen die "Hinweis auf tempor?re 
IP-Adresse"-${link}Einstellung${/link} benutzen. Es w?re auch eine gute Idee, 
den UDP-Port ${port} in Ihrem Router weiterzuleiten, um es Anderen zu 
erleichtern zu Ihrem Knoten Kontakt aufzunehmen.
+IPUndetectedUserAlert.unknownAddress=Freenet war nicht imstande Ihre 
IP-Adresse zu ermitteln (oder die IP-Adresse ihrer Firewall oder Ihres 
NAT-Ger?ts (Router)). Sie k?nnen immer noch Referenzen mit anderen Menschen 
austauschen, dies wird aber nur funktionieren, wenn der andere Benutzer sich 
nicht hinter einem NAT-Ger?t oder einer Firewall befindet. Sobald Sie sich auf 
diesem Weg mit einem anderen Benutzer verbunden haben, wird Freenet in der Lage 
sein ihre externe IP-Adresse zu ermitteln. Sie k?nnen auch ihre momentane 
IP-Adresse feststellen und diese Ihrem Knoten mit der "Hinweis auf tempor?re 
IP-Adresse"-${link}Einstellung${/link} mitteilen. Es w?re auch eine gute Idee, 
den UDP-Port ${port} in Ihrem Router weiterzuleiten, um es Anderen zu 
erleichtern zu Ihrem Knoten Kontakt aufzunehmen.
+IPUndetectedUserAlert.unknownAddressTitle=Unbekannte externe Adresse
+IPUndetectedUserAlert.unknownAddressWithConfigLink=Freenet war nicht imstande 
Ihre IP-Adresse zu ermitteln (oder die IP-Adresse ihrer Firewall oder Ihres 
NAT-Ger?ts (Router)). Sie k?nnen immer noch Referenzen mit anderen Menschen 
austauschen, dies wird aber nur funktionieren, wenn der andere Benutzer sich 
nicht hinter einem NAT-Ger?t oder einer Firewall befindet. Sobald Sie sich auf 
diesem Weg mit einem anderen Benutzer verbunden haben, wird Freenet in der Lage 
sein ihre externe IP-Adresse zu ermitteln. Sie k?nnen auch ihre momentane 
IP-Adresse feststellen und diese Ihrem Knoten mit der "Hinweis auf tempor?re 
IP-Adresse"-${link}Einstellung${/link} mitteilen. Es w?re auch eine gute Idee, 
den UDP-Port ${port} in Ihrem Router weiterzuleiten, um es Anderen zu 
erleichtern zu Ihrem Knoten Kontakt aufzunehmen.
+InsertException.longError.1=Der Aufrufer lieferte einen URI den wir nicht 
benutzen k?nnen"
+InsertException.longError.10=Abbruch durch den Benutzer
+InsertException.longError.11=Im URI wurde eine Meta-Zeichenfolge 
(wahrscheinlich ein '/') benutzt
+InsertException.longError.12=Bin?rer Klumpen-Format-Fehler (binary blob format 
error)
+InsertException.longError.2=Interner Speicher(bucket)-Fehler: kein freier 
Speicherplatz/Problem mit den Zugriffsrechten?
+InsertException.longError.3=Interner Fehler
+InsertException.longError.4=Ein nachgelagerter Knoten hatte eine 
Zeit?berschreitung oder war ernsthaft ?berlastet
+InsertException.longError.5=Konnte die Einf?ge-Operation nicht bei gen?gend 
Knoten bekannt machen (normal bei kleinen Netzwerken, versuche trotzdem sie 
abzurufen)
+InsertException.longError.6=Fatale Fehler beim Einf?gen einer Teildatei
+InsertException.longError.7=Konnte die Teil-Datei nicht einf?gen: Es sind 
keine Wiederholungs-Versuche mehr ?brig (nicht-fatale Fehler)
+InsertException.longError.8=Die Einf?ge-Operation konnte den Knoten nicht 
verlassen
+InsertException.longError.9=Einf?ge-Operation kollidierte mit verschiedenen, 
vorher existierenden Daten beim selben Schl?ssel
+InsertException.shortError.1=Ung?ltiger URI
+InsertException.shortError.10=Abgebrochen
+InsertException.shortError.11=Im Schl?ssel wurde eine Meta-Zeichenfolge benutzt
+InsertException.shortError.12=Bin?rer Klumpen-Format-Fehler
+InsertException.shortError.2="Tempor?re Dateien"-Fehler
+InsertException.shortError.3=Interner Fehler
+InsertException.shortError.4=Zeit?berschreitung oder ?berlastung
+InsertException.shortError.5=Route nicht gefunden
+InsertException.shortError.6=Manche Bl?cke wiesen fatale Fehler auf
+InsertException.shortError.7=Manche Bl?cke hatten keine Wiederholungs-Versuche 
mehr
+InsertException.shortError.8=Anfrage konnte den Knoten nicht verlassen
+InsertException.shortError.9=Kollision mit existierenden Daten
+IntOption.parseError=Der gegebene Wert konnte nicht als 32-bit-Ganzzahl 
erkannt werden: ${val}
+JPEGFilter.notJpeg=Die Datei, die Sie versucht haben abzurufen, ist keine 
JPEG-Datei. Es k?nnte sich um ein anderes Format handeln und Ihr Browser k?nnte 
etwas gef?hrliches damit tun, deshalb haben wir sie blockiert.
+JPEGFilter.tooShort=Die Datei ist zu klein um eine JPEG-Datei zu sein.
+JPEGFilter.tooShortTitle=Zu klein
+KnownUnsafeContentTypeException.dangerousInlines=Dieser Inhalts-Typ kann 
eingebundene Bilder oder Videos enthalten, und somit Inhalte vom nicht-anonymen 
offenen Web (Internet) herunterladen, wobei Ihre IP-Adresse offen gelegt wird.
+KnownUnsafeContentTypeException.dangerousInlinesLabel=Gef?hrliche eingebettete 
Dateien:
+KnownUnsafeContentTypeException.dangerousLinks=Dieser Inhalts-Typ kann Links 
ins nicht-anonyme Web (Internet) enthalten; wenn Sie diese anklicken (und sie 
k?nnten getarnt sein), k?nnte dies Ihre IP-Adresse offen legen.
+KnownUnsafeContentTypeException.dangerousLinksLabel=Gef?hrliche Links:
+KnownUnsafeContentTypeException.dangerousMetadata=Dieser Inhalts-Typ kann 
Meta-Daten enthalten (welche von manchen Browsern oder anderer Software 
angezeigt werden k?nnten), welche gef?hrliche Links oder eingebettete Dateien 
enthalten k?nnen.
+KnownUnsafeContentTypeException.dangerousMetadataLabel=Gef?hrliche Meta-Daten:
+KnownUnsafeContentTypeException.dangerousScripts=Dieser Inhalts-Typ kann 
gef?hrliche Scripts (ausf?hrbare Inhalte) enthalten, welche, wenn sie 
ausgef?hrt werden, Ihre Anonymit?t kompromittieren (blo?stellen/offen legen) 
k?nnten indem sie Verbindung zum offenen Web (Internet) aufnehmen, oder auf 
andere Weise die Sicherheit verletzen.
+KnownUnsafeContentTypeException.dangerousScriptsLabel=Gef?hrliches Scripting:
+KnownUnsafeContentTypeException.knownUnsafe=Dies ist ein potenziell 
gef?hrlicher MIME-Typ. Wenn der Knoten ihn durchl?sst, k?nnte Ihr Browser 
schlechte Dinge tun, welche zu einer Kompromittierung 
(Blo?stellung/Offenlegung) Ihrer Anonymit?t und Offenlegung ihrer IP-Adresse in 
Verbindung mit dieser Seite f?hren k?nnten. Im speziellen:
+KnownUnsafeContentTypeException.noFilter=Da es keinen eingebauten Filter f?r 
diese Daten gibt, sollten Sie ?u?erst vorsichtig sein!
+KnownUnsafeContentTypeException.title=Bekannter gef?hrlicher Typ: ${type}
+LocalFileInsertToadlet.checkPathExist=Stellen Sie sicher, dass der angegebene 
Pfad existiert.
+LocalFileInsertToadlet.checkPathIsDir=Stellen Sie sicher, dass der angegebene 
Pfad ein Verzeichnis ist.
+LocalFileInsertToadlet.checkPathReadable=Stellen Sie sicher, dass auf den 
angegebenen Pfad, von dem Benutzer der den Knoten ausf?hrt, zugegriffen werden 
kann.
+LocalFileInsertToadlet.dirAccessDenied=Sie k?nnen dieses Verzeichnis nicht 
durchsuchen
+LocalFileInsertToadlet.dirCannotBeRead=Das Verzeichnis "${path}" kann nicht 
gelesen werden.
+LocalFileInsertToadlet.fileHeader=Datei
+LocalFileInsertToadlet.insert=Einf?gen
+LocalFileInsertToadlet.listing=Verzeichnis-Liste: ${path}
+LocalFileInsertToadlet.listingTitle=Auflistung von ${path}
+LocalFileInsertToadlet.sizeHeader=Gr??e
+LogConfigHandler.detaildPriorityThreshold=Detaillierte Priorit?ts-Schwellen
+LogConfigHandler.detaildPriorityThresholdLong=Detaillierte 
Priorit?ts-Schwellwerte, zum Beispiel freenet:normal,freenet.node:minor
+LogConfigHandler.dirName=Protokoll-Verzeichnis
+LogConfigHandler.dirNameLong=Verzeichnis in dem Protokoll-Dateien gespeichert 
werden
+LogConfigHandler.enabled=Protokollierung (Logging) aktivieren?
+LogConfigHandler.enabledLong=Auf false (falsch) setzen um die Protokollierung 
komplett abzuschalten
+LogConfigHandler.maxCachedBytes=Maximal im RAM zwischengespeicherte 
Protokoll-Bytes
+LogConfigHandler.maxCachedBytesLong=Maximale Anzahl von Bytes der 
Protokollierung, die im RAM (Hauptspeicher) zwischengespeichert werden
+LogConfigHandler.maxCachedLines=Maximal im RAM zwischengespeicherte 
Protokoll-Zeilen
+LogConfigHandler.maxCachedLinesLong=Maximale Anzahl von Zeilen der 
Protokollierung, die im RAM (Hauptspeicher) zwischengespeichert werden
+LogConfigHandler.maxZippedLogsSize=Maximaler von alten Protokoll-Dateien 
belegter Speicherplatz
+LogConfigHandler.maxZippedLogsSizeLong=Maximaler von alten Log-Dateien 
belegter Speicherplatz
+LogConfigHandler.minLoggingPriority=Minimale Priorit?t ab der Nachrichten 
protokolliert werden
+LogConfigHandler.minLoggingPriorityLong=Minimale Priorit?t ab der Nachrichten 
protokolliert werden. Zur Auswahl stehen debug (Fehleranalyse), minor 
(unwichtig), normal und error (Fehler).
+LogConfigHandler.rotationInterval=Protokoll-Wechsel-Intervall
+LogConfigHandler.rotationIntervalLong=Protokoll-Wechsel-Intervall - Zeitraum 
nach dem die Protokolle gewechselt werden. Wir behalten die letzten zwei 
Protokoll-Dateien (current (aktuell) und prev (vorhergehend)) und viele 
komprimierte Protokoll-Dateien bis zu maxZippedLogsSize 
(maxKomprimierteProtokollGr??e)
+LoggerHook.unrecognizedPriority=Unbekannter Priorit?ts-Name: ${name}.
+LongOption.parseError=Der angegebene Wert konnte nicht als eine, 64-bit lange, 
ganze Zahl erkannt werden: ${val}
+MeaningfulNodeNameUserAlert.noNodeNick=Anscheinend kennt Ihr Knoten Ihren 
Nickname (Spitznamen) nicht. Ihre E-Mail-Adresse oder IRC-Nickname hier 
einzuf?gen, ist allgemein gesprochen, eine gute Idee und hilft ihren Freunden 
ihren Knoten zu identifizieren. (Beachten Sie, dass nur Ihre darknet-Partner, 
die auf der Freunde-Seite aufgelistet sind, Ihren Knoten-Namen sehen k?nnen, 
Fremde k?nnen ihn nicht einsehen).
+MeaningfulNodeNameUserAlert.noNodeNickTitle=Ihr Knoten-Name ist nicht gesetzt.
+N2NTMToadlet.composingMessageLabel=N2NTM zum Senden an die folgenden Partner 
verfassen:
+N2NTMToadlet.delayed=Zur?ckgezogen: Das Senden der Nachricht zum Partner 
erfolgt m?glicherweise verz?gert
+N2NTMToadlet.delayedTitle=Verz?gert
+N2NTMToadlet.failed=Nachricht nicht an Partner gesendet: Partner nicht 
verbunden
+N2NTMToadlet.failedTitle=Fehlgeschlagen
+N2NTMToadlet.friends=Freunde
+N2NTMToadlet.noSuchFileOrCannotRead=Nicht in der Lage die Datei zu senden: 
Entweder existiert sie nicht, oder sie kann nicht gelesen (zugegriffen) werden.
+N2NTMToadlet.peerName=Name des Partners
+N2NTMToadlet.peerNotFoundTitle=Partner nicht gefunden
+N2NTMToadlet.peerNotFoundWithHash=Der Partner mit dem Hash-Code 
\u201c${hash}\u201d konnte nicht gefunden werden.
+N2NTMToadlet.processingSend=Senden der Knoten-zu-Knoten-Text-Nachricht in 
Bearbeitung
+N2NTMToadlet.queued=Eingereiht: Der Partner ist nicht verbunden, deshalb wurde 
die Nachricht in die Warteschlange eingereiht, f?r den Zeitpunkt wenn er sich 
verbindet.
+N2NTMToadlet.queuedTitle=Eingereiht
+N2NTMToadlet.returnToFriends=Zur?ck zur Freundes-Liste
+N2NTMToadlet.sendMessage=Knoten-zu-Knoten-Text-Nachricht (N2NTM) senden
+N2NTMToadlet.sendMessageShort=Nachricht senden
+N2NTMToadlet.sendStatus=N2NTM-Sende-Status
+N2NTMToadlet.sent=Nachricht wurde an den Partner gesendet
+N2NTMToadlet.sentTitle=Gesendet
+N2NTMToadlet.tooLong=N2NTMs sind auf 1024 Zeichen beschr?nkt
+N2NTMToadlet.tooLongTitle=Zu lang
+N2NTMUserAlert.delete=L?schen
+N2NTMUserAlert.header=Von: ${from} (erstellt ${composed} | gesendet ${sent} | 
empfangen ${received})
+N2NTMUserAlert.reply=Antworten
+N2NTMUserAlert.title=Knoten-zu-Knoten-Text-Nachricht ${number} von ${peername} 
(${peer})
+Node.alwaysAllowLocalAddresses=Das Verbinden mit Knoten ?ber lokale Adressen 
immer erlauben?
+Node.alwaysAllowLocalAddressesLong=Wenn aktiviert, wird der Knoten versuchen 
sich sowohl ?ber die lokalen Adressen (localhost, LAN) als auch ?ber die 
?ffentlichen IPs mit den Knoten zu verbinden. Wenn dies nicht aktiviert ist, 
k?nnen Sie es immer noch f?r spezifische Darknet-Partner einschalten (aber 
nicht f?r Opennet-Partner). Aktivieren Sie dies, wenn Sie sich mit anderen 
Knoten im selben LAN (Netzwerk) oder Computer verbinden wollen und es Ihnen 
nichts ausmacht, dass fehlerhafte Referenzen Ihren Knoten dazu veranlassen 
k?nnen, UDP-Pakete zu Ger?ten in Ihrem LAN zu senden.
+Node.bandwidthLimitMustBePositiveOrMinusOne=Das Bandbreiten-Limit muss positiv 
oder -1 sein
+Node.bindTo=Zu benutzende IP-Adresse(n)
+Node.bindToLong=Zu benutzende IP-Adresse(n)
+Node.buggyJVM=Die JVM, die Sie benutzen (${version}), ist bekannterma?en 
fehlerhaft. Sie kann OutOfMemoryError's (KeinVerbleibenderSpeicherFehler) 
verursachen obwohl noch mehr als genug Speicher verf?gbar ist. Bitte 
aktualisieren Sie mindestens auf Sun Java 1.4.2_13, 1.5.0_10 oder 1.6 
(empfohlen). Siehe http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4855795 .
+Node.buggyJVMTitle=Warnung vor fehlerhafter JVM
+Node.buggyJVMWithLink=Die JVM, die Sie benutzen (${version}), ist 
bekannterma?en ${link}fehlerhaft${/link}.  Sie kann OutOfMemoryError's 
(KeinVerbleibenderSpeicherFehler) verursachen obwohl noch mehr als genug 
Speicher verf?gbar ist. Bitte aktualisieren Sie mindestens auf Sun Java 
1.4.2_13, 1.5.0_10 oder 1.6 (empfohlen).
+Node.bwlimitMustBePositive=Das Bandbreiten-Limit muss positiv sein
+Node.databaseMemory=Maximale Speichernutzung des Datenspeichers
+Node.databaseMemoryLong=Maximale Speichernutzung der Datenbank die die 
Datenspeicher-Indizies vorh?lt. 0 bedeutet kein Limit (auf ~30% der maximalen 
Speicherkapazit?t begrenzt)
+Node.deadlockTitle=M?gliches Einfrieren aufgrund fehlerhafter 
JVM/Kernel-Kombination
+Node.deadlockWarning=WARNUNG: Ihr System scheint eine Sun JVM mit NTPL 
auszuf?hren. Es ist bekannt, dass dies den Knoten, dadurch, dass die JVM eine 
Sperre verliert, zum einfrieren bringen kann. Bitte deaktivieren Sie NTPL, 
falls m?glich, indem Sie die folgende Umgebungsvariable setzen 
LD_ASSUME_KERNEL=2.4.1 . Neuere Versionen des Freenet-Installationsprogramms 
sollten dies bereits beinhalten; installieren Sie Freenet entweder neu, oder 
bearbeiten Sie die run.sh 
(https://emu.freenetproject.org/svn/trunk/apps/installer/installclasspath/run.sh).
 Auf manchen Systemen m?ssen Sie vielleicht die pthreads-Bibliotheken 
installieren, damit es funktioniert. Beachten Sie, dass der Knoten automatisch 
versuchen wird sich selbst neu zu starten, wenn es zu einem Deadlock 
(Verklemmung) kommt, aber dies ist nicht 100%-ig zuverl?ssig und ben?tigt Zeit.
+Node.disableHangCheckers=Alle Funktions-Pr?fungen ausschalten
+Node.disableHangCheckersLong=Alle ?berpr?fungs-/?berwachungs-Funktionen 
ausschalten (werden haupts?chlich benutzt um die Funktionst?chtigkeit des 
Knotens zu ?berpr?fen). Aktivieren Sie dies wenn Sie ein 
Anti-Profiling-Fanatiker sind.
+Node.disablePHTLS=Wahrscheinlichkeitsbasierte HTL ausschalten
+Node.disablePHTLSLong=Wahrscheinlichkeitsbasierte HTL ausschalten (ver?ndern 
Sie dies nur wenn Sie genau wissen was Sie tun)
+Node.dropPacketEvery=Paket-Verlust-Frequenz f?r Tests
+Node.dropPacketEveryLong=Frequenz mit der Pakete vernichtet werden. 
Test-Option die von den Entwicklern genutzt wird um Paket-Verlust zu 
simulieren. 0 bedeutet, dass kein Paket k?nstlich "verloren" wird. Bitte nicht 
ver?ndern!
+Node.errorApplyingConfig=Fehler beim Anwenden der neuen Konfiguration: ${error}
+Node.extraPeerDir=Partner-Daten-Verzeichnis
+Node.extraPeerDirLong=Name des Verzeichnis in dem zus?tzliche Partner-Daten 
gespeichert werden.
+Node.forceBigShrink=Gro?e Daten-Kompressionen sofort ausf?hren
+Node.forceBigShrinkLong=Wenn aktiviert, werden gro?e Speicher-Komprimierungen 
(?ber 10%) sofort ausgef?hrt (anstatt auf den n?chsten Neustart des Knotens zu 
warten). Komprimierungen im laufenden Betrieb bewahren nicht die zuletzt 
benutzten Daten, daher ist dies nicht empfehlenswert; benutzen Sie dies nur, 
wenn Sie ein sofortiges Ergebnis haben m?ssen.
+Node.inBWLimit=Limit f?r ausgehende Bandbreite (Bytes pro Sekunde)
+Node.inBWLimitLong=Limit f?r die hereinkommende Bandbreite (Bytes/Sek); der 
Knoten wird versuchen dieses nicht zu ?berschreiten; -1 = 4x das gesetzte Limit 
f?r die ausgehende Bandbreite
+Node.invalidStoreSize=Die Speicher-Gr??e muss mindestens 32MB betragen
+Node.l10nLanguage=Die Sprache die der Knoten benutzt um Nachrichten anzuzeigen
+Node.l10nLanguageLong=Diese Einstellung ?ndert die Sprache in der Nachrichten 
angezeigt werden. Beachten Sie, dass manche Zeichenfolgen erst beim n?chsten 
Start des Knotens ?bersetzt werden.
+Node.maxHTL=Maximale HTL
+Node.maxHTLLong=Maximale HTL (NUR F?R ENTWICKLUNGS-ZWECKE BENUTZEN!)
+Node.mustBePositive=Der Konfigurations-Wert muss positiv sein
+Node.nodeDir=Knoten-Verzeichnis
+Node.nodeDirLong=Der Name des Verzeichnisses in welchem Knoten-bezogene 
Dateien (z.B. die Partner-Liste) gespeichert werden
+Node.nodeName=Spitzname f?r diesen Freenet-Knoten
+Node.nodeNameLong=Knoten-Name (Nickname). Dieser wird nur f?r Ihre Freunde 
sichtbar sein.
+Node.oneConnectionPerIP=Auf eine Verbindung pro Adresse beschr?nken?
+Node.oneConnectionPerIPLong=Nicht mehr als eine Verbindung pro Adresse 
erlauben? Dies wird es Angreifern ein bisschen schwerer machen sich, als 
verschiedene Identit?ten, mehr als einmal mit Ihrem Knoten zu verbinden, um Ihr 
Routing (Weiterleitungs-Strategie) zu beherrschen oder Harvesting (Sammeln von 
Daten) zu erleichtern. Es verhindert auch gleichzeitig mit demselben Knoten 
?ber darknet und opennet verbunden zu sein.
+Node.opennetEnabled=Promiskuitiven Modus einschalten (automatisch mit nicht 
vertrauensw?rdigen Knoten verbinden)?
+Node.opennetEnabledLong=Promiskuitiven Modus einschalten? Wenn dies aktiviert 
ist, wird der Knoten automatisch Referenzen mit anderen nicht 
vertrauensw?rdigen Knoten (Fremden im Gegensatz zu Freunden) austauschen. Dies 
bedeutet aber, dass die Tatsache, dass Sie einen Knoten betreiben, nicht l?nger 
vertraulich ist und viele Attacken viel einfacher sind. Wenn Sie gen?gend Leute 
kennen, die Freenet benutzen, sollten Sie bei vertrauensw?rdigen (Freunde) 
Verbindungen mit ihnen bleiben.
+Node.outBWLimit=Limit f?r die ausgehende Bandbreite (Bytes pro Sekunde)
+Node.outBWLimitLong=Strenges Limit f?r die ausgehende Bandbreite (Bytes/Sek); 
der Knoten sollte dies fast niemals ?berschreiten
+Node.passOpennetPeersThroughDarknet=Opennet-Knotenreferenzen ?ber 
Darknet-Partner weiterleiten?
+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) mit 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.storeDirectory=Speicher-Verzeichnis
+Node.storeDirectoryLong=Name des Verzeichnisses in welchem die 
Speicher-Dateien platziert werden
+Node.storeMaxMemTooHigh=Mehr als 80% Ihres RAMs (Hauptspeichers) an BDB 
(Datenbank) abzugeben ist wahrscheinlich nicht das was Sie tun wollen!
+Node.storeSize=Speicher-Gr??e in Bytes
+Node.storeSizeLong=Gr??e des Speichers in Bytes.
+Node.swapRInterval=Tausch-Anfragen-Sende-Intervall (ms)
+Node.swapRIntervalLong=Intervall in dem Tausch-Anfragen gesendet werden in 
Millisekunden. Lassen Sie dies in Ruhe!
+NodeClientCore.allowInsecureCHK=Unsichere CHKs erlauben?
+NodeClientCore.allowInsecureCHKLong=Vor (Version) 1010, waren alle CHKs 
unsicher (nur halb verschl?sselt). Alte CHKs erlauben?
+NodeClientCore.allowInsecureSSK=Unsichere SSKs erlauben?
+NodeClientCore.allowInsecureSSKLong=Vor (Version) 1010, waren alle SSKs 
unsicher (nur halb verschl?sselt). Alte SSKs erlauben?
+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!
+NodeClientCore.downloadDir=Standard Download-Verzeichnis
+NodeClientCore.downloadDirLong=Das Verzeichnis in welchem heruntergeladene 
Dateien standardm??ig gespeichert werden
+NodeClientCore.fileForClientStats=Datei in welcher die Statistiken gespeichert 
werden
+NodeClientCore.fileForClientStatsLong=Datei in der die Drosselungs-Statistiken 
gespeichert werden (wird ben?tigt um herauszufinden wie oft Anfragen gesendet 
werden sollen).
+NodeClientCore.ignoreTooManyPathComponents=Zu viele Pfad-Komponenten ignorieren
+NodeClientCore.ignoreTooManyPathComponentsLong=Wenn aktiviert, wird der Knoten 
keine "TOO_MANY_PATH_COMPONENTS"-Fehler erzeugen wenn er einen URI bekommt, 
welcher am Ende dessen was zum abrufen des Schl?ssels notwendig ist, 
zus?tzliche, sinnlose Unterverzeichnisse (/blah/blah) enth?lt (zum Beispiel 
haben alte CHKs oft Dateinamen am Ende welche nicht Teil der urspr?nglichen 
Einf?geoperation waren; dies ist ?berholt, weil man nun den Dateinamen mit 
angeben kann und es verwirrend ist willk?rliche Zeichenketten zu einem URI 
hinzuf?gen zu k?nnen, und es macht es schwer sie zu vergleichen). Aktivieren 
Sie diese Einstellung nur, wenn Sie dies f?r die Kompatibilit?t mit anderen 
Anwendungen ben?tigen; sie wird in K?rze entfernt.
+NodeClientCore.lazyResume=Vervollst?ndige das Laden persistenter Anfragen beim 
Start? (Braucht mehr Speicher)
+NodeClientCore.lazyResumeLong=Der Knoten kann persistente (dauerhafte) 
anstehende Anfragen beim Starten laden, oder er kann die Daten in den Speicher 
einlesen und dann die Anfragen nachdem der Knoten vollst?ndig gestartet ist 
komplettieren. K?rzere Start-Zeiten, braucht aber mehr Speicher.
+NodeClientCore.maxUSKFetchers=Maximal erlaubte Anzahl von USK-Abrufern
+NodeClientCore.maxUSKFetchersLong=Maximale Anzahl zugelassener USK-Abrufer
+NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=Muss gr??er als Null sein
+NodeClientCore.movingTempDirOnTheFlyNotSupported=Das Verschieben des 
tempor?ren Verzeichnisses im laufenden Betrieb, wird im Moment (noch?) nicht 
unterst?tzt
+NodeClientCore.persistentTempDir=Verzeichnis f?r persistente tempor?re Dateien
+NodeClientCore.persistentTempDirLong=Name des Verzeichnisses in welchem 
persistente (dauerhafte) tempor?re Dateien gespeichert werden
+NodeClientCore.tempDir=Tempor?res Verzeichnis
+NodeClientCore.tempDirLong=Name des Verzeichnisses, in dem die tempor?ren 
Dateien gespeichert werden
+NodeClientCore.uploadAllowedDirs=Verzeichnisse von denen das Hochladen erlaubt 
ist
+NodeClientCore.uploadAllowedDirsLong=Per Semikolon getrennte Liste von 
Verzeichnissen, von denen das Hochladen erlaubt ist. Ein leeres Feld bedeutet, 
dass das Hochladen von Datentr?gern nicht erlaubt ist, "all" bedeutet, dass 
Dateien von jedem Ort hochgeladen werden d?rfen (inklusive System-Dateien 
usw.!). WARNUNG! Wenn diese Option auf "all" gesetzt ist, kann jede Datei auf 
Ihrem Computer von jedem Benutzer hochgeladen werden.
+NodeIPDectector.inclLocalAddress=Lokale Adressen zur noderef (Knoten ID) 
hinzuf?gen
+NodeIPDectector.inclLocalAddressLong=Lokale Adressen (LAN und localhost) zur 
noderef (Knoten-ID) hinzuf?gen. Dies ist nur dann von Nutzen, wenn beide Seiten 
f?r die entsprechenden Knoten-IDs "allowLocalAddresses=true" 
(=erlaubeLokaleAdressen) gesetzt haben (dies k?nnen Sie im erweiterten Modus 
auf der Freunde-Seite tun).
+NodeIPDectector.ipOverride=IP-Adresse ?berschreiben
+NodeIPDectector.ipOverrideLong=IP-Adresse ?berschreiben (normalerweise nicht 
ben?tigt) - aktivieren Sie dies, wenn Sie eine *statische* IP-Adresse oder eine 
Domain (z.B. per DynDNS) haben, und sich hinter einer Firewall befinden.
+NodeIPDectector.tempAddressHint=Hinweis auf tempor?re IP-Adresse
+NodeIPDectector.tempAddressHintLong=Tempor?rer Hinweis wie die aktuelle 
IP-Adresse lauten k?nnte; wird nach Gebrauch gel?scht.
+NodeIPDetector.maybeSymmetric=Es scheint als ob Ihr Knoten hinter einer 
symmetrischen NAT (Router) sein k?nnte. Sie k?nnten Verbindungs-Probleme haben: 
Wenn Sie sich hinter einer symmetrischen NAT befinden, werden Sie sich 
wahrscheinlich nur mit Partnern verbinden k?nnen, die offen im Internet (direkt 
mit dem Internet verbunden) sind.
+NodeIPDetector.maybeSymmetricTitle=Verbindungs-Probleme
+NodeIPDetector.unknownHostErrorInIPOverride=Unbekannter Host (Computer): 
${error}
+NodeStat.aggressiveGC="Aggressiver GC"-Modifikator
+NodeStat.aggressiveGCLong=Erlaubt es dem Benutzer die Zeit zwischen GC 
(Garbage-Collection) und gezwungenem Abschluss anzupassen. SOLLTE NICHT 
VER?NDERT WERDEN es sei denn Sie wissen genau was Sie tun! -1 bedeutet: 
gezwungenen Aufruf von System.gc() und System.runFinalization() deaktivieren
+NodeStat.freeHeapBytesThreshold="Freier Heap-Speicher"-Schwelle
+NodeStat.freeHeapBytesThresholdLong=Der Knoten wird versuchen, die Anzahl 
seiner freien Heap(Halden-Speicher)-Bytes ?ber dem Schwellwert zu halten, indem 
er neue Anfragen ablehnt
+NodeStat.freeHeapPercentThreshold="Freier Heap-Anteil"-Schwelle (in %)
+NodeStat.freeHeapPercentThresholdLong=Der Knoten wird versuchen, seinen 
Prozentsatz an freiem Heap (Halden-Speicher) (von der maximal erlaubten 
Heap-Gr??e) ?ber dem Schwellwert zu halten, indem er neue Anfragen ablehnt
+NodeStat.memCheck=Speicher-?berpr?fung einschalten
+NodeStat.memCheckLong=Speicher-?berpr?fung einschalten (schreibt eine 
Nachricht in die Log-Datei, Voraussetzung damit der aggressiveGCModificator 
("aggressiver Garbage-Collector"-Modifikator) Wirkung zeigt!)
+NodeStat.statsPersister=Datei in der die Knoten-Statistiken gespeichert werden
+NodeStat.statsPersisterLong=Datei, in der die Knoten-Statistiken gespeichert 
werden (nicht die Client(Kunden)-Statistiken, und diese werden benutzt um zu 
entscheiden ob Anfragen akzeptiert werden, deshalb bitte nicht l?schen)
+NodeStat.threadLimit=Thread-Limit
+NodeStat.threadLimitLong=Der Knoten wird versuchen seinen 
Thread(Prozess-Strang)-Gebrauch auf den angegebenen Wert zu beschr?nken, indem 
er neue Anfragen ablehnt
+NodeStats.mustBePercentValueNotFull=Dieser Wert muss eine Prozent-Zahl 
zwischen 0 und 99 sein.
+NodeStats.valueTooLow=Dieser Wert ist zu niedrig f?r diese Einstellung, 
erh?hen Sie ihn!
+NodeUpdateManager.enabled=Auf neue Versionen pr?fen und herunterladen
+NodeUpdateManager.enabledLong=Soll Ihr Knoten automatisch nach neuen Versionen 
von Freenet suchen? Wenn ja, werden neue Versionen automatisch erkannt und 
heruntergeladen, aber nicht unbedingt installiert. Diese Einstellung stellt 
sich immer wieder auf "false" (falsch), es sei denn der Knoten l?uft innerhalb 
des Wrappers (Schutz-/Hilfsumgebung).
+NodeUpdateManager.extURI=Wo soll der Knoten nach Aktualisierungen f?r 
freenet-ext.jar suchen?
+NodeUpdateManager.extURILong=Wo soll der Knoten nach Aktualisierungen f?r 
freenet-ext.jar suchen?
+NodeUpdateManager.installNewVersions=Neue Versionen automatisch installieren
+NodeUpdateManager.installNewVersionsLong=Soll ihr Knoten sich automatisch auf 
die neuste Version von Freenet aktualisieren ohne Nachzufragen?
+NodeUpdateManager.invalidExtURI=Ung?ltiger externer URI: ${error}
+NodeUpdateManager.invalidRevocationURI=Ung?ltiger Widerrufs-URI: ${error}
+NodeUpdateManager.invalidUpdateURI=Ung?ltiger Aktualisierungs-URI: ${error}
+NodeUpdateManager.noUpdateWithoutWrapper=Kann den Knoten nicht aktualisieren, 
da er nicht unter dem Wrapper (Schutz-/Hilfsumgebung) ausgef?hrt wird
+NodeUpdateManager.revocationURI=Wo soll der Knoten nach dem 
Widerrufs-Schl?ssel suchen?
+NodeUpdateManager.revocationURILong=URI f?r den Widerrufs-Schl?ssel. Wenn 
dieser gefunden wird, wird der Knoten den Inhalt anzeigen und die automatischen 
Aktualisierungen deaktivieren.
+NodeUpdateManager.updateCatastropheTitle=Katastrophaler Fehler bei der 
Aktualisierung!
+NodeUpdateManager.updateFailed=Aktualisierung fehlgeschlagen: ${reason}
+NodeUpdateManager.updateFailedTitle=Aktualisierung fehlgeschlagen!
+NodeUpdateManager.updateURI=Wo soll der Knoten nach Aktualisierungen suchen?
+NodeUpdateManager.updateURILong=Wo soll der Knoten nach Aktualisierungen 
suchen?
+OpennetConnectionsToadlet.fullTitle=${counts} Fremde (nicht vertrauensw?rdige 
Partner) von ${name}
+OpennetConnectionsToadlet.peersListTitle=Meine Opennet-Partner (nicht 
vertrauensw?rdige Partner die vom Knoten im promiskuitiven Modus hinzugef?gt 
wurden)
+OpennetUserAlert.warning=Ihr Knoten l?uft gerade im promiskuitiven Modus. Er 
wird sich mit Fremden verbinden und dies bedeutet, dass jeder herausfinden 
kann, dass Sie einen Knoten betreiben. Die meisten Attacken sind einfacher, das 
blockieren Ihres Knotens (zum Beispiel durch eine nationale Firewall) ist sehr 
viel einfacher und Sie haben keine Kontrolle ?ber wen sich Ihr Knoten 
verbindet. Wir empfehlen Ihnen dringend, dass Sie Verbindungen mit Freunden 
(vertrauensw?rdigen Knoten, die von Leuten die Sie bereits kennen betrieben 
werden) bekommen; der promiskuitive Modus ist nur als eine tempor?re (zeitlich 
beschr?nkte) Ma?nahme gedacht, bis Sie in der Lage sind, sich nur noch mit 
Ihren Freunden zu verbinden. Wenn Sie sich nur mit Ihren Freunden verbinden, 
ist es weniger wahrscheinlich, obwohl es ihnen m?glich ist, dass Sie 
angegriffen werden, als wenn Ihr Knoten jeder Regierungs-Beh?rde (Geheimdienst) 
oder anderen b?sen Menschen ausgesetzt ist, die sich mit ihm verbinden wollen. 
Beachten Sie, dass das Hinzuf?gen eines Partners in der Freundes-Liste nicht 
viel hilft, wenn der Partner nicht jemandem geh?rt, den Sie kennen (aus 
Routing(Weiterleitungs)- und Sicherheits-Gr?nden)!
+OpennetUserAlert.warningTitle=Warnung: Promiskuitiver Modus aktiviert: Ihr 
Knoten wird sich mit Fremden verbinden
+PNGFilter.invalidHeader=Die Datei, die Sie versucht haben abzurufen, ist keine 
PNG-Datei. Sie enth?lt keinen g?ltigen PNG-Header (Dateikopf). Es k?nnte sich 
um ein anderes Dateiformat handeln und Ihr Browser k?nnte etwas gef?hrliches 
damit tun, deshalb wurde sie blockiert.
+PNGFilter.invalidHeaderTitle=Keine PNG-Datei - ung?ltiger Header
+PeerManagerUserAlert.clockProblem=${count} Ihrer Knoten sind nicht in der Lage 
sich zu verbinden, weil ihre System-Zeit sich um mehr als 24 Stunden von Ihrer 
unterscheidet. Bitte ?berpr?fen Sie, ob Ihr Computer die korrekte Zeit hat. 
Eine inkorrekte Zeit wird das Fehlschlagen von vielen Knoten- und 
Client-Mechanismen verursachen.
+PeerManagerUserAlert.clockProblemTitle=Zeit-Problem
+PeerManagerUserAlert.connError=${count} Ihrer Partner k?nnen sich aus einer 
unbekannten Ursache nicht verbinden, m?glicherweise aufgrund eines fehlerhaften 
Knotens oder einer besch?digten Knoten-Referenz.
+PeerManagerUserAlert.connErrorTitle=Einige Partner k?nnen sich nicht verbinden
+PeerManagerUserAlert.noConns=Dieser Knoten war bis jetzt nicht in der Lage, 
sich mit anderen Knoten zu verbinden; er wird nicht normal funktionieren. 
Hoffentlich werden sich ein paar Ihrer Partner bald verbinden; wenn nicht, 
versuchen Sie einige weitere Partner zu finden. Sie brauchen zu jedem Zeitpunkt 
mindestens 3 (verbundene) Partner und sollten 5-10 anstreben.
+PeerManagerUserAlert.noConnsTitle=Keine offenen Verbindungen
+PeerManagerUserAlert.noPeersDarknet=Dieser Knoten hat keine Partner mit denen 
er sich verbinden kann, deshalb wird er nicht normal funktionieren. Im 
Idealfall sollten Sie sich mit Partner-Knoten verbinden, die von Leuten 
betrieben werden die Sie kennen (wenn Sie paranoid sind, dann Leute denen Sie 
vertrauen; wenn nicht, dann zumindest Leute mit denen Sie gesprochen haben). 
Sie brauchen zu jeder Zeit mindestens 3 verbundene Partner und im Idealfall 
5-10. Sie k?nnten sich auf irc.freenode.net im Kanal #freenet-refs einloggen 
und nach jemandem zum Verbinden fragen, aber bedenken Sie, dass Sie gegen?ber 
denen, mit denen Sie direkt verbunden sind, verletzbar sind. (Dies gilt vor 
allem f?r diese fr?he Alpha-Version von Freenet 0.7...) GEHEN SIE SICHER, DASS 
DIE ANDERE PERSON IHRE REFERENZ AUCH HINZUGEF?GT HAT, DA EINSEITIGE 
VERBINDUNGEN NICHT FUNKTIONIEREN WERDEN!
+PeerManagerUserAlert.noPeersTestnet=Dieser Knoten hat keine Partner mit denen 
er sich verbinden kann, deshalb wird er nicht normal funktionieren. Im 
Idealfall sollten Sie sich mit Partner-Knoten verbinden, die von Leuten 
betrieben werden die Sie kennen (wenn Sie paranoid sind, dann Leute denen Sie 
vertrauen; wenn nicht, dann zumindest Leute mit denen Sie gesprochen haben). 
Sie brauchen zu jeder Zeit mindestens 3 verbundene Partner und im Idealfall 
5-10. Aber da dies ein Testnet-Knoten ist, schlagen wir Ihnen vor, sich auf 
irc.freenode.net im Kanal #freenet-refs einzuloggen und nach jemandem zum 
Verbinden zu fragen.
+PeerManagerUserAlert.noPeersTitle=Keine Partner gefunden
+PeerManagerUserAlert.oneConn=Dieser Knoten hat nur eine Verbindung. Die 
Leistung wird beeintr?chtigt sein und Sie haben weder Anonymit?t noch 
glaubw?rdige Abstreitbarkeit wenn diese Person b?swillig ist. Ihr Knoten ist 
wie ein "Blatt" mit dem Netzwerk verbunden und tr?gt nicht zur Gesundheit des 
Netzwerks bei. Versuchen Sie zu jedem Zeitpunkt mindestens 3 (im Idealfall 
mehr, wie 5-10) verbundene Partner zu bekommen.
+PeerManagerUserAlert.onlyFewConnsTitle=Nur ${count} offene Verbindung(en)
+PeerManagerUserAlert.tooHighBwlimitDelayTime=Dieser Knoten muss zu lange auf 
verf?gbare Bandbreite warten (${delay}ms > ${max}ms). Erh?hen Sie ihre 
ausgehende Bandbreite und/oder entfernen Sie ein paar Partner (von der 
Freundes-Liste) um die Situation zu verbessern.
+PeerManagerUserAlert.tooHighBwlimitDelayTimeTitle=bwlimitDelayTime zu hoch
+PeerManagerUserAlert.tooHighPingTime=Dieser Knoten hat Schwierigkeiten schnell 
genug mit seinen Partnern zu kommunizieren (${ping}ms > ${max}ms). Erh?hen Sie 
ihre ausgehende Bandbreite und/oder entfernen Sie ein paar Partner (von der 
Freundes-Liste) um die Situation zu verbessern.
+PeerManagerUserAlert.tooHighPingTimeTitle=nodeAveragePingTime 
(knotenDurchschnittsAntwortZeit) zu hoch
+PeerManagerUserAlert.tooManyConns=Dieser Knoten  hat zu viele Verbindungen 
(${count} > ${max}). Gro?e Mengen von Knoten automatisch (zur Freundes-Liste) 
hinzuzuf?gen, bildet keine Small-World-Topologie (Kleine-Welt-Netzstruktur), 
schadet dem Routing (weiterleiten von Anfragen) und riskiert 
Single-Points-of-Failure (einzelne Fehlerstellen) zu bilden.
+PeerManagerUserAlert.tooManyConnsTitle=Zu viele offene Verbindungen
+PeerManagerUserAlert.tooManyDisconnected=Dieser Knoten hat zu viele nicht 
verbundene Partner (${count} > ${max}). Dies wird einen leichten Einfluss auf 
die Leistung haben, da nicht verbundene Partner auch einen kleinen Teil der 
Bandbreite und CPU (Rechenleistung) verbrauchen. Ziehen Sie ein "S?ubern" Ihrer 
Freundes-Liste in Betracht. Beachten Sie, dass Sie sich idealerweise mit 
Leuten, die Sie kennen, verbinden sollten. Selbst wenn nicht, das Hinzuf?gen 
von gro?en Mengen von Knoten ist schlecht, da es keine optimale Topologie 
(Netzstruktur) herstellt.
+PeerManagerUserAlert.tooManyDisconnectedTitle=Zu viele nicht verbundene Partner
+PeerManagerUserAlert.tooManyNeverConnected=Viele Partner dieses Knotens haben 
sich nicht ein einziges Mal verbunden: ${count}. Sie sollten Partner nicht 
hinzuf?gen, wenn Sie nicht wissen, dass diese Ihre Referenz auch hinzugef?gt 
haben. Andernfalls werden diese sich nicht verbinden.
+PeerManagerUserAlert.tooManyNeverConnectedTitle=Viele Partner haben sich noch 
nicht ein einziges Mal verbunden
+PeerManagerUserAlert.tooManyNeverConnectedWithLink=Viele Partner dieses 
Knotens haben sich nicht ein einziges Mal verbunden: ${count}. Sie sollten 
Partner nicht hinzuf?gen, wenn Sie nicht wissen, dass diese ${link}Ihre 
Referenz${/link} auch hinzugef?gt haben. Andernfalls werden diese sich nicht 
verbinden.
+PeerManagerUserAlert.tooManyPeers=Dieser Knoten hat zu viele Partner (${count} 
> ${max}). Wir empfehlen nicht, "?ber-Knoten" mit automatischem Hinzuf?gen von 
Partnern zu betreiben; Dies bildet keine Small-World-Topologie 
(Kleine-Welt-Netzstruktur). Dies wird au?erdem Ihre Leistung marginal 
beeinflussen, da nicht verbundene Partner einen kleinen Teil der Bandbreite und 
CPU (Rechenleistung) verbrauchen. Ziehen Sie ein "S?ubern" Ihrer Freundes-Liste 
in Betracht.
+PeerManagerUserAlert.tooManyPeersTitle=Zu viele Partner
+PeerManagerUserAlert.tooOldNeverConnectedPeers=Ein oder mehrere Partner Ihres 
Knotens haben sich in den zwei Wochen, seit Sie hinzugef?gt wurden, niemals 
verbunden. Ziehen Sie in Betracht sie zu entfernen, da sie die Leistung 
marginal beeinflussen (es werden Pakete daran verschwendet, mit Knoten zu 
kommunizieren, die nicht da sind).
+PeerManagerUserAlert.tooOldNeverConnectedPeersTitle=Zu alte(r), niemals 
verbundene(r) Partner
+PeerManagerUserAlert.twoConns=Dieser Knoten hat nur zwei Verbindungen. 
Leistung und Sicherheit werden nicht sehr gut sein, und ihr Knoten betreibt 
kein Routing (Weiterleiten von Anfragen) f?r andere Knoten. Ihr Knoten ist im 
Netzwerk wie in einer "Kette" eingebunden und tr?gt nicht (viel) zur Gesundheit 
des Netzwerks bei. Versuchen Sie zu jeder Zeit mindestens 3 (im Idealfall mehr, 
wie 5-10) verbundene Partner zu bekommen.
+PeersSayKeyBlownAlert.connectedSayBlownLabel=Diese verbundenen Knoten melden, 
dass der Schl?ssel unbrauchbar gemacht wurde (wir versuchen das 
Widerrufs-Zertifikat von Ihnen herunterzuladen):
+PeersSayKeyBlownAlert.disconnectedSayBlownLabel=Diese Knoten meldeten uns, 
dass der Schl?ssel unbrauchbar w?re, trennten jedoch dann die Verbindung, 
weshalb wir das Widerrufs-Zertifikat nicht abrufen konnten:
+PeersSayKeyBlownAlert.failedFetch=Ihr Knoten war nicht in der Lage, das 
Widerrufs-Zertifikat herunterzuladen. M?gliche Ursachen beinhalten eine Attacke 
auf Ihren Knoten um zu versuchen Sie zum Aktualisieren zu bewegen obwohl der 
Schl?ssel unbrauchbar ist, oder dass Ihre Knoten, bez?glich des unbrauchbaren 
Schl?ssels, l?gen. Bitte kontaktieren Sie die Entwickler oder andere 
Freenet-Benutzer um dieses Durcheinander zu l?sen.
+PeersSayKeyBlownAlert.failedTransferSayBlownLabel=Diese Knoten meldeten uns, 
dass der Schl?ssel unbrauchbar sei, scheiterten aber darin das 
Widerrufs-Zertifikat zu ?bertragen:
+PeersSayKeyBlownAlert.fetching=Ihr Knoten versucht gerade das 
Widerrufs-Zertifikat herunterzuladen, um mehr Details in Erfahrung zu bringen.
+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.titleWithCount=Laut ${count} Partner(n) ist der 
Schl?ssel der automatischen Aktualisierung unbrauchbar!
+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
+PluginManager.loadedPlugins=Plugins die beim Start geladen werden
+PluginManager.loadedPluginsLong=Eine Liste von Plugins (Erweiterungen) die 
gestartet werden wenn der Knoten startet
+PluginManager.pluginReqNewerJVM=Das Plugin (Erweiterung) ${name} scheint eine 
neuere JVM zu ben?tigen. Bitte installieren Sie mindestens Sun Java 1.5, oder 
entfernen Sie das Plugin.
+PluginManager.pluginReqNewerJVMTitle=Das Plugin (Erweiterung) ${name} ben?tigt 
eine neuere JVM.
+PluginToadlet.addPluginTitle=Ein Plugin hinzuf?gen
+PluginToadlet.failedToLoadPlugin=Laden des Plugins fehlgeschlagen.
+PluginToadlet.failedToLoadPluginCheckClass=Das Plugin (Erweiterung) das Sie 
angefordert haben konnte nicht geladen werden. Bitte ?berpr?fen Sie den Namen 
der Klasse des Plugins und die URL, wenn Sie eine angegeben haben.
+PluginToadlet.failedToLoadPluginTitle=Laden des Plugins fehlgeschlagen
+PluginToadlet.internalNameTitle=Interner Name
+PluginToadlet.loadPluginCommand=Plugin laden
+PluginToadlet.noWebInterface=Das Plugin hat kein Web-Interface 
(Web-Schnittstelle), deshalb gibt es nichts anzuzeigen.
+PluginToadlet.noWebInterfaceTitle=Plugin hat kein Web-Interface
+PluginToadlet.pluginList=Plugin-Liste
+PluginToadlet.pluginListTitle=Liste der Plugins (Erweiterungen)
+PluginToadlet.pluginNameTitle=Plugin-Name
+PluginToadlet.pluginNotFound=Das angeforderte Plugin konnte nicht gefunden 
werden.
+PluginToadlet.pluginNotFoundTitle=Plugin nicht gefunden
+PluginToadlet.returnToPluginsWithLinks=Bitte gehen Sie ${link}zur?ck${/link} 
zur Liste der Plugins.
+PluginToadlet.unsupportedMethod=Nicht unterst?tzte Methode.
+PluginToadlet.unsupportedMethodTitle=Nicht unterst?tzte Methode
+PluginToadlet.visit=Besuch
+PproxyToadlet.classNameTitle=Klassen-Name
+PproxyToadlet.internalIDTitle=Interne ID
+PproxyToadlet.loadPluginLabel=Plugin laden:
+PproxyToadlet.noPlugins=Keine Plugins geladen
+PproxyToadlet.pluginNotFoundReload=Das angegebene Plugin konnte nicht gefunden 
werden um es neu zu laden.
+PproxyToadlet.pluginNotFoundReloadTitle=Plugin nicht gefunden (neu laden)
+PproxyToadlet.pluginUnloaded=Plugin entfernt
+PproxyToadlet.pluginUnloadedWithName=Das Plugin ${name} wurde entfernt.
+PproxyToadlet.plugins=Plugins
+PproxyToadlet.pluginsWithNodeName=Plugins von ${name}
+PproxyToadlet.reload=Neu laden
+PproxyToadlet.returnToPluginPage=Zur?ck zur Plugin-Seite
+PproxyToadlet.startedAtTitle=Gestartet um
+PproxyToadlet.unload=Entfernen
+PproxyToadlet.unloadPluginTitle=Plugin entfernen?
+PproxyToadlet.unloadPluginWithName=Sind Sie sich sicher, dass Sie ${name} 
entfernen wollen?
+QueueToadlet.DUinProgress=Laufende Verzeichnis-Uploads
+QueueToadlet.DinProgress=Laufende Downloads
+QueueToadlet.UinProgress=Laufende Uploads
+QueueToadlet.change=?ndern
+QueueToadlet.completedDU=Vollst?ndige Verzeichnis-Uploads
+QueueToadlet.completedDinDownloadDirectory=Vollst?ndig: Downloads ins 
Download-Verzeichnis (${size})
+QueueToadlet.completedDinTempDirectory=Vollst?ndig: Downloads in das tempor?re 
Verzeichnis (${size})
+QueueToadlet.completedDtoDisk=Vollst?ndige Downloads auf einen Datentr?ger
+QueueToadlet.completedDtoTemp=Vollst?ndige Downloads ins Tempor?re Verzeichnis
+QueueToadlet.completedU=Vollst?ndig: Uploads (${size})
+QueueToadlet.completedUDirectory=Vollst?ndig: Verzeichnis-Uploads (${size})
+QueueToadlet.download=Download
+QueueToadlet.errorAccessDenied=Fehler: Zugriff verweigert!
+QueueToadlet.errorAccessDeniedFile=Die aktuelle Konfiguration des Knotens 
untersagt Ihnen das Hochladen der Datei "${file}".
+QueueToadlet.errorDToDisk=Kann nicht auf den Datentr?ger herunterladen
+QueueToadlet.errorDToDiskConfig=Die aktuelle Konfiguration des Knotens 
erlaubtes Ihnen nicht, Dateien in das Download-Verzeichnis herunterzuladen.
+QueueToadlet.errorDownloadNotCompleted=Download nicht abgeschlossen
+QueueToadlet.errorDownloadNotFound=Download nicht gefunden
+QueueToadlet.errorDownloadNotFoundExplanation=Der Download konnte nicht 
gefunden werden. Vielleicht wurde er schon gel?scht?
+QueueToadlet.errorInvalidURI=Ung?ltiger URI
+QueueToadlet.errorInvalidURIToD=Der URI ist ung?ltig und kann nicht 
heruntergeladen werden.
+QueueToadlet.errorInvalidURIToU=Sie haben keinen g?ltigen URI angegeben bei 
dem die Datei eingef?gt wird.
+QueueToadlet.errorNoFileOrCannotRead=Die Datei existiert nicht oder kann nicht 
gelesen (zugegriffen) werden
+QueueToadlet.errorNoFileSelected=Keine Datei ausgew?hlt
+QueueToadlet.errorNoFileSelectedU=Sie haben keine Datei zum Hochladen 
angegeben.
+QueueToadlet.errorNoKey=Kein Schl?ssel zum Herunterladen angegeben
+QueueToadlet.errorNoKeyToD=Sie haben keinen Schl?ssel angegeben, der 
heruntergeladen werden soll.
+QueueToadlet.failedD=Fehlgeschlagene Downloads
+QueueToadlet.failedDU=Fehlgeschlagene Verzeichnis-Uploads
+QueueToadlet.failedToRemove=Entfernen fehlgeschlagen ${id}: ${message}
+QueueToadlet.failedToRemoveId=Entfernen fehlgeschlagen: ${id}
+QueueToadlet.failedToRemoveRequest=Entfernen der Anfrage fehlgeschlagen
+QueueToadlet.failedToRestart=Neustart fehlgeschlagen: ${id}
+QueueToadlet.failedToRestartRequest=Neustart der Anfrage fehlgeschlagen
+QueueToadlet.failedU=Fehlgeschlagene Uploads
+QueueToadlet.fcpIsMissing=Der FCP-Server fehlt
+QueueToadlet.fileName=Dateiname
+QueueToadlet.files=Dateien
+QueueToadlet.follow=Umleitung folgen
+QueueToadlet.globalQueueIsEmpty=Die globale Warteschlange ist leer
+QueueToadlet.identifier=Bezeichnung
+QueueToadlet.insertAs=Einf?gen als:
+QueueToadlet.insertFile=Datei einf?gen
+QueueToadlet.key=Schl?ssel
+QueueToadlet.legend=Legende
+QueueToadlet.mimeType=MIME-Typ
+QueueToadlet.noTaskOnGlobalQueue=Im Moment ist keine Aufgabe in der globalen 
Warteschlange.
+QueueToadlet.none=keine/r
+QueueToadlet.panicButton=Alarm-Knopf
+QueueToadlet.panicButtonConfirmation=Alle Anfragen ohne Best?tigung entfernen!
+QueueToadlet.persistence=Persistenz (Speicher-Dauer)
+QueueToadlet.persistenceForever=ewig
+QueueToadlet.persistenceNone=keine
+QueueToadlet.persistenceReboot=neustart
+QueueToadlet.pleaseEnableFCP=Sie m?ssen den FCP-Server einschalten um diese 
Seite aufzurufen
+QueueToadlet.priority=Priorit?t
+QueueToadlet.priority0=Notfall
+QueueToadlet.priority1=sehr hoch
+QueueToadlet.priority2=hoch
+QueueToadlet.priority3=mittel
+QueueToadlet.priority4=niedrig
+QueueToadlet.priority5=sehr niedrig
+QueueToadlet.priority6=wird niemals fertiggestellt
+QueueToadlet.progress=Fortschritt
+QueueToadlet.progressbarAccurate=Dieser Fortschrittswert ist exakt
+QueueToadlet.progressbarNotAccurate=Dieser Fortschritts-Wert ver?ndert sich 
wahrscheinlich, da der Datei-Download-Prozess noch nicht abgeschlossen ist
+QueueToadlet.reason=Grund
+QueueToadlet.remove=Entfernen
+QueueToadlet.requestNavigation=Anfragen-Navigation
+QueueToadlet.restart=Neustart
+QueueToadlet.size=Gr??e
+QueueToadlet.starting=STARTE
+QueueToadlet.title=Globale Warteschlange von ${nodeName}
+QueueToadlet.totalSize=Gesamt-Gr??e
+QueueToadlet.unknown=Unbekannt
+QueueToadlet.warningUnsafeContent=Potenziell unsicherer Inhalt
+QueueToadlet.warningUnsafeContentExplanation=Die Datei, die Sie herunterladen 
wollen, wird zurzeit nicht von Freenets  Inhalts-Filter ?berpr?ft! Dies 
bedeutet, dass Ihre Anonymit?t durch das ?ffnen der Datei kompromittiert 
(blo?gestellt) werden kann!
+QueueToadlet.wipD=In Arbeit: Downloads (${size})
+QueueToadlet.wipDU=In Arbeit: Verzeichnis-Uploads (${size})
+QueueToadlet.wipU=In Arbeit: Uploads (${size})
+RequestStarterGroup.scheduler=Priorit?ts-Verfahren des Planers
+RequestStarterGroup.schedulerLong=Das Priorit?ts-Verfahren, das vom Planer 
genutzt wird, einstellen.
+RevocationKeyFoundUserAlert.text=Ihr Knoten hat den Widerrufs-Schl?ssel der 
automatischen Aktualisierung im Netzwerk gefunden. Das hei?t, dass unser 
automatisches Aktualisierungs-System wahrscheinlich KOMPROMITTIERT 
(blo?gestellt/offen gelegt) wurde! Deshalb wurde es auf Ihrem Knoten 
deaktiviert, um zu verhindern, dass "b?se Sachen" installiert werden. Wir 
empfehlen Ihnen dringend, auf der Internet-Seite des Projekts nachzusehen ob 
Aktualisierungen verf?gbar sind. Bitte achten Sie darauf zu pr?fen, dass die 
Internet-Seite nicht gef?lscht wurde. Die Widerrufs-Nachricht lautet: 
${message}.
+RevocationKeyFoundUserAlert.title=Der private Schl?ssel des Projekts wurde 
kompromittiert (offengelegt)!
+ShortOption.parseError=Kann den Wert nicht als String-Array 
(Zeichenfolgen-Feld) erkennen: ${error}
+SimpleToadletServer.advancedMode=Erweiterten Modus aktivieren?
+SimpleToadletServer.advancedModeLong=Ob Informationen f?r fortgeschrittene 
Benutzer/Ger?te angezeigt werden sollen oder nicht. Diese Einstellung sollte in 
den meisten F?llen auf false (falsch) stehen.
+SimpleToadletServer.allowedFullAccess=Hosts die vollen Zugriff auf FProxy 
haben (Warnung lesen)
+SimpleToadletServer.allowedFullAccessLong=Hosts (Computer) die vollen Zugriff 
(z.B. Konfiguration ?ndern, Neustarten usw.) auf den Knoten haben. WARNUNG: 
Seien Sie vorsichtig wem Sie vollen FProxy-Zugriff geben!
+SimpleToadletServer.allowedHosts=Hostnamen oder IP-Adressen denen es erlaubt 
ist sich mit FProxy zu verbinden.
+SimpleToadletServer.allowedHostsLong=Kann eine Komma-getrennte Liste von 
einzelnen IPs und CIDR-maskierten IPs wie 192.168.0.0/24 sein. Beachten Sie, 
dass diese, innerhalb der von anderen Optionen gesetzten Grenzen, auf die 
Festplatte zugreifen k?nnen.
+SimpleToadletServer.bindTo=Zu benutzende IP-Adresse
+SimpleToadletServer.bindToLong=Zu benutzende IP-Adresse
+SimpleToadletServer.cannotChangePortOnTheFly=Kann die FProxy-Port-Nummer nicht 
im laufenden Betrieb ?ndern
+SimpleToadletServer.couldNotChangeBindTo=Konnte die von FProxy zu benutzende 
Adresse nicht ?ndern: ${error}.
+SimpleToadletServer.cssName=CSS-Name
+SimpleToadletServer.cssNameLong=Name des CSS das FProxy benutzen soll
+SimpleToadletServer.cssOverride=Das CSS durch eine benutzerdefiniertes 
ersetzen (WARNUNG!)
+SimpleToadletServer.cssOverrideCantRead=Wir k?nnen die gegebene 
CSS-Ersatz-Datei nicht lesen: ${filename}
+SimpleToadletServer.cssOverrideLong=Diese Einstellung erlaubt es Ihnen, die 
CSS-Datei des Knotens mit einer benutzerdefinierten Datei zu ersetzen. WARNUNG: 
CSS-Dateien k?nnen gef?hrlich sein und werden nicht gefiltert! Benutzung auf 
eigene Gefahr. (denken Sie dar?ber nach devl at freenetproject zu kontaktieren 
um sie in die Haupt-Distribution aufnehmen zu lassen ;-) )
+SimpleToadletServer.cssOverrideNotInUploads=Wir k?nnen Sie diese Einstellung 
nicht setzen lassen: "${filename}" ist kein Verzeichnis, von welchem Uploads 
(Hochladen) erlaubt sind!
+SimpleToadletServer.doRobots=Roboter mithilfe der robots.txt ausschlie?en?
+SimpleToadletServer.doRobotsLong=Ob eine /robots.txt bereitgehalten werden 
soll, die Google, spiders, wget, usw. sagt, dass sie weggehen sollen
+SimpleToadletServer.enableJS=Benutzung von Javascript durch FProxy aktivieren?
+SimpleToadletServer.enableJSLong=Ob FProxy Javascript-"Helfer" benutzen soll 
oder nicht. Diese Einstellung sollte in den meisten F?llen auf false (falsch) 
gesetzt werden. Beachten Sie, dass Freesites kein Javascript benutzen k?nnen, 
auch wenn dies aktiviert ist.
+SimpleToadletServer.enabled=FProxy aktivieren?
+SimpleToadletServer.enabledLong=Ob FProxy und verwandte HTTP-Dienste aktiviert 
werden sollen
+SimpleToadletServer.illegalCSSName=CSS-Namen d?rfen keine Schr?gstriche oder 
Doppelpunkte enthalten!
+SimpleToadletServer.panicButton=Den Alarm-Knopf anzeigen?
+SimpleToadletServer.panicButtonLong=Ob der Alarm-Knopf auf der 
Warteschlangen-Seite (/queue/) angezeigt werden soll oder nicht.
+SimpleToadletServer.port=FProxy-Port-Nummer
+SimpleToadletServer.portLong=FProxy-Port-Nummer
+StaticToadlet.pathInvalidChars=Der angegebene URI enth?lt nicht erlaubte 
Zeichen.
+StaticToadlet.pathNotFound=Der Pfad, den Sie angegeben haben, existiert nicht.
+StaticToadlet.pathNotFoundTitle=Pfad nicht gefunden
+StatisticsToadlet.activityInserts=Einf?gen: ${totalSenders} Sender insgesamt, 
${CHKhandlers} CHK handler, ${SSKhandlers} SSK handler
+StatisticsToadlet.activityRequests=Anfragen: ${totalSenders} Sender insgesamt, 
${CHKhandlers} CHK handler, ${SSKhandlers} SSK handler
+StatisticsToadlet.allocMemory=Zugewiesener Java-Speicher: ${memory}
+StatisticsToadlet.bandwidthTitle=Bandbreite
+StatisticsToadlet.cpus=Verf?gare CPUs: ${count}
+StatisticsToadlet.getLogs=Letzte Log-Datei des Knotens Abrufen
+StatisticsToadlet.inputRate=Eingehend: ${rate}/Sekunde (von ${max})
+StatisticsToadlet.jeDumpButton=JE-Dump generieren
+StatisticsToadlet.jvmInfoTitle=JVM-Info
+StatisticsToadlet.jvmVendor=JVM-Hersteller: ${vendor}
+StatisticsToadlet.jvmVersion=JVM-Version: ${version}
+StatisticsToadlet.maxMemory=Maximaler Java-Speicher: ${memory}
+StatisticsToadlet.noRequests=Ihr Knoten bearbeitet gerade keine Anfragen
+StatisticsToadlet.osArch=OS-Architektur: ${arch}
+StatisticsToadlet.osName=OS-Name: ${name}
+StatisticsToadlet.osVersion=OS-Version: ${version}
+StatisticsToadlet.outputRate=Ausgehend: ${rate}/Sekunde (von ${max})
+StatisticsToadlet.payloadOutput=Nutzlast ausgehend: ${total} 
(${rate}/Sekunde)(${percent}%)
+StatisticsToadlet.peerStatsTitle=Partner-Statistiken
+StatisticsToadlet.threadDumpButton=Thread-Dump generieren
+StatisticsToadlet.threads=Laufende Threads: ${running}/${max}
+StatisticsToadlet.totalInput=Gesamt eingehend: ${total} (${rate}/Sekunde)
+StatisticsToadlet.totalOutput=Gesamt ausgehend: ${total} (${rate}/Sekunde)
+StatisticsToadlet.transferringRequests=?bertrage Anfragen: sende ${senders}, 
empfange ${receivers}
+StatisticsToadlet.usedMemory=Benutzter Java-Speicher: ${memory}
+StatisticsToadlet.versionTitle=Knoten-Versions-Informationen
+SymlinkerToadlet.symlinks=Verkn?pfungen im ToadletServer
+SymlinkerToadlet.symlinksLong=Eine Liste von "alias#target"'s 
("Bezeichnung#Ziel") die eine Menge von Verkn?pfungen darstellt
+TestnetHandler.cannotEnableDisableOnTheFly=Aktivieren/deaktivieren des 
Testnet-Modus im laufenden Betrieb unm?glich; starten Sie den Knoten neu und 
erhalten Sie neue  Verbindungen
+TestnetHandler.enable=Testnet-Modus einschalten? (GEF?HRLICH)
+TestnetHandler.enableLong=Testnet-Modus einschalten (GEF?HRLICH!). Der 
Testnet-Modus macht ihre Anonymit?t zunichte, im Gegenzug hilft er den 
Entwicklern enorm beim Debuggen (Fehler suchen) des Knotens.
+TestnetHandler.port=Testnet-Port
+TestnetHandler.portLong=Testnet-Portnummer (-1 = EmpfangsPort+1000)
+TextModeClientInterfaceServer.allowedHosts=Erlaubte Hosts
+TextModeClientInterfaceServer.allowedHostsLong=Host(Computer)-Namen oder 
IP-Adressen denen es erlaubt ist sich mit dem TMCI zu verbinden. Dies kann eine 
Komma-getrennte Liste von Host-Namen, einzelnen IPs und sogar CIDR-maskierten 
IPs wie 192.168.0.0/24 sein
+TextModeClientInterfaceServer.bindTo=Zu benutzende IP-Adresse
+TextModeClientInterfaceServer.bindToLong=Zu benutzende IP-Adresse
+TextModeClientInterfaceServer.enableInputOutput=Auf stdout/stdin aktivieren?
+TextModeClientInterfaceServer.enableInputOutputLong=Text-Modus-Client-Schnittstelle
 in der Standard-Ein-/Ausgabe aktivieren? (.enabled (.aktiviert) bezieht sich 
auf das stellen eines Servers im Telnet-Stil, dieser f?hrt es ?ber einen Sockel 
(Socket) aus)
+TextModeClientInterfaceServer.enabled=TMCI aktivieren
+TextModeClientInterfaceServer.enabledLong=Ob das TMCI aktiviert werden soll
+TextModeClientInterfaceServer.telnetPortNumber=Telnet-Port
+TextModeClientInterfaceServer.telnetPortNumberLong=Telnet-Port-Nummer
+TimeSkewDetectedUserAlert.text=Es wurde vom Knoten eine Zeitversetzung 
erkannt. Das ist SEHR schlimm. Ihr Knoten wird nicht richtig laufen bis dies 
behoben ist; h?ufige Ursachen sind ein falsch konfigurierter 
Energie-Spar-Modus, Netzwerk-Zeit-Synchronisations-Programme, oder fehlerhafte 
Hardware.
+TimeSkewDetectedUserAlert.title=Zeitversetzung erkannt!
+Toadlet.cancel=Abbrechen
+Toadlet.clickHere=Hier klicken
+Toadlet.homepage=Startseite
+Toadlet.internalErrorPleaseReport=Interner Fehler: Bitte melden Sie dies
+Toadlet.internalErrorTitle=Interner Fehler
+Toadlet.no=Nein
+Toadlet.nodeHomepage=Knoten-Startseite
+Toadlet.notSupportedTitle=Nicht unterst?tzt
+Toadlet.notSupportedWithClass=Ihr Browser hat eine Anfrage gesendet, die 
Freenet (${class}) nicht verstehen konnte.
+Toadlet.ok=OK
+Toadlet.permRedirectWithReason=Permanente Weiter-/Umleitung: ${reason}
+Toadlet.returnToNodeHomepage=Zur?ck zur Knoten-Startseite
+Toadlet.returnToPrevPage=Zur?ck zur vorherigen Seite
+Toadlet.tempRedirectWithReason=Tempor?re Weiter-/Umleitung: ${reason}
+Toadlet.unauthorized=Der Zugriff aus diese Seite ist Ihnen nicht gestattet.
+Toadlet.yes=Ja
+ToadletContextImpl.cannotParseContentLength=Fehler beim Erkennen der 
Inhalts-L?nge: ${error}
+ToadletContextImpl.headersLineTooLong=Zeile zu lang, beim Analysieren der 
Header (Dateik?pfe)
+ToadletContextImpl.methodNotAllowed=HTTP-Methode (Funktion) nicht erlaubt
+ToadletContextImpl.noContentLengthInPOST=Keine Inhalts-L?nge im POST
+ToadletContextImpl.noSuchToadlet=Kein Toadlet mit diesem Namen
+ToadletContextImpl.parseErrorWithError=Erkennungs-Fehler: ${error}
+ToadletContextImpl.uriParseErrorTitle=URI-Erkennungs-Fehler
+TranslationToadlet.bracketRemoveOverride=(Die ?bersetzungs-Korrektur l?schen!)
+TranslationToadlet.bracketTranslateIt=(in Ihre Muttersprache ?bersetzen!)
+TranslationToadlet.bracketUpdateTranslation=(?bersetzung aktualisieren)
+TranslationToadlet.confirmRemoveOverride=Sind Sie sich sicher, dass Sie den 
folgenden ?bersetzungs-Schl?ssel l?schen wollen: (${key} - ${value}) ?
+TranslationToadlet.contributingToLabelWithLang=Sie tragen gerade zur ${lang} 
?bersetzung bei:
+TranslationToadlet.currentTranslationLabel=Aktuelle ?bersetzung
+TranslationToadlet.downloadTranslationsFile=Ihre ?bersetzungs-Datei 
herunterladen
+TranslationToadlet.hideAlreadyTranslated=Bereits ?bersetzte Zeichenketten 
verstecken
+TranslationToadlet.noCustomTranslations=Es ist keine spezielle ?bersetzung 
verf?gbar.
+TranslationToadlet.originalVersionLabel=Original (Englische Version)
+TranslationToadlet.reEdit=?bersetzung ?ndern
+TranslationToadlet.remove=Entfernen
+TranslationToadlet.removeOverrideTitle=Einen ?bersetzungs-Korrektur-Schl?ssel 
l?schen
+TranslationToadlet.removeOverrideWarningTitle=Sie sind dabei einen 
?bersetzungs-Korrektur-Schl?ssel zu l?schen!
+TranslationToadlet.returnToTranslations=Zur?ck zur ?bersetzungs-Seite
+TranslationToadlet.showEverything=Alles anzeigen, auch schon ?bersetzte 
Zeichenketten
+TranslationToadlet.translationKeyLabel=?bersetzungs-Schl?ssel
+TranslationToadlet.translationUpdateTitle=?bersetzung aktualisieren
+TranslationToadlet.translationUpdatedTitle=?bersetzung aktualisiert!
+TranslationToadlet.updateTranslationCommand=Die ?bersetzung aktualisieren!
+UnknownContentTypeException.explanation=Ihr Freenet-Knoten wei? nichts ?ber 
diesen MIME-Typ. Das hei?t, dass Ihr Browser etwas gef?hrliches tun k?nnte, 
wenn diese Datei heruntergeladen wird. Zum Beispiel k?nnen viele Formate 
eingebettete Bilder oder Videos enthalten, welche aus dem Internet (genauer dem 
Web) heruntergeladen werden; dies ist keineswegs harmlos, weil dies Ihre 
Anonymit?t zunichte machen und Ihre IP-Adresse verraten kann(wenn der Angreifer 
die Web-Seite betreibt oder Zugriff auf deren Log-Dateien hat). Links ins Web 
(Internet) k?nnen auch eine Gefahr darstellen, aus fast denselben Gr?nden, 
genau wie Scripting, aus diesen und anderen Gr?nden.
+UnknownContentTypeException.title=Unbekannter und potenziell gef?hrlicher 
Inhaltstyp: ${type}
+UpdateDeployContext.cannotUpdateNoExtJar=Die freenet-ext.jar in der 
wrapper.conf konnte nicht gefunden werden (gefunden wurde die freenet.jar: 
${mainFilename})
+UpdateDeployContext.cannotUpdateNoJars=Konnte die Freenet-jars in der 
wrapper.conf nicht finden
+UpdateDeployContext.cannotUpdateNoMainJar=Die freenet.jar in der wrapper.conf 
konnte nicht gefunden werden (gefunden wurde die freenet-ext.jar: 
${extFilename})
+UpdateDeployContext.updateCatastrophe=KATASTROPHALER FEHLER: ${old} wurde 
gel?scht, ${new} konnte jedoch nicht zu ${old} umbenannt werden, deshalb WIRD 
DER KNOTEN NICHT STARTEN! Bitte beheben Sie das Problem, indem Sie ${new} 
manuell zu ${old} umbenennen.
+UpdateDeployContext.updateFailedCannotDeleteOldConfig=Die ${old} kann nicht 
gel?scht werden, deshalb kann eine andere Datei nicht in ${old} umbenannt 
werden. Aktualisierung fehlgeschlagen.
+UpdateDeployContext.updateFailedNonStandardConfig=Es ist, wegen einer nicht 
dem Standard entsprechenden Konfiguration(s-Datei), nicht m?glich zu 
Aktualisieren: gesetzt main=${main} ext=${ext} - dies sollte nicht passieren! 
Berichten Sie dies den Entwicklern, f?gen Sie dabei Ihre wrapper.conf hinzu.
+UpdatedVersionAvailableUserAlert.alsoDownloadedNewExtJar=Ihr Knoten hat auch 
eine neue Version der Freenet-Extra-Jar heruntergeladen, Version ${version}
+UpdatedVersionAvailableUserAlert.armed=Ihr Knoten wird automatisch neustarten, 
sobald er die neue Version von Freenet fertig heruntergeladen und ?berpr?ft hat.
+UpdatedVersionAvailableUserAlert.clickToUpdateASAP=Klicken Sie unten um Ihren 
Knoten zu aktualisieren sobald die Aktualisierung ?berpr?ft wurde.
+UpdatedVersionAvailableUserAlert.clickToUpdateNow=Klicken Sie unten, um Ihren 
Knoten sofort zu aktualisieren.
+UpdatedVersionAvailableUserAlert.downloadedNewExtJar=Ihr Knoten hat eine neue 
Version der Freenet-Extra-Jar heruntergeladen, Version ${version}.
+UpdatedVersionAvailableUserAlert.downloadedNewJar=Ihr Knoten hat eine neue 
Version von Freenet heruntergeladen, Version ${version}.
+UpdatedVersionAvailableUserAlert.fetchingNewBoth=Ihr Knoten l?dt gerade eine 
neue Version von Freenet herunter (Knoten-Version ${nodeVersion} und 
Extra-Jar-Version ${extVersion}).
+UpdatedVersionAvailableUserAlert.fetchingNewExt=Ihr Knoten l?dt gerade eine 
neue Version von Freenet herunter (Extra-Jar-Version ${extVersion}).
+UpdatedVersionAvailableUserAlert.fetchingNewNode=Ihr Knoten l?dt gerade eine 
neue Version von Freenet herunter (Knoten-Version ${nodeVersion}).
+UpdatedVersionAvailableUserAlert.finalCheck=Ihr Knoten macht gerade einen 
abschlie?enden Test um die Sicherheit der Aktualisierung zu ?berpr?fen 
(${count} von ${max}, maximal verbleibende Zeit ${time}).
+UpdatedVersionAvailableUserAlert.notLatest=Ihr Knoten scheint nicht die neuste 
Version der Software auszuf?hren.
+UpdatedVersionAvailableUserAlert.title=Eine neue stabile Version von Freenet 
ist verf?gbar
+UpdatedVersionAvailableUserAlert.updateASAPButton=Sobald wie m?glich 
aktualisieren
+UpdatedVersionAvailableUserAlert.updateASAPQuestion=M?chten Sie, dass der 
Knoten automatisch neustartet sobald er die Aktualisierung heruntergeladen hat?
+UpdatedVersionAvailableUserAlert.updateNowButton=Jetzt aktualisieren!
+UserAlert.apply=?bernehmen
+UserAlert.hide=Verstecken
+UserAlert.reset=Zur?cksetzen
+UserAlertManager.alertsOnHomepage=| Betrachten Sie sie auf ${link}der 
Freenet-Startseite${/link}.
+UserAlertManager.alertsTitle=Ausstehende Warnungen
+UserAlertManager.criticalErrorCountLabel=Kritische Fehler:
+UserAlertManager.errorCountLabel=Fehler:
+UserAlertManager.minorCountLabel=Unwichtig:
+UserAlertManager.totalLabel=Gesamt:
+UserAlertManager.warningCountLabel=Warnungen:
+WelcomeToadlet.activityTitle=Derzeitige Aktivit?t
+WelcomeToadlet.arkFetchCount=ARK-Abrufer: ${total}
+WelcomeToadlet.confirmAddBookmarkSubTitle=Hinzuf?gen des Lesezeichens 
best?tigen
+WelcomeToadlet.confirmAddBookmarkTitle=Ein Lesezeichen hinzuf?gen
+WelcomeToadlet.confirmAddBookmarkWithKey=Bitte best?tigen Sie, dass Sie den 
Schl?ssel ${key} zu Ihren Lesezeichen hinzuf?gen wollen und geben Sie die 
Beschreibung, die Sie vorziehen, ein:
+WelcomeToadlet.confirmExternalLinkSubTitle=Externen Link best?tigen
+WelcomeToadlet.confirmExternalLinkTitle=WARNUNG: Externer Link
+WelcomeToadlet.confirmExternalLinkWithURL=Bitte best?tigen Sie, dass Sie auf 
${url} gehen wollen. WARNUNG: Sie verlassen FREENET! Das Anklicken dieses Links 
WIRD Ihre Anonymit?t ernsthaft aufs Spiel setzen! Es wird dringend empfohlen 
dies nicht zu tun!
+WelcomeToadlet.confirmFIN=Wollen Sie die folgende Frost-Nachricht einf?gen?
+WelcomeToadlet.databaseStatsSubTitle=Datenbank-Statistiken
+WelcomeToadlet.databaseStatsTitle=JE-Statistiken abrufen
+WelcomeToadlet.disabledAlert=Alarm ausgeschaltet
+WelcomeToadlet.extVersion=Freenet-ext Build #${build} r${rev}
+WelcomeToadlet.extVersionWithRecommended=Freenet-ext Build #${build} 
(${recbuild} ist empfohlen) r${rev}
+WelcomeToadlet.fetch=Abrufen
+WelcomeToadlet.fetchKeyLabel=Einen Schl?ssel abrufen
+WelcomeToadlet.finInsertSuccessWithKey=Die Nachricht wurde erfolgreich in 
${key} eingef?gt.
+WelcomeToadlet.finInsertedTitle=Einf?gen
+WelcomeToadlet.finTitle=Frost-Sofort-Notiz einf?gen
+WelcomeToadlet.fromHeader=Von
+WelcomeToadlet.goToExternalLink=Zum angegebenen Link gehen
+WelcomeToadlet.homepageFullTitleWithName=Freenet-FProxy-Startseite von ${name}
+WelcomeToadlet.ieWarning=Sie scheinen Microsoft Internet Explorer zu benutzen. 
Das bedeutet, dass manche Seiten im Freenet in der Lage sein k?nnten Ihre 
Anonymit?t zu kompromittieren (gef?hrden)!
+WelcomeToadlet.ieWarningTitle=Sicherheits-Risiko!
+WelcomeToadlet.insertCount=Einf?gen: ${total}
+WelcomeToadlet.insertFailedTitle=Einf?gen fehlgeschlagen
+WelcomeToadlet.insertFailedWithMessage=Das Einf?gen schlug mit folgender 
Nachricht fehl: ${message}
+WelcomeToadlet.insertSucceededTitle=Einf?gen erfolgreich
+WelcomeToadlet.insertedTitle=Einf?gen
+WelcomeToadlet.keyInsertedSuccessfullyWithKeyAndName=Der Schl?ssel 
${link}${name}${/link} wurde erfolgreich eingef?gt.
+WelcomeToadlet.keyRequestLabel=Schl?ssel:
+WelcomeToadlet.messageHeader=Nachricht
+WelcomeToadlet.nodeUpdateConfirm=Sind Sie sich sicher, dass Sie Ihren 
Freenet-Knoten aktualisieren wollen?
+WelcomeToadlet.nodeUpdateConfirmTitle=Knoten-Aktualisierung best?tigen
+WelcomeToadlet.post=Post
+WelcomeToadlet.privateKeyHeader=Privater Schl?ssel
+WelcomeToadlet.publicKeyHeader=?ffentlicher Schl?ssel
+WelcomeToadlet.requestCount=Anfragen: ${total}
+WelcomeToadlet.restart=Neustarten
+WelcomeToadlet.restartConfirm=Sind Sie sich sicher, dass Sie Ihren 
Freenet-Knoten neustarten wollen?
+WelcomeToadlet.restartConfirmTitle=Knoten-Neustart
+WelcomeToadlet.restartNode=Den Knoten neustarten
+WelcomeToadlet.restarting=Bitte warten Sie, w?hrend der Knoten neugestartet 
wird. Dies kann bis zu 3 Minuten dauern. Danke, dass Sie Freenet benutzen.
+WelcomeToadlet.restartingTitle=Der Freenet-Knoten wird neugestartet.
+WelcomeToadlet.shutdown=Herunterfahren
+WelcomeToadlet.shutdownConfirm=Sind Sie sich sicher, dass Sie Ihren 
Freenet-Knoten herunterfahren wollen?
+WelcomeToadlet.shutdownConfirmTitle=Knoten herunterfahren
+WelcomeToadlet.shutdownDone=Der Freenet-Knoten wurde erfolgreich 
heruntergefahren.
+WelcomeToadlet.shutdownNode=Den Knoten herunterfahren
+WelcomeToadlet.splitfileErrorLabel=Teil-Datei-spezifischer Fehler:
+WelcomeToadlet.startIndexHeader=Start-Index
+WelcomeToadlet.subjectHeader=Betreff
+WelcomeToadlet.targetBoardHeader=Ziel-Forum
+WelcomeToadlet.testnetWarning=L?uft im Testnet-Modus. Dies WIRD Ihre 
Anonymit?t ernsthaft aufs Spiel setzen!
+WelcomeToadlet.testnetWarningTitle=Testnet-Modus
+WelcomeToadlet.thanks=Danke, dass Sie Freenet benutzen.
+WelcomeToadlet.threadDumpNotUsingWrapper=Es ist nicht m?glich, den Knoten 
einen Thread-Dump (Prozess-Abbild) erstellen zu lassen, wenn Sie den Wrapper 
(Schutz-/Hilfsumgebung) nicht benutzen!
+WelcomeToadlet.threadDumpSubTitle=Thread-Dump Erzeugung
+WelcomeToadlet.threadDumpTitle=Einen Thread-Dump abrufen
+WelcomeToadlet.threadDumpWithFilename=Ein Thread-Dump (Prozess-Abbild) wurde 
erstellt, es ist verf?gbar in ${filename}.
+WelcomeToadlet.transferringRequestCount=?bertragungs-Anfragen: ${total}
+WelcomeToadlet.update=Aktualisieren
+WelcomeToadlet.updating=Der Freenet-Knoten wird aktualisiert und wird sich 
selbst neustarten. Der Neustart kann bis zu 10 Minuten dauern, weil der Knoten 
versuchen wird, vor der Aktualisierung einen Widerrufs-Schl?ssel abzurufen.
+WelcomeToadlet.updatingTitle=Knoten-Aktualisierung
+WelcomeToadlet.uriWouldHaveBeen=Der URI w?re gewesen: ${uri}
+WelcomeToadlet.version=Freenet ${fullVersion} Build #${build} r${rev}
+WelcomeToadlet.versionHeader=Versions-Informationen & Knoten-Steuerung
+WelcomeToadlet.writtenDatabaseStats=Datenbank-Laufzeit-Statistiken wurden in 
die Wrapper-Protokoll-Datei geschrieben
+testing.test=test$(test1)test$(test2)test
+End

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.en.properties    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.en.properties    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -24,6 +24,7 @@
 BookmarkEditorToadlet.error=Error
 BookmarkEditorToadlet.invalidKeyTitle=Invalid Key
 BookmarkEditorToadlet.invalidKeyWithReason=Invalid Freenet key.
+BookmarkEditorToadlet.invalidKey=The Freenet key is invalid.
 BookmarkEditorToadlet.keyLabel=Key:
 BookmarkEditorToadlet.moveDown=Down
 BookmarkEditorToadlet.moveUp=Up
@@ -34,6 +35,7 @@
 BookmarkEditorToadlet.pasteTitle=Cut/Paste
 BookmarkEditorToadlet.save=Save
 BookmarkEditorToadlet.title=Bookmark Editor
+BookmarkEditorToadlet.urlDecodeError=URL Decode Error
 BookmarkItem.bookmarkUpdated=The bookmarked site ${name} has been updated to 
edition ${edition}.
 BookmarkItem.bookmarkUpdatedTitle=Bookmark Updated: ${name}
 BookmarkItem.bookmarkUpdatedWithLink=The bookmarked site 
${link}${name}${/link} has been updated to edition ${edition}.
@@ -58,6 +60,7 @@
 ConfigToadlet.appliedTitle=Configuration Applied
 ConfigToadlet.apply=Apply
 ConfigToadlet.configNavTitle=Configuration Navigation
+ConfigToadlet.contributeTranslation=Contribute to the translation
 ConfigToadlet.defaultIs=The default for that configuration option is: 
'${default}'.
 ConfigToadlet.fullTitle=Freenet Node Configuration of ${name}
 ConfigToadlet.possibilitiesTitle=Your Possibilities
@@ -99,25 +102,29 @@
 DarknetConnectionsToadlet.cantFetchNoderefURL=Unable to retrieve node 
reference from ${url}. Please try again.
 DarknetConnectionsToadlet.cantParseTryAgain=Unable to parse the given text as 
a node reference: (${error}). Please try again.
 DarknetConnectionsToadlet.cantParseWrongEnding=Unable to parse the node 
reference: It should end with End on a line by itself, but it ends with: ${end}
+DarknetConnectionsToadlet.clockProblem=Your clock and the node's clock differ 
by more than 24 hours. We have disabled the connection as this may cause 
problems with updating and clients.
+DarknetConnectionsToadlet.clockProblemShort=Clock Problem
 DarknetConnectionsToadlet.confirmRemoveNode=Are you sure you wish to remove 
"${name}" ? Before it has at least one week downtime, it's not recommended to 
do so, as it may be down only temporarily, and many users cannot run their 
nodes 24x7.
 DarknetConnectionsToadlet.confirmRemoveNodeTitle=Please confirm
 DarknetConnectionsToadlet.confirmRemoveNodeWarningTitle=Node Removal
 DarknetConnectionsToadlet.connected=Connected: We're successfully connected to 
these nodes
 DarknetConnectionsToadlet.connectedShort=Connected
+DarknetConnectionsToadlet.darknetFnpPort=Darknet FNP: ${port}/UDP (used to 
connect to trusted peers i.e. Friends; forward this port if you can)
 DarknetConnectionsToadlet.disabled=Not connected and disabled: because the 
user has instructed the node not to connect to these peers.
 DarknetConnectionsToadlet.disabledShort=Disabled
 DarknetConnectionsToadlet.enterDescription=Enter description:
+DarknetConnectionsToadlet.connError=Connection failed (buggy node?)
+DarknetConnectionsToadlet.connErrorShort=Connection Error
 DarknetConnectionsToadlet.failedToAddNodeInternalError=Unable to parse the 
given text as a node reference. Please report the following to the developers:
 DarknetConnectionsToadlet.failedToAddNodeInternalErrorTitle=Failed to Add 
Node: Internal Error
 DarknetConnectionsToadlet.failedToAddNodeTitle=Failed To Add Node
 DarknetConnectionsToadlet.fcpDisabled=FCP is disabled (for Freenet clients 
such as Frost and Thaw)
 DarknetConnectionsToadlet.fcpPort=FCP: ${port}/TCP (for Freenet clients such 
as Frost and Thaw)
 DarknetConnectionsToadlet.fileReference=Choose the file containing the 
reference here:
-DarknetConnectionsToadlet.fnpPort=FNP: ${port}/UDP (between nodes; this is 
usually the only port that you might want to port forward)
 DarknetConnectionsToadlet.forceRemove=Force Remove
 DarknetConnectionsToadlet.fproxyDisabled=FProxy is disabled (this web 
interface)
 DarknetConnectionsToadlet.fproxyPort=FProxy: ${port}/TCP (this web interface)
-DarknetConnectionsToadlet.fullTitle=${counts} Friends of ${name}
+DarknetConnectionsToadlet.fullTitle=${counts} Friends (Trusted Peers) of 
${name}
 DarknetConnectionsToadlet.go=Go
 DarknetConnectionsToadlet.idleTime=How long since the node connected or was 
last seen
 DarknetConnectionsToadlet.idleTimeTitle=Connected / Idle
@@ -128,7 +135,7 @@
 DarknetConnectionsToadlet.listenOnlyShort=Listen only
 DarknetConnectionsToadlet.listening=Not connected but listening: this node 
won't try to connect to these peers very often because the user has set 
BurstOnly on it.
 DarknetConnectionsToadlet.listeningShort=Listening
-DarknetConnectionsToadlet.myFriends=My Friends
+DarknetConnectionsToadlet.myFriends=My Friends (trusted peers added by me)
 DarknetConnectionsToadlet.myReferenceHeader=${linkref}My Node 
Reference${/linkref} (${linktext}as text${/linktext})
 DarknetConnectionsToadlet.nameClickToMessage=The node's name. Click on the 
name link to send the node a N2NTM (Node To Node Text Message)
 DarknetConnectionsToadlet.nameTitle=Name
@@ -139,6 +146,7 @@
 DarknetConnectionsToadlet.nodePortsTitle=Ports used by the Node
 DarknetConnectionsToadlet.notConnected=Not connected: No connection so far but 
this node is continuously trying to connect.
 DarknetConnectionsToadlet.notConnectedShort=Disconnected
+DarknetConnectionsToadlet.opennetFnpPort=Opennet FNP: ${port}/UDP (used to 
connect to untrusted peers i.e. Strangers; forward this port if you can) 
 DarknetConnectionsToadlet.pasteReference=Paste the reference here:
 DarknetConnectionsToadlet.privateNote=A private note concerning this peer
 DarknetConnectionsToadlet.privateNoteTitle=Private Note
@@ -165,25 +173,30 @@
 ExtOldAgeUserAlert.extTooOldTitle=Freenet-ext too old
 FirstTimeWizardToadlet.homepageTitle=Freenet first time wizard!
 FirstTimeWizardToadlet.chooseNodeName=Node name required!
-FirstTimeWizardToadlet.chooseNodeNameLong=Please enter a node name in the 
field below. This name won't be published to anyone except the peers you are 
connected to. We recommend to put your real-name or IRC nick in there possibly 
with some contact information so that you can be reached in case there is a 
problem ("John Doe <noname at nowhere.com>"). 
-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. Please
+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 
name your node. 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=If you let Freenet connect to 
strangers, Freenet will be less secure for you, everybody can find out that you 
are using Freenet, and any bad guy can connect to your node. If you don't, you 
will have to manually contact at least three other friends (people you already 
know) who are using Freenet and connect to them.
+FirstTimeWizardToadlet.enableOpennet=Automatically connect to untrusted 
strangers' nodes?
+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. Please
 FirstTimeWizardToadlet.bandwidthLimit=Bandwidth limits
 FirstTimeWizardToadlet.bandwidthLimitLong=Please select your internet 
connection type and speed from the dropdown menu below.
+FirstTimeWizardToadlet.continue=Continue
 FirstTimeWizardToadlet.datastoreSize=Datastore size
-FirstTimeWizardToadlet.datastoreSizeLong=Please select a size for your 
datastore. The datastore acts like a cache; storing data for the network will 
help you to get better thoughput when requesting popular files. The more space 
you can afford the better it is for the community and the faster your node will 
go.
+FirstTimeWizardToadlet.datastoreSizeLong=Please select a size for your 
datastore. The datastore acts like a cache; storing data for the network will 
help you to get better throughput when requesting popular files. The more space 
you can afford the better it is for the community and the faster your node will 
go.
 FirstTimeWizardToadlet.isNetworkTrusted=Is your local network trusted ?
 FirstTimeWizardToadlet.isNetworkTrustedLong=Is your local network trusted ? If 
you answer yes here all the services provided by your node will be wide open to 
everyone willing to access them on the given network. You will be able to do 
selective access controls from the configuration page when the wizard is over.
-FirstTimeWizardToadlet.noNetworkIF=No additionnal network interface found
-FirstTimeWizardToadlet.noNetworkIFLong=Freenet hasn't found any additionnal 
network interface. It will assume that you will connect to it from your 
computer and only from it.
+FirstTimeWizardToadlet.noNetworkIF=No additional network interface found
+FirstTimeWizardToadlet.noNetworkIFLong=Freenet hasn't found any additional 
network interface. It will assume that you will connect to it from your 
computer and only from it.
 FirstTimeWizardToadlet.iDoTrust=Do you trust people connected to ${interface} 
(${ip}) ?
 FirstTimeWizardToadlet.congratz=Welcome on board!
-FirstTimeWizardToadlet.congratzLong=Contratulations, the base configuration of 
your freenet node is now done. You can change and edit any of the parameters 
you have just set going to the "configuration" page, reachable anytime from the 
menu on the left of the interface. We wish you a pleasant freenet experience.
-FirstTimeWizardToadlet.step1Title=Freenet first time wizard! - Choose a node 
name
-FirstTimeWizardToadlet.step2Title=Freenet first time wizard! - Bandwidth limits
-FirstTimeWizardToadlet.step3Title=Freenet first time wizard! - Datastore size
-FirstTimeWizardToadlet.step4Title=Freenet first time wizard! - Network 
configuration
-FirstTimeWizardToadlet.step5Title=Freenet first time wizard! - 
Congratulations, your node is now configured
+FirstTimeWizardToadlet.congratzLong=Congratulations, the base configuration of 
your Freenet node is now done. You can change and edit any of the parameters 
you have just set going to the "configuration" page, reachable anytime from the 
menu on the left of the interface. We wish you a pleasant Freenet experience.
+FirstTimeWizardToadlet.step1Title=Freenet first time wizard! - Friends and 
strangers
+FirstTimeWizardToadlet.step2Title=Freenet first time wizard! - Choose a node 
name
+FirstTimeWizardToadlet.step3Title=Freenet first time wizard! - Bandwidth limits
+FirstTimeWizardToadlet.step4Title=Freenet first time wizard! - Datastore size
+FirstTimeWizardToadlet.step5Title=Freenet first time wizard! - Network 
configuration
+FirstTimeWizardToadlet.step6Title=Freenet first time wizard! - 
Congratulations, your node is now configured
 FirstTimeWizardToadlet.skipWizard=I am not a newbie, please skip the wizard!
 FProxyToadlet.abortToHomepage=Abort and return to the FProxy home page
 FProxyToadlet.backToFProxy=${link}Click here${/link} to go to the FProxy home 
page.
@@ -199,6 +212,7 @@
 FProxyToadlet.errorIsFatal= This is a fatal error. It is unlikely that 
retrying will solve the problem.
 FProxyToadlet.errorWithReason=Error: ${error}
 FProxyToadlet.expectedKeyButGot=Expected a Freenet key, but got:
+FProxyToadlet.expectedMimeType=Expected MIME type: ${mime}
 FProxyToadlet.explanationTitle=Explanation
 FProxyToadlet.fetchLargeFileAnywayAndDisplay=Fetch anyway and display file in 
browser
 FProxyToadlet.fileInformationTitle=File Information
@@ -212,17 +226,18 @@
 FProxyToadlet.largeFile=Large file
 FProxyToadlet.largeFileExplanationAndOptions=The Freenet key you requested 
refers to a large file. Files of this size cannot generally be sent directly to 
your browser since they take too long for your Freenet node to retrieve. The 
following options are available:
 FProxyToadlet.mayChange= (may change)
-FProxyToadlet.mimeType=Expected MIME type: ${mime}
 FProxyToadlet.mimeType=MIME type: ${mime}
 FProxyToadlet.notEnoughMetaStrings=Not enough meta-strings
 FProxyToadlet.notFoundTitle=Not Found
 FProxyToadlet.openAsText=${link}Click here${/link} to open the file as plain 
text (this should not be dangerous but it may be garbled).
 FProxyToadlet.openForce=${link}Click here${/link} to open the file as ${mime} 
(read warning above!).
 FProxyToadlet.openForceDisk=${link}Click here${/link} to force your browser to 
download the file to disk.
-FProxyToadlet.openPossRSSAsForceDisk=${link}Click here${/link} to try to force 
your browser to download the file to disk (this ${bold}this may also be 
dangerous${/bold} if you run Firefox 2.0.0, 2.0.1 should fix this).
+FProxyToadlet.openPossRSSForceDisk=${link}Click here${/link} to try to force 
your browser to download the file to disk (this ${bold}this may also be 
dangerous${/bold} if you run Firefox 2.0.0, 2.0.1 should fix this).
 FProxyToadlet.openPossRSSAsPlainText=${link}Click here${/link} to open the 
file as plain text (this ${bold}may be dangerous${/bold} if you are running IE7 
or FF2).
 FProxyToadlet.openRSSAsRSS=${link}Click here${/link} to open the file as RSS 
(this ${bold}is dangerous${/bold} if the site author is malicious as Freenet 
does not know how to filter RSS yet).
 FProxyToadlet.openRSSForce=${link}Click here${/link} to open the file as 
${mime} (this ${bold}may be dangerous${/bold} on IE7 or FF2).
+FProxyToadlet.opennet=manage untrusted connections
+FProxyToadlet.opennetTitle=Strangers
 FProxyToadlet.options=Your options are:
 FProxyToadlet.pathNotFound=The specified path does not exist.
 FProxyToadlet.pathNotFoundTitle=Path Not Found
@@ -235,8 +250,6 @@
 FProxyToadlet.sizeUnknown=Size: unknown
 FProxyToadlet.stats=view statistics
 FProxyToadlet.statsTitle=Statistics
-FProxyToadlet.translation=helper to translate the node's interface into your 
native language
-FProxyToadlet.translationTitle=Translation
 FProxyToadlet.unableToRetrieve=Freenet was unable to retrieve this file.
 FProxyToadlet.unknownMIMEType=MIME type: unknown
 FProxyToadlet.welcome=homepage
@@ -289,7 +302,9 @@
 FetchException.longError.26=Archive restarted
 FetchException.longError.27=Permanent redirect: use the new URI
 FetchException.longError.28=Not enough data found; some data was fetched but 
redirect may point to nowhere
+FetchException.longError.29=Wrong MIME Type: The key was not in the list of 
allowed MIME types provided by the client
 FetchException.longError.2=Don't know what to do with splitfile
+FetchException.longError.30=The request was terminated by a node because it 
had recently received a request for the same key and that request had failed
 FetchException.longError.3=Don't know what to do with metadata
 FetchException.longError.4=Failed to parse metadata
 FetchException.longError.5=Failure in extracting files from an archive
@@ -317,7 +332,9 @@
 FetchException.shortError.26=Archive restarted
 FetchException.shortError.27=New URI
 FetchException.shortError.28=All data not found
+FetchException.shortError.29=Wrong MIME type
 FetchException.shortError.2=Unknown splitfile metadata
+FetchException.shortError.30=Data not found (recently failed)
 FetchException.shortError.3=Unknown metadata
 FetchException.shortError.4=Invalid metadata
 FetchException.shortError.5=Archive failure
@@ -325,6 +342,19 @@
 FetchException.shortError.7=Too many metadata levels
 FetchException.shortError.8=Too many archive restarts
 FetchException.shortError.9=Too much recursion
+FileOffer.askUserTitle=Direct file transfer
+FileOffer.offeredFileHeader=The node ${name} has offered a file:
+FileOffer.fileLabel=File:
+FileOffer.senderLabel=Sender:
+FileOffer.commentLabel=Comment:
+FileOffer.mimeLabel=MIME Type:
+FileOffer.sizeLabel=Size:
+FileOffer.acceptTransferButton=Accept Transfer
+FileOffer.rejectTransferButton=Reject Transfer
+FileOffer.failedReceiveHeader=The transfer of the file ${filename} from 
${node} has failed.
+FileOffer.failedReceiveTitle=Failed to receive file
+FileOffer.succeededReceiveTitle=Successfully received file
+FileOffer.succeededReceiveHeader=The transfer of the file ${filename} from 
${node} was a success.
 GIFFilter.invalidHeader=The file does not contain a valid GIF header.
 GIFFilter.invalidHeaderTitle=Invalid header
 GIFFilter.notGif=The file you tried to fetch is not a GIF. It might be some 
other file format, and your browser may do something dangerous with it, 
therefore we have blocked it.
@@ -431,7 +461,7 @@
 LogConfigHandler.rotationIntervalLong=Log rotation interval - period after 
which logs are rotated. We keep the last two log files (current and prev), plus 
lots of compressed logfiles up to maxZippedLogsSize
 LoggerHook.unrecognizedPriority=Unrecognised priority name: ${name}.
 LongOption.parseError=The value specified can't be parsed as a 64-bit integer 
: ${val}
-MeaningfulNodeNameUserAlert.noNodeNick=It seems that your node doesn't know 
your nickname. Putting your e-mail address or IRC nickname here is generally 
speaking a good idea and helps your friends to identify your node (note that 
only your darknet peers listed on the friends page can see your node name, it 
will not be displayed to opennet peers).
+MeaningfulNodeNameUserAlert.noNodeNick=It seems that your node doesn't know 
your nickname. Putting your e-mail address or IRC nickname here is generally 
speaking a good idea and helps your friends to identify your node (note that 
only your darknet peers listed on the friends page can see your node name, it 
will not be displayed to strangers).
 MeaningfulNodeNameUserAlert.noNodeNickTitle=Your node name isn't defined.
 N2NTMToadlet.composingMessageLabel=Composing N2NTM to send to the following 
peers:
 N2NTMToadlet.delayed=Backed off: Sending of message possibly delayed to peer
@@ -458,6 +488,8 @@
 N2NTMUserAlert.header=From: ${from} (composed ${composed} | sent ${sent} | 
received ${received})
 N2NTMUserAlert.reply=Reply
 N2NTMUserAlert.title=Node to Node Text Message ${number} from ${peername} 
(${peer})
+Node.alwaysAllowLocalAddresses=Always allow connecting to nodes via local 
addresses?
+Node.alwaysAllowLocalAddressesLong=If true, the node will attempt to connect 
to nodes via their local (localhost, LAN) addresses as well as their public 
IPs. If this is not set, you can still enable it for specific darknet peers 
(but not opennet peers). Set this if you want to connect to other nodes on the 
same LAN or computer, and don't mind that bogus references can cause your node 
to send UDP packets to machines on your LAN.
 Node.bandwidthLimitMustBePositiveOrMinusOne=Bandwidth limit must be positive 
or -1
 Node.bindTo=IP address to bind to
 Node.bindToLong=IP address to bind to
@@ -492,8 +524,14 @@
 Node.nodeDirLong=Name of directory to put node-related files e.g. peers list in
 Node.nodeName=Nickname for this Freenet node
 Node.nodeNameLong=Node nickname. This will be visible to your friends only.
+Node.oneConnectionPerIP=Limit to one connection per address?
+Node.oneConnectionPerIPLong=Don't allow more than one connection per address? 
This will make it slightly harder for an attacker to connect more than once to 
your node as different identities, in order to dominate your routing or make 
harvesting easier. Also prevents having the same node connected on darknet and 
opennet simultaneously. 
+Node.opennetEnabled=Enable promiscuous mode (automatically connect to 
untrusted nodes)?
+Node.opennetEnabledLong=Enable promiscuous mode? If this is enabled, the node 
will automatically exchange node references with other untrusted nodes 
(Strangers as opposed to Friends). But this means that the fact that you are 
running a node is no longer private, and many attacks are much easier. If you 
know enough people running Freenet, you should stick to trusted (Friends) 
connections to them.
 Node.outBWLimit=Output bandwidth limit (bytes per second)
 Node.outBWLimitLong=Hard output bandwidth limit (bytes/sec); the node should 
almost never exceed this
+Node.passOpennetPeersThroughDarknet=Relay opennet noderefs through darknet 
peers?
+Node.passOpennetPeersThroughDarknetLong=If true, opennet noderefs (NEVER our 
own darknet noderef) will be relayed through our darknet peers. So a node (this 
node, or its peers) can get opennet peers from its darknet peers. This is 
useful because it allows us to bootstrap new opennet peers after having lost 
our peers due to downtime, for example. However, it may make traffic analysis 
slightly easier, so turn it off if you are paranoid.
 Node.port=FNP port number (UDP)
 Node.portLong=UDP port for node-to-node communications (Freenet Node Protocol)
 Node.storeDirectory=Store directory
@@ -568,8 +606,16 @@
 NodeUpdateManager.updateFailedTitle=Update Failed!
 NodeUpdateManager.updateURI=Where should the node look for updates?
 NodeUpdateManager.updateURILong=Where should the node look for updates?
+OpennetConnectionsToadlet.fullTitle=${counts} Strangers (Untrusted Peers) of 
${name}
+OpennetConnectionsToadlet.peersListTitle=My Opennet Peers (untrusted peers 
added by the node in promiscuous mode)
+OpennetUserAlert.warningTitle=Warning: Promiscuous Mode Enabled: Your node 
will connect to Strangers
+OpennetUserAlert.warning=Your node is currently running in promiscuous mode. 
It will connect to Strangers, and this means that anyone can find out that you 
are running a node. Most attacks are easier, blocking your node (for example at 
a national firewall) is much easier, and you have no control over who your node 
connects to. We strongly recommend you get some connections to Friends (trusted 
nodes run by people you already know); promiscuous mode is only intended as a 
temporary measure until you are able to just connect to your Friends. If you 
only connect to your friends, while it may be possible for them to attack you, 
it is less likely than if your node is exposed to any government agency/other 
bad guy who wants to connect to it. Note that adding a peer in the Friends 
section does not help much unless that peer belongs to somebody you actually 
know (both for routing and security reasons)!
 PNGFilter.invalidHeader=The file you tried to fetch is not a PNG. It does not 
include a valid PNG header. It might be some other file format, and your 
browser may do something dangerous with it, therefore we have blocked it.
 PNGFilter.invalidHeaderTitle=Not a PNG - invalid header
+PeerManagerUserAlert.connErrorTitle=Some peers cannot connect
+PeerManagerUserAlert.connError=${count} of your peers cannot connect for an 
unknown reason, possibly because of a buggy node or a corrupt node reference.
+PeerManagerUserAlert.clockProblemTitle=Clock problem
+PeerManagerUserAlert.clockProblem=${count} of your nodes are unable to connect 
because their system clock is more than 24 hours different to yours. Please 
check whether your computer has the correct time. Incorrect time will cause 
many node and client mechanisms to fail.
 PeerManagerUserAlert.noConns=This node has not been able to connect to any 
other nodes so far; it will not be able to function normally. Hopefully some of 
your peers will connect soon; if not, try to get some more peers. You need at 
least 3 peers at any time, and should aim for 5-10.
 PeerManagerUserAlert.noConnsTitle=No open connections
 PeerManagerUserAlert.noPeersDarknet=This node has no peers to connect to, 
therefore it will not be able to function normally. Ideally you should connect 
to peers run by people you know (if you are paranoid, then people you trust; if 
not, then at least people you've talked to). You need at least 3 connected 
peers at all times, and ideally 5-10. You could log on to irc.freenode.net 
channel #freenet-refs and ask around for somebody to connect to, but remember 
that you are vulnerable to those you are directly connected to. (This is 
especially true in this early alpha of Freenet 0.7...) BE SURE THAT THE OTHER 
PERSON HAS ADDED YOUR REFERENCE, TOO, AS ONE-WAY CONNECTIONS WON'T WORK!
@@ -593,6 +639,13 @@
 PeerManagerUserAlert.tooOldNeverConnectedPeers=One or more of your node's 
peers have never connected in the two weeks since they were added.  Consider 
removing them since they are marginally affecting performance (wasting packets 
talking to nodes that aren't there).
 PeerManagerUserAlert.tooOldNeverConnectedPeersTitle=Never connected peer(s) 
too old
 PeerManagerUserAlert.twoConns=This node has only two connections. Performance 
and security will not be very good, and your node is not doing any routing for 
other nodes. Your node is embedded like a 'chain' in the network and does not 
contribute (much) to the network's health. Try to get at least 3 (ideally more 
like 5-10) connected peers at any given time.
+PeersSayKeyBlownAlert.intro=One or more of your peers says that the 
auto-update key is blown! This means that an attacker may know the private key 
for the auto-update system and can therefore cause your node to run code of his 
choice (if you update)! The auto-update system has been disabled. It is also 
possible that your peers are deliberately lying about it.
+PeersSayKeyBlownAlert.fetching=Your node is attempting to download the 
revocation certificate to find out more details.
+PeersSayKeyBlownAlert.failedFetch=Your node has been unable to download the 
revocation certificate. Possible causes include an attack on your node to try 
to get you to update despite the key being blown, or your nodes lying about the 
key being blown. Please contact the developers or other Freenet users to sort 
out this mess.
+PeersSayKeyBlownAlert.connectedSayBlownLabel=These connected nodes say that 
the key has been blown (we are trying to download the revocation cert from 
them):
+PeersSayKeyBlownAlert.disconnectedSayBlownLabel=These nodes told us that the 
key has been blown, but then disconnected, so we could not fetch the revocation 
certificate:
+PeersSayKeyBlownAlert.failedTransferSayBlownLabel=These nodes told us that the 
key has been blown, but then failed to transfer the revocation certificate:
+PeersSayKeyBlownAlert.titleWithCount=Auto-update key blown according to 
${count} peer(s)!
 PluginManager.cannotSetOnceLoaded=Cannot set the plugins list once loaded
 PluginManager.loadedOnStartup=Plugins to load on startup
 PluginManager.loadedOnStartupLong=Classpath, name and location for plugins to 
load when node starts up
@@ -689,7 +742,7 @@
 QueueToadlet.persistence=Persistence
 QueueToadlet.persistenceForever=forever
 QueueToadlet.persistenceNone=none
-QueueToadlet.persistenceRebootr=reboot
+QueueToadlet.persistenceReboot=reboot
 QueueToadlet.pleaseEnableFCP=You need to enable the FCP server to access this 
page
 QueueToadlet.priority0=emergency
 QueueToadlet.priority1=very high
@@ -758,7 +811,7 @@
 StatisticsToadlet.bandwidthTitle=Bandwidth
 StatisticsToadlet.cpus=Available CPUs: ${count}
 StatisticsToadlet.getLogs=Get latest node's logfile
-StatisticsToadlet.inputRate=Input Rate: ${rate}/second (of ${max})
+StatisticsToadlet.inputRate=Input Rate: ${rate}/sec (of ${max})
 StatisticsToadlet.jeDumpButton=Generate a JE Dump
 StatisticsToadlet.jvmInfoTitle=JVM Info
 StatisticsToadlet.jvmVendor=JVM Vendor: ${vendor}
@@ -768,13 +821,13 @@
 StatisticsToadlet.osArch=OS Architecture: ${arch}
 StatisticsToadlet.osName=OS Name: ${name}
 StatisticsToadlet.osVersion=OS Version: ${version}
-StatisticsToadlet.outputRate=Output Rate: ${rate}/second (of ${max})
-StatisticsToadlet.payloadOutput=Payload Output: ${total} 
(${rate}/second)(${percent}%)
+StatisticsToadlet.outputRate=Output Rate: ${rate}/sec (of ${max})
+StatisticsToadlet.payloadOutput=Payload Output: ${total} 
(${rate}/sec)(${percent}%)
 StatisticsToadlet.peerStatsTitle=Peer statistics
 StatisticsToadlet.threadDumpButton=Generate a Thread Dump
 StatisticsToadlet.threads=Running threads: ${running}/${max}
-StatisticsToadlet.totalInput=Total Input: ${total} (${rate}/second)
-StatisticsToadlet.totalOutput=Total Output: ${total} (${rate}/second)
+StatisticsToadlet.totalInput=Total Input: ${total} (${rate}/sec)
+StatisticsToadlet.totalOutput=Total Output: ${total} (${rate}/sec)
 StatisticsToadlet.transferringRequests=Transferring Requests: sending 
${senders}, receiving ${receivers}
 StatisticsToadlet.usedMemory=Used Java memory: ${memory}
 StatisticsToadlet.versionTitle=Node Version Information
@@ -795,6 +848,8 @@
 TextModeClientInterfaceServer.enabledLong=Whether to enable the TMCI
 TextModeClientInterfaceServer.telnetPortNumber=Telnet port
 TextModeClientInterfaceServer.telnetPortNumberLong=Telnet port number
+TimeSkewDetectedUserAlert.title=Time skew detected!
+TimeSkewDetectedUserAlert.text=A time skew has been detected by the node. 
That's VERY bad. Your node won't perform correctly until it's fixed; Common 
causes are missconfigured powersafe mode, network time synchronization clients 
or buggy hardware.
 Toadlet.yes=Yes
 Toadlet.no=No
 Toadlet.cancel=Cancel
@@ -804,6 +859,7 @@
 Toadlet.nodeHomepage=Node Homepage
 Toadlet.notSupportedTitle=Not Supported
 Toadlet.notSupportedWithClass=Your browser sent a request that Freenet 
(${class}) could not understand.
+Toadlet.ok=Ok
 Toadlet.permRedirectWithReason=Permanent redirect: ${reason}
 Toadlet.returnToNodeHomepage=Return to node homepage
 Toadlet.returnToPrevPage=Return to the previous page
@@ -854,7 +910,7 @@
 UpdatedVersionAvailableUserAlert.fetchingNewBoth=Your node is currently 
downloading a new version of Freenet (node version ${nodeVersion} and extra jar 
version ${extVersion}).
 UpdatedVersionAvailableUserAlert.fetchingNewExt=Your node is currently 
downloading a new version of Freenet (extra jar version ${extVersion}).
 UpdatedVersionAvailableUserAlert.fetchingNewNode=Your node is currently 
downloading a new version of Freenet (node version ${nodeVersion}).
-UpdatedVersionAvailableUserAlert.finalCheck=Your node is currently doing a 
final check to verify the security of the update (${count} of ${max}).
+UpdatedVersionAvailableUserAlert.finalCheck=Your node is currently doing a 
final check to verify the security of the update (${count} of ${max}, maximum 
time remaining ${time}).
 UpdatedVersionAvailableUserAlert.notLatest=It seems that your node isn't 
running the latest version of the software.
 UpdatedVersionAvailableUserAlert.title=A new stable version of Freenet is 
available
 UpdatedVersionAvailableUserAlert.updateASAPButton=Update ASAP
@@ -900,6 +956,7 @@
 WelcomeToadlet.insertSucceededTitle=Insert Succeeded
 WelcomeToadlet.insertedTitle=Insertion
 WelcomeToadlet.keyInsertedSuccessfullyWithKeyAndName=The key 
${link}${name}${/link} has been inserted successfully.
+WelcomeToadlet.keyRequestLabel=Key:
 WelcomeToadlet.messageHeader=Message
 WelcomeToadlet.nodeUpdateConfirm=Are you sure you wish to update your Freenet 
node?
 WelcomeToadlet.nodeUpdateConfirmTitle=Confirm Node Update

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.fr.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.fr.properties    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.fr.properties    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -21,6 +21,7 @@
 BookmarkEditorToadlet.editBookmarkTitle=Modifier un marque-page
 BookmarkEditorToadlet.editCategoryTitle=Modifier cat?gorie
 BookmarkEditorToadlet.error=Erreur
+BookmarkEditorToadlet.invalidKey=La cl? Freenet est invalide.
 BookmarkEditorToadlet.invalidKeyTitle=Cl? invalide
 BookmarkEditorToadlet.invalidKeyWithReason=Cl? Freenet invalide.
 BookmarkEditorToadlet.keyLabel=Cl? :
@@ -33,6 +34,7 @@
 BookmarkEditorToadlet.pasteTitle=Couper/Coller
 BookmarkEditorToadlet.save=Enregistrer
 BookmarkEditorToadlet.title=Editeur de marque-page
+BookmarkEditorToadlet.urlDecodeError=Erreur d'URL
 BookmarkItem.bookmarkUpdated=L'?dition ${edition} du site ${name} a ?t? 
publi?e.
 BookmarkItem.bookmarkUpdatedTitle=Signet mis ? jour : ${name}
 BookmarkItem.bookmarkUpdatedWithLink=Le signet ${link}${name}${/link} a ?t? 
mis ? jour vers l'?dition ${edition}.
@@ -57,6 +59,7 @@
 ConfigToadlet.appliedTitle=Configuration appliqu?e
 ConfigToadlet.apply=Appliquer
 ConfigToadlet.configNavTitle=Sections de configuration
+ConfigToadlet.contributeTranslation=Aider ? la traduction
 ConfigToadlet.defaultIs=La valeur par d?faut est : '${default}'.
 ConfigToadlet.fullTitle=Configuration du noeud ${name}
 ConfigToadlet.possibilitiesTitle=Vos possibilit?s
@@ -98,11 +101,16 @@
 DarknetConnectionsToadlet.cantFetchNoderefURL=Impossible de r?cup?rer une 
r?f?rence ? partir de ${url}. Essayez ? nouveau.
 DarknetConnectionsToadlet.cantParseTryAgain=Ce texte n'est pas une r?f?rence : 
(${error}). Essayez ? nouveau.
 DarknetConnectionsToadlet.cantParseWrongEnding=Impossible d'analyser cette 
r?f?rence : elle devrait se terminer par une lige contenant "End", mais elle se 
termine par : ${end}
-DarknetConnectionsToadlet.confirmRemoveNode=Etes-vous s?r de vouloir supprimer 
"+peerNodes[i].getName()+" ? Ce n'est pas recommand? avant une semaine 
d'inactivit?, cette inactivit? peut ?tre temporaire, et beaucoup d'utilisateurs 
ne peuvent pas laisser leur noeud tourner 24h/24.
+DarknetConnectionsToadlet.clockProblem=Votre horloge et celle de ce noeud 
pr?sentent plus de 24h de d?callage. Cette connexion a ?t? d?sactiv?e car cela 
peut poser des probl?mes avec les mises ? jour et les clients.
+DarknetConnectionsToadlet.clockProblemShort=Probl?me d'horloge
+DarknetConnectionsToadlet.confirmRemoveNode=Etes-vous s?r de vouloir supprimer 
"${name}" ? Ce n'est pas recommand? avant une semaine d'inactivit?, cette 
inactivit? peut ?tre temporaire, et beaucoup d'utilisateurs ne peuvent pas 
laisser leur noeud tourner 24h/24.
 DarknetConnectionsToadlet.confirmRemoveNodeTitle=Veuillez confirmer
 DarknetConnectionsToadlet.confirmRemoveNodeWarningTitle=Suppression de noeud
+DarknetConnectionsToadlet.connError=La connexion a ?chou? (noeud bugg? ?)
+DarknetConnectionsToadlet.connErrorShort=Connexion ?chou?e
 DarknetConnectionsToadlet.connected=Connect?s : Vous ?tes connect?s ? ces 
noeuds
 DarknetConnectionsToadlet.connectedShort=Connect?
+DarknetConnectionsToadlet.darknetFnpPort=Darknet FNP : ${port}/UDP 
(communication entre les noeuds ; vous voudrez sans doute ouvrir ce port dans 
votre firewall)
 DarknetConnectionsToadlet.disabled=Non connect?s et d?sactiv?s : le noeud a 
re?u pour instruction de ne pas se connecter ? ces noeuds.
 DarknetConnectionsToadlet.disabledShort=D?sactiv?
 DarknetConnectionsToadlet.enterDescription=Entrez un commentaire :
@@ -116,7 +124,7 @@
 DarknetConnectionsToadlet.forceRemove=Forcer la suppression
 DarknetConnectionsToadlet.fproxyDisabled=FProxy est d?sactiv? (cette interface 
web)
 DarknetConnectionsToadlet.fproxyPort=FProxy: ${port}/TCP (cette interface web)
-DarknetConnectionsToadlet.fullTitle=${counts} amis de ${name}
+DarknetConnectionsToadlet.fullTitle=${counts} Amis (noeuds de confiance) de 
${name}
 DarknetConnectionsToadlet.go=Ex?cuter
 DarknetConnectionsToadlet.idleTime=Depuis combien de temps on est connect? ? 
ce noeud, ou quand il a ?t? vu pour la derni?re fois
 DarknetConnectionsToadlet.idleTimeTitle=Connect? / en attente
@@ -127,7 +135,7 @@
 DarknetConnectionsToadlet.listenOnlyShort=Ecouter seulement
 DarknetConnectionsToadlet.listening=Non connect? mais ? l'?coute : le noeud 
n'essaie pas de se connecter ? ces noeuds tr?s souvent car le mode rafale est 
activ? sur eux.
 DarknetConnectionsToadlet.listeningShort=A l'?coute
-DarknetConnectionsToadlet.myFriends=Mes amis
+DarknetConnectionsToadlet.myFriends=Mes Amis
 DarknetConnectionsToadlet.myReferenceHeader=${linkref}Ma r?f?rence${/linkref} 
(${linktext}en texte brut${/linktext})
 DarknetConnectionsToadlet.nameClickToMessage=Le nom du noeud. Cliquez sur son 
nom pour lui envoyer un message
 DarknetConnectionsToadlet.nameTitle=Nom
@@ -138,6 +146,7 @@
 DarknetConnectionsToadlet.nodePortsTitle=Ports utilis?s par le noeud
 DarknetConnectionsToadlet.notConnected=Non connect? : Pas de connexion jusqu'? 
pr?sent mais ce noeud continue d'essayer.
 DarknetConnectionsToadlet.notConnectedShort=D?connect?
+DarknetConnectionsToadlet.opennetFnpPort=Opennet FNP : ${port}/UDP 
(communication entre les noeuds ; vous voudrez sans doute ouvrir ce port dans 
votre firewall)
 DarknetConnectionsToadlet.pasteReference=Copiez/collez sa r?f?rence ici :
 DarknetConnectionsToadlet.privateNote=Un commentaire priv? concernant ce noeud
 DarknetConnectionsToadlet.privateNoteTitle=Commentaire priv?
@@ -176,11 +185,12 @@
 FProxyToadlet.errorIsFatal=Cette erreur est fatale. Il y a peu de chances pour 
que le probl?me se r?solve en r?essayant.
 FProxyToadlet.errorWithReason=Erreur : ${error}
 FProxyToadlet.expectedKeyButGot=Le noeud attendait une cl? Freenet et non pas :
+FProxyToadlet.expectedMimeType=Type MIME attendu : ${mime}
 FProxyToadlet.explanationTitle=Explication
 FProxyToadlet.fetchLargeFileAnywayAndDisplay=R?cup?rer quand m?me et afficher 
le fichier dans le navigateur
 FProxyToadlet.fileInformationTitle=Informations sur le fichier
 FProxyToadlet.filenameLabel=Nom de fichier :
-FProxyToadlet.friends=G?rer les connexions avec les amis
+FProxyToadlet.friends=G?rer les connexions avec les Amis
 FProxyToadlet.friendsTitle=Amis
 FProxyToadlet.goBack=Retour
 FProxyToadlet.goBackToPrev=Revenir ? la page pr?c?dente
@@ -197,8 +207,11 @@
 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).
 FProxyToadlet.openPossRSSAsPlainText=${link}Cliquez ici${/link} pour ouvrir le 
fichier en texte brut (cela ${bold}peut ?tre dangereux${/bold} si vous utilisez 
Internet Explorer 7.0 ou Firefox 2.0.0).
-FProxyToadlet.openRSSAsRSS=${link}Cliquez ici${/link} pour ouvrir le fichier 
en tant que RSS (ceci ${bold}est dangereux${/bold} dans le cas o? l'auteur du 
site est malicieux car Freenet n'est pas encore capable de filtrer le RSS).
+FProxyToadlet.openPossRSSForceDisk=${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, la version 2.0.1 
devrait corriger le probl?me).
+FProxyToadlet.openRSSAsRSS=${link}Cliquez ici${/link} pour ouvrir le fichier 
en tant que RSS (ceci <b>est dangereux</b> dans le cas o? l'auteur du site est 
malicieux car Freenet n'est pas encore capable de filtrer le RSS).
 FProxyToadlet.openRSSForce=${link}Cliquez ici${/link} pour ouvrir le fichier 
en tant que ${mime} (ceci ${bold}peut ?tre dangereux${/bold} avec IE7 et FF2).
+FProxyToadlet.opennet=G?rer les connexions aux Inconnus
+FProxyToadlet.opennetTitle=Inconnus
 FProxyToadlet.options=Les choix possibles sont :
 FProxyToadlet.pathNotFound=Le chemin sp?cifi? n'existe pas.
 FProxyToadlet.pathNotFoundTitle=Chemin non trouv?
@@ -266,7 +279,9 @@
 FetchException.longError.26=Archive red?marr?e
 FetchException.longError.27=Redirection permanente : utilisez la nouvelle URI
 FetchException.longError.28=Pas assez de donn?es trouv?es; des donn?es ont ?t? 
trouv?es mais la redirection pointe dans le vide
+FetchException.longError.29=Type MIME invalide : La cl? n'?tait pas dans la 
liste de types MIME autoris?s par le client
 FetchException.longError.3=Ne sait pas quoi faire avec les m?ta-donn?es
+FetchException.longError.30=Cette requ?te a ?t? interrompu car le noeud a 
r?cemment re?u une requ?te pour cette cl? et que cette requ?te avait ?chou?.
 FetchException.longError.4=L'analyse des m?ta-donn?es a ?chou?
 FetchException.longError.5=L'extraction du fichier de l'achive a ?chou?
 FetchException.longError.6=Le d?codage d'un bloc a ?chou?
@@ -294,13 +309,55 @@
 FetchException.shortError.26=Archive relanc?e
 FetchException.shortError.27=Nouvelle URI
 FetchException.shortError.28=Aucune donn?e trouv?e
+FetchException.shortError.29=Type MIME invalide
 FetchException.shortError.3=M?ta-donn?e inconnue
+FetchException.shortError.30=Donn?e introuvable (?chec r?cent)
 FetchException.shortError.4=M?ta-donn?e invalide
 FetchException.shortError.5=Echec de l'archive
 FetchException.shortError.6=Erreur de d?codage de bloc
 FetchException.shortError.7=Trop de niveaux de m?ta-donn?es
 FetchException.shortError.8=Trop de red?marrages
 FetchException.shortError.9=Trop de r?cursivit?
+FileOffer.acceptTransferButton=Accepter le transfert
+FileOffer.askUserTitle=Transfert de fichier direct
+FileOffer.commentLabel=Commentaire :
+FileOffer.failedReceiveHeader=Le transfert du fichier ${filename} de ${node} a 
?chou?.
+FileOffer.failedReceiveTitle=La r?ception a ?chou?
+FileOffer.fileLabel=Fichier :
+FileOffer.mimeLabel=Type MIME :
+FileOffer.offeredFileHeader=Le noeud ${name} vous propose un transfert de 
fichier :
+FileOffer.rejectTransferButton=Refuser le transfert
+FileOffer.senderLabel=Exp?diteur :
+FileOffer.sizeLabel=Taille :
+FileOffer.succeededReceiveHeader=Le transfert du fichier ${filename} de 
${node} a r?ussi.
+FileOffer.succeededReceiveTitle=Fichier re?u
+FirstTimeWizardToadlet.bandwidthLimit=Limites de bande passante
+FirstTimeWizardToadlet.bandwidthLimitLong=Veuillez choisir le type et la 
vitesse de votre connexion dans la liste ci-dessous.
+FirstTimeWizardToadlet.chooseNodeName=Vous devez choisir un nom !
+FirstTimeWizardToadlet.chooseNodeNameLong=Veuillez choisir un nom pour votre 
noeud. Ce nom ne sera visible que des noeuds auxquels vous vous connecterez. 
Nous recommandons de mettre un nom ou un pseudo IRC, ?ventuellement des 
informations permettant de vous contacter en cas de probl?me ("Thomas Durand 
<tom93 at hotmail.com>).
+FirstTimeWizardToadlet.congratz=Bienvenue ? bord !
+FirstTimeWizardToadlet.congratzLong=F?licitations, la configuration de votre 
noeud est termin?e. Vous pouvez modifier cette configuration ? tout moment en 
cliquant sur le lien "Configuration" dans le menu de gauche. Bienvenue sur 
Freenet.
+FirstTimeWizardToadlet.connectToStrangers=Se connecter aux Inconnus ?
+FirstTimeWizardToadlet.connectToStrangersLong=Si vous laissez votre noeud se 
connecter aux Inconnus, vous serez moins en s?curit?, n'importe qui pourra 
savoir que vous utilisez Freenet et tous les vilains pouront se connecter ? 
votre noeud. Si vous ne le faites pas, vous devrez vous connecter manuellement 
? au moins trois Amis (des gens que vous connaissez d?ja) qui utilisent Freenet.
+FirstTimeWizardToadlet.continue=Continuer
+FirstTimeWizardToadlet.datastoreSize=Taille du store
+FirstTimeWizardToadlet.datastoreSizeLong=Veuillez chosir une taille pour votre 
datastore. Le datastore sert ? stocker les donn?es du r?seau. Plus vous stockez 
de donn?es, plus vous aidez la communaut? et plus votre noeud sera rapide.
+FirstTimeWizardToadlet.enableOpennet=Se connecter automatiquement aux noeuds 
Inconnus ?
+FirstTimeWizardToadlet.homepageTitle=Assistant premiers pas sur Freenet !
+FirstTimeWizardToadlet.iDoTrust=Faire confiance aux personnes connect?s par 
${interface} (${ip}) ?
+FirstTimeWizardToadlet.isNetworkTrusted=Faire confiance au r?seau local ?
+FirstTimeWizardToadlet.isNetworkTrustedLong=Faire confiance au r?seau local ? 
Si vous r?pondez oui, tous les services de notre noeud seront accessibles 
depuis ce r?seau. Vous pourrez contr?ler pr?cisemment les droits d'acc?s depuis 
la page de configuration.
+FirstTimeWizardToadlet.noNetworkIF=Pas de carte r?seau suppl?mentaire trouv?e
+FirstTimeWizardToadlet.noNetworkIFLong=Freenet n'a pas trouv? d'autre carte 
r?seau. Il consid?rera que vous vous y connectez seulement depuis votre 
ordinateur.
+FirstTimeWizardToadlet.skipWizard=Je ne suis pas un d?butant, passer 
l'assistant !
+FirstTimeWizardToadlet.step1Title=Assistant premiers pas sur Freenet ! - 
Choisissez le nom de votre noeud
+FirstTimeWizardToadlet.step2Title=Assistant premiers pas sur Freenet ! - 
Limites de bande-passante
+FirstTimeWizardToadlet.step3Title=Assistant premiers pas sur Freenet ! - 
Taille du store
+FirstTimeWizardToadlet.step4Title=Assistant premiers pas sur Freenet ! - 
Configuration r?seau
+FirstTimeWizardToadlet.step5Title=Assistant premiers pas sur Freenet ! - 
F?licitations, votre noeud est configur?
+FirstTimeWizardToadlet.step6Title=Assistant premiers pas sur Freenet ! - 
F?licitations, votre noeud est configur?.
+FirstTimeWizardToadlet.welcomeInfoboxContent1=Bienvenue dans l'assistant 
premiers pas sur Freenet . Cet outil vous permettra de configurer rapidement et 
facilement votre noeud. Veuillez
+FirstTimeWizardToadlet.welcomeInfoboxTitle=Bienvenue sur l'assistant premiers 
pas sur Freenet !
 GIFFilter.invalidHeader=Ce fichier ne contient pas d'en-t?te GIF valide.
 GIFFilter.invalidHeaderTitle=En-t?te invalide
 GIFFilter.notGif=Ce fichier n'est pas un GIF. Il peut s'agir de n'importe quoi 
et votre navigateur risque de faire n'importe quoi avec. Nous l'avons donc 
bloqu?.
@@ -343,6 +400,7 @@
 InsertException.longError.1=L'appelant a fourni une URI inutilisable "
 InsertException.longError.10=Annul? par l'utilisateur
 InsertException.longError.11=Cha?ne en trop (surement un  '/') utilis?e dans 
l'URI
+InsertException.longError.12=Erreur de format binaire
 InsertException.longError.2=Erreur interne : disque plein ou probl?me de 
permissions ?
 InsertException.longError.3=Erreur interne
 InsertException.longError.4=Un noeud sur le trajet retour ne r?pond pas ou est 
surcharg?
@@ -354,6 +412,7 @@
 InsertException.shortError.1=URI invalide
 InsertException.shortError.10=Annul?
 InsertException.shortError.11=La cl? contient une cha?ne en trop
+InsertException.shortError.12=Erreur de format binaire
 InsertException.shortError.2=Erreur de fichiers temporaires
 InsertException.shortError.3=Erreur interne
 InsertException.shortError.4=D?lai d?pass? ou surcharge
@@ -405,7 +464,7 @@
 LogConfigHandler.rotationIntervalLong=Intervalle de rotation des journaux. Les 
deux derniers journaux (l'actuel et le pr?c?dent) sont conserv?s, on conserve 
?galement maxZippedLogsSize journaux pr?c?dents, compress?s.
 LoggerHook.unrecognizedPriority=Nom de priorit? non reconnu : ${name}.
 LongOption.parseError=La valeur entr?e ne peut ?tre utilis?e comme un entier 
sur 64 bits : ${val}
-MeaningfulNodeNameUserAlert.noNodeNick=Il semble que votre noeud n'ait pas de 
nom. Mettre une adresse mail ou un surnom IRC est g?n?ralement une bonne ?a 
aidera vos amis ? identifier votre node (le nom n'est affich? qu'aux amis, pas 
aux noeuds connect?s par l'opennet).
+MeaningfulNodeNameUserAlert.noNodeNick=Il semble que votre noeud n'ait pas de 
nom. Mettre une adresse mail ou un surnom IRC est g?n?ralement une bonne id?e, 
?a aidera vos Amis ? identifier votre noeud (ce nom n'est affich? qu'aux Amis, 
pas aux Inconnus connect?s par l'Opennet).
 MeaningfulNodeNameUserAlert.noNodeNickTitle=Votre noeud n'a pas de nom.
 N2NTMToadlet.composingMessageLabel=Message inter-noeud envoy? aux noeuds 
suivants :
 N2NTMToadlet.delayed=Repouss? : Le message sera envoy? prochainement
@@ -413,13 +472,14 @@
 N2NTMToadlet.failed=Message non envoy? : ce noeud n'est pas connect?
 N2NTMToadlet.failedTitle=Echou?
 N2NTMToadlet.friends=Amis
+N2NTMToadlet.noSuchFileOrCannotRead=L'envoi du fichier a ?chou? : soit il 
n'existe pas, soit il est illisible.
 N2NTMToadlet.peerName=Nom du noeud
 N2NTMToadlet.peerNotFoundTitle=Noeud introuvable
 N2NTMToadlet.peerNotFoundWithHash=Le noeud ayant pour hash \u201c${hash}\u201d 
est introuvable.
 N2NTMToadlet.processingSend=Envoi du message inter-noeud en cours
 N2NTMToadlet.queued=En attente : Ce pair n'est pas connect?, le message est en 
attente jusqu'? ce qu'il se connecte
 N2NTMToadlet.queuedTitle=En attente
-N2NTMToadlet.returnToFriends=Retour ? la liste des amis
+N2NTMToadlet.returnToFriends=Retour ? la liste des Amis
 N2NTMToadlet.sendMessage=Envoyer le message inter-noeud
 N2NTMToadlet.sendMessageShort=Envoyer le message
 N2NTMToadlet.sendStatus=Etat de l'envoi de message inter-noeud
@@ -431,6 +491,8 @@
 N2NTMUserAlert.header=De : ${from} (r?dig? ${composed} | envoy? ${sent} | re?u 
${received})
 N2NTMUserAlert.reply=R?pondre
 N2NTMUserAlert.title=Message inter-noeud ${number} de ${peername} (${peer})
+Node.alwaysAllowLocalAddresses=Se connecter aux adresses locales ?
+Node.alwaysAllowLocalAddressesLong=Si activ?, le noeud essaiera de se 
connecter aux autres en utilisant leur adresse locale (localhost, LAN) en plus 
de leur adresse publique. Si vous ne l'activez pas, vous pourrez quand m?me 
l'activer pour certains de vos Amis (mais pas les Inconnus). Activez ceci si 
vous voulez vous connecter ? des noeuds pr?sents sur votre r?seau local, en 
sachant que des r?f?rences bugg?es peuvent vous amener ? envoyer des paquets 
UDP ? des machines de votre r?seau local.
 Node.bandwidthLimitMustBePositiveOrMinusOne=La limite de bande passante doit 
?tre un entier positif ou ?gal ? -1
 Node.bindTo=Adresse IP o? ?couter
 Node.bindToLong=Adresse IP o? ?couter
@@ -464,9 +526,15 @@
 Node.nodeDir=Dossier du noeud
 Node.nodeDirLong=Dossier o? placer la liste des noeuds
 Node.nodeName=Nom de ce noeud freenet
-Node.nodeNameLong=Nom du noeud. Seuls vos amis pourront le voir.
+Node.nodeNameLong=Nom du noeud. Seuls les noeuds Amis pourront le voir.
+Node.oneConnectionPerIP=N'autoriser qu'une seule connexion par adresse IP ?
+Node.oneConnectionPerIPLong=Ne pas autoriser plus d'une connexion par adresse 
IP ? Cela rendra les choses un peu plus difficile pour un attaquant qui 
essaierait de se connecter plusieurs fois ? vous sous des identiti?s 
diff?rentes, dans le but de nuire ? votre anonymat. Cela ?vite ?galement d'?tre 
connect? en m?me temps par le darknet et par l'opennet ? un m?me noeud.
+Node.opennetEnabled=Activer l'Opennet ?
+Node.opennetEnabledLong=Activer l'Opennet ? Si vous faites cela, votre noeud 
?changera automatiquement ses r?f?rences avec les autres noeuds. Cela implique 
que tout le monde peut savoir que vous poss?dez un noeud Freenet et que 
certaines attaques sont plus faciles. Si vous connaissez suffisamment de 
personnes utilisant Freenet, vous devriez rester en Darknet.
 Node.outBWLimit=Limite d'upload (octet/seconde)
 Node.outBWLimitLong=Limite d'upload (octet/seconde); le noeud ne devrait 
jamais la d?passer
+Node.passOpennetPeersThroughDarknet=Relayer les r?f?rences opennet ? travers 
les connexions darknet ?
+Node.passOpennetPeersThroughDarknetLong=Si activ?, les r?f?rences opennet 
(JAMAIS votre r?f?rence darknet) seront relay?es ? travers nos Amis (connexions 
darknet). Cela permet ? un noeud (le votre ou celui de vos Amis) d'obtenir des 
connexions opennet ? partir de ses connexions darknet. C'est utile car cela 
nous permet de retrouver de nouvelles connexions opennet quand on les a toutes 
perdues suite ? un arr?t prolong? du noeud. Cependant, cela rend l'analyse de 
traffic l?g?rement plus facile ; d?sactivez-le si vous ?tes parano.
 Node.port=Num?ro de port FNP (UDP)
 Node.portLong=Port UDP des communications inter-noeuds (Freenet Node Protocol)
 Node.storeDirectory=Dossier du store
@@ -491,6 +559,9 @@
 NodeClientCore.ignoreTooManyPathComponentsLong=Si activ?, le noeud ne g?n?rera 
plus d'erreurs TOO_MANY_PATH_COMPONENTS quand on lui soumet une URL avec trop 
de noms de dossiers ? la fin (/bla/bla/), inutiles pour r?cup?rer la cl? (par 
exemple, les anciennes CHK avaient souvent des noms de fichiers coll?es ? la 
fin qui ne faisaient pas partie de l'insertion originale ; ?a ne sert plus ? 
rien car on peut ajouter le nom de fichier maintenant). N'activez ceci que si 
vous en avez besoin pour utiliser de vieilles applications ; cette option sera 
retir?e bient?t.
 NodeClientCore.lazyResume=Terminer le chargement des requ?tes persistantes 
apr?s le d?marrage ? (Utilise plus de m?moire)
 NodeClientCore.lazyResumeLong=Le noeud peut charger les requ?tes persistantes 
en attente pendant le d?marrage, ou il peut lire les infos en m?moire et 
relancer les requ?tes apr?s le d?marrage. Le d?marrage est plus rapide mais 
cela consomme plus de m?moire.
+NodeClientCore.maxUSKFetchers=Nombre maximum de r?cup?rateurs d'USK
+NodeClientCore.maxUSKFetchersLong=Nombre maximum de r?cup?rateurs d'USK
+NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=Doit ?tre sup?rieur ? z?ro
 NodeClientCore.movingTempDirOnTheFlyNotSupported=D?placer le dossier 
temporaire ? la vol?e n'est pas possible pour le moment
 NodeClientCore.persistentTempDir=Dossier pour les fichiers temporaires 
persistants
 NodeClientCore.persistentTempDirLong=Dossier o? placer les fichiers 
temporaires persistants
@@ -538,31 +609,46 @@
 NodeUpdateManager.updateFailedTitle=Mise ? jour ?chou?e !
 NodeUpdateManager.updateURI=O? le noeud doit-il chercher ses mises ? jour ?
 NodeUpdateManager.updateURILong=O? le noeud doit il rechercher ses mises ? 
jour ?
+OpennetConnectionsToadlet.fullTitle=${counts} Inconnus connect?s ? ${name}
+OpennetConnectionsToadlet.peersListTitle=Mes noeuds Opennet (Inconnus ajout?s 
par mon noeud)
+OpennetUserAlert.warning=Votre noeud fonctionne en mode Opennet. Il se 
connecte ? des Inconnus et cela veut dire que n'importe qui peut savoir que 
vous utilisez Freenet. Il est plus facile de vous attaquer ou de vous bloquer 
(au niveau d'un firewall national) et vous ne controlez pas ? qui votre noeud 
se connecte. Nous vous recommandons de vous connecter ? des Amis (noeuds de 
confiance appartenant ? des gens que vous connaissez d?j?) ; l'Opennet est 
cens? n'?tre qu'une mesure transitoire en attendant de pouvoir vous connecter ? 
vos Amis. Si vous ne vous connectez qu'? des Amis, et bien qu'il leur soit 
possible de vous attaquer, c'est moins risqu? que si vous vous connectez ? 
n'importe quel vilain appartenant ? une organisation gouvernementale ou pire. 
Notez qu'ajouter un noeud ? votre liste d'Amis n'est utile (pour le routage et 
la s?curit?) que s'il appartient ? quelqu'un que vous connaissez vraiment !
+OpennetUserAlert.warningTitle=Attention : Opennet activ?, votre noeud va se 
connecter ? des Inconnus
 PNGFilter.invalidHeader=Ce fichier n'est pas une image PNG. Il ne contient pas 
d'en-t?te PNG valide. Il peut s'agir de n'importe quoi et votre navigateur 
risque de faire quelque chose de dangereux avec. Nous l'avons donc bloqu?.
 PNGFilter.invalidHeaderTitle=Ceci n'est pas une image PNG - en-t?te invalide
-PeerManagerUserAlert.noConns=Ce noeud ne peut pas fonctionner normalement car 
il n'est pas parvenu ? se connecter ? un seul autre noeud jusqu'ici. Esp?rons 
que certains de vos amis se connecteront bient?t. Sinon, essayez de vous 
connecter ? d'autre noeuds. Il vous faut toujours 3 noeuds connect?s et vous 
devriez essayer d'en avoir 5 ? 10.
+PeerManagerUserAlert.clockProblem=${count} noeuds ne peuvent pas se connecter 
? vous car leur horloge et la votre sont d?call?es de plus de 24 heures. 
Veuillez vous assurer que votre ordinateur est ? l'heure. Une heure erron?e 
g?ne le fonctionnement du noeud et des clients.
+PeerManagerUserAlert.clockProblemTitle=Probl?me d'horloge
+PeerManagerUserAlert.connError=${count} noeud ne peuvent pas se connecter pour 
une raison inconnue, peut-?tre parce que votre noeud est bugg? ou votre 
r?f?rence corrompue.
+PeerManagerUserAlert.connErrorTitle=Impossible de se connecter ? certains 
noeuds
+PeerManagerUserAlert.noConns=Ce noeud ne peut pas fonctionner normalement car 
il n'est pas parvenu ? se connecter ? un seul autre noeud jusqu'ici. Esp?rons 
que certains de vos Amis se connecteront bient?t. Sinon, essayez de vous 
connecter ? d'autre noeuds. Il vous faut toujours 3 noeuds connect?s et vous 
devriez essayer d'en avoir 5 ? 10.
 PeerManagerUserAlert.noConnsTitle=Pas de connection ouverte
 PeerManagerUserAlert.noPeersDarknet=Ce noeud ne peut pas encore fonctionner 
car il n'est connect? ? aucun autre. Id?alement, vous devriez vous connecter ? 
des noeuds appartenant ? des gens que vous connaissez (et en qui vous avez 
confiance, si vous ?tes parano. Sinon, au moins des gens ? qui vous avez d?ja 
parl?). Il vous faut au moins 3 noeuds connect?s ? tous moment, et id?alement 
entre 5 et 10. Vous pourriez vous connecter ? irc.freenode.net, canal 
#freenet-refs pour demander si quelqu'un veut se connecter ? vous, mais 
rappelez-vous que vous ?tes vuln?rable ? ceux ? qui vous ?tes connect?s (C'est 
particuli?rement vrai alors que Freenet 0.7 est tout jeune...). ASSUREZ-VOUS 
QUE L'AUTRE PERSONNE AJOUTE, ELLE AUSSI, VOTRE REFERENCE. LES CONNEXIONS A SENS 
UNIQUE NE FONCTIONNENT PAS.
 PeerManagerUserAlert.noPeersTestnet=Ce noeud ne peut pas encore fonctionner 
car il n'est connect? ? aucun autre. Id?alement, vous devriez vous connecter ? 
des noeuds appartenant ? des gens que vous connaissez (et en qui vous avez 
confiance, si vous ?tes parano. Sinon, au moins des gens ? qui vous avez d?ja 
parl?). Il vous faut au moins 3 noeuds connect?s ? tous moment, et id?alement 
entre 5 et 10. Mais comme il s'agit d'un noeud de test, nous vous sugg?rons de 
vous connecter ? irc.freenode.net, canal #freenet-refs pour demander si 
quelqu'un veut se connecter ? vous.
 PeerManagerUserAlert.noPeersTitle=Aucun ami trouv?
 PeerManagerUserAlert.oneConn=Ce noeud n'a qu'une seule connexion. Les 
performances vont s'en ressentir et vous n'aurez aucun anonymat ou moyen de 
nier si cette personne vous espionne. Votre noeud est attach? au r?seau comme 
une "feuille" et ne contribue pas ? la sant? du r?seau. Essayez d'avoir au 
moins 3 (id?alement entre 5 et 10) noeuds connect?s ? tout moment.
 PeerManagerUserAlert.onlyFewConnsTitle=Seulement ${count} connexion(s) 
ouverte(s)
-PeerManagerUserAlert.tooHighBwlimitDelayTime=Le noeud est oblig? d'attendre 
trop longtemps avant de pouvoir communiquer (${delay}ms > ${max}ms). Augmentez 
la limite d'upload et/ou retirez des amis pour am?liorer la situation.
+PeerManagerUserAlert.tooHighBwlimitDelayTime=Le noeud est oblig? d'attendre 
trop longtemps avant de pouvoir communiquer (${delay}ms > ${max}ms). Augmentez 
la limite d'upload et/ou retirez des connexions pour am?liorer la situation.
 PeerManagerUserAlert.tooHighBwlimitDelayTimeTitle=bwlimitDelayTime trop ?lec?
-PeerManagerUserAlert.tooHighPingTime=Ce noeud a du mal ? communiquer avec les 
autres assez rapidement (${ping}ms > ${max}ms). Augmentez la limite d'upload 
et/ou supprimez des amis pour am?liorer la situation.
+PeerManagerUserAlert.tooHighPingTime=Ce noeud a du mal ? communiquer avec les 
autres assez rapidement (${ping}ms > ${max}ms). Augmentez la limite d'upload 
et/ou supprimez des connexions pour am?liorer la situation.
 PeerManagerUserAlert.tooHighPingTimeTitle=Ping moyen trop ?lev?
 PeerManagerUserAlert.tooManyConns=Ce noeud a trop de connexions (${count} > 
${max}). Ajouter plein de noeuds automatiquement viole la th?orie du "petit 
monde", nuit au routage et risque de produire des "points individuels de 
d?faillance".
 PeerManagerUserAlert.tooManyConnsTitle=Trop de connexions ouvertes
-PeerManagerUserAlert.tooManyDisconnected=Ce noeud a trop d'amis d?connect?s 
(${count} > ${max}). Cela a une impact sensible sur les performances car les 
amis d?connect?s consomment un peu de m?moire et de processeur. Vous devriez 
peut-?tre "nettoyer" votre liste d'amis. Id?alement, vous ne devriez vous 
connecter qu'? des gens que vous connaissez. M?me si vous ne le faites pas, 
ajouter automatiquement des tas de noeuds est mauvais car cela nuit au 
fonctionnement du r?seau.
+PeerManagerUserAlert.tooManyDisconnected=Ce noeud a trop de noeuds d?connect?s 
(${count} > ${max}). Cela a un impact sensible sur les performances car les 
noeuds d?connect?s consomment un peu de m?moire et de processeur. Vous devriez 
peut-?tre "nettoyer" votre liste d'Amis. Id?alement, vous ne devriez vous 
connecter qu'? des gens que vous connaissez. M?me si vous ne le faites pas, 
ajouter automatiquement des tas de noeuds est mauvais car cela nuit au 
fonctionnement du r?seau.
 PeerManagerUserAlert.tooManyDisconnectedTitle=Trop de noeuds d?connect?s
 PeerManagerUserAlert.tooManyNeverConnected=De nombreux noeuds ne se sont 
jamais connect?s: ${count}. Vous ne devriez ajouter des noeuds que s'ils ont 
?galement ajout? votre r?f?rence. Ils ne se connecteront pas sinon.
 PeerManagerUserAlert.tooManyNeverConnectedTitle=De nombreux noeuds ne se sont 
pas encore connect?s
-PeerManagerUserAlert.tooManyNeverConnectedWithLink=Beaucoup de vos amis ne se 
sont m?me jamais connet?s : ${count}. Vous ne devriez ajouter des amis que si 
vous savez qu'ils ont ?galement ajout? ${link}votre r?f?rence${/link}. Sinon, 
ils ne se connecteront pas.
-PeerManagerUserAlert.tooManyPeers=Ce noeud a trop d'amis (${count} > ${max}). 
Il n'est pas recommand? de faire fonctionner un "super-noeud" avec ajout d'amis 
automatiques ; cela ne produit pas une topologie de type "petit monde". Cela va 
?galement impacter les performances car les noeuds non connect?s consomment de 
la bande passante et du CPU. Vous devriez "nettoyer" votre liste d'amis.
-PeerManagerUserAlert.tooManyPeersTitle=Trop d'amis
-PeerManagerUserAlert.tooOldNeverConnectedPeers=Un ou plusieurs noeuds amis ne 
se sont pas connect?s dans les deux semaines suivant leur ajout. Vous devriez 
les supprimer car ils affectent l?g?rement les performances (votre noeud g?che 
des paquets en essayant de parler ? des noeuds qui ne sont pas l?).
+PeerManagerUserAlert.tooManyNeverConnectedWithLink=Beaucoup de vos Amis ne se 
sont m?me jamais connect?s : ${count}. Vous ne devriez ajouter des Amis que si 
vous savez qu'ils ont ?galement ajout? ${link}votre r?f?rence${/link}. Sinon, 
ils ne se connecteront pas.
+PeerManagerUserAlert.tooManyPeers=Ce noeud a trop d'Amis (${count} > ${max}). 
Il n'est pas recommand? de faire fonctionner un "super-noeud" avec ajout d'Amis 
automatiques ; cela ne produit pas une topologie de type "petit monde". Cela va 
?galement impacter les performances car les noeuds non connect?s consomment de 
la bande passante et du CPU. Vous devriez "nettoyer" votre liste d'Amis.
+PeerManagerUserAlert.tooManyPeersTitle=Trop de noeuds connect?s
+PeerManagerUserAlert.tooOldNeverConnectedPeers=Un ou plusieurs noeuds Amis ne 
se sont pas connect?s dans les deux semaines suivant leur ajout. Vous devriez 
les supprimer car ils affectent l?g?rement les performances (votre noeud g?che 
des paquets en essayant de parler ? des noeuds qui ne sont pas l?).
 PeerManagerUserAlert.tooOldNeverConnectedPeersTitle=Noeuds jamais connect?s 
depuis trop longtemps
 PeerManagerUserAlert.twoConns=Ce noeud n'a que deux connexions. Les 
performances et la s?curit? ne seront pas tr?s bons et votre noeud ne fait 
aucun routage pour les autres. Votre noeud se comporte comme un "lien" dans le 
r?seau et ne contribue pas (beaucoup) ? la sant? du r?seau. Essayez d'avoir au 
moins 3 (id?alement entre 5 et 10) noeuds connect?s ? tout moment.
+PeersSayKeyBlownAlert.connectedSayBlownLabel=Ces noeuds disent que la cl? a 
?t? compromise (nous essayons de r?cup?rer le certificat de r?vocation) :
+PeersSayKeyBlownAlert.disconnectedSayBlownLabel=Ces noeuds nous ont dit qua la 
cl? ?tait compromise mais sont d?connect?s depuis. Impossible de r?cup?rer le 
certificat de r?vocation :
+PeersSayKeyBlownAlert.failedFetch=Votre noeud n'est pas parvenu ? t?l?charger 
le certificat de r?vocation. Cela peut venir d'une attaque contre vous visant ? 
vous faire installer une mise ? jour frauduleuse, ou alors que votre noeud ment 
? propos de la validit? de la cl?. Veuillez contacter un d?veloppeur ou un 
autre utilisateur de Freenet afin de tirer les choses au clair.
+PeersSayKeyBlownAlert.failedTransferSayBlownLabel=Ces noeuds pr?tendent que la 
cl? est compromise mais sont incapables de pr?senter le certificat de 
r?vocation :
+PeersSayKeyBlownAlert.fetching=Votre noeud essaie de r?cup?rer le certificat 
de r?vocation pour avoir plus de d?tails.
+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.titleWithCount=${count} noeud(s) affirme(nt) que la cl? 
de mise ? jour automatique est compromise !
 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
@@ -657,6 +743,7 @@
 QueueToadlet.persistence=Persistance
 QueueToadlet.persistenceForever=toujours
 QueueToadlet.persistenceNone=aucune
+QueueToadlet.persistenceReboot=Red?marrer
 QueueToadlet.persistenceRebootr=Red?marrer
 QueueToadlet.pleaseEnableFCP=Vous devez activer les serveur FCP pour acc?der ? 
cette page
 QueueToadlet.priority=Priorit?
@@ -671,6 +758,7 @@
 QueueToadlet.progressbarAccurate=Cette valeur est pr?cise
 QueueToadlet.progressbarNotAccurate=Cette valeur risque de changer car le 
traitement du fichier n'est pas termin?e
 QueueToadlet.reason=Raison
+QueueToadlet.remove=Supprimer
 QueueToadlet.requestNavigation=Parcours des requ?tes
 QueueToadlet.restart=Red?marrer
 QueueToadlet.size=Taille
@@ -704,6 +792,8 @@
 SimpleToadletServer.cssOverrideCantRead=Le fichier CSS personnalis? est 
introuvable : ${filename}
 SimpleToadletServer.cssOverrideLong=Ce param?tre vous permet d'utiliser un CSS 
personnalis?. ATTENTION : les CSS peuvent ?tre dangereux et ne sont pas filtr?s 
! Utilisez-le ? vos risques et p?rils. (vous devriez contacter devl at 
freenetproject pour qu'il soit inclus dans la distribution principale ;) )
 SimpleToadletServer.cssOverrideNotInUploads=Vous de pouvez pas configurer ce 
param?tre : "${filename} n'est pas un r?pertoire d'o? l'upload est autoris? !
+SimpleToadletServer.doRobots=Exclure les robots dans robots.txt ?
+SimpleToadletServer.doRobotsLong=Pr?senter un fichier robots.txt disant ? 
Google, aux spiders et ? wget d'aller voir ailleurs
 SimpleToadletServer.enableJS=Autoriser FProxy ? utiliser du Javascript ?
 SimpleToadletServer.enableJSLong=FProxy doit-il utiliser le Javascript ou non. 
Ce param?tre devrait ?tre sur faux dans la plupart des cas. Les freesites ne 
pourront pas utiliser de Javascript, m?me si ceci est activ?.
 SimpleToadletServer.enabled=Activer FProxy ?
@@ -734,7 +824,7 @@
 StatisticsToadlet.osVersion=Version de l'OS : ${version}
 StatisticsToadlet.outputRate=Envoi : ${rate}/seconde (sur ${max}/seconde)
 StatisticsToadlet.payloadOutput=Envoi utile : ${total} (${rate}/seconde) 
(${percent}%)
-StatisticsToadlet.peerStatsTitle=Statistiques des amis
+StatisticsToadlet.peerStatsTitle=Statistiques des noeuds
 StatisticsToadlet.threadDumpButton=G?n?rer un dump de processus
 StatisticsToadlet.threads=Processus en cours : ${running}/${max}
 StatisticsToadlet.totalInput=Total re?u : ${total} (${rate}/seconde)
@@ -759,18 +849,24 @@
 TextModeClientInterfaceServer.enabledLong=Activer le serveur TMCI (interface 
en ligne de commande) ou non
 TextModeClientInterfaceServer.telnetPortNumber=Port telnet
 TextModeClientInterfaceServer.telnetPortNumberLong=Num?ro de port telnet
+TimeSkewDetectedUserAlert.text=Un d?callage horaire a ?t? d?tect? par votre 
noeud. C'est TRES mauvais. Votre noeud ne fonctionnera pas correctement tant 
que ce ne sera pas corrig?. Les causes habituelles sont une mauvaise 
configuration de la mise en veille, du client de synchronisation horaire ou un 
probl?me mat?riel.
+TimeSkewDetectedUserAlert.title=D?callage horaire d?tect? !
 Toadlet.cancel=Annuler
 Toadlet.clickHere=Cliquez ici
+Toadlet.homepage=Page d'accueil
 Toadlet.internalErrorPleaseReport=Erreur interne : merci de pr?venir les 
d?veloppeurs
 Toadlet.internalErrorTitle=Erreur interne
+Toadlet.no=Non
 Toadlet.nodeHomepage=Page d'accueil
 Toadlet.notSupportedTitle=Non support?
 Toadlet.notSupportedWithClass=Votre navigateur a envoy? une requ?te que 
Freenet (${class}) ne comprend pas.
+Toadlet.ok=Ok
 Toadlet.permRedirectWithReason=Redirection permanente : ${reason}
 Toadlet.returnToNodeHomepage=Revenir ? la page d'accueil
 Toadlet.returnToPrevPage=Retour ? la page pr?c?dente
 Toadlet.tempRedirectWithReason=Redirection temporaire : ${reason}
 Toadlet.unauthorized=Vous n'?tes pas autoris? ? acc?der ? cette page.
+Toadlet.yes=Oui
 ToadletContextImpl.cannotParseContentLength=Erreur de content-length : ${error}
 ToadletContextImpl.headersLineTooLong=L'analyse des en-t?tes a rencontr? une 
ligne trop longue
 ToadletContextImpl.methodNotAllowed=La m?thode HTTP n'est pas autoris?e
@@ -861,6 +957,7 @@
 WelcomeToadlet.insertSucceededTitle=Insertion r?ussie
 WelcomeToadlet.insertedTitle=Insertion
 WelcomeToadlet.keyInsertedSuccessfullyWithKeyAndName=La cl? 
${link}${name}${/link} a ?t? ins?r?e avec succ?s.
+WelcomeToadlet.keyRequestLabel=Cl? :
 WelcomeToadlet.messageHeader=Message
 WelcomeToadlet.nodeUpdateConfirm=Etes-vous s?r de vouloir mettre ? jour votre 
noeud Freenet ?
 WelcomeToadlet.nodeUpdateConfirmTitle=Confirmation de mise ? jour

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.it.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.it.properties    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.it.properties    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -22,6 +22,7 @@
 BookmarkEditorToadlet.editBookmarkTitle=Modifica
 BookmarkEditorToadlet.editCategoryTitle=Modifica Categoria
 BookmarkEditorToadlet.error=Errore
+BookmarkEditorToadlet.invalidKey=La chiave Freenet non ? valida.
 BookmarkEditorToadlet.invalidKeyTitle=Chiave non valida
 BookmarkEditorToadlet.invalidKeyWithReason=Chiave Freenet non valida.
 BookmarkEditorToadlet.keyLabel=Chiave :
@@ -34,6 +35,7 @@
 BookmarkEditorToadlet.pasteTitle=Taglia/Incolla
 BookmarkEditorToadlet.save=Salva
 BookmarkEditorToadlet.title=Modifica Preferiti
+BookmarkEditorToadlet.urlDecodeError=URL Decode Error
 BookmarkItem.bookmarkUpdated=Il sito ${name} ? stato aggiornato all'edizione 
${edition}.
 BookmarkItem.bookmarkUpdatedTitle=${name}: link aggiornato
 BookmarkItem.bookmarkUpdatedWithLink=Il Sito ${link}${name}${/link} ? stato 
aggiornato all'edizione ${edition}.
@@ -58,6 +60,7 @@
 ConfigToadlet.appliedTitle=Configurazione Applicata
 ConfigToadlet.apply=Conferma
 ConfigToadlet.configNavTitle=Esplora Configurazione
+ConfigToadlet.contributeTranslation=Contribuisci alla traduzione
 ConfigToadlet.defaultIs=Il valore predefinito per l'opzione di configurazione 
?: '${default}'.
 ConfigToadlet.fullTitle=Configurazione del Nodo Freenet di ${name}
 ConfigToadlet.homepage=Homepage del Nodo
@@ -105,11 +108,16 @@
 DarknetConnectionsToadlet.cantFetchNoderefURL=Non ? stato possibile richiamare 
la referenza di un nodo da ${url}. Riprovare.
 DarknetConnectionsToadlet.cantParseTryAgain=L'analisi del testo non ha 
prodotto la referenza di un nodo: Riprovare.
 DarknetConnectionsToadlet.cantParseWrongEnding=Non ? stato possibile 
analizzare le referenza: L'ultimo rigo dovrebbe contenere solo la parola End, 
esso contiene invece: ${end}
+DarknetConnectionsToadlet.clockProblem=Il tuo orologio e l'orologio del nodo 
differiscono di oltre 24 ore. La connessione ? stata diabilitata perch? questo 
pu? causare problami con l'aggiornamento ed i client.
+DarknetConnectionsToadlet.clockProblemShort=Problema relativo all' orologio
 DarknetConnectionsToadlet.confirmRemoveNode=Si ? certi di voler rimuovere 
"+peerNodes[i].getName()+" ? Rimuovere un nodo che ? stato disconnesso per meno 
di una settimana non consigliabile; meglio sarebbe attendere ancora un po', 
considerando che potrebbe trattarsi di un problema temporaneo, e che ci sono 
utenti che non hanno la possibilit? lasciar girare il proprio nodo giorno e 
notte.
 DarknetConnectionsToadlet.confirmRemoveNodeTitle=Conferma
 DarknetConnectionsToadlet.confirmRemoveNodeWarningTitle=Rimozione Nodo
+DarknetConnectionsToadlet.connError=Connessione fallita (il nodo ha un 'bug'?)
+DarknetConnectionsToadlet.connErrorShort=Errore di Connessione
 DarknetConnectionsToadlet.connected=Connesso: Il nodo ? connesso a questi peer:
 DarknetConnectionsToadlet.connectedShort=Connessi
+DarknetConnectionsToadlet.darknetFnpPort=Darknet FNP: ${port}/UDP (da nodo a 
nodo; questa ? la porta che potrebbe essere necessario forwardare su router)
 DarknetConnectionsToadlet.disabled=Non connesso e disabilitato: L'utente ha 
configurato il nodo in maniera da non connettersi a questo peer.
 DarknetConnectionsToadlet.disabledShort=Disabilitato
 DarknetConnectionsToadlet.enterDescription=Descrizione:
@@ -149,6 +157,7 @@
 DarknetConnectionsToadlet.nodePortsTitle=Porte utilizzate dal nodo
 DarknetConnectionsToadlet.notConnected=Non connesso: Finora non ci sono state 
connessioni ma il nodo cerca continuamente di connettersi
 DarknetConnectionsToadlet.notConnectedShort=Disconnessi
+DarknetConnectionsToadlet.opennetFnpPort=Opennet FNP: ${port}/UDP (da nodo a 
nodo; potrebbe essere necessario forwardare questa porta su router)
 DarknetConnectionsToadlet.pasteReference=Incolla la referenza qui:
 DarknetConnectionsToadlet.privateNote=Nota privata relativa a questo nodo:
 DarknetConnectionsToadlet.privateNoteTitle=Nota Privata
@@ -189,6 +198,7 @@
 FProxyToadlet.errorIsFatal=Questo ? un errore grave (fatal error). Riprovare 
probabilmente non risolver? il problema.
 FProxyToadlet.errorWithReason=Errore: ${error}
 FProxyToadlet.expectedKeyButGot=Invece di una chiave Freenet, ? stato ricevuto:
+FProxyToadlet.expectedMimeType=MIME type che ci si aspettava: ${mime}
 FProxyToadlet.explanationTitle=Spiegazione
 FProxyToadlet.fetchLargeFileAnywayAndDisplay=Apri comunque e mostra il file 
nel browser
 FProxyToadlet.fileInformationTitle=Informazioni sul file
@@ -214,8 +224,11 @@
 FProxyToadlet.openForceDisk=${link}Clicka qui${/link} per forzare il browser a 
scaricare il file su disco.
 FProxyToadlet.openPossRSSAsForceDisk=${link}Clicka qui${/link} per scaricare 
il file su disco (${bold}possibile pericolo${/bold} se si sta usando Firefox 
2.0.0.0; il problema dovrebbe essere stato risolto in 2.0.0.1
 FProxyToadlet.openPossRSSAsPlainText=${link}Clicka qui${/link} per aprire il 
file come testo 'liscio (plain text) (ci? ${bold}pu? rappresentare un 
pericolo${/bold} se si sta usando IE7 o FF2).
+FProxyToadlet.openPossRSSForceDisk=${link}Clicka qui${/link} per cercare di 
costringere il browser a scaricare il file su disco (${bold}Pu? essere 
pericoloso se si sta usando Firefox 2.0.0${/bold} ; il problema dovrebbe essere 
risolto in 2.0.1).
 FProxyToadlet.openRSSAsRSS=${link}Clicka qui${/link} per aprire il file come 
RSS (ci? ${bold}rappresenta un pericolo${/bold} se l'autore del sito ? mal 
intenzionato, perch? il filtraggio di RSS in Freenet non ? ancora stato 
implementato).
 FProxyToadlet.openRSSForce=${link}Clicka qui${/link} per aprire il file come 
${mime} (ci? ${bold}pu? costituire un pericolo${/bold} se si usa IE7 o FF2).
+FProxyToadlet.opennet=gestisci connessioni non fidate
+FProxyToadlet.opennetTitle=Sconosciuti
 FProxyToadlet.options=Opzioni disponibili:
 FProxyToadlet.pathNotFound=Il percorso specificato non esiste.
 FProxyToadlet.pathNotFoundTitle=Percorso non trovato
@@ -283,7 +296,9 @@
 FetchException.longError.26=Archivio riavviato
 FetchException.longError.27=Redirezione permanente: usare la nuova URI
 FetchException.longError.28=Non sono stati trovati dati sufficienti; alcuni 
dati sono stati richiamati ma il redirect potrebbe puntare su localit? non 
valide
+FetchException.longError.29=MIME Type sbagliato: la chiave non era nella lista 
dei MIME type permessi fornita dal client.
 FetchException.longError.3=Il nodo non sa cosa fare dei metadati
+FetchException.longError.30=La richiesta ? stata terminata da un nodo perch? 
esso aveva da poco ricevuto un'altra richiesta per la stessa chiave e quella 
richiesta non era andata a buon fine.
 FetchException.longError.4=Fallimento nell'analisi dei metadati
 FetchException.longError.5=Fallimento durante l'estrazione di file da un 
archivio
 FetchException.longError.6=Non ? stato possibile decodificare un blocco
@@ -311,23 +326,55 @@
 FetchException.shortError.26=Archivio riavviato
 FetchException.shortError.27=Nuova URI
 FetchException.shortError.28=Non sono stati trovati tutti i dati
+FetchException.shortError.29=Errore: MIME type sbagliato
 FetchException.shortError.3=Metadati sconosciuti
+FetchException.shortError.30=Non ? stato possibile trovare i dati richiesti 
(fallimento recente)
 FetchException.shortError.4=Metadati non validi
 FetchException.shortError.5=Fallimento archivio
 FetchException.shortError.6=Errore decodifica block
 FetchException.shortError.7=Troppi livelli di metadati
 FetchException.shortError.8=Troppi riavvii di archivio
 FetchException.shortError.9=Troppa recursione
+FileOffer.acceptTransferButton=Accetta Trasferimento
+FileOffer.askUserTitle=Trasferimento file diretto.
+FileOffer.commentLabel=Commenti:
+FileOffer.failedReceiveHeader=Non ? stato possibile completare il 
trasferimento del file ${filename} da ${node}.
+FileOffer.failedReceiveTitle=Trasferimento fallito
+FileOffer.fileLabel=File:
+FileOffer.mimeLabel=MIME Type:
+FileOffer.offeredFileHeader=Il nodo ${name} offre il file:
+FileOffer.rejectTransferButton=Rifiuta trasferimento
+FileOffer.senderLabel=Mittente:
+FileOffer.sizeLabel=Dimensioni:
+FileOffer.succeededReceiveHeader=Il trasferimento del file ${filename} da 
${node} ? stato completato.
+FileOffer.succeededReceiveTitle=Trasferimento file completato
 FirstTimeWizardToadlet.bandwidthLimit=Limitazioni dell'ampiezza di banda
 FirstTimeWizardToadlet.bandwidthLimitLong=Scegliere il tipo di connessione
+FirstTimeWizardToadlet.chooseNodeName=Il nome del nodo ? obbligatorio!
+FirstTimeWizardToadlet.chooseNodeNameLong=Inserire un nome per il nodo nello 
spazio sottostante. Tale nome sara visibile soltanto ai peer ai quali il nodo ? 
connesso e a nessun altro. Si consiglia di usare il proprio nickname IRC 
possibilmente con qualche contatto in modo da essere reperibili in caso di 
problemi ("Mario Rossi nessuno at niente.com").
+FirstTimeWizardToadlet.congratz=Benvenuto a bordo!
+FirstTimeWizardToadlet.congratzLong=Congratulazioni, la configurazione di base 
del nodo Freenet ? completa. E' possibile cambiare e modificare ognuno dei 
parametri appena impostati usando la pagina "configurazione" che ? 
raggiungibile attraverso il menu sulla sinistra dell'interfaccia. Vi auguriamo 
una piacevole esperienza con Freenet.
+FirstTimeWizardToadlet.connectToStrangers=Connetti con sconosciuti?
+FirstTimeWizardToadlet.connectToStrangersLong=Permettendo connessioni con 
sconosciuti, questo nodo Freenet sar? meno sicuro; chiunque port? determinare 
che un nodo sta girando sul tuo computer e qualsiasi malintenzionato portebbe 
connettersi ad esso. per non permettere che Freenet si connetta a chiunque, 
bisogner? contattare almeno altre tre persone conosciute e fidate che gi? 
stiano usando Freenet e conettersi a loro manualmente.
+FirstTimeWizardToadlet.continue=Continua
 FirstTimeWizardToadlet.datastoreSize=Dimensioni magazzino dati
 FirstTimeWizardToadlet.datastoreSizeLong=Selezionare le dimensione desiderata 
per il file di immagazzinaggio dati
+FirstTimeWizardToadlet.enableOpennet=Connettersi automaticamente a nodi 
gestiti da sconosciuti?
 FirstTimeWizardToadlet.homepageTitle=Utilit? di configurazione automatica
+FirstTimeWizardToadlet.iDoTrust=Possiamo fidarci degli utenti connessi a 
${interface} (${ip}) ?
+FirstTimeWizardToadlet.isNetworkTrusted=La rete locale ? affidabile?
+FirstTimeWizardToadlet.isNetworkTrustedLong=Possiamo fidarci della rete 
locale? Rispondendo di si a questa domanda, tutti i servizii forniti dal nodo 
Freenet saranno pienamente accessibili da chiunque sul network suddetto. E' 
possibile configurare un accesso pi? selettivo attraverso la pagina 
"configurazione" dopo aver completato questo wizard.
+FirstTimeWizardToadlet.noNetworkIF=Non ? stata trovata nessuna interfaccia di 
rete addizionale
+FirstTimeWizardToadlet.noNetworkIFLong=Freenet non ha trovato altre interfacce 
di rete, quindi dar? per scontato che l'utente si connetter? dal computer 
locale e solo da quello.
 FirstTimeWizardToadlet.selectLanguage=Lingua
 FirstTimeWizardToadlet.selectLanguageLong=Selezionare una lingua dalla lista 
qui sotto:
+FirstTimeWizardToadlet.skipWizard=Non sono un novellino, lascia perdere il 
wizard.
 FirstTimeWizardToadlet.step1Title=Configurazione automatica Freenet - 
Scegliere la lingua da utilizzare
 FirstTimeWizardToadlet.step2Title=Configurazione automatica Freenet - Limite 
banda
 FirstTimeWizardToadlet.step3Title=Configurazione automatica di Freenet - 
Dimensioni magazzino dati
+FirstTimeWizardToadlet.step4Title=Freenet first time wizard! - Configurazione 
di rete
+FirstTimeWizardToadlet.step5Title=Freenet first time wizard! - 
Congratulazioni, il nodo ? ora configurato.
+FirstTimeWizardToadlet.step6Title=Configurazione automatica di Freenet - 
Congratulazioni, il tuo nodo ? ora configurato.
 FirstTimeWizardToadlet.welcomeInfoboxContent1=Benvenuti all'utilit? di 
configurazione automatica che vi permetter? di configurare Freenet e cominciare 
ad usarlo, velocemente e facilmente
 FirstTimeWizardToadlet.welcomeInfoboxTitle=Benvenuti all'utilit? di 
configurazione automatica
 FproxyToadlet.dangerousRSSTitle=Contenuto potenzialmente pericoloso (RSS)
@@ -373,6 +420,7 @@
 InsertException.longError.1=Caller ha fornito una URI che non ? possibile 
utilizzare"
 InsertException.longError.10=Annullato dall'utente
 InsertException.longError.11=Meta string (probabilmente '/') usata nella URI
+InsertException.longError.12=Binary blob format error
 InsertException.longError.2=Errore bucket interno: potrebbe trattarsi di 
mancanza di spazio sufficiente su disco rigido o di mancanza di autorizzazione.
 InsertException.longError.3=Errore interno
 InsertException.longError.4=Un nodo 'a valle' (downstream) ? andato in time 
out o ? stato gravemente sovraccaricato
@@ -384,6 +432,7 @@
 InsertException.shortError.1=URI non valida
 InsertException.shortError.10=Annullato
 InsertException.shortError.11=Meta string usata nella chiave
+InsertException.shortError.12=Binary blob format error
 InsertException.shortError.2=Errore nei temp files
 InsertException.shortError.3=Errore Interno
 InsertException.shortError.4=Timeout o sovraccarico
@@ -444,6 +493,7 @@
 N2NTMToadlet.failedTitle=Fallito
 N2NTMToadlet.friends=Amici
 N2NTMToadlet.homepage=Homepage
+N2NTMToadlet.noSuchFileOrCannotRead=Trasferimento fallito:File inesistente o 
illegibile.
 N2NTMToadlet.peerName=Nome del Peer
 N2NTMToadlet.peerNotFoundTitle=Peer non trovato
 N2NTMToadlet.peerNotFoundWithHash=Non ? stato possibile trovare il peer con 
hash code \u201c${hash}\u201d
@@ -464,6 +514,8 @@
 N2NTMUserAlert.header=Da: ${from} (scritto ${composed} | inviato ${sent} | 
ricevuto ${received})
 N2NTMUserAlert.reply=Rispondi
 N2NTMUserAlert.title=Messaggio di testo da nodo a nodo (N2NTM) ${number} da: 
${peername} (${peer})
+Node.alwaysAllowLocalAddresses=Permettere sempre la connessione a nodi via 
indirizzi locali?
+Node.alwaysAllowLocalAddressesLong=Se impostato su "true" (vero) il nodo 
cercher? di connettersi ad altri nodi usando il loro indirizzo locale 
(localhost, LAN) oltre al loro indirizzo IP pubblico. Se questa opzione non e' 
impostata, si pu? comunque abilitarla separatamente per singoli peer darknet 
(ma non per i peer opennet). Impostare questa opzione se ci si vuole connettere 
ad altri nodi sulla stessa rete locale (LAN) o che girano sullo stesso 
computer, e non far caso alle referenze "bogus" ("scr?use") che possono far 
mandare al nodo pacchetti UDP alle macchine sulla rete locale.
 Node.bandwidthLimitMustBePositiveOrMinusOne=Il valore dell'ampiezza di banda 
deve essere positivo o -1
 Node.bindTo=Indirizzo IP collegato
 Node.bindToLong=Indirizzo IP collegato
@@ -498,8 +550,14 @@
 Node.nodeDirLong=Directory contenente relativi al nodo, per esempio la lista 
dei peer
 Node.nodeName=Nickname di questo nodo Freenet
 Node.nodeNameLong=Nickname del nodo. Visibile solo dagli Amici
+Node.oneConnectionPerIP=Limita ad una connessione per indirizzo?
+Node.oneConnectionPerIPLong=Non permettere pi? di una connessione per 
indirizzo? Questo render? leggermente pi? difficile eseguire un attacco 
connettendosi al tuo nodo pi? volte con diverse identit? in modo da dominare il 
tuo routing (instradamento) e rendere pi? facile raccogliere dati con lo 
"harvesting" (lett: "il raccolto", in agricoltura). Abilitare questa opzione 
rende anche impossibile che un peer sia connesso al tuo nodo sia su darknet che 
su opennet.
+Node.opennetEnabled=Abilita supporto per Opennet?
+Node.opennetEnabledLong=Abilita Opennet? Se Opennet ("rete aperta") ? 
abilitato, il nodo scambier? automaticamente referenze on altri nodi. Ma questo 
significa anche che il fatto che sul tuo computer gira un nodo Freenet non sar? 
pi? un segreto, e molti attacchi saranno pi? facili da eseguire. Se conosci 
abbastanza gente che usa Freenet, la miglior cosa da fare ? di continuare ad 
usare darknet.
 Node.outBWLimit=Limite dell'ampiezza di banda in uscita (bytes per secondo)
 Node.outBWLimitLong=Limite "duro" dell' ampiezza di banda in uscita 
(bytes/sec); di solito il nodo non eccede questo limite
+Node.passOpennetPeersThroughDarknet=Trasmettere referenze opennet attraverso 
peer darknet?
+Node.passOpennetPeersThroughDarknetLong=Se impostata su "true" (vero) 
referenze opennet verranno trasmesse attraverso peer darknet (ma MAI la 
referenza del nodo locale). In questo modo un nodo (nodo locale, o i suoi peer) 
pu? ottenere peer opennet dai suoi peer darknet. Questo ? utile perch? permette 
di riconnettersi in caso di perdita dei peer, per esempio dopo una lunga 
disconnessione, ma rende l'analisi del traffico leggermente pi? facile, quindi 
chi ? paranoico dovrebbe disabilitare questa opzione.
 Node.port=Numero della porta FNP (UDP)
 Node.portLong=Porta UDP per le comunicazioni da nodo a nodo (Freenet Node 
Protocol)
 Node.storeDirectory=Directory dello store
@@ -574,8 +632,16 @@
 NodeUpdateManager.updateFailedTitle=Aggiornamento Fallito!
 NodeUpdateManager.updateURI=Dove cercare aggiornamenti?
 NodeUpdateManager.updateURILong=Dove cercare aggiornamenti?
+OpennetConnectionsToadlet.fullTitle=${counts} Sconosciuti (Peers non Fid?ti) 
di ${name}
+OpennetConnectionsToadlet.peersListTitle=I Miei Opennet Peer (nodi gestiti da 
sconosciuti, aggiunti dal nodo)
+OpennetUserAlert.warning=Il modo promiscuo ? attualmente attivato. Ci? 
significa che esso si connetter? con Sconosiuti, e quindi che chiunque potr? 
facilmente determinare che l'utente titolare del tale abbonamento sta usando 
Freenet. Molti attacchi vengono facilitati da questa modalit?, bloccare il nodo 
(per esempio a un firewall nazionale) diventa molto pi? facile, ed intoltre 
l'utente non ha controllo su chi si connette al suo nodo. Si raccomanda 
vivamente di procurarsi qualce connessione ad Amici (nodi fidati, gestiti da 
persone conosciute); il modo promiscuo va considerato una misura temporanea da 
utilizzarsi finch? non ci si potr? connettere ad Amici. Connettendosi 
esclusivamente a nodi gestiti da persone conosciute, pur non essendo impossibli 
degli attacchi da parte loro, risulter? comunque meno probabile l'esposizione 
ad agenzie governative (tipo servizi segreti o che so io) o altri "cattivi". 
Noa che aggiungendo un peer alla sezione Amici non cambia molto la situazione a 
meno che tale nodo non sia gestito da qualcuno di consciuto e di cui ci si fida 
(per ragioni sia di routing [instradamento] che di sicurezza)!
+OpennetUserAlert.warningTitle=Avvertenza: Modo Promiscuo Attivato: Il nodo si 
connetter? a degli sconosciuti
 PNGFilter.invalidHeader=Il file che si sta cercando di richiamare non e' un 
PNG: esso non include un header PNG valido. Potrebbe trattarsi di un file in 
altro formato e il browser potrebbe fare qualcosa di pericoloso a causa della 
confusione creata dalla mancata corrispondenza; il file ? stato pertanto 
bloccato.
 PNGFilter.invalidHeaderTitle=Header PNG non valido
+PeerManagerUserAlert.clockProblem=${count} dei tuoi peer non riescono a 
connettersi perch? il loro orologio di sistema differisce dal tuo di oltre 24 
ore. Verifica che l'orologio del computer sia regolato sull'ora esatta. 
Regolazioni inaccurate dell'orologio causano molti problemi nel funzionamento 
dei meccanismi tra nodo e client.
+PeerManagerUserAlert.clockProblemTitle=Problema relativo all'orologio.
+PeerManagerUserAlert.connError=${count} dei tupi peer non riescono a 
connettersi per motivi sconosciuti, forse per causa di 'bug' nel nodo o di una 
referenza di un nodo che e' corrotta.
+PeerManagerUserAlert.connErrorTitle=Alcuni peer non riescono a connettersi
 PeerManagerUserAlert.noConns=Non ? stato finora possibile connettersi ad alcun 
nodo. Forse qualcuno dei peer si connettera entro breve. in caso contrario sar? 
necessario aggiungere altri peer; c'e' bisogno di almeno tre peer connessi in 
ogni momento, meglio 5-10.
 PeerManagerUserAlert.noConnsTitle=Non ci sono connessioni aperte
 PeerManagerUserAlert.noPeersDarknet=Questo nodo non ha alcun peer al quale 
connettersi e non pu? quindi funzionare normalmente. In teoria ci si dovrebbe 
connettere esclusivamente a persone che si conosce (per i paranoici: persone di 
cui ci si fida, per i non paranoici: persone con le quali si ha parlato). Per 
un corretto funzionamento c'e' bisogno di almeno tre peer connessi in ogni 
momento, idealmente 5-10. Ci si pu? connettere a irc.freenode.net canale 
#freenet-refs e chiedere chi vuole connettersi, ma ? bene tenere presente che 
si ? vulnerabili per i proprii peer.. (Speciamente in queste prime versioni 
alfa di Freenet 0.7...) VERIFICARE CHE L'ATRA PARE AGGIUNGA LA REFERENZA ALLA 
SUA LISTA: LE CONNESSIONI "A SENSO UNICO" NON FUNZIONANO!
@@ -599,6 +665,13 @@
 PeerManagerUserAlert.tooOldNeverConnectedPeers=Uno o pi? peer non si sono mai 
connessi nelle due settimane trascorse da quando sono stati aggiunti.  E' da 
prendere in considerazione l'idea di rimuoverli in quanto essi stanno 
marginalmente compromettendo il rendimento (pacchetti sprecati cencando di 
comunicare con nodi che non ci sono).
 PeerManagerUserAlert.tooOldNeverConnectedPeersTitle=Peer mai connesso/i troopo 
vecchio/i
 PeerManagerUserAlert.twoConns=Il nodo ha solo due connessioni. Rendimento e 
sicurezza risulteranno di qualit? inferiore, e non sara possibile provvedere 
instradamento (routing) per altri nodi. Il nodo ? collegato al network come un 
"anello di catena" e non contribuisce (molto) alla salute generale del network 
stesso. Per un corretto funzionamento del nodo, ? necessario che almeno tre 
peers (e idealmente 5-10) siano connessi in ogni momento.
+PeersSayKeyBlownAlert.connectedSayBlownLabel=I seguenti nodi hanno rilevato un 
problema con la chiave  (si cerca attualmente di ottenere il certificato di 
revoca da loro):
+PeersSayKeyBlownAlert.disconnectedSayBlownLabel=I seguenti nodi si sono 
disconnessi dopo aver comunicato che la chiave ? saltata, quindi non ? stato 
possibile richiamare il certificato di revoca:
+PeersSayKeyBlownAlert.failedFetch=Non ? stato possibile scaricare il 
certificato di revoca. Le possibili cause di tale evento includono la 
possibilit? di un attacco sul nodo locale volto a fargli scaricare ed 
installare un aggiornamento nonostante la chiave sia saltata, o la possibilit? 
che dei nodi stiano "mentendo" circa la chiave di revoca. Si prega di 
contattare gli sviluppatori di Freenet per cercare di mettere ordine in questo 
casino.
+PeersSayKeyBlownAlert.failedTransferSayBlownLabel=Questi nodi hanno comunicato 
che la chiave di revoca ? saltata ma poi non hanno completato il trasferimento 
del certificato di revoca:
+PeersSayKeyBlownAlert.fetching=Il nodo sta scaricando il certificato di revoca 
contentnte spiegazioni pi? dettagliate.
+PeersSayKeyBlownAlert.intro=Uno o pi? peers ci comunicano che la chiave di 
revoca per l'aggiornamento automatico ? saltata. Questo significa che qualcuno 
potrebbe essere venuto a conoscenza della chiave privata del sistema di 
autoaggiornamento, cosa che potrebbe far eseguire al nodo il codice che 
l'attaccante volesse fargli eseguire (se l'aggiornamento venisse eseguito): per 
prevenire tale eventualit?, il sistema di autoaggiornamento ? stato 
disabilitato. E' anche possibile che dei peers stiano deliberatamente mentendo 
a proposito della chiave di revoca.
+PeersSayKeyBlownAlert.titleWithCount=chiave di Auto-aggiornamento saltata, 
evento confermato da ${count} peer!
 PluginManager.cannotSetOnceLoaded=Una volta caricata, non ? pi? possibile 
impostare la lista dei plugin
 PluginManager.loadedOnStartup=Plugin da caricare all'avvio
 PluginManager.loadedOnStartupLong=Classpath, nome e locazione dei plugin da 
caricare all'avvio
@@ -698,7 +771,7 @@
 QueueToadlet.persistence=Persistenza
 QueueToadlet.persistenceForever=illimitata
 QueueToadlet.persistenceNone=nessuna
-QueueToadlet.persistenceRebootr=reboot
+QueueToadlet.persistenceReboot=reboot
 QueueToadlet.pleaseEnableFCP=Per accedere a questa pagina ? necessario 
abilitare il server FCP
 QueueToadlet.priority=Priorit?
 QueueToadlet.priority0=urgenza estrema
@@ -712,6 +785,7 @@
 QueueToadlet.progressbarAccurate=Questo valore e' accurato
 QueueToadlet.progressbarNotAccurate=I valori possono essere imprecisi perch? 
il processo di download del file non e' stato finalizzato
 QueueToadlet.reason=Motivo
+QueueToadlet.remove=Elimina
 QueueToadlet.requestNavigation=Esplora Richieste
 QueueToadlet.restart=Riavvia
 QueueToadlet.size=Dimensioni
@@ -748,6 +822,8 @@
 SimpleToadletServer.cssOverrideCantRead=Non ? stato possibile leggere il file 
di sovrascrittura CSS fornito: ${filename}
 SimpleToadletServer.cssOverrideLong=Questa impostazione permette di utilizzare 
un CSS personalizzato invece di quello normalmente usato. AVVERTENZA: i CSS 
possono essere pericolosi, e *non* vengono filtrati: utilizzare a proprio 
rischio. Per includerli nella distribuzione principale e-mail devl at 
freenetroject.org
 SimpleToadletServer.cssOverrideNotInUploads=Non ? possibile usare questa 
impostazione: "${filename} non ? una directory dalla quale sono permessi gli 
upload.
+SimpleToadletServer.doRobots=Escludere robots via robots.txt?
+SimpleToadletServer.doRobotsLong=Pubblica /robots.txt che dice a persone, 
Google, spiders, wget, ecc. di lasciarci in pace.
 SimpleToadletServer.enableJS=Permetti a FProxy di usare Javascript
 SimpleToadletServer.enableJSLong=Determina se FProxy pu? o meno fare uso di 
Javascript. Questa impostazione di solito va tenuta su 'false'. Nota che i 
freesite non fanno uso di javascript nemmeno se qui abilitata.
 SimpleToadletServer.enabled=Abilita FProxy
@@ -803,20 +879,25 @@
 TextModeClientInterfaceServer.enabledLong=Abilita TMCI
 TextModeClientInterfaceServer.telnetPortNumber=Porta telnet
 TextModeClientInterfaceServer.telnetPortNumberLong=Numero della porta telnet
+TimeSkewDetectedUserAlert.text=Una discepanza temporale ? stata rilevata dal 
nodo. Questo ? un inconveniente grave, il nodo non potr? funzionare 
correttamente finch? non vi si sar? ovviato. Tra le cause pi? comuni, la 
modalit? powersafe mal configurata, cattiva sincronizzazione tra i clients del 
network, hardware bugs.
+TimeSkewDetectedUserAlert.title=Discrepanza temporale!
 Toadlet.cancel=Cancella
 Toadlet.clickHere=Clicka qui
 Toadlet.homepage=Homepage
 Toadlet.internalErrorPleaseReport=Errore interno: se possibile, riportarlo
 Toadlet.internalErrorTitle=Errore Interno
+Toadlet.no=No
 Toadlet.nodeHomepage=Homepage del Nodo
 Toadlet.notSupportedTitle=Non Supportato
 Toadlet.notSupportedWithClass=Il browser ha mandato una richiesta che Freenet 
(${class}) non capisce.
+Toadlet.ok=Ok
 Toadlet.permRedirectWithReason=Redirect permanente: ${reason}
 Toadlet.returnToHomepage=Torna alla homepage del nodo
 Toadlet.returnToNodeHomepage=Torna alla homepage del nodo
 Toadlet.returnToPrevPage=Torna alla pagina precedente
 Toadlet.tempRedirectWithReason=Redirect temporaneo: ${reason}
 Toadlet.unauthorized=L'accesso a questa pagina ? interdetto.
+Toadlet.yes=S?
 ToadletContextImpl.cannotParseContentLength=errore di analisi content-length: 
${error}
 ToadletContextImpl.headersLineTooLong=Un rigo troppo lungo sta analizzando gli 
headers
 ToadletContextImpl.methodNotAllowed=Metodo HTTP Non Permesso

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.no.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.no.properties    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.no.properties    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -43,6 +43,7 @@
 BookmarkManager.malformedBookmark=Missformet Bokmerke
 BooleanOption.parseError=Ukjent boolean: ${val} - pr?v true eller false
 BuildOldAgeUserAlert.tooOld=Denne nodens programvare er eldre enn den eldste 
versjonen (Build #${lastgood}) tillatt av de nyeste 'peers' vi pr?ver ? koble 
til. Vennligst oppdater noden so fort som mulig ettersom du ikke kan koble til 
'peers' markert "For ny" f?r du gj?r dette. (Hvis du ikke oppdaterer kan du 
etterhvert miste tilkoblingen til Freenet.)
+BuildOldAgeUserAlert.tooOldTitle=For gammel versjon
 CSSTokenizerFilter.deletedDisallowedString=Slett ulovlig streng
 CSSTokenizerFilter.supplementalCharsNotSupported=UCS-4 TEGN OVER 0xFFFF ER 
IKKE ST?TTET!
 ConfigToadlet.appliedFailureExceptions=Endringene du gjorde i konfigurasjonen 
blir brukt med f?lgende unntak:
@@ -51,8 +52,10 @@
 ConfigToadlet.appliedTitle=Konfigurasjon I Bruk
 ConfigToadlet.apply=Bruk
 ConfigToadlet.configNavTitle=Navigasjon
+ConfigToadlet.contributeTranslation=Bidra til oversettelsen
 ConfigToadlet.fullTitle=Freenet Node Konfigurasjon for ${name}
 ConfigToadlet.possibilitiesTitle=Dine Muligheter
+ConfigToadlet.reset=Tilbakestill
 ConfigToadlet.returnToNodeConfig=Returner til nodens konfigurasjon
 ConfigToadlet.shortTitle=Konfigurasjon
 ConfigToadlet.title=Freenet Node Konfigurasjon
@@ -88,11 +91,15 @@
 DarknetConnectionsToadlet.cantFetchNoderefURL=Kunne ikke hente node referansen 
fra ${url}. Vennligst pr?v p? nytt.
 DarknetConnectionsToadlet.cantParseTryAgain=Kunne ikke tolke den gitte teksten 
som en referanse: (${error}). Vennligst pr?v p? nytt.
 DarknetConnectionsToadlet.cantParseWrongEnding=Kunne ikke tolke nodens 
referanse: Den skal slutte med End p? en linje for seg selv, men den slutter 
med: ${end}
+DarknetConnectionsToadlet.clockProblem=Forskjellen mellom klokken din og denne 
nodens klokke er mer enn 24 timer. Vi har deaktivert tilkoblingen ettersom 
dette kan f?re til problemer med oppdatering og klienter.
+DarknetConnectionsToadlet.clockProblemShort=Klokkeproblem
 DarknetConnectionsToadlet.confirmRemoveNode=Er du sikker p? at du vil fjerne 
"${sname}"? F?r den har minst en uke nedetid, er det ikke anbefalt ? gj?re det, 
ettersom det kan v?re midlertidig, og mange brukere ikke kan kj?re noden 24x7.
 DarknetConnectionsToadlet.confirmRemoveNodeTitle=Vennlist bekreft
 DarknetConnectionsToadlet.confirmRemoveNodeWarningTitle=Node Fjerning
+DarknetConnectionsToadlet.connErrorShort=Tilkoblingsfeil
 DarknetConnectionsToadlet.connected=Tilkoblet: Vi er vellykket tilkoblet disse 
nodene
 DarknetConnectionsToadlet.connectedShort=Tilkoblet
+DarknetConnectionsToadlet.darknetFnpPort=Darknet FNP: ${port}/UDP (mellom 
noder; du vil kanskje videresende denne porten)
 DarknetConnectionsToadlet.disabled=Ikke tilkoblet og deaktivert: fordi 
brukeren har instruert noden til ? ikke koble til disse naboene.
 DarknetConnectionsToadlet.disabledShort=Deaktivert
 DarknetConnectionsToadlet.enterDescription=Legg til beskrivelse:
@@ -127,6 +134,7 @@
 DarknetConnectionsToadlet.nodePortsTitle=Porter som brukes av noden
 DarknetConnectionsToadlet.notConnected=Ikke tilkoblet: Ingen tilkobling s? 
langt, men noden pr?ver kontinuerlig ? koble til.
 DarknetConnectionsToadlet.notConnectedShort=Frakoblet
+DarknetConnectionsToadlet.opennetFnpPort=Opennet  FNP: ${port}/UDP (mellom 
noder; du vil kanskje videresende denne porten)
 DarknetConnectionsToadlet.pasteReference=Lim inn referansen her:
 DarknetConnectionsToadlet.privateNote=Et privat notat om denne naboen
 DarknetConnectionsToadlet.privateNoteTitle=Privat Notat
@@ -135,6 +143,7 @@
 DarknetConnectionsToadlet.removePeers=Fjern valgte naboer
 DarknetConnectionsToadlet.selectAction=-- Velg handling --
 DarknetConnectionsToadlet.sendMessageTitle=Send Node to Node Text Message
+DarknetConnectionsToadlet.sendMessageToPeers=Send N2NTM til valgte peers
 DarknetConnectionsToadlet.separator=-- -- --
 DarknetConnectionsToadlet.statusTitle=Status
 DarknetConnectionsToadlet.tmciDisabled=TMCI er deaktivert (enkel telnet-basert 
kommandolinje-grensesnitt)
@@ -148,12 +157,18 @@
 DarknetConnectionsToadlet.updateChangedPrivnotes=Oppdater endrede private 
notater
 DarknetConnectionsToadlet.urlReference=Skriv inn URLen til referansen her:
 DarknetConnectionsToadlet.versionTitle=Versjon
+ExtOldAgeUserAlert.extTooOld=Det ser ut som freenet-ext.jar filen din er 
utdatert: vi anbefaler sterkt at du oppgraderer den til 
http://downloads.freenetproject.org/alpha/freenet-ext.jar.
+ExtOldAgeUserAlert.extTooOldTitle=Freenet-ext er for gammel
 FProxyToadlet.backToFProxy=${link}Klikk her${/link} for ? g? til FProxy 
hjemmesiden.
+FProxyToadlet.config=konfigruer noden din
 FProxyToadlet.configTitle=Konfigurasjon
+FProxyToadlet.dangerousContentTitle=Potensiellt Farlig Innhold
+FProxyToadlet.dangerousRSSTitle=Potensielt Farlig Innhold (RSS)
 FProxyToadlet.downloadInBackgroundToDisk=Last ned i bakgrunnen og lagre i 
nedlastingsmappen
 FProxyToadlet.errorIsFatal=Dette er en fatal feil. Det er usannsynlig at 
problemet vil l?ses ved ? pr?ve p? nytt.
 FProxyToadlet.errorWithReason=Feil: ${error}
 FProxyToadlet.expectedKeyButGot=Forventet en Freenet n?kkel, men fikk:
+FProxyToadlet.expectedMimeType=Forventet MIME-type: ${mime}
 FProxyToadlet.explanationTitle=Forklaring
 FProxyToadlet.fetchLargeFileAnywayAndDisplay=Hent uansett og vis filen i 
nettleseren
 FProxyToadlet.fileInformationTitle=Fil Informasjon
@@ -164,22 +179,32 @@
 FProxyToadlet.invalidKeyTitle=Ugyldig N?kkel
 FProxyToadlet.largeFile=Stor fil
 FProxyToadlet.largeFileExplanationAndOptions=Freenet n?kkelen du ba om peker 
til en stor fil. Filer p? denne st?rrelsen kan generelt sett ikke sendes 
direkte til nettleseren siden de tar for lang til for Freenet noden ? hente. De 
f?lgende mulighetene er tilgjengelige:
+FProxyToadlet.mayChange=(kan bli endret)
 FProxyToadlet.mimeType=MIME type: ${mime}
 FProxyToadlet.notFoundTitle=Ikke Funnet
+FProxyToadlet.openAsText=${link}Klikk her${/link} for ? ?pne filen som tekst 
(dette b?r ikke v?re farlig, men det kan se ?delagt ut).
+FProxyToadlet.openForce=${link}Klikk her${/link} for ? ?pne filen som ${mime} 
(les advarselen over!).
 FProxyToadlet.openRSSAsRSS=${link}Klikk her${/link} for ? ?pne filen som RSS 
(dette er ${bold}farlig${/bold} hvis forfatteren av siden er ondskapsfull 
ettersom Freenet ikke kan filtrere RSS enda).
+FProxyToadlet.opennetTitle=Fremmede
 FProxyToadlet.options=Dine valg er:
 FProxyToadlet.pluginsTitle=Tillegg
+FProxyToadlet.queue=h?ndter foresp?rsler satt i k?
+FProxyToadlet.queueTitle=K?
 FProxyToadlet.sizeLabel=Str?rrelse:
 FProxyToadlet.sizeUnknown=St?rrelse: ukjent
 FProxyToadlet.stats=vis statistikk
 FProxyToadlet.statsTitle=Statistikk
 FProxyToadlet.translationTitle=Oversettelse
+FProxyToadlet.unableToRetrieve=Freenet kunne ikke hente denne filen.
 FProxyToadlet.unknownMIMEType=MIME type: ukjent
 FProxyToadlet.welcome=hjemmeside
 FProxyToadlet.welcomeTitle=Hjem
 FcpServer.downloadsFileCanCreateCannotReadOrWrite=Opprettet fil, men kan ikke 
lese og skrive den
 FcpServer.downloadsFileDoesNotExistCannotCreate=Filen finnes ikke og kan ikke 
lages
+FcpServer.downloadsFileExistsCannotReadOrWrite=Filen eksisterer, men kan ikke 
skrives eller leses
 FcpServer.downloadsFileUnreadable=Filen finnes, men kan ikke leses
+FcpServer.isEnabled=Er FCP serveren aktivert?
+FcpServer.isEnabledLong=Er FCP serveren aktivert?
 FcpServer.portNumber=FCP port nummer
 FcpServer.portNumberLong=FCP port nummer.
 FetchException.longError.1=For mange lag med gjentakelse inn i arkivet
@@ -200,6 +225,7 @@
 FetchException.longError.26=Arkivet ble startet p? nytt
 FetchException.longError.27=Permanent omdirigering: bruk den nye URIen
 FetchException.longError.28=Ikke nok data ble funnet; noe data ble hentet, men 
omdirigeringer kan peke til ingenting
+FetchException.longError.29=Feil MIME-type: N?kkelen var ikke i listen over 
tillatte MIME-typer fra klienten
 FetchException.longError.3=Vet ikke hva som skal gj?res med metadata
 FetchException.longError.4=Tolkning av metadata mislyktes
 FetchException.longError.5=Mislyktes i ? trekke ut filer fra et arkiv
@@ -224,23 +250,43 @@
 FetchException.shortError.26=Arkiv startet p? nytt
 FetchException.shortError.27=Ny URI
 FetchException.shortError.28=Alle data ikke funnet
+FetchException.shortError.29=Feil MIME-type
 FetchException.shortError.3=Ukjent metadata
+FetchException.shortError.30=Data ikke funnet (mislyktes nylig)
 FetchException.shortError.4=Ugyldig metadata
 FetchException.shortError.5=Arkiv feil
 FetchException.shortError.6=Blokkdekodingsfeil
 FetchException.shortError.7=For mange metadata-lag
 FetchException.shortError.8=For mange arkiv omstarter
 FetchException.shortError.9=For mye gjentakelse
+FileOffer.acceptTransferButton=Aksepter overf?ring
+FileOffer.askUserTitle=Direkte filoverf?ring
+FileOffer.commentLabel=Kommentar:
+FileOffer.failedReceiveHeader=Overf?ringen av ${filename} fra ${node} 
mislyktes.
+FileOffer.failedReceiveTitle=Mottak av fil mislyktes
+FileOffer.fileLabel=Fil:
+FileOffer.mimeLabel=MIME Type:
+FileOffer.offeredFileHeader=Noden ${name} har tilbydt en fil:
+FileOffer.rejectTransferButton=Avsl? Overf?ring
+FileOffer.senderLabel=Sender:
+FileOffer.sizeLabel=St?rrelse:
+FileOffer.succeededReceiveHeader=Overf?ringen av filen ${filename} fra ${node} 
var vellykket.
+FileOffer.succeededReceiveTitle=Vellykket mottak av fil
 FirstTimeWizardToadlet.bandwidthLimit=B?ndbreddebegrensninger
 FirstTimeWizardToadlet.bandwidthLimitLong=Vennligst velg din tilkoblingstype 
fra nedtrekksmenyen under.
+FirstTimeWizardToadlet.congratz=Velkommen ombord!
 FirstTimeWizardToadlet.datastoreSize=Datalagerst?rrelse
 FirstTimeWizardToadlet.datastoreSizeLong=Vennligst velg en st?rrelse for 
datalageret ditt.
 FirstTimeWizardToadlet.homepageTitle=Freenet f?rstegangsveiviser!
+FirstTimeWizardToadlet.iDoTrust=Stoler du p? folk koblet til ${interface} 
(${ip})?
+FirstTimeWizardToadlet.isNetworkTrusted=Stoler du p? ditt lokale nettverk?
+FirstTimeWizardToadlet.noNetworkIF=Fant ingen ekstra nettverkstilkobling
 FirstTimeWizardToadlet.selectLanguage=Spr?k
 FirstTimeWizardToadlet.selectLanguageLong=Vennligst velg et spr?k fra listen 
under:
 FirstTimeWizardToadlet.step1Title=Freenet f?rstegangsveiviser! - Velg ditt 
spr?k
 FirstTimeWizardToadlet.step2Title=Freenet f?rstegangsveiviser! 
B?ndbreddebegrensninger
 FirstTimeWizardToadlet.step3Title=Freenet f?rstegangsveiviser! - 
Datalagerst?rrelse
+FirstTimeWizardToadlet.step5Title=Freenet f?rstegangsveiviser! - Gratulerer, 
noden din er n? konfigurert
 FirstTimeWizardToadlet.welcomeInfoboxContent1=Velkommen til freenet 
f?rstegangsveiviser. Dette verkt?yet vil gi deg mulighet til ? konfigurere 
noden din fort og enkelt for ? f? deg i gang. Vennligst
 FirstTimeWizardToadlet.welcomeInfoboxTitle=Velkommen til Freenet 
f?rstegangsveiviser!
 GIFFilter.invalidHeader=Filen inneholder ikke et gyldig GIF hode.
@@ -274,12 +320,15 @@
 InsertException.longError.10=Avbrutt av bruker
 InsertException.longError.12=Bin?r blob formateringsfeil
 InsertException.longError.3=Intern feil
+InsertException.longError.5=Kunne ikke sende innsettingen til nok noder 
(normalt i sm? nettverk, pr?v ? hente den uansett)
 InsertException.longError.9=Innsettingen kolliderte med forskjellig, 
eksisterende data p? samme n?kkel
 InsertException.shortError.1=Ugyldig URI
 InsertException.shortError.10=Avbrutt
 InsertException.shortError.12=Bin?r blob formateringsfeil
 InsertException.shortError.3=Intern feil
 InsertException.shortError.5=Rute ikke funnet
+InsertException.shortError.6=Noen blokker mislyktes fatalt
+InsertException.shortError.8=Foresp?rselen kunne ikke forlate noden
 InsertException.shortError.9=Kolliderte med eksisterende data
 IntOption.parseError=Den spesifiserte verdien kan ikke tolkes som en 32-bits 
integer: ${val}
 JPEGFilter.notJpeg=Filen du pr?vde ? hente er ikke JPEG. Den kan v?re et annet 
filformat, og nettleseren din kan gj?re noe farlig med den. Derfor har filen 
blitt blokkert.
@@ -295,7 +344,10 @@
 KnownUnsafeContentTypeException.knownUnsafe=Dette er en potensielt farlig 
MIME-type. Hvis noden slipper den gjennom kan nettleseren din gj?re mange 
d?rlige ting som kan lede til kompromittering av din anonymitet, og at IP 
adressen din blir avsl?rt i forbindelse med denne siden. Spesielt:
 KnownUnsafeContentTypeException.noFilter=Siden det ikke finnes noe innebygd 
filter for denne filen b?r du utvise den h?yeste forsiktighet!
 KnownUnsafeContentTypeException.title=Kjent farlig type: ${type}
+LocalFileInsertToadlet.checkPathReadable=Sjekk at den spesifiserte stien er 
lesbar for brukeren som kj?rer noden.
+LocalFileInsertToadlet.dirCannotBeRead=Stien "${path}" kan ikke leses.
 LocalFileInsertToadlet.fileHeader=Fil
+LocalFileInsertToadlet.insert=Sett inn
 LocalFileInsertToadlet.sizeHeader=St?rrelse
 LogConfigHandler.detaildPriorityThreshold=Detaljert prioritetsgrense
 LogConfigHandler.detaildPriorityThresholdLong=Detaljert prioritetsbegrensning, 
eksempel freenet:normal,freenet.node:minor
@@ -316,14 +368,23 @@
 LongOption.parseError=Den spesifiserte verdien kan ikke tolkes som en 64-bits 
integer: ${val}
 MeaningfulNodeNameUserAlert.noNodeNick=Det ser ut som noden din ikke har et 
kallenavn. ? legge inn e-mail adressen eller IRC kallenavnet ditt her er 
generelt en god ide og det hjelper vennene dine ? kjenne igjen noden din (merk 
at bare darknet naboer dine listet p? vennesiden kan se nodens navn, det vil 
ikke bli vist til opennet naboer).
 MeaningfulNodeNameUserAlert.noNodeNickTitle=Noden din har ikke et navn.
+N2NTMToadlet.composingMessageLabel=Skriver N2NTM til de f?lgende mottakerne:
+N2NTMToadlet.delayed=Opptatt: Sending av meldingen til mottaker ble kanskje 
forsinket
 N2NTMToadlet.delayedTitle=Forsinket
+N2NTMToadlet.failed=Meldingen kunne ikke sendes til mottakeren: mottakeren er 
ikke tilkoblet
 N2NTMToadlet.failedTitle=Mislyktes
 N2NTMToadlet.friends=Venner
+N2NTMToadlet.noSuchFileOrCannotRead=Kunne ikke sende filen: Den finnes ikke 
eller kan ikke leses.
+N2NTMToadlet.peerName=Peer Navn
+N2NTMToadlet.peerNotFoundTitle=Peer ble ikke funnet
 N2NTMToadlet.processingSend=Sending av Node to Node Text Message Behandles
+N2NTMToadlet.queued=Satt i k?: Mottakeren var ikke tilkoblet, s? meldingen er 
satt i k? til denne kobler til
+N2NTMToadlet.queuedTitle=Satt i k?
 N2NTMToadlet.returnToFriends=Returner til venneliste
 N2NTMToadlet.sendMessage=Send Node til Node Tekst Melding
 N2NTMToadlet.sendMessageShort=Send melding
 N2NTMToadlet.sendStatus=N2NTM Sendt Status
+N2NTMToadlet.sent=Meldingen ble sendt til mottakeren
 N2NTMToadlet.sentTitle=Sendt
 N2NTMToadlet.tooLong=N2NTMer er begrenset til 1024 tegn
 N2NTMToadlet.tooLongTitle=For lang
@@ -348,6 +409,8 @@
 Node.mustBePositive=Konfigurasjonsverdien m? v?re positiv
 Node.nodeName=Kallenavn for denne Freenet noden
 Node.nodeNameLong=Nodens kallenavn. Dette vil bare v?re synlig for venner.
+Node.oneConnectionPerIP=Begrens til en tilkobling per adresse?
+Node.opennetEnabled=Aktiver Opennet-st?tte?
 Node.outBWLimit=Utg?ende b?ndbreddebegrensning (byte per sekund)
 Node.outBWLimitLong=Hard utg?ende b?ndbreddebegrensning (byte/sek); noden b?r 
nesten aldri g? over dette
 Node.port=FNP port nummer (UDP)
@@ -363,6 +426,8 @@
 NodeClientCore.couldNotFindOrCreateDir=Kunne ikke finne eller opprette mappe
 NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=M? v?re st?rre enn null
 NodeIPDectector.inclLocalAddress=Inkluder lokal adresse i noderef
+NodeIPDectector.inclLocalAddressLong=Skal din lokale adresse (LAN og 
localhost) inkluderes i nodens referanse? Dette vil ikke v?re nyttig med mindre 
begge sider setter allowLocalAddress=true for den respektive referansen (bruk 
vennesiden i avansert modus for ? sette denne verdien).
+NodeIPDectector.ipOverride=Overstyring av IPadresse
 NodeIPDectector.ipOverrideLong=IP adresseoverstyring (vanligvis ikke 
n?dvendig) - sett denne hvis du har *statisk* IP adresse eller et domenenavn 
(f.eks via dyndns), og du er bak en brannmur.
 NodeIPDectector.tempAddressHint=Midlertidig hint om IP adresse
 NodeIPDectector.tempAddressHintLong=Midlertidig hint om hva IP adressen v?r 
kan v?re; slettes etter bruk
@@ -371,6 +436,7 @@
 NodeStat.threadLimit=Tr?d-grense
 NodeStats.mustBePercentValueNotFull=Denne verdien m? v?re en prosent mellom 0 
og 99.
 NodeStats.valueTooLow=Denne verdien er for lav for den innstillingen, ?k den!
+NodeUpdateManager.enabled=Let etter og last ned nye versjoner
 NodeUpdateManager.enabledLong=Skal noden automatisk sjekke etter nye versjoner 
av Freenet. Hvis ja, nye versjoner vil automatisk oppdages og lastes ned, men 
ikke n?dvendigvis installert. Denne innstillingen settes automatisk til 'false' 
(nei) hvis noden kj?rer uten innpakningen.
 NodeUpdateManager.extURI=Hvor skal noden se etter oppdateringer til 
freenet-ext.jar?
 NodeUpdateManager.extURILong=Hvor skal noden se etter oppdateringer til 
freenet-ext.jar?
@@ -389,8 +455,20 @@
 NodeUpdateManager.updateURILong=Hvor skal noden se etter oppdateringer?
 PNGFilter.invalidHeader=Filen du pr?vde ? hente er ikke et PNG. Det inkluderer 
ikke et gyldig PNG hode. Det kan v?re et annet format, og nettleseren din kan 
gj?re noe farlig med det. Det har derfor blitt blokkert.
 PNGFilter.invalidHeaderTitle=Ikke et PNG - ugyldig hode
+PeerManagerUserAlert.clockProblemTitle=Klokkeproblem
 PeerManagerUserAlert.noConns=Denne noden har ikke kunnet koble til noen andre 
noder s? langt; den vil ikke kunne fungere normalt. Forh?pentligvis vil noen av 
naboene koble til snart; hvis ikke, pr?v ? skaffe noen flere naboer. Du trenger 
minst 3 naboer, og b?r ha 5-10.
+PeerManagerUserAlert.noPeersTitle=Ingen peers funnet
+PeerManagerUserAlert.onlyFewConnsTitle=Bare ${count} fungerende tilkobling(er)
+PeerManagerUserAlert.tooManyConnsTitle=For mange ?pne tilkoblinger
 PeerManagerUserAlert.tooManyDisconnectedTitle=For mange frakoblede naboer
+PeerManagerUserAlert.tooManyPeersTitle=For mange peers
+PeersSayKeyBlownAlert.connectedSayBlownLabel=Disse tilkoblede nodene sier at 
oppdateringssystemet er kompromitert (vi pr?ver ? laste ned 
revokasjonssertifikatet fra dem):
+PeersSayKeyBlownAlert.disconnectedSayBlownLabel=Disse nodene fortalte oss at 
n?kkelen har blitt kompromitert, men koblet deretter fra. Derfor kunne vi ikke 
hente revokasjonssertifikatet fra disse:
+PeersSayKeyBlownAlert.failedFetch=Noden din kunne ikke laste ned 
revokasjonsertifikatet. Mulige ?rsaker inkluderer ett angrep p? noden din for ? 
f? den til ? oppdatere selv om n?kkelen er kompromitert. Vennligst kontakt 
utviklerne eller andre Freenet-brukere for ? rydde opp i dette.
+PeersSayKeyBlownAlert.failedTransferSayBlownLabel=Disse nodene sa at n?kkelen 
har blitt kompromitert, men overf?rte ikke revokasjonssertifikatet:
+PeersSayKeyBlownAlert.fetching=Noden din pr?ver ? laste ned 
revokasjonssertifikatet for ? finne ut flere detaljer.
+PeersSayKeyBlownAlert.intro=En eller flere av dine peers sier at det 
automatiske oppdaterinssystemet er kompromitert! Dette betyr at en angriper kan 
vite den private n?kkelen for oppdateringssystemet og kan derfor f?r noden din 
til ? kj?re kode han velger (hvis du oppdaterer)! Det automatiske 
oppdateringssystemet har blitt deaktivert. Det er ogs? mulig at dine peers med 
overlegg lyver om dette.
+PeersSayKeyBlownAlert.titleWithCount=Auto-oppdateringssystemet er kompromitert 
i f?lge ${count} peer(s)!
 PluginManager.cannotSetOnceLoaded=Kan ikke endre tilleggslisten n?r den er 
lastet
 PluginManager.loadedOnStartup=Tillegg som skal lastes ved oppstart
 PluginManager.loadedPlugins=Tillegg som skal lastes ved oppstart
@@ -399,6 +477,7 @@
 PluginManager.pluginReqNewerJVMTitle=Nyere JVM kreves av ${name}.
 PluginToadlet.addPluginTitle=Legg til et tillegg
 PluginToadlet.failedToLoadPlugin=Mislyktes i ? laste tillegg.
+PluginToadlet.failedToLoadPluginCheckClass=Tillegget du bad om kunne ikke 
lastes. Vennligst sjekk navnet p? tileggets klasse og URL, hvis du angav en.
 PluginToadlet.failedToLoadPluginTitle=Lasting av tillegg mislyktes
 PluginToadlet.internalNameTitle=Internt Navn
 PluginToadlet.loadPluginCommand=Last tillegg
@@ -408,27 +487,48 @@
 PluginToadlet.pluginListTitle=Liste over Tillegg
 PluginToadlet.pluginNameTitle=Navn p? Tillegg
 PluginToadlet.pluginNotFound=Det forespurte tillegget ble ikke funnet.
+PluginToadlet.pluginNotFoundTitle=Tillegg ble ikke funnet
 PluginToadlet.returnToPluginsWithLinks=Vennligst ${link}returner${/link} til 
listen over tillegg.
 PluginToadlet.visit=Vis
+PproxyToadlet.classNameTitle=Klassenavn
 PproxyToadlet.internalIDTitle=Intern ID
+PproxyToadlet.noPlugins=Ingen tillegg lastet
+PproxyToadlet.plugins=Tillegg
+PproxyToadlet.reload=Last p? nytt
 PproxyToadlet.startedAtTitle=Startet
+QueueToadlet.DinProgress=P?begynte nedlastinger
+QueueToadlet.UinProgress=P?begynte opplastinger
+QueueToadlet.change=Endre
 QueueToadlet.completedDinDownloadDirectory=Fullf?rt: Nedlastinger til 
nedlastingsmappen (${size})
+QueueToadlet.completedU=Fullf?rt: Opplastinger (${size})
 QueueToadlet.delete=Slett
+QueueToadlet.download=Last ned
 QueueToadlet.errorDToDisk=Kan ikke laste ned til disk
 QueueToadlet.errorNoFileOrCannotRead=Filen finnes ikke eller kan ikke leses
+QueueToadlet.errorNoFileSelected=Ingen fil valgt
+QueueToadlet.errorNoFileSelectedU=Du valgte ingen fil for opplasting.
 QueueToadlet.errorNoKey=Ingen n?kkel ble spesifisert for nedlasting
 QueueToadlet.errorNoKeyToD=Du spesifiserte ingen n?kkel ? laste ned.
 QueueToadlet.failedD=Mislykkede nedlastinger
+QueueToadlet.failedToRemove=Kunne ikke fjerne ${id}: ${message}
+QueueToadlet.failedToRemoveId=Kunne ikke fjerne: ${id}
+QueueToadlet.failedToRemoveRequest=Kunne ikke fjerne foresp?rselen
 QueueToadlet.failedU=Mislykkede opplastinger
 QueueToadlet.fileName=Filnavn
 QueueToadlet.files=Filer
+QueueToadlet.follow=F?lg Omdirigeringer
+QueueToadlet.insertAs=Sett inn som:
+QueueToadlet.insertFile=Sett inn fil
 QueueToadlet.key=N?kkel
 QueueToadlet.legend=Forklaring
 QueueToadlet.mimeType=MIME Type
 QueueToadlet.none=ingen
 QueueToadlet.panicButton=Panikk knapp
 QueueToadlet.panicButtonConfirmation=Slett alt uten bekreftelse!
+QueueToadlet.persistence=Varighet
+QueueToadlet.persistenceForever=for evig
 QueueToadlet.persistenceNone=ingen
+QueueToadlet.pleaseEnableFCP=Du m? aktivere FCP serveren for ? f? tilgang til 
denne siden
 QueueToadlet.priority=Prioritet
 QueueToadlet.priority0=krise
 QueueToadlet.priority1=veldig h?y
@@ -436,12 +536,21 @@
 QueueToadlet.priority3=medium
 QueueToadlet.priority4=lav
 QueueToadlet.priority5=veldig lav
+QueueToadlet.priority6=blir aldri ferdig
+QueueToadlet.progress=Fremgang
+QueueToadlet.progressbarAccurate=Denne fremgangsverdien er n?yaktig
+QueueToadlet.reason=Grunn
+QueueToadlet.remove=Fjern
 QueueToadlet.restart=Start p? nytt
 QueueToadlet.size=St?rrelse
+QueueToadlet.starting=STARTER
+QueueToadlet.title=Global k? for ${nodeName}
 QueueToadlet.totalSize=Total St?rrelse
 QueueToadlet.unknown=Ukjent
 QueueToadlet.warningUnsafeContent=Potensielt Farlig Innhold
 QueueToadlet.warningUnsafeContentExplanation=Filen du vil laste ned kan ikke 
filtrert av Freenets innholdsfilter! Det betyr at anonymiteten din kan bli 
kompromittert ved ? ?pne filen!
+QueueToadlet.wipD=P?g?ende: Nedlastinger (${size})
+QueueToadlet.wipU=P?g?ende: Opplastinger (${size})
 RevocationKeyFoundUserAlert.text=Noden din har funnet auto-oppdateringens 
revokasjons n?kkel p? nettverket. Det betyr at auto-oppdateringssystemet 
sannsynligvis har blitt KOMPROMITTERT! Derfor har det blitt deaktivert p? noden 
din for ? forhindre at "onde ting" blir installert. Vi anbefaler sterkt at du 
bes?ker prosjektets webside for oppdateringer. Vennligst verifiser at websiden 
ikke har blitt erstattet. Revokasjonsmeldingen er den f?lgende: ${message}.
 RevocationKeyFoundUserAlert.title=Prosjektets private n?kkel har blitt 
kompromittert!
 ShortOption.parseError=Kan ikke tolke verdien som en streng-tabell: ${error}
@@ -451,6 +560,7 @@
 SimpleToadletServer.cssName=CSS Navn
 SimpleToadletServer.cssNameLong=Navn p? CSSen FProxy skal bruke
 SimpleToadletServer.cssOverrideCantRead=Vi kan ikke lese den gitte CSS-filen: 
${filename}
+SimpleToadletServer.enabled=Aktiver FProxy?
 SimpleToadletServer.illegalCSSName=CSS navnet kan ikke inneholde skr?streker 
eller kolon!
 SimpleToadletServer.panicButton=Vis panikknappen?
 SimpleToadletServer.panicButtonLong=Skal panikknappen vises p? /queue/ siden.
@@ -463,9 +573,11 @@
 StatisticsToadlet.allocMemory=Allokert Java minne: ${memory}
 StatisticsToadlet.bandwidthTitle=B?ndbredde
 StatisticsToadlet.cpus=Tilgjengelige CPUer: ${count}
+StatisticsToadlet.getLogs=Vis nodens siste loggfil
 StatisticsToadlet.inputRate=Inndata fart: ${rate}/sekund (av ${max}/sekund)
 StatisticsToadlet.jeDumpButton=Generer en JE Dump
 StatisticsToadlet.jvmInfoTitle=JVM Info
+StatisticsToadlet.jvmVendor=JVM produsent: ${vendor}
 StatisticsToadlet.jvmVersion=JVM Versjon: ${version}
 StatisticsToadlet.maxMemory=Maksimum Java minne: ${memory}
 StatisticsToadlet.noRequests=Noden din behandler ingen foresp?rsler akkurat n?.
@@ -473,12 +585,19 @@
 StatisticsToadlet.osName=OS Navn: ${name}
 StatisticsToadlet.osVersion=OS Versjon: ${version}
 StatisticsToadlet.outputRate=Utdata fart: ${rate}/sekund (av ${max}/sekund)
+StatisticsToadlet.peerStatsTitle=Tilkoblingsstatistikk
+StatisticsToadlet.threadDumpButton=Generer en Tr?d Dump
 StatisticsToadlet.threads=Kj?rende tr?der: ${running}/${max}
 StatisticsToadlet.totalInput=Inndate totalt: ${total} (${rate}/sekund)
 StatisticsToadlet.totalOutput=Utdata totalt: ${total} (${rate}/sekund)
 StatisticsToadlet.usedMemory=Brukt Java minne: ${memory}
 StatisticsToadlet.versionTitle=Node Versjonsinformasjon
 SymlinkerToadlet.symlinksLong=En liste over "alias#target" som danner en 
gruppe lenker
+TestnetHandler.cannotEnableDisableOnTheFly=Det er ikke mulig ? 
aktivere/deaktivere testnet-modus mens noden er p?; restart noden og skaff nye 
tilkoblinger
+TestnetHandler.enable=Aktiver testnet-modus? (FARLIG)
+TestnetHandler.enableLong=Skal testnet-modus aktiveres (FARLIG!)? 
Testnet-modus eliminerer annonymiteten i bytte mot ? hjelpe utviklerne i ? 
feils?ke noden.
+TestnetHandler.port=Testnet port
+TestnetHandler.portLong=Testnet port nummer (-1 = lyttePort+1000)
 TextModeClientInterfaceServer.enabled=Aktiver TMCI
 TextModeClientInterfaceServer.enabledLong=Skal TMCI aktiveres
 TextModeClientInterfaceServer.telnetPortNumber=Telnet port
@@ -488,6 +607,7 @@
 Toadlet.homepage=Hjemmeside
 Toadlet.internalErrorPleaseReport=Intern feil: vennligst rapporter
 Toadlet.internalErrorTitle=Intern Feil
+Toadlet.no=Nei
 Toadlet.nodeHomepage=Nodens Hjemmeside
 Toadlet.notSupportedTitle=Ikke St?ttet
 Toadlet.notSupportedWithClass=Nettleseren din sendte en foresp?rsel Freenet 
(${class}) ikke forst?r.
@@ -496,6 +616,7 @@
 Toadlet.returnToPrevPage=Returner til den forrige siden
 Toadlet.tempRedirectWithReason=Midlertidig omdirigering: ${reason}
 Toadlet.unauthorized=Du er ikke lov til ? vise denne siden.
+Toadlet.yes=Ja
 ToadletContextImpl.cannotParseContentLength=Tolkningsfeil i innholdslengde: 
${error}
 ToadletContextImpl.methodNotAllowed=HTTP Method er ikke Lovlig
 ToadletContextImpl.noContentLengthInPOST=Ingen innholdslengde i POST
@@ -523,9 +644,12 @@
 TranslationToadlet.updateTranslationCommand=Oppdater oversettelsen!
 UnknownContentTypeException.explanation=Freenet noden din vet ingen ting om 
denne MIME typen. Det betyr at nettleseren din kan gj?re noe farlig som respons 
til nedlasting av denne filen. For eksempel inneholder mange formater innlagte 
bilder eller videoer, som lastes ned fra internett; dette er p? ingen m?te 
ufarlig, ettersom det kan ?delegge anonymiteten din og vise IP adressen (hvis 
en angriper har kontroll over websiden eller har tilgang til loggene). Linker 
til internett kan ogs? v?re en trussel, av samme grunn, og det samme gjelder 
makroer, av disse og andre grunner.
 UnknownContentTypeException.title=Ukjent og potensielt farlig filtype: ${type}
+UpdateDeployContext.cannotUpdateNoExtJar=Fant ikke freenet-ext.jar i 
wrapper.conf (fant freenet.jar: ${mainFilename})
+UpdateDeployContext.cannotUpdateNoJars=Fant ingen av Freenet sine jar-filer i 
wrapper.conf
 UpdateDeployContext.cannotUpdateNoMainJar=Kunne ikke finne freenet.jar i 
wrapper.conf (fant freenet-ext.jar: ${extFilename})
 UpdateDeployContext.updateCatastrophe=KATASTROFAL FEIL: Slettet ${old}, men 
kan ikke bytte navn fra ${new} til ${old}, derfor VIL IKKE NODEN STARTE! 
Vennligst l?s problemet ved ? endre navnet p? ${new} til ${old} manuelt.
 UpdateDeployContext.updateFailedCannotDeleteOldConfig=Kan ikke slette ${old} 
s? kan ikke bytte navn til den. Oppdatering mislyktes.
+UpdateDeployContext.updateFailedNonStandardConfig=Kunne ikke oppdatere p? 
grunn av ikke-standard konfigurasjon: skrev main=${main} ext=${ext} - dette b?r 
ikke skje! Rapporter dette til utviklerne og inkluder din wrapper.conf.
 UpdatedVersionAvailableUserAlert.alsoDownloadedNewExtJar=Noden din har ogs? 
lastet ned en ny versjon av Freenet extra jar, versjon ${version}
 UpdatedVersionAvailableUserAlert.armed=Noden din vil automatisk starte p? nytt 
etter den har fullf?rt nedlastingen og verifikasjonen av den nye versjonen av 
Freenet.
 UpdatedVersionAvailableUserAlert.clickToUpdateASAP=Klikk under for ? oppdatere 
noden s? fort oppdateringen har blitt verifisert.
@@ -541,7 +665,9 @@
 UpdatedVersionAvailableUserAlert.updateASAPButton=Oppdater ASAP
 UpdatedVersionAvailableUserAlert.updateASAPQuestion=Vil du at noden automatisk 
skal starte p? nytt s? fort den har lastet ned oppdateringen?
 UpdatedVersionAvailableUserAlert.updateNowButton=Oppdater N?!
+UserAlert.apply=Bruk
 UserAlert.hide=Skjul
+UserAlert.reset=Tilbakestill
 UserAlertManager.alertsOnHomepage=| Se dem p? ${link}Freenet 
hjemmesiden${/link}.
 UserAlertManager.alertsTitle=Gjenst?ende advarsler
 UserAlertManager.criticalErrorCountLabel=Kritiske feil:

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.se.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.se.properties    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.se.properties    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,9 +1,9 @@
 BookmarkEditorToadlet.addBookmark=L?gg till bokm?rke
 BookmarkEditorToadlet.addCategory=L?gg till kategori
+BookmarkEditorToadlet.addNewBookmark=L?gg till bokm?rke
+BookmarkEditorToadlet.addNewCategory=L?gg till en kategori
 BookmarkEditorToadlet.addedNewBookmark=Bokm?rket sparades.
 BookmarkEditorToadlet.addedNewBookmarkTitle=Bokm?rke sparat
-BookmarkEditorToadlet.addNewBookmark=L?gg till bokm?rke
-BookmarkEditorToadlet.addNewCategory=L?gg till en kategori
 BookmarkEditorToadlet.bookmarkDoesNotExist="${bookmark}" finns inte.
 BookmarkEditorToadlet.cancelCut=Avbryt klipp ut
 BookmarkEditorToadlet.cancelDelete=Avbryt
@@ -11,11 +11,11 @@
 BookmarkEditorToadlet.changesSavedTitle=?ndringar sparade
 BookmarkEditorToadlet.confirmDelete=Radera
 BookmarkEditorToadlet.cut=Klipp ut
+BookmarkEditorToadlet.delete=Radera
+BookmarkEditorToadlet.deleteBookmark=Radera bokm?rke
 BookmarkEditorToadlet.deleteBookmarkConfirm=Vill du verkligen radera 
${bookmark}?
-BookmarkEditorToadlet.deleteBookmark=Radera bokm?rke
+BookmarkEditorToadlet.deleteCategory=Radera kategori
 BookmarkEditorToadlet.deleteCategoryConfirm=?r det s?kert att du vill radera 
${bookmark} och l?nkarna inuti?
-BookmarkEditorToadlet.deleteCategory=Radera kategori
-BookmarkEditorToadlet.delete=Radera
 BookmarkEditorToadlet.deleteSucceeded=Bokm?rket har raderats.
 BookmarkEditorToadlet.deleteSucceededTitle=Raderat
 BookmarkEditorToadlet.edit=?ndra
@@ -40,6 +40,8 @@
 BookmarkManager.list=Bokm?rken
 BookmarkManager.listLong=En lista med dina bokm?rken.
 BuildOldAgeUserAlert.tooOldTitle=F?r gammal version
+CSSTokenizerFilter.deletedUnofficialIdentWithURL=Raderade inofficiell 
identitet med url
+CSSTokenizerFilter.unknownAtIdentifierLabel=Ok?nd @identifierare:
 ConfigToadlet.appliedFailureExceptions=Inst?llningarna ?ndrades med f?ljande 
undantag:
 ConfigToadlet.appliedFailureTitle=Inst?llningarna kan inte till?mpas
 ConfigToadlet.appliedSuccess=?ndringarna sparades.
@@ -66,6 +68,7 @@
 ContentFilter.imageGifReadAdvice=GIF bild - antagligen inte farligt
 ContentFilter.imageGifWriteAdvice=GIF bild - antagligen inte farligt men du 
b?r radera eventuella kommentarer i filen
 ContentFilter.imageIcoReadAdvice=Ikon fil - antagligen inte farligt
+ContentFilter.imageIcoWriteAdvice=Ikon fil - antagligen inte farlig (kanske 
kan ineh?lla annan data?)
 ContentFilter.imageJpegReadAdvice=JPEG bild - antagligen inte farligt
 ContentFilter.imageJpegWriteAdvice=JPEG bild - antagligen inte farligt men kan 
ineh?lla EXIF data
 ContentFilter.imagePngReadAdvice=PNG bild - antagligen inte farligt
@@ -74,19 +77,18 @@
 ContentFilter.textHtmlWriteAdvice=HTML - kan ineh?lla farlig metadata o.s.v.; 
kontrollera filen i en textl?sare f?r s?kerhets skull.
 ContentFilter.textPlainReadAdvice=Vanlig text - inte farligt s?vida du inte 
anv?nder en farlig browser (Internet Explorer)
 ContentFilter.textPlainWriteAdvice=Ren text - inte farligt s?vida filen inte 
ineh?ller personlig information
-CSSTokenizerFilter.deletedUnofficialIdentWithURL=Raderade inofficiell 
identitet med url
 DarknetConnectionsToadlet.activityTitle=Nuvarande aktivitet
 DarknetConnectionsToadlet.add=L?gg till
 DarknetConnectionsToadlet.addPeerTitle=L?gg till en nod
 DarknetConnectionsToadlet.alreadyInReferences=Referensen finns redan.
 DarknetConnectionsToadlet.backedOff=Ansluten men upptagen: Vi skickar inga 
f?rfr?gningar till en upptagen nod.
 DarknetConnectionsToadlet.backedOffShort=Upptagen
+DarknetConnectionsToadlet.busy=Upptagna: Dessa noder ?r anslutna men upptagna 
s? d?rf?r skckar vi inga f?rfr?gningar till dem.
 DarknetConnectionsToadlet.busyShort=Upptagen
-DarknetConnectionsToadlet.busy=Upptagna: Dessa noder ?r anslutna men upptagna 
s? d?rf?r skckar vi inga f?rfr?gningar till dem.
 DarknetConnectionsToadlet.cancel=Avbryt
 DarknetConnectionsToadlet.cantFetchNoderefURL=Kunde inte h?mta referens fr?n 
${url}. F?rs?k igen.
 DarknetConnectionsToadlet.cantParseTryAgain=Kunde inte anv?nda texten som nod 
referens: (${error}). F?rs?k igen.
-DarknetConnectionsToadlet.confirmRemoveNode=?r du s?ker p? att du vill ta bort 
"+peerNodes[i].getName()+" ? Innan den har minst en veckas netid, ?r det inte 
rekommenderat att g?ra s? d? anv?ndaren kan komma tillbaka.
+DarknetConnectionsToadlet.confirmRemoveNode=?r du s?ker p? att du vill ta bort 
"${name}"? Innan noden har haft  minst en veckas nedtid s? ?r det inte 
rekommenderat att ta bort den d? anv?ndaren kanska bara ?r tillf?lligt inaktiv, 
v?nta g?rna n?gra dagar till innan du tar bort den.
 DarknetConnectionsToadlet.confirmRemoveNodeTitle=Godk?nn
 DarknetConnectionsToadlet.confirmRemoveNodeWarningTitle=Nod borttagning
 DarknetConnectionsToadlet.connected=Anslutna: Noder som tar emot f?rfr?gningar.
@@ -111,31 +113,31 @@
 DarknetConnectionsToadlet.invalidSignature=Kan inte verifiera signaturen p? 
referensen (${error}).
 DarknetConnectionsToadlet.ipAddress=Nodens IP:Port
 DarknetConnectionsToadlet.ipAddressTitle=Adress
-DarknetConnectionsToadlet.listeningShort=Lyssnar
 DarknetConnectionsToadlet.listenOnly=Inte ansluten och lyssnar endast: Vi 
kommer inte f?rs?ka kontakta den h?r noden d? inst?llningarna endast medger att 
vi lyssnar.
 DarknetConnectionsToadlet.listenOnlyShort=Lyssna endast
+DarknetConnectionsToadlet.listeningShort=Lyssnar
 DarknetConnectionsToadlet.myFriends=V?nner
 DarknetConnectionsToadlet.myReferenceHeader=${linkref}Denna nods 
referens${/linkref} (${linktext}som text${/linktext})
 DarknetConnectionsToadlet.nameClickToMessage=Nodens namn. Klicka p? namnet f?r 
att skicka N2NTM (Nod till nod text meddelanden)
 DarknetConnectionsToadlet.nameTitle=Namn
 DarknetConnectionsToadlet.neverConnected=Aldrig ansluten: Den h?r noden har 
aldrig varit ansluten.
 DarknetConnectionsToadlet.neverConnectedShort=Aldrig ansluten
-DarknetConnectionsToadlet.nodeHomepage=nod hemsida
-DarknetConnectionsToadlet.nodePortsTitle=Portar som noden anv?nder
 DarknetConnectionsToadlet.noPeersFirstHalf=Freenet kan inte fungera ?nnu d? du 
inte lagt till n?gra noder ?nnu. V?nligen g? till
 DarknetConnectionsToadlet.noPeersSecondHalf=och l?s informationen.
 DarknetConnectionsToadlet.noRefOrURL=Kunde inte hitta en nod referens eller 
URL. F?rs?k igen.
 DarknetConnectionsToadlet.noRequests=Noden hanterar inga f?rfr?gningar just nu.
+DarknetConnectionsToadlet.nodeHomepage=nod hemsida
+DarknetConnectionsToadlet.nodePortsTitle=Portar som noden anv?nder
 DarknetConnectionsToadlet.notConnected=Inte ansluten: Ingen anslutning ?nnu, 
forts?tter att f?rs?ka.
 DarknetConnectionsToadlet.notConnectedShort=Icke ansluten
 DarknetConnectionsToadlet.pasteReference=Klistra in referens h?r:
 DarknetConnectionsToadlet.privateNoteTitle=Beskrivning
-DarknetConnectionsToadlet.referenceCopyWarning=Nod referens, M?ste kopieras 
${bold}som den ?r${/bold}. ?ndringar kommer att g?ra den 
${bold}oanv?ndbar${/bold}.
-DarknetConnectionsToadlet.removePeers=Ta bort markerad nod
+DarknetConnectionsToadlet.referenceCopyWarning=Nod referensen m?ste kopieras 
${bold}som den ?r${/bold}. ?ndringar kommer att g?ra den 
${bold}oanv?ndbar${/bold}.
 DarknetConnectionsToadlet.remove=Ta bort
+DarknetConnectionsToadlet.removePeers=Ta bort markerad(e) nod(er)
 DarknetConnectionsToadlet.selectAction=-- V?lj ?tg?rd --
 DarknetConnectionsToadlet.sendMessageTitle=Skicka nod till nod text meddelande
-DarknetConnectionsToadlet.sendMessageToPeers=Skicka N2NTM till markerade noder
+DarknetConnectionsToadlet.sendMessageToPeers=Skicka N2NTM till markerad(e) 
nod(er)
 DarknetConnectionsToadlet.separator=-- -- --
 DarknetConnectionsToadlet.statusTitle=Status
 DarknetConnectionsToadlet.tmciDisabled=TMCI ?r avst?ngd! (simple telnet-based 
command-line interface)
@@ -147,44 +149,10 @@
 DarknetConnectionsToadlet.triedToAddSelf=Du kan inte l?gga till din egen nod.
 DarknetConnectionsToadlet.unauthorized=Du har inte r?ttigheter att bes?ka den 
h?r sidan.
 DarknetConnectionsToadlet.unknownAddress=(ok?nd adress)
-DarknetConnectionsToadlet.updateChangedPrivnotes=Updatera ?ndrade nod 
beskrivningar
+DarknetConnectionsToadlet.updateChangedPrivnotes=Uppdatera beskrivning(ar) p? 
markerad(e) nod(er)
 DarknetConnectionsToadlet.urlReference=URL till referens:
 DarknetConnectionsToadlet.versionTitle=Version
 ExtOldAgeUserAlert.extTooOldTitle=Freenet-ext ?r f?r gammal
-FcpServer.allowedHostsFullAccessLong=IP adresser med full FCP tillg?ng till 
noden. Klienter med dessa IP nummer kan starta om noden, ?ndra inst?llningar, 
m.m. Notera att ALLA klienter har tillg?ng till direct disk I/O!
-FcpServer.allowedHostsFullAccess=V?rdar med full tillg?ng till FCP
-FcpServer.allowedHostsLong=IP addresser som f?r ansluta till FCP. Kan vara en 
komma separerad lista med IP nummer och ?ven CIDR maskerade IP;n som 
192.168.0.0/24. Varning! Alla med tillg?ng till FCP kan ladda upp och ner filer 
(noden f?rs?ker dock att inte skriva ?ver existerande filer).
-FcpServer.allowedHosts=Till?tna v?rddatorer (l?s varningen!)
-FcpServer.bindTo=IP adress att knyta till
-FcpServer.bindToLong=IP adress att knyta FCP till
-FcpServer.cannotStartOrStopOnTheFly=FCP kan inte startas eller stoppas n?r 
Freenet k?rs
-FcpServer.downloadsFileCanCreateCannotReadOrWrite=Filen skapades men kan inte 
l?sas eller skrivas
-FcpServer.downloadsFileDoesNotExistCannotCreate=Filen existerar redan och kan 
d?rf?r inte skapas
-FcpServer.downloadsFileExistsCannotReadOrWrite=Filen existerar men kan inte 
l?sas eller skrivas
-FcpServer.downloadsFileUnreadable=Filen finns men kan inte l?sas
-FcpServer.enablePersistentDownload=Aktivera ?terupptagningsbara nedladdningar?
-FcpServer.enablePersistentDownloadLong=Denna inst?llning g?r s? att du kan 
?teruppta en nedladdning efter omstart av Freenet. F?r att detta ska vara 
m?jligt kr?vs att nedladdningarna skrivs till disk vilket kan inneb?ra en fara 
f?r vissa.
-FcpServer.filenameToStorePData=Fil d?r ?terupptagningsbara nedladdningar sparas
-FcpServer.filenameToStorePDataLong=Fil d?r information om  ?terupptagningsbara 
nedladdningar sparas
-FcpServer.intervalBetweenWrites=Intervall mellan skrivning av information om 
?terupptagningsbara nedladdningar
-FcpServer.intervalBetweenWritesLong=Intervall mellan skrivning av information 
om ?terupptagningsbara nedladdningar (M?ts i milliseknder)
-FcpServer.isEnabled=Aktivera FCP?
-FcpServer.isEnabledLong=FCP (Freenet Client Protocol) anv?nds av externa 
program s?som Frost, Thaw och FreeMulET.
-FcpServer.portNumber=FCP port
-FcpServer.portNumberLong=FCP port nummer.
-FetchException.longError.10=Filen kunde inte hittas i arkivet
-FetchException.longError.13=Datan kunde inte hittas
-FetchException.longError.20=Felaktig URI
-FetchException.longError.21=F?r stor
-FetchException.longError.22=Metadatan ?r f?r stor
-FetchException.longError.23=F?r m?nga block per segment
-FetchException.longError.25=Avbruten
-FetchException.shortError.17=Internt fel
-FetchException.shortError.18=?verf?ringen misslyckades
-FetchException.shortError.20=Felaktig URI
-FetchException.shortError.23=F?r m?nga block per segment
-FetchException.shortError.28=All data kunde inte hittas
-FetchException.shortError.4=Felaktig metadata
 FProxyToadlet.abortToHomepage=Avbryt och ?terv?nd till FProxys hemsida
 FProxyToadlet.backToFProxy=${link}Klicka h?r${/link} f?r att g? till FProxys 
hemsida.
 FProxyToadlet.backToReferrer=${link}Klicka h?r${/link} f?r att g? tillbaka.
@@ -194,8 +162,8 @@
 FProxyToadlet.dangerousContentTitle=Potentiellt farligt ineh?ll
 FProxyToadlet.dangerousRSSSubtitle=RSS fl?de kan vara farligt
 FProxyToadlet.dangerousRSSTitle=Potentiellt farligt ineh?ll (RSS)
-FproxyToadlet.dangerousRSSTitle=Potentionellt farligt ineh?ll (RSS)
 FProxyToadlet.downloadInBackgroundToDisk=Ladda ner i bakgrunden och spara i 
downloads mappen
+FProxyToadlet.errorIsFatal=Det h?r ?r ett fatalt fel. Det ?r osannolikt att 
ett nytt f?rs?k kommer l?sa problemet.
 FProxyToadlet.errorWithReason=Fel: ${error}
 FProxyToadlet.expectedKeyButGot=Noden f?rv?ntade sig en Freenet nyckel, men 
fick :
 FProxyToadlet.explanationTitle=F?rklaring
@@ -212,14 +180,14 @@
 FProxyToadlet.internalErrorTitle=Internt fel
 FProxyToadlet.invalidKeyTitle=Ogiltig nyckel
 FProxyToadlet.invalidKeyWithReason=Ogiltig nyckel: ${reason}
+FProxyToadlet.largeFile=Stor fil
 FProxyToadlet.largeFileExplanationAndOptions=Freenet nyckeln du efterfr?gar ?r 
en stor fil. Filer i den h?r storleken kan oftast inte skickas direkt till din 
webbl?sare d? det tar f?r l?nge f?r Freenet att h?mta dem. F?ljande val finns:
-FProxyToadlet.largeFile=Stor fil
 FProxyToadlet.mimeType=MIME typ: ${mime}
 FProxyToadlet.notEnoughMetaStrings=Inte tillr?ckligt med meta-str?ngar
 FProxyToadlet.notFoundTitle=Ej hittad
 FProxyToadlet.openAsText=${link}Klicka h?r${/link} f?r att ?ppna filen som 
text (Ska inte vara farligt men ineh?llet kan visas felaktigt).
+FProxyToadlet.openForce=${link}Klicka h?r${/link} f?r att ?ppna filen som 
${mime} (L?s varningen ovan!).
 FProxyToadlet.openForceDisk=${link}Klicka h?r${/link} f?r att ladda ner filen 
med webbl?saren.
-FProxyToadlet.openForce=${link}Klicka h?r${/link} f?r att ?ppna filen som 
${mime} (L?s varningen ovan!).
 FProxyToadlet.openPossRSSAsForceDisk=${link}Klicka h?r${/link} f?r att tvinga 
webbl?saren att ladda ner filen (detta kan vara ${bold}farligt${/bold} om du 
anv?nder Firefox 2.0.0, i 2.0.1 ska problmet vara l?st)
 FProxyToadlet.openPossRSSAsPlainText=${link}Klicka h?r${/link} f?r att ?ppna 
filen som ren text (Detta kan vara ${bold}farligt${/bold} om du anv?nder IE7 
eller FF2).
 FProxyToadlet.openRSSAsRSS=${link}Klicka h?r${/link} f?r att ?ppna filen som 
RSS (detta ?r ${bold}farligt${/bold} ifall upphovsmannen ?r ondsint d? Freenet 
inte filtrerar RSS ?nnu).
@@ -229,30 +197,88 @@
 FProxyToadlet.pathNotFoundTitle=S?kv?g ej funnen
 FProxyToadlet.plugins=Konfigurera och hantera plugins
 FProxyToadlet.pluginsTitle=Plugins
+FProxyToadlet.queue=Upp / nedladdningar
 FProxyToadlet.queueTitle=K?
-FProxyToadlet.queue=Upp / nedladdningar
 FProxyToadlet.retryNow=F?rs?k igen
 FProxyToadlet.sizeLabel=Storlek:
 FProxyToadlet.sizeUnknown=Storlek: ok?nd
 FProxyToadlet.stats=Se statistik
 FProxyToadlet.statsTitle=Statistik
+FProxyToadlet.translation=Verktyg f?r att ?vers?tta nodens gr?nssnitt till 
ditt eget spr?k
 FProxyToadlet.translationTitle=?vers?ttning
-FProxyToadlet.translation=Verktyg f?r att ?vers?tta nodens gr?nssnitt till 
ditt eget spr?k
 FProxyToadlet.unableToRetrieve=Freenet kunde inte h?mta filen.
 FProxyToadlet.unknownMIMEType=MIME typ: ok?nd
 FProxyToadlet.welcome=Hemsida
 FProxyToadlet.welcomeTitle=Hem
+FcpServer.allowedHosts=Till?tna v?rddatorer (l?s varningen!)
+FcpServer.allowedHostsFullAccess=V?rdar med full tillg?ng till FCP
+FcpServer.allowedHostsFullAccessLong=IP adresser med full FCP tillg?ng till 
noden. Klienter med dessa IP nummer kan starta om noden, ?ndra inst?llningar, 
m.m. Notera att ALLA klienter har tillg?ng till direct disk I/O!
+FcpServer.allowedHostsLong=IP addresser som f?r ansluta till FCP. Kan vara en 
komma separerad lista med IP nummer och ?ven CIDR maskerade IP;n som 
192.168.0.0/24. Varning! Alla med tillg?ng till FCP kan ladda upp och ner filer 
(noden f?rs?ker dock att inte skriva ?ver existerande filer).
+FcpServer.bindTo=IP adress att knyta till
+FcpServer.bindToLong=IP adress att knyta FCP till
+FcpServer.cannotStartOrStopOnTheFly=FCP kan inte startas eller stoppas n?r 
Freenet k?rs
+FcpServer.downloadsFileCanCreateCannotReadOrWrite=Filen skapades men kan inte 
l?sas eller skrivas
+FcpServer.downloadsFileDoesNotExistCannotCreate=Filen existerar redan och kan 
d?rf?r inte skapas
+FcpServer.downloadsFileExistsCannotReadOrWrite=Filen existerar men kan inte 
l?sas eller skrivas
+FcpServer.downloadsFileUnreadable=Filen finns men kan inte l?sas
+FcpServer.enablePersistentDownload=Aktivera ?terupptagningsbara nedladdningar?
+FcpServer.enablePersistentDownloadLong=Denna inst?llning g?r s? att du kan 
?teruppta en nedladdning efter omstart av Freenet. F?r att detta ska vara 
m?jligt kr?vs att nedladdningarna skrivs till disk vilket kan inneb?ra en fara 
f?r vissa.
+FcpServer.filenameToStorePData=Fil d?r ?terupptagningsbara nedladdningar sparas
+FcpServer.filenameToStorePDataLong=Fil d?r information om  ?terupptagningsbara 
nedladdningar sparas
+FcpServer.intervalBetweenWrites=Intervall mellan skrivning av information om 
?terupptagningsbara nedladdningar
+FcpServer.intervalBetweenWritesLong=Intervall mellan skrivning av information 
om ?terupptagningsbara nedladdningar (M?ts i milliseknder)
+FcpServer.isEnabled=Aktivera FCP?
+FcpServer.isEnabledLong=FCP (Freenet Client Protocol) anv?nds av externa 
program s?som Frost, Thaw och FreeMulET.
+FcpServer.portNumber=FCP port
+FcpServer.portNumberLong=FCP port nummer.
+FetchException.longError.10=Filen kunde inte hittas i arkivet
+FetchException.longError.13=Datan kunde inte hittas
+FetchException.longError.15=En nod var ?verbelastad eller tog f?r l?ng tid p? 
sig
+FetchException.longError.17=Internt fel, antagligen en bugg
+FetchException.longError.20=Felaktig URI
+FetchException.longError.21=F?r stor
+FetchException.longError.22=Metadatan ?r f?r stor
+FetchException.longError.23=F?r m?nga block per segment
+FetchException.longError.25=Avbruten
+FetchException.shortError.13=Datan kunde inte hittas
+FetchException.shortError.17=Internt fel
+FetchException.shortError.18=?verf?ringen misslyckades
+FetchException.shortError.20=Felaktig URI
+FetchException.shortError.23=F?r m?nga block per segment
+FetchException.shortError.27=Ny URI
+FetchException.shortError.28=All data kunde inte hittas
+FetchException.shortError.4=Felaktig metadata
+FileOffer.acceptTransferButton=Acceptera ?verf?ring
+FileOffer.askUserTitle=Direkt fil?verf?ring
+FileOffer.commentLabel=Kommentar:
+FileOffer.failedReceiveHeader=?verf?ringen av ${filename} fr?n ${node} 
misslyckades.
+FileOffer.failedReceiveTitle=Fil?verf?ringen misslyckades
+FileOffer.fileLabel=Fil:
+FileOffer.mimeLabel=MIME Typ:
+FileOffer.offeredFileHeader=${name} f?rs?ker skicka en fil till dig:
+FileOffer.senderLabel=Avs?ndare:
+FileOffer.sizeLabel=Storlek:
+FileOffer.succeededReceiveHeader=?verf?ringen av ${filename} fr?n ${node} 
lyckades.
+FirstTimeWizardToadlet.congratz=V?lkommen till Freenet!
+FirstTimeWizardToadlet.noNetworkIF=Inget extra n?tverkskort kunde hittas
+FproxyToadlet.dangerousRSSTitle=Potentionellt farligt ineh?ll (RSS)
+GIFFilter.notGif=Filen du f?rs?ker h?mta ?r inte en GIF. Det kan vara n?got 
annat filformat som din webbl?sare kanske hanterar p? ett farligt s?tt s? 
d?rf?r har vi blockerat den.
+GIFFilter.tooShort=Filen ?r f?r kort f?r att vara en GIF.
+GIFFilter.tooShortTitle=F?r kort
 HTMLFilter.deletedUnknownStyle=Raderade ok?nd stil
+IPUndetectedUserAlert.detecting=Freenet f?rs?ker f?r tillf?llet detektera din 
externa IP adress. Om detta tar mer ?n n?gra minuter s? ?r n?got fel.
+IPUndetectedUserAlert.unknownAddressTitle=Ok?nd extern adress
 InsertException.longError.10=Avbruten av anv?ndaren
 InsertException.longError.3=Internt fel
+InsertException.shortError.1=Felaktig URI
 InsertException.shortError.10=Avbruten
-InsertException.shortError.1=Felaktig URI
 InsertException.shortError.3=Internt fel
-IPUndetectedUserAlert.detecting=Freenet f?rs?ker f?r tillf?llet detektera din 
externa IP adress. Om detta tar mer ?n n?gra minuter s? ?r n?got fel.
-IPUndetectedUserAlert.unknownAddressTitle=Ok?nd extern adress
+JPEGFilter.notJpeg=Filen du f?rs?ker h?mta ?r inte en JPEG. Det kan vara n?got 
annat filformat som din webbl?sare kanske hanterar p? ett farligt s?tt s? 
d?rf?r har vi blockerat den.
 JPEGFilter.tooShort=Filen ?r f?r liten f?r att vara en JPEG bild.
 KnownUnsafeContentTypeException.dangerousLinksLabel=Farliga l?nkar:
 KnownUnsafeContentTypeException.dangerousMetadataLabel=Farlig metadata:
+KnownUnsafeContentTypeException.dangerousScriptsLabel=Farligt skript:
+KnownUnsafeContentTypeException.noFilter=Eftersom det inte finns n?got inbyggt 
filter f?r den h?r typen av data s? b?r du iakta stor f?rsiktighet!
 KnownUnsafeContentTypeException.title=Datatypen ?r k?nd f?r att vara farlig: 
${type}
 LocalFileInsertToadlet.checkPathExist=Kontroller att den angivna s?kv?gen 
finns.
 LocalFileInsertToadlet.checkPathIsDir=Kontrollera att den angivna s?kv?gen 
leder till en mapp.
@@ -264,25 +290,25 @@
 LocalFileInsertToadlet.sizeHeader=Storlek
 LogConfigHandler.detaildPriorityThreshold=Detaljerad loggningsniv?
 LogConfigHandler.detaildPriorityThresholdLong=Detaljerad loggningsniv?, 
exempelvis  freenet:normal,freenet.node:minor
+LogConfigHandler.dirName=Mapp f?r loggfiler
 LogConfigHandler.dirNameLong=Mapp d?r loggfiler sparas
-LogConfigHandler.dirName=Mapp f?r loggfiler
 LogConfigHandler.enabled=Aktvera loggning?
 LogConfigHandler.enabledLong=Ja aktiverar loggning (Loggnings niv? st?lls in 
separat) Nej inaktiverar all loggning.
+LogConfigHandler.maxCachedBytes=Maximal storlek p? loggar i RAM minnet.
 LogConfigHandler.maxCachedBytesLong=Maximal storlek p? loggar i Ram minnet
-LogConfigHandler.maxCachedBytes=Maximal storlek p? loggar i RAM minnet.
+LogConfigHandler.maxCachedLines=Maximalt antal loggade linjer i RAM minnet
 LogConfigHandler.maxCachedLinesLong=Maximalt antal loggade linjer i RAM minnet
-LogConfigHandler.maxCachedLines=Maximalt antal loggade linjer i RAM minnet
+LogConfigHandler.maxZippedLogsSize=Maximalt utrymme f?r gamla logg filer
 LogConfigHandler.maxZippedLogsSizeLong=Maximalt utrymme f?r gamla logg filer
-LogConfigHandler.maxZippedLogsSize=Maximalt utrymme f?r gamla logg filer
 LogConfigHandler.minLoggingPriority=Loggningsniv?
 LogConfigHandler.minLoggingPriorityLong=Loggningsniv?
+LogConfigHandler.rotationInterval=Tidsintervall f?r logg rotation
 LogConfigHandler.rotationIntervalLong=Logg rotations intervall - Tidsintervall 
f?r logg rotation. Vi sparar de senaste 2 logg filerna (Nuvarande och 
f?reg?ende) ?ldre logg filer komprimeras och sparas. Max disk utrymme f?r 
sparade logg filer st?lls in separat.
-LogConfigHandler.rotationInterval=Tidsintervall f?r logg rotation
 LoggerHook.unrecognizedPriority=Ok?nd prioritet: ${name}.
 MeaningfulNodeNameUserAlert.noNodeNickTitle=Du har inte angett ett namn till 
din nod.
 N2NTMToadlet.composingMessageLabel=Skriv meddelande som ska skickas till 
f?ljande:
+N2NTMToadlet.delayed=Upptagen: S?ndning av meddelande kan vara f?rsenad
 N2NTMToadlet.delayedTitle=F?rsenad
-N2NTMToadlet.delayed=Upptagen: S?ndning av meddelande kan vara f?rsenad
 N2NTMToadlet.failed=Meddelandet kunde inte skickas: Noden ?r inte ansluten.
 N2NTMToadlet.failedTitle=Misslyckades
 N2NTMToadlet.friends=V?nner
@@ -295,8 +321,8 @@
 N2NTMToadlet.queuedTitle=K?ad
 N2NTMToadlet.returnToFriends=?terg? till V?nner
 N2NTMToadlet.returnToNodeHomepage=?terv?nd till hemsidan
+N2NTMToadlet.sendMessage=Skicka nod till nod text meddelande
 N2NTMToadlet.sendMessageShort=Skicka meddelande
-N2NTMToadlet.sendMessage=Skicka nod till nod text meddelande
 N2NTMToadlet.sendStatus=N2NTM s?nd status
 N2NTMToadlet.sent=Meddelande skickat till noden
 N2NTMToadlet.sentTitle=Skickat
@@ -312,82 +338,90 @@
 Node.buggyJVM=JVM;en du anv?nder (${version}) ?r k?nd f?r att vara buggig. Du 
kan f? felmeddelanden som s?ger att du har slut p? minne n?r det egentligen 
finns rikligt med minne kvar. V?nligen uppgradera till minst Sun Java 1.4.2_13, 
1.5.0_10 or 1.6 (rekommenderas). Se 
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4855795 .
 Node.buggyJVMTitle=Varning f?r buggig JVM
 Node.bwlimitMustBePositive=Bandbreddsbegr?nsningen m?ste vara h?gre ?n 0
-NodeClientCore.allowInsecureCHKLong=F?re version 1010 var alla CHKs os?kra 
(endast halv kryptering anv?ndes). Vill du till?ta gamla CHKs?
-NodeClientCore.allowInsecureCHK=Till?t os?kra CHKs?
-NodeClientCore.allowInsecureSSKLong=F?re version 1010 var alla SSKs os?kra 
(endast halv kryptering anv?ndes). Vill du till?ta gamla SSKs?
-NodeClientCore.allowInsecureSSK=Till?t os?kra SSKs?
-NodeClientCore.couldNotFindOrCreateDir=Kunde inte hitta eller skapa mappen
-NodeClientCore.downloadAllowedDirs=Mapp/ar dit nedladdning ?r till?ten
-NodeClientCore.downloadDirLong=f?rvald nedladdningsmapp
-NodeClientCore.downloadDir=Nedladdningsmapp
-NodeClientCore.fileForClientStats=Fil d?r klient statistik lagras
-NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=V?rdet m?ste vara st?rre ?n 
noll
-NodeClientCore.tempDirLong=Mapp f?r tempor?ra filer.
-NodeClientCore.tempDir=Mapp f?r tempor?ra filer
-NodeClientCore.uploadAllowedDirs=Mappar som uppladdning till?ts fr?n
 Node.databaseMemory=Max utrymme f?r freenets databas
 Node.disableHangCheckers=Avativera alla h?ngningskontroller
 Node.errorApplyingConfig=Ett fel uppstod n?r de nya inst?llningarna skulle 
anv?ndas : ${error}
+Node.extraPeerDir=mapp till extra peer data
 Node.extraPeerDirLong=Mapp f?r extra peer data
-Node.extraPeerDir=mapp till extra peer data
+Node.inBWLimit=Maximal nedladdningshastighet (bytes per sekund)
 Node.inBWLimitLong=Maximal nedladningshastighet (bytes per sekund), noden ska 
n?stan aldrig ?verskrida detta v?rde, -1 = 4 * inst?lld uppladdningshastighet.
-Node.inBWLimitLong=Maximal nedladningshastighet (bytes per sekund), noden ska 
n?stan aldrig ?verskrida detta v?rde, -1 = 4x inst?lld uppladdningshastighet.
-Node.inBWLimit=Maximal nedladdningshastighet (bytes per sekund)
 Node.invalidStoreSize=Lagringsutrymmet m?ste vara minst 32MB
-NodeIPDectector.inclLocalAddress=Inkludera lokala adresser i nod-referens?
-NodeIPDectector.tempAddressHintLong=Tempor?r fingervisning om v?r IP adress, 
raderas efter anv?ndning.
-NodeIPDectector.tempAddressHint=Tempor?r fingervisning om v?r IP adress
-NodeIPDetector.maybeSymmetricTitle=Anslutnings problem
-NodeIPDetector.unknownHostErrorInIPOverride=Ok?nd v?rd: ${error}
+Node.l10nLanguage=Spr?k
 Node.l10nLanguageLong=Den h?r inst?llningen ?ndrar spr?ket i din nods 
web-gr?nssnitt. Vissa ?vers?ttningar kan beh?va en omstart f?r att fungera.
-Node.l10nLanguage=Spr?k
 Node.maxHTL=Max HTL
 Node.mustBePositive=V?rdet m?ste vara positivt
+Node.nodeDir=Nod mapp
 Node.nodeDirLong=Mapp d?r nodrelaterade filer sparas.
-Node.nodeDir=Nod mapp
+Node.nodeName=Nodnamn
 Node.nodeNameLong=Nodnamn. Synligt endast f?r de noder du ?r ansluten till.
-Node.nodeName=Nodnamn
+Node.outBWLimit=Maximal uppladningshastighet (bytes per sekund)
 Node.outBWLimitLong=Maximal uppladningshastighet (bytes per sekund), noden ska 
n?stan aldrig ?verskrida detta v?rde.
-Node.outBWLimit=Maximal uppladningshastighet (bytes per sekund)
 Node.port=FNP port (UDP)
 Node.portLong=UDP port f?r nod-till-nod kommunikation (Freenet Node Protocol)
+Node.storeDirectory=Mapp f?r datalagring
+Node.storeDirectoryLong=Mapp f?r datalagring
+Node.storeMaxMemTooHigh=Att ge mer ?n 80% av ram minnet till BDB (Berkeley 
DataBase) ?r antagligen inte vad du vill g?ra!
+Node.storeSize=Max storlek p? Freenets datalagring
+Node.storeSizeLong=Max storlek p? Freenets datalagring, det utrymme i din nod 
som anv?nds till freenets datalagring. St?rre data utrymme ?n standard ?r att 
f?redra.
+NodeClientCore.allowInsecureCHK=Till?t os?kra CHKs?
+NodeClientCore.allowInsecureCHKLong=F?re version 1010 var alla CHKs os?kra 
(endast halv kryptering anv?ndes). Vill du till?ta gamla CHKs?
+NodeClientCore.allowInsecureSSK=Till?t os?kra SSKs?
+NodeClientCore.allowInsecureSSKLong=F?re version 1010 var alla SSKs os?kra 
(endast halv kryptering anv?ndes). Vill du till?ta gamla SSKs?
+NodeClientCore.couldNotFindOrCreateDir=Kunde inte hitta eller skapa mappen
+NodeClientCore.downloadAllowedDirs=Mapp/ar dit nedladdning ?r till?ten
+NodeClientCore.downloadDir=Nedladdningsmapp
+NodeClientCore.downloadDirLong=f?rvald nedladdningsmapp
+NodeClientCore.fileForClientStats=Fil d?r klient statistik lagras
+NodeClientCore.maxUSKFetchers=Maximalt antal USK h?mtare
+NodeClientCore.maxUSKFetchersLong=Maximalt antal USK h?mtare
+NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=V?rdet m?ste vara st?rre ?n 
noll
+NodeClientCore.movingTempDirOnTheFlyNotSupported=Flytt av temp mappen kan inte 
g?ras n?r noden ?r ig?ng
+NodeClientCore.tempDir=Mapp f?r tempor?ra filer
+NodeClientCore.tempDirLong=Mapp f?r tempor?ra filer.
+NodeClientCore.uploadAllowedDirs=Mappar som uppladdning till?ts fr?n
+NodeIPDectector.inclLocalAddress=Inkludera lokala adresser i nod-referens?
+NodeIPDectector.ipOverride=?verskrid IP adress
+NodeIPDectector.ipOverrideLong=?verskridning av IP adress (beh?vs oftast inte) 
- Ifall du har en fast IP adress eller ett dom?n namn (t.e.x. via dyndns) kan 
du ange den/det h?r ifall du ?r bakom en brandv?gg.
+NodeIPDectector.tempAddressHint=Tempor?r fingervisning om v?r IP adress
+NodeIPDectector.tempAddressHintLong=Tempor?r fingervisning om v?r IP adress, 
raderas efter anv?ndning.
+NodeIPDetector.maybeSymmetricTitle=Anslutnings problem
+NodeIPDetector.unknownHostErrorInIPOverride=Ok?nd v?rd: ${error}
 NodeStat.aggressiveGC=Aggressiv GC modifikator
 NodeStat.aggressiveGCLong=L?ter anv?ndaren ?ndra tiden mellan GC och tvingat 
avslut. SKA INTE ?NDRAS om du inte vet vad du g?r! -1 betyder : st?ng av 
tvingat anrop till System.gc() och System.runFinalization()
 NodeStat.memCheck=Aktivera minneskontrollen
 NodeStat.memCheckLong=Aktivera minneskontrollen (Skriver ett meddelande i 
logg, N?dv?ndig f?r aggressiv GC modifikator)
-NodeStats.mustBePercentValueNotFull=V?rdet m?ste vara mellan 0-99 (uttrycks i 
procent).
 NodeStat.statsPersister=Fil d?r nodstatistik lagras
 NodeStat.statsPersisterLong=Fil att lagra nod statistik i (klient statistik 
lagras inte) Statistiken anv?nds f?r att avg?ra om f?rfr?gningar ska 
accepteras, radera INTE denna fil!
+NodeStat.threadLimit=Max antal tr?dar
+NodeStat.threadLimitLong=Noden f?rs?ker att inte ?verskrida max antal tr?dar 
genom att v?gra nya anrop.
+NodeStats.mustBePercentValueNotFull=V?rdet m?ste vara mellan 0-99 (uttrycks i 
procent).
 NodeStats.valueTooLow=V?rdet f?r den h?r inst?llningen ?r f?r l?gt, du m?ste 
?ka det!
-NodeStat.threadLimitLong=Noden f?rs?ker att inte ?verskrida max antal tr?dar 
genom att v?gra nya anrop.
-NodeStat.threadLimit=Max antal tr?dar
-Node.storeDirectoryLong=Mapp f?r datalagring
-Node.storeDirectory=Mapp f?r datalagring
-Node.storeMaxMemTooHigh=Att ge mer ?n 80% av ram minnet till BDB (Berkeley 
DataBase) ?r antagligen inte vad du vill g?ra!
-Node.storeSizeLong=Max storlek p? Freenets datalagring, det utrymme i din nod 
som anv?nds till freenets datalagring. St?rre data utrymme ?n standard ?r att 
f?redra.
-Node.storeSize=Max storlek p? Freenets datalagring
 NodeUpdateManager.enabled=Ladda ner nya versioner automatiskt?
 NodeUpdateManager.enabledLong=Ska noden s?ka efter och ladda ner upgraderingar 
automatiskt? Den h?r inst?llningen st?ller sig p? false om noden inte k?rs inom 
wrappern.
 NodeUpdateManager.extURI=Adress d?r noden letar efter uppdateringar till 
freenet-ext.jar
 NodeUpdateManager.extURILong=Adress d?r noden letar efter uppdateringar till 
freenet-ext.jar
 NodeUpdateManager.installNewVersions=Installera nya versioner automatiskt?
 NodeUpdateManager.installNewVersionsLong=Ska nya Freenet versioner installeras 
automatiskt utan att f?rst fr?ga?
+NodeUpdateManager.invalidExtURI=Felaktig ext URI: ${error}
+NodeUpdateManager.invalidUpdateURI=Felaktig uppdaterings URI: ${error}
 NodeUpdateManager.noUpdateWithoutWrapper=Kan inte uppdatera eftersom Freenet 
inte k?rs med wrappern
+NodeUpdateManager.updateFailed=Uppdateringen misslyckades: ${reason}
 NodeUpdateManager.updateFailedTitle=Uppdateringen misslyckades!
-NodeUpdateManager.updateFailed=Uppdateringen misslyckades: ${reason}
 NodeUpdateManager.updateURI=Adress d?r noden letar efter uppdateringar
 NodeUpdateManager.updateURILong=Adress d?r noden letar efter uppdateringar
+PNGFilter.invalidHeader=Filen du f?rs?ker h?mta ?r inte en PNG. Det kan vara 
n?got annat filformat som din webbl?sare kanske hanterar p? ett farligt s?tt s? 
d?rf?r har vi blockerat den.
 PeerManagerUserAlert.noConnsTitle=Inga ?ppna anslutningar
 PeerManagerUserAlert.noPeersTitle=Inga peers hittades
+PeerManagerUserAlert.onlyFewConnsTitle=Endast ${count} ?ppen/?ppna 
anslutning(ar)
 PeerManagerUserAlert.tooHighPingTimeTitle=Nodens medel ping tid ?r f?r h?g
 PeerManagerUserAlert.tooManyConnsTitle=F?r m?nga ?ppna anslutningar
 PeerManagerUserAlert.tooManyDisconnectedTitle=F?r m?nga icke anslutna peers
 PeerManagerUserAlert.tooManyNeverConnectedTitle=M?nga peers har aldrig anslutit
 PeerManagerUserAlert.tooManyPeersTitle=F?r m?nga peers
+PluginManager.loadedOnStartup=Plugins som ska laddas vid uppstart
 PluginManager.loadedOnStartupLong=Classpath, namn och s?kv?g till plugins som 
ska startas samtidigt som noden.
-PluginManager.loadedOnStartup=Plugins som ska laddas vid uppstart
+PluginManager.loadedPlugins=Plugins som ska laddas vid uppstart
 PluginManager.loadedPluginsLong=En lista med plugins som ska startas samtidigt 
som noden.
-PluginManager.loadedPlugins=Plugins som ska laddas vid uppstart
 PluginManager.pluginReqNewerJVM=Pluginen ${name} verkar kr?va en nyare JVM. 
V?nligen installera minst Sun java 1.5, eller ta bort pluginen.
 PluginManager.pluginReqNewerJVMTitle=Nyare JVM kr?vs av pluginen ${name}.
 PluginToadlet.addPluginTitle=L?gg till plugin
@@ -412,36 +446,37 @@
 PproxyToadlet.loadPluginLabel=Plugin att ladda:
 PproxyToadlet.noPlugins=Inga plugins laddade
 PproxyToadlet.pluginNotFoundReloadTitle=Pluginen kunde inte hittas (laddar om)
+PproxyToadlet.pluginUnloaded=Plugin stoppad
+PproxyToadlet.pluginUnloadedWithName=Pluginen ${name} har stoppats.
 PproxyToadlet.plugins=Plugins
 PproxyToadlet.pluginsWithNodeName=Plugins f?r ${name}
-PproxyToadlet.pluginUnloaded=Plugin stoppad
-PproxyToadlet.pluginUnloadedWithName=Pluginen ${name} har stoppats.
 PproxyToadlet.reload=Starta om
 PproxyToadlet.returnToPluginPage=?terg? till plugins sidan
 PproxyToadlet.startedAtTitle=Startad vid
+PproxyToadlet.unload=Stoppa
 PproxyToadlet.unloadPluginTitle=Stoppa plugin?
 PproxyToadlet.unloadPluginWithName=Vill du verkligen stoppa ${name}?
-PproxyToadlet.unload=Stoppa
+QueueToadlet.DUinProgress=P?g?ende mapp uppladdningar
+QueueToadlet.DinProgress=P?g?ende nedladdningar (${size})
+QueueToadlet.UinProgress=P?g?ende uppladdningar (${size})
 QueueToadlet.change=?ndra
+QueueToadlet.completedDU=Slutf?rda mapp uppladdningar
 QueueToadlet.completedDinDownloadDirectory=Slutf?rda nedladdningar till 
download mappen (${size})
 QueueToadlet.completedDinTempDirectory=Slutf?rda nedladdningar till tempor?r 
mapp (${size})
 QueueToadlet.completedDtoDisk=Slutf?rda nedladdningar
 QueueToadlet.completedDtoTemp=Slutf?rda nedladdningar till temp
-QueueToadlet.completedDU=Slutf?rda mapp uppladdningar
+QueueToadlet.completedU=Slutf?rda uppladdningar (${size})
 QueueToadlet.completedUDirectory=Slutf?rda mapp uppladdningar (${size})
-QueueToadlet.completedU=Slutf?rda uppladdningar (${size})
 QueueToadlet.delete=Ta bort
-QueueToadlet.DinProgress=P?g?ende nedladdningar (${size})
 QueueToadlet.download=Ladda ner
-QueueToadlet.DUinProgress=P?g?ende mapp uppladdningar
 QueueToadlet.emergency=N?dfall
 QueueToadlet.errorAccessDenied=Fel: ?tkomst nekad!
 QueueToadlet.errorAccessDeniedFile=De nuvarande inst?llningarna medger inte 
uppladdning av "${file}".
+QueueToadlet.errorDToDisk=Kan inte ladda ner till h?rddisk
+QueueToadlet.errorDToDiskConfig=Nodens nuvarande inst?llningar till?ter inte 
nedladdningar till downloads mappen.
 QueueToadlet.errorDownloadNotCompleted=Nedladdning inte slutf?rd
+QueueToadlet.errorDownloadNotFound=Nedladdning inte hittad
 QueueToadlet.errorDownloadNotFoundExplanation=Nedladdningen kunde inte hittas. 
?r den redan raderad?
-QueueToadlet.errorDownloadNotFound=Nedladdning inte hittad
-QueueToadlet.errorDToDiskConfig=Nodens nuvarande inst?llningar till?ter inte 
nedladdningar till downloads mappen.
-QueueToadlet.errorDToDisk=Kan inte ladda ner till h?rddisk
 QueueToadlet.errorInvalidURI=Felaktig URI
 QueueToadlet.errorInvalidURIToD=URI;n ?r felaktig och kan inte laddas ner.
 QueueToadlet.errorInvalidURIToU=Du angav inte en giltig URI att ladda upp 
filen med.
@@ -452,8 +487,8 @@
 QueueToadlet.errorNoKeyToD=Du angav ingen nyckel.
 QueueToadlet.failedD=Misslyckade nedladdningar
 QueueToadlet.failedDU=Misslyckad mapp uppladdning
+QueueToadlet.failedToRemove=Misslyckades med att ta bort ${id}: ${message}
 QueueToadlet.failedToRemoveId=Kunde inte ta bort: ${id}
-QueueToadlet.failedToRemove=Misslyckades med att ta bort ${id}: ${message}
 QueueToadlet.failedToRemoveRequest=Misslyckades med att ta bort f?rfr?gan
 QueueToadlet.failedToRestart=Kunde inte starta om: ${id}
 QueueToadlet.failedToRestartRequest=Omstart misslyckades
@@ -469,18 +504,18 @@
 QueueToadlet.insertFile=Ladda upp fil
 QueueToadlet.key=Nyckel
 QueueToadlet.legend=Historia
-QueueToadlet.legend=Prioritet
 QueueToadlet.low=L?g
 QueueToadlet.medium=Medium
 QueueToadlet.mimeType=MIME Typ
+QueueToadlet.noTaskOnGlobalQueue=Den globala k?n ?r tom.
 QueueToadlet.none=ingen
-QueueToadlet.noTaskOnGlobalQueue=Den globala k?n ?r tom.
+QueueToadlet.panicButton=Panik knapp
 QueueToadlet.panicButtonConfirmation=Radera allt utan att fr?ga!\u00a0
-QueueToadlet.panicButton=Panik knapp
 QueueToadlet.persistenceForever=F?revigt
 QueueToadlet.persistenceNone=ingen
-QueueToadlet.persistenceRebootr=starta om
+QueueToadlet.persistenceReboot=starta om
 QueueToadlet.pleaseEnableFCP=Du m?ste aktivera FCP f?r att f? ?tkomst till den 
h?r sidan.
+QueueToadlet.priority=Prioritet
 QueueToadlet.priority0=n?dfall
 QueueToadlet.priority1=v?ldigt h?g
 QueueToadlet.priority2=h?g
@@ -488,45 +523,46 @@
 QueueToadlet.priority4=l?g
 QueueToadlet.priority5=v?ldigt l?g
 QueueToadlet.priority6=pause
-QueueToadlet.priority=Prioritet
+QueueToadlet.progress=Nedladdat
 QueueToadlet.progressbarAccurate=V?rdet st?mmer
 QueueToadlet.progressbarNotAccurate=V?rdet kommer troligen att ?ndras eftersom 
nedladdningen inte kommit nog l?ngt
-QueueToadlet.progress=Nedladdat
 QueueToadlet.reason=Orsak
+QueueToadlet.remove=Ta bort
 QueueToadlet.requestNavigation=Navigation
 QueueToadlet.restart=Omstart
 QueueToadlet.size=Storlek
 QueueToadlet.starting=STARTAR
 QueueToadlet.title=Global k? f?r ${nodeName}
 QueueToadlet.totalSize=Total storlek
-QueueToadlet.UinProgress=P?g?ende uppladdningar (${size})
 QueueToadlet.unknown=Ok?nd
 QueueToadlet.veryhigh=V?ldigt h?g
 QueueToadlet.verylow=V?ldigt l?g
+QueueToadlet.warningUnsafeContent=Potentionellt farligt ineh?ll
 QueueToadlet.warningUnsafeContentExplanation=Filen du vill ladda ner filtreras 
inte av Freenet! Din anonymitet kan hotas om du ?ppnar denna fil!
-QueueToadlet.warningUnsafeContent=Potentionellt farligt ineh?ll
 QueueToadlet.willneverfinish=Pause
 QueueToadlet.wipD=P?g?ende nedladdningar (${size})
 QueueToadlet.wipDU=P?g?ende mapp uppladdningar (${size})
 QueueToadlet.wipU=P?g?ende uppladdningar (${size})
 SimpleToadletServer.advancedMode=Aktivera avancerat l?ge?
 SimpleToadletServer.advancedModeLong=Ska information och inst?llningar avsedda 
f?r avancerade anv?ndare/utvecklare visas?   Denna inst?llning ska normalt vara 
avst?ngd.
+SimpleToadletServer.allowedFullAccess=V?rdar med full tillg?ng til  Fproxy 
(L?s Varning)
 SimpleToadletServer.allowedFullAccessLong=V?rdar med full FProxy tillg?ng till 
noden, dessa kan starta om noden, ?ndra inst?llningar, m.m.. VARNING: Var 
f?rsiktig med vilka du ger full tillg?ng till noden!
-SimpleToadletServer.allowedFullAccess=V?rdar med full tillg?ng til  Fproxy 
(L?s Varning)
+SimpleToadletServer.allowedHosts=V?rdnamn eller IP addresser som f?r ansluta 
till FProxy.
 SimpleToadletServer.allowedHostsLong=Kan vara en komma separerad lista med IP 
nummer och ?ven CIDR maskerade IP;n som 192.168.0.0/24. Notera att dessa har 
tillg?ng till h?rddisken inom ramarna f?r en annan inst?llning.
-SimpleToadletServer.allowedHosts=V?rdnamn eller IP addresser som f?r ansluta 
till FProxy.
 SimpleToadletServer.bindTo=IP adress att knyta till
 SimpleToadletServer.bindToLong=IP adress att knyta FProxy till
+SimpleToadletServer.cannotChangePortOnTheFly=Kan inte ?ndra FProxy;s port 
nummer n?r noden ?r ig?ng
+SimpleToadletServer.couldNotChangeBindTo=Kunde inte ?ndra adressen som FProxy 
knyts till: ${error}.
+SimpleToadletServer.cssName=Stilmall
 SimpleToadletServer.cssNameLong=Namnet p? den stilmall du vill anv?nda.
-SimpleToadletServer.cssName=Stilmall
 SimpleToadletServer.cssOverride=Ladda egen stilmall (VARNING!)
 SimpleToadletServer.cssOverrideLong=Du kan ladda in en egen stilmall. VARNING: 
Stilmallar kan vara farliga och filtreras inte! Anv?nd p? egen risk. (F?rdiga 
stilmallar kan skickas till devl at freenetproject.org f?r att inkluderas i 
Freenet ;) )
+SimpleToadletServer.enableJS=Ska FProxy f? anv?nda Javascript?
+SimpleToadletServer.enableJSLong=Ska FProxy f? anv?nda Javascript "Hj?lpare"? 
Den h?r inst?llningen ska normalt vara avst?ngd. Den h?r inst?llningen g?r inte 
s? att Freesiter kan anv?nda Javascript.
 SimpleToadletServer.enabled=Aktivera FProxy?
 SimpleToadletServer.enabledLong=Aktivera FProxy och tillh?rande HTTP tj?nster.
-SimpleToadletServer.enableJSLong=Ska FProxy f? anv?nda Javascript "Hj?lpare"? 
Den h?r inst?llningen ska normalt vara avst?ngd. Den h?r inst?llningen g?r inte 
s? att Freesiter kan anv?nda Javascript.
-SimpleToadletServer.enableJS=Ska FProxy f? anv?nda Javascript?
+SimpleToadletServer.panicButton=Visa panik knappen?
 SimpleToadletServer.panicButtonLong=Ska panik knappen synas p? /queue/ sidan.
-SimpleToadletServer.panicButton=Visa panik knappen?
 SimpleToadletServer.port=FProxy port
 SimpleToadletServer.portLong=FProxy port nummer
 StaticToadlet.pathInvalidChars=URI;n ineh?ller ogiltiga tecken.
@@ -557,21 +593,23 @@
 SymlinkerToadlet.symlinks=Syml?nkar i ToadletServer
 TestnetHandler.enable=Aktivera testnet L?ge? (FARLIGT)
 TestnetHandler.enableLong=Ska testnet aktiveras (FARLIGT!). Testnet eliminerar 
din anonymitet i utbyte mot att du hj?lper utvecklarna att debugga din nod.
+TestnetHandler.port=Testnet port
 TestnetHandler.portLong=Testnet port nummer (-1 = lystningsPort+1000)
-TestnetHandler.port=Testnet port
+TextModeClientInterfaceServer.allowedHosts=Till?tna v?rdar
 TextModeClientInterfaceServer.allowedHostsLong=V?rdnamn eller IP addresser som 
f?r ansluta till TMCI. Kan vara en komma separerad lista med v?rdnamn, IP 
nummer och ?ven CIDR maskerade IP;n som 192.168.0.0/24
-TextModeClientInterfaceServer.allowedHosts=Till?tna v?rdar
 TextModeClientInterfaceServer.bindTo=IP adress att knyta till
 TextModeClientInterfaceServer.bindToLong=IP adress att knyta TMCI till
+TextModeClientInterfaceServer.enableInputOutput=Aktivera vid stdout/stdin?
 TextModeClientInterfaceServer.enabled=Aktivera TMCI
 TextModeClientInterfaceServer.enabledLong=Ska TMCI (Text Mode Client 
Interface) aktiveras
-TextModeClientInterfaceServer.enableInputOutput=Aktivera vid stdout/stdin?
+TextModeClientInterfaceServer.telnetPortNumber=Telnet port
 TextModeClientInterfaceServer.telnetPortNumberLong=Telnet port nummer
-TextModeClientInterfaceServer.telnetPortNumber=Telnet port
 Toadlet.cancel=Avbryt
 Toadlet.clickHere=Klicka h?r
+Toadlet.homepage=Hemsida
 Toadlet.internalErrorPleaseReport=Internt fel  : v?nligen rapportera
 Toadlet.internalErrorTitle=Internt fel
+Toadlet.no=Nej
 Toadlet.nodeHomepage=Nod hemsida
 Toadlet.notSupportedTitle=St?ds ej
 Toadlet.notSupportedWithClass=Din webb l?sare skickade en f?rfr?gan som 
Freenet (${class}) inte f?rstod.
@@ -581,6 +619,8 @@
 Toadlet.returnToPrevPage=?terg? till f?rra sidan
 Toadlet.tempRedirectWithReason=Tempor?r omdirigering: ${reason}
 Toadlet.unauthorized=Du har inte r?ttigheter att bes?ka den h?r sidan.
+Toadlet.yes=Ja
+ToadletContextImpl.methodNotAllowed=HTTP Metoden ?r inte till?ten
 TranslationToadlet.bracketRemoveOverride=(Radera ?vers?ttningen!)
 TranslationToadlet.bracketTranslateIt=(?vers?tt till ditt spr?k!)
 TranslationToadlet.bracketUpdateTranslation=(Uppdatera ?vers?ttningen)
@@ -591,36 +631,46 @@
 TranslationToadlet.hideAlreadyTranslated=G?m str?ngar som redan ?versatts
 TranslationToadlet.originalVersionLabel=Original (engelsk version)
 TranslationToadlet.reEdit=?ndra ?vers?ttningen
+TranslationToadlet.remove=Ta bort
 TranslationToadlet.removeOverrideTitle=Radera ?vers?ttning
 TranslationToadlet.removeOverrideWarningTitle=Du ?r p? v?g att radera en 
?vers?ttning!
-TranslationToadlet.remove=Ta bort
 TranslationToadlet.returnToTranslations=?terv?nd till ?vers?ttnings sidan
 TranslationToadlet.showEverything=Visa allt, inkluderar redan ?versatta 
str?ngar
 TranslationToadlet.translationKeyLabel=?vers?ttnings nyckel
+TranslationToadlet.translationUpdateTitle=Uppdatera ?vers?ttning
 TranslationToadlet.translationUpdatedTitle=Uppdatera ?vers?ttning!
-TranslationToadlet.translationUpdateTitle=Uppdatera ?vers?ttning
 TranslationToadlet.updateTranslationCommand=Uppdatera ?vers?ttningen!
 UnknownContentTypeException.title=Ok?nt och potentiellt farligt ineh?ll: 
${type}
 UpdateDeployContext.cannotUpdateNoExtJar=Hittade inte freenet-ext.jar i 
wrapper.conf (freenet.jar hittades: ${mainFilename})
 UpdateDeployContext.cannotUpdateNoJars=Kunde inte hitta Freenet jars i 
wrapper.conf
 UpdateDeployContext.cannotUpdateNoMainJar=Hittade inte freenet.jar i 
wrapper.conf (freenet-ext.jar hitades: ${extFilename})
+UpdateDeployContext.updateCatastrophe=KATASTROFALT FEL: Raderade ${old} men 
kunde inte byta namn p? ${new} till ${old} d?rf?r kommer noden INTE ATT STARTAS 
OM! V?nligen byt namn p?  ${new} till ${old} manuellt.
+UpdateDeployContext.updateFailedCannotDeleteOldConfig=Kan inte radera ${old} 
s? d?rf?r kan inte en ny skrivas. Uppdatering misslyckades.
 UpdatedVersionAvailableUserAlert.armed=Noden kommer att startas om d? den nya 
versionen har laddats ner och verifierats.
 UpdatedVersionAvailableUserAlert.clickToUpdateASAP=Klicka nedan f?r att 
uppdatera din nod s? fort som uppdateringen har verifierats.
 UpdatedVersionAvailableUserAlert.clickToUpdateNow=Klicka nedan f?r att 
uppdatera din nod direkt.
 UpdatedVersionAvailableUserAlert.downloadedNewExtJar=Din nod har laddat ner en 
ny version av Freenet extra jar, version ${version}.
 UpdatedVersionAvailableUserAlert.downloadedNewJar=Noden har laddat ner en ny 
version av Freenet, version ${version}.
+UpdatedVersionAvailableUserAlert.fetchingNewBoth=Din nod laddar nu ner en ny 
version av Freenet (nod version ${nodeVersion} och extra jar version 
${extVersion}).
+UpdatedVersionAvailableUserAlert.fetchingNewExt=Din nod laddar f?r tillf?llet 
ner en ny version av Freenet (extra jar version ${extVersion}).
+UpdatedVersionAvailableUserAlert.notLatest=Det verkar som att din nod inte k?r 
den senaste versionen av mjukvaran.
 UpdatedVersionAvailableUserAlert.title=A ny stabil version av Freenet finns 
tillg?nglig
 UpdatedVersionAvailableUserAlert.updateASAPButton=Uppdatera
 UpdatedVersionAvailableUserAlert.updateNowButton=Uppdatera Nu!
 UserAlert.apply=Anv?nd
 UserAlert.hide=G?m
+UserAlert.reset=?terst?ll
+UserAlertManager.alertsOnHomepage=| Mer information finns p? 
${link}hemsidan${/link}.
+UserAlertManager.alertsTitle=Kritiska fel
 UserAlertManager.criticalErrorCountLabel=Kritiska fel:
 UserAlertManager.errorCountLabel=Fel:
+UserAlertManager.totalLabel=Totalt:
 UserAlertManager.warningCountLabel=Varningar:
-UserAlert.reset=?terst?ll
 WelcomeToadlet.activityTitle=Nuvarande aktivitet
 WelcomeToadlet.arkFetchCount=ARK H?mtare: ${total}
+WelcomeToadlet.confirmAddBookmarkSubTitle=Godk?nn att bokm?rket l?ggs till
 WelcomeToadlet.confirmAddBookmarkTitle=L?gg till bokm?rke
+WelcomeToadlet.confirmAddBookmarkWithKey=Godk?nn att du vill addera ${key} 
till dina bokm?rken och ange en beskrivning:
 WelcomeToadlet.confirmExternalLinkSubTitle=Godk?nn extern l?nk
 WelcomeToadlet.confirmExternalLinkTitle=VARNING: Extern l?nk
 WelcomeToadlet.confirmExternalLinkWithURL=Vill du verkligen bes?ka ${url}. 
VARNING: Du l?mnar FREENET! Att bes?ka den h?r l?nken kommer allvarligt att 
?ventyra din anonymitet! Du rekommenderas att inte forts?tta!
@@ -631,50 +681,11 @@
 WelcomeToadlet.extVersionWithRecommended=Freenet-ext Build #${build} 
(${recbuild} ?r rekommenderad) r${rev}
 WelcomeToadlet.fetch=H?mta
 WelcomeToadlet.fetchKeyLabel=H?mta en Freenet nyckel
+WelcomeToadlet.finInsertSuccessWithKey=Meddelandet har laddats upp till ${key}.
 WelcomeToadlet.finInsertedTitle=Uppladdning
-WelcomeToadlet.finInsertSuccessWithKey=Meddelandet har laddats upp till ${key}.
 WelcomeToadlet.fromHeader=Fr?n
 WelcomeToadlet.goToExternalLink=G? till den angivna l?nken
 WelcomeToadlet.homepageFullTitleWithName=Freenet FProxy Hemsida f?r ${name}
 WelcomeToadlet.ieWarning=Du verkar anv?nda Microsoft Internet Explorer. 
Ondsint skrivna Freenet sidor kan vara en fara f?r din anonymitet!
 WelcomeToadlet.ieWarningTitle=S?kerhetsrisk!
-WelcomeToadlet.insertCount=Uppladdningar: ${total}
-WelcomeToadlet.insertedTitle=Uppladdning
-WelcomeToadlet.insertFailedTitle=Uppladdningen misslyckades
-WelcomeToadlet.insertFailedWithMessage=Uppladdningen misslyckades med f?ljande 
meddelande: ${message}
-WelcomeToadlet.insertSucceededTitle=Uppladdning slutf?rd
-WelcomeToadlet.messageHeader=Meddelande
-WelcomeToadlet.nodeUpdateConfirmTitle=Godk?nn uppgradering av noden
-WelcomeToadlet.nodeUpdateConfirm=Vill du uppdatera din Freenet nod?
-WelcomeToadlet.privateKeyHeader=Privat nyckel
-WelcomeToadlet.publicKeyHeader=Publik nyckel
-WelcomeToadlet.requestCount=F?rfr?gningar: ${total}
-WelcomeToadlet.restartConfirmTitle=Nod omstart
-WelcomeToadlet.restartConfirm=Vill du verkligen starta om noden?
-WelcomeToadlet.restartingTitle=Freenet startas om.
-WelcomeToadlet.restarting=V?nligen v?nta medans noden starts om, detta kan ta 
flera minuter. Tack f?r att du anv?nder Freenet.
-WelcomeToadlet.restartNode=Starta om noden
-WelcomeToadlet.restart=Starta om
-WelcomeToadlet.shutdownConfirmTitle=St?ng av noden
-WelcomeToadlet.shutdownDone=Freenet noden har st?ngts av korrekt.
-WelcomeToadlet.shutdownNode=St?ng av noden
-WelcomeToadlet.shutdown=St?ng av
-WelcomeToadlet.splitfileErrorLabel=Splitfile-specifikt fel:
-WelcomeToadlet.startIndexHeader=Index att starta vid
-WelcomeToadlet.subjectHeader=?mne
-WelcomeToadlet.targetBoardHeader=M?l forum
-WelcomeToadlet.testnetWarning=K?r i testnet l?ge. Detta ?r en stor fara f?r 
din anonymitet!
-WelcomeToadlet.testnetWarningTitle=Testnet l?ge
-WelcomeToadlet.thanks=Tack f?r att du anv?nder Freenet.
-WelcomeToadlet.threadDumpSubTitle=Generera en tr?d dump
-WelcomeToadlet.threadDumpTitle=Generera en tr?d dump
-WelcomeToadlet.threadDumpWithFilename=En tr?d dump har gernererats och sparats 
i ${filename}.
-WelcomeToadlet.transferringRequestCount=Vidarebefodringar: ${total}
-WelcomeToadlet.update=Uppdatera
-WelcomeToadlet.updatingThanks=Tack f?r att du anv?nder Freenet.
-WelcomeToadlet.updatingTitle=Nod Uppdatering
-WelcomeToadlet.uriWouldHaveBeen=URI;n skulle ha varit: ${uri}
-WelcomeToadlet.version=Freenet ${fullVersion} Build ${build} r${rev}
-WelcomeToadlet.versionHeader=Information om Freenet & nod kontroll
-WelcomeToadlet.writtenDatabaseStats=Runtime databas statistik har skrivits 
till wrapper loggen
-End
+WelcomeToadlet.insertCount

Modified: 
branches/freenet-jfk/src/freenet/pluginmanager/AccessDeniedPluginHTTPException.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/pluginmanager/AccessDeniedPluginHTTPException.java
 2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/pluginmanager/AccessDeniedPluginHTTPException.java
 2007-08-21 20:26:59 UTC (rev 14828)
@@ -11,7 +11,7 @@
 public class AccessDeniedPluginHTTPException extends PluginHTTPException {
        private static final long serialVersionUID = -1;

-       public final short code = 403; // Access Denied
+       public static final short code = 403; // Access Denied

        public AccessDeniedPluginHTTPException(String errorMessage, String 
location) {
                super(errorMessage, location);

Modified: 
branches/freenet-jfk/src/freenet/pluginmanager/DownloadPluginHTTPException.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/pluginmanager/DownloadPluginHTTPException.java 
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/pluginmanager/DownloadPluginHTTPException.java 
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -11,7 +11,7 @@
 public class DownloadPluginHTTPException extends PluginHTTPException {
        private static final long serialVersionUID = -1;

-       public final short code = 200; // Found
+       public static final short CODE = 200; // Found
        public final String filename;
        public final String mimeType;
        public final byte[] data;

Modified: branches/freenet-jfk/src/freenet/pluginmanager/FredPlugin.java
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/FredPlugin.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/pluginmanager/FredPlugin.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -8,5 +8,6 @@

        public void terminate();

+       /** Run the plugin. Called after node startup. Should be able to access 
queue etc at this point. */
        public void runPlugin(PluginRespirator pr);
 }

Copied: 
branches/freenet-jfk/src/freenet/pluginmanager/FredPluginHTTPAdvanced.java 
(from rev 14796, 
trunk/freenet/src/freenet/pluginmanager/FredPluginHTTPAdvanced.java)
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/FredPluginHTTPAdvanced.java  
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/pluginmanager/FredPluginHTTPAdvanced.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,4 @@
+package freenet.pluginmanager;
+public interface FredPluginHTTPAdvanced{
+       
+}
\ No newline at end of file

Modified: 
branches/freenet-jfk/src/freenet/pluginmanager/NotFoundPluginHTTPException.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/pluginmanager/NotFoundPluginHTTPException.java 
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/pluginmanager/NotFoundPluginHTTPException.java 
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -11,7 +11,7 @@
 public class NotFoundPluginHTTPException extends PluginHTTPException {
        private static final long serialVersionUID = -1;

-       public final short code = 404; // Not Found
+       public static final short code = 404; // Not Found

        public NotFoundPluginHTTPException(String errorMessage, String 
location) {
                super(errorMessage, location);

Modified: 
branches/freenet-jfk/src/freenet/pluginmanager/PluginHTTPException.java
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/PluginHTTPException.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/pluginmanager/PluginHTTPException.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -11,7 +11,7 @@
 public class PluginHTTPException extends Exception {
        private static final long serialVersionUID = -1;

-       public final short code = 400; // Bad Request
+       public static final short code = 400; // Bad Request
        public final String message;
        public final String location;


Modified: branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -20,17 +20,24 @@
         * @param plug
         */
        public static PluginInfoWrapper startPlugin(PluginManager pm, String 
filename, FredPlugin plug, PluginRespirator pr) {
-               PluginStarter ps = new PluginStarter(pr);
-               PluginInfoWrapper pi = new PluginInfoWrapper(plug, ps, 
filename);
+               final PluginStarter ps = new PluginStarter(pr);
+               final PluginInfoWrapper pi = new PluginInfoWrapper(plug, ps, 
filename);

                // This is an ugly trick... sorry ;o)
                // The thread still exists as an identifier, but is never 
started if the
                // plugin doesn't require it
                ps.setPlugin(pm, plug);
-               if (!pi.isThreadlessPlugin())
-                       ps.start();
-               else
-                       ps.run();
+               // Run after startup
+               // FIXME this is horrible, wastes a thread, need to make 
PluginStarter a Runnable 
+               // not a Thread, and then deal with the consequences of that 
(removePlugin(Thread)) ...
+               pm.getTicker().queueTimedJob(new Runnable() {
+                       public void run() {
+                               if (!pi.isThreadlessPlugin())
+                                       ps.start();
+                               else
+                                       ps.run();
+                       }
+               }, 0);
                return pi;
        }

@@ -71,11 +78,13 @@
                                        System.err.println("Caught Throwable 
while running plugin: "+t);
                                        t.printStackTrace();
                                }
+                               if(!(plugin instanceof FredPluginThreadless))
+                                       pm.removePlugin(this);
+                       } else {
+                               // If not FredPlugin, then the whole thing is 
aborted,
+                               // and then this method will return, killing 
the thread
+                               return;
                        }
-                       // If not FredPlugin, then the whole thing is aborted,
-                       // and then this method will return, killing the thread
-                       
-                       pm.removePlugin(this);
                }

        }

Modified: branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java   
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,15 +1,11 @@
 /* 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. */
+ * 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;

-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
-import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -31,6 +27,7 @@
 import freenet.l10n.L10n;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
+import freenet.node.Ticker;
 import freenet.node.useralerts.SimpleUserAlert;
 import freenet.node.useralerts.UserAlert;
 import freenet.support.Logger;
@@ -42,15 +39,15 @@
 public class PluginManager {

        /*
-       *
-       * TODO: Synchronize
-       * TODO: Synchronize
-       * TODO: Synchronize
-       * TODO: Synchronize
-       * TODO: Synchronize
-       *
-       */
-       
+        *
+        * TODO: Synchronize
+        * TODO: Synchronize
+        * TODO: Synchronize
+        * TODO: Synchronize
+        * TODO: Synchronize
+        *
+        */
+
        private HashMap toadletList;
        private HashMap pluginInfo;
        private PluginRespirator pluginRespirator = null;
@@ -58,7 +55,7 @@
        private final NodeClientCore core;
        SubConfig pmconfig;
        private boolean logMINOR;
-       
+
        public PluginManager(Node node) {
                pluginInfo = new HashMap();
                toadletList = new HashMap();
@@ -66,7 +63,7 @@
                this.core = node.clientCore;
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                pluginRespirator = new PluginRespirator(node, this);
-               
+
                pmconfig = new SubConfig("pluginmanager", node.config);
                // Start plugins in the config
                pmconfig.register("loadplugin", null, 9, true, false, 
"PluginManager.loadedOnStartup", "PluginManager.loadedOnStartupLong",
@@ -79,8 +76,8 @@
                                                // FIXME
                                                throw new 
InvalidConfigValueException(L10n.getString("PluginManager.cannotSetOnceLoaded"));
                                        }
-               });
-               
+                               });
+
                String fns[] = pmconfig.getStringArr("loadplugin");
                if (fns != null) {
                        for (int i = 0 ; i < fns.length ; i++) {
@@ -88,33 +85,33 @@
                                startPlugin(fns[i], false);
                        }
                }
-                       
+
                pmconfig.finishedInitialization();
                /*System.err.println("=================================");
-               pmconfig.finishedInitialization();
-               fns = pmconfig.getStringArr("loadplugin");
-               for (int i = 0 ; i < fns.length ; i++)
-                       System.err.println("Load: " + 
StringArrOption.decode(fns[i]));
-               System.err.println("=================================");
-               */
+                 pmconfig.finishedInitialization();
+                 fns = pmconfig.getStringArr("loadplugin");
+                 for (int i = 0 ; i < fns.length ; i++)
+                 System.err.println("Load: " + StringArrOption.decode(fns[i]));
+                 System.err.println("=================================");
+                 */
        }
-       
+
        private String[] getConfigLoadString() {
                try{
                        Iterator it = getPlugins().iterator();

                        Vector v = new Vector();
-                       
+
                        while(it.hasNext())
                                
v.add(((PluginInfoWrapper)it.next()).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];
                }
        }
-       
+
        public void startPlugin(String filename, boolean store) {
                if (filename.trim().length() == 0)
                        return;
@@ -124,14 +121,14 @@
                        plug = LoadPlugin(filename);
                        PluginInfoWrapper pi = PluginHandler.startPlugin(this, 
filename, plug, pluginRespirator);
                        // handles FProxy? If so, register
-                       
+
                        if (pi.isPproxyPlugin())
                                registerToadlet(plug);

                        if(pi.isIPDetectorPlugin()) {
                                
node.ipDetector.registerIPDetectorPlugin((FredPluginIPDetector) plug);
                        }
-                       
+
                        synchronized (pluginInfo) {
                                pluginInfo.put(pi.getThreadName(), pi);
                        }
@@ -147,14 +144,14 @@
                                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),
-                                               UserAlert.ERROR));
+                                                       
l10n("pluginReqNewerJVMTitle", "name", filename),
+                                                       
l10n("pluginReqNewerJVM", "name", filename),
+                                                       UserAlert.ERROR));
                        }
                }
                if(store) core.storeConfig();
        }
-       
+
        private String l10n(String key, String pattern, String value) {
                return L10n.getString("PluginManager."+key, pattern, value);
        }
@@ -166,7 +163,7 @@
                }
                Logger.normal(this, "Added HTTP handler for 
/plugins/"+pl.getClass().getName()+ '/');
        }
-       
+
        public void removePlugin(Thread t) {
                Object removeKey = null;
                synchronized (pluginInfo) {
@@ -180,28 +177,28 @@
                                                try {
                                                        
toadletList.remove(pi.getPluginClassName());
                                                        Logger.normal(this, 
"Removed HTTP handler for /plugins/"+
-                                                                       
pi.getPluginClassName()+ '/');
+                                                                       
pi.getPluginClassName()+ '/', new Exception("debug"));
                                                } catch (Throwable ex) {
                                                        Logger.error(this, 
"removing Plugin", ex);
                                                }
                                        }
                                }
                        }
-                       
+
                        if (removeKey != null)
                                pluginInfo.remove(removeKey);
                }
                if(removeKey != null)
                        core.storeConfig();
        }
-       
+
        public void addToadletSymlinks(PluginInfoWrapper pi) {
                synchronized (toadletList) {
                        try {
                                String targets[] = 
pi.getPluginToadletSymlinks();
                                if (targets == null)
                                        return;
-                               
+
                                for (int i = 0 ; i < targets.length ; i++) {
                                        toadletList.remove(targets[i]);
                                        Logger.normal(this, "Removed HTTP 
symlink: " + targets[i] +
@@ -212,7 +209,7 @@
                        }
                }
        }
-       
+
        public void removeToadletSymlinks(PluginInfoWrapper pi) {
                synchronized (toadletList) {
                        String rm = null;
@@ -220,7 +217,7 @@
                                String targets[] = 
pi.getPluginToadletSymlinks();
                                if (targets == null)
                                        return;
-                               
+
                                for (int i = 0 ; i < targets.length ; i++) {
                                        rm = targets[i];
                                        toadletList.remove(targets[i]);
@@ -246,7 +243,7 @@
                }
                return out.toString();
        }
-       
+
        public Set getPlugins() {
                HashSet out = new HashSet();
                synchronized (pluginInfo) {
@@ -258,37 +255,37 @@
                }
                return out;
        }
-       
+
        public String handleHTTPGet(String plugin, HTTPRequest request) throws 
PluginHTTPException {
                FredPlugin handler = null;
                synchronized (toadletList) {
                        handler = (FredPlugin)toadletList.get(plugin);
                }
                /*if (handler == null)
-                       return null;
-                       */
-               
+                 return null;
+                 */
+
                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);
                }
                /*if (handler == null)
-                       return null;
-                       */
-               
+                 return null;
+                 */
+
                if (handler instanceof FredPluginHTTP)
                        return 
((FredPluginHTTP)handler).handleHTTPPost(request);
-               
+
                throw new NotFoundPluginHTTPException("Plugin not found!", 
"/plugins");
        }
-       
+
        public void killPlugin(String name) {
                PluginInfoWrapper pi = null;
                boolean found = false;
@@ -306,18 +303,18 @@
                        else
                                pi.stopPlugin();
        }
-       
-       
+
+
        /**
-       * Method to load a plugin from the given path and return is as an 
object.
-       * Will accept filename to be of one of the following forms:
-       * "classname" to load a class from the current classpath
-       * "classame at file:/path/to/jarfile.jar" to load class from an other 
jarfile.
-       *
-       * @param filename       The filename to load from
-       * @return                       An instanciated object of the plugin
-       * @throws PluginNotFoundException       If anything goes wrong.
-       */
+        * Method to load a plugin from the given path and return is as an 
object.
+        * Will accept filename to be of one of the following forms:
+        * "classname" to load a class from the current classpath
+        * "classame at file:/path/to/jarfile.jar" to load class from an other 
jarfile.
+        *
+        * @param filename      The filename to load from
+        * @return                      An instanciated object of the plugin
+        * @throws PluginNotFoundException      If anything goes wrong.
+        */
        private FredPlugin LoadPlugin(String filename) throws 
PluginNotFoundException {
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                Class cls = null;
@@ -334,41 +331,29 @@
                        }
                        String pluginname = filename.substring(0, 
filename.length()-1);
                        filename = null;
-                       
+
                        URL url;
-                       DataInputStream dis;
-            InputStream is = null;
-            BufferedInputStream bis = null;
-            FileOutputStream fos = null;
-            BufferedOutputStream bos = null;
-                       
+                       InputStream is = null;
+
                        try {
                                url = new 
URL("http://downloads.freenetproject.org/alpha/plugins/"; + pluginname + 
".jar.url");
                                if(logMINOR) Logger.minor(this, "Downloading 
"+url);
                                is = url.openStream();
-                               bis = new BufferedInputStream(is);
-                dis = new DataInputStream(bis);
-                
-                File pluginsDirectory = new File("plugins");
-                if(!pluginsDirectory.exists()) {
-                       Logger.normal(this, "The plugin directory hasn't been 
found, let's create it");
-                       if(!pluginsDirectory.mkdir())
-                               return null;
-                }
-                
-                File finalFile = new File("plugins/" + pluginname + ".jar");
-                File f = File.createTempFile(pluginname, ".tmp", 
pluginsDirectory);
-                fos = new FileOutputStream(f);
-                bos = new BufferedOutputStream(fos);
-                int len = 0, writenBytes = 0;
-                byte[] buffer = new byte[4096];
-                while ((len = dis.read(buffer)) != -1) {
-                        bos.write(buffer, writenBytes, len);
-                        writenBytes +=len;
-                }
-                f.renameTo(finalFile);
-                       filename = "*@file://" + FileUtil.getCanonicalFile(f);
-                       if(logMINOR) Logger.minor(this, "Rewritten to 
"+filename);
+                               
+                               File pluginsDirectory = new File("plugins");
+                               if(!pluginsDirectory.exists()) {
+                                       Logger.normal(this, "The plugin 
directory hasn't been found, let's create it");
+                                       if(!pluginsDirectory.mkdir())
+                                               return null;
+                               }
+
+                               File finalFile = new File("plugins/" + 
pluginname + ".jar");
+                               if(!FileUtil.writeTo(is, finalFile))
+                                       Logger.error(this, "Failed to rename 
the temporary file into "+finalFile);
+                                       
+                               filename = "*@file://" + 
FileUtil.getCanonicalFile(finalFile);
+                               if(logMINOR) Logger.minor(this, "Rewritten to 
"+filename);
+                               
                        } catch (MalformedURLException mue) {
                                Logger.error(this, "MAlformedURLException has 
occured : "+ mue, mue);
                                return null;
@@ -381,16 +366,13 @@
                                return null;
                        } finally {
                                try {
-                                       if(bis != null) bis.close();
                                        if(is != null) is.close();
-                                       if(bos != null) bos.close();
-                                       if(fos != null) fos.close();
                                } catch (IOException ioe) {}
                        }
                        if(filename == null)
                                return null;
                }
-               
+
                BufferedReader in = null;
                InputStream is = null;
                if ((filename.indexOf("@") >= 0)) {
@@ -400,7 +382,7 @@
                                try {
                                        String realURL = null;
                                        String realClass = null;
-                                       
+
                                        // Load the jar-file
                                        String[] parts = filename.split("@");
                                        if (parts.length != 2) {
@@ -409,7 +391,7 @@
                                        realClass = parts[0];
                                        realURL = parts[1];
                                        if(logMINOR) Logger.minor(this, "Class: 
"+realClass+" URL: "+realURL);
-                                       
+
                                        if (filename.endsWith(".url")) {
                                                if(!assumeURLRedirect) {
                                                        // Load the txt-file
@@ -417,7 +399,7 @@
                                                        URLConnection uc = 
url.openConnection();
                                                        in = new BufferedReader(
                                                                        new 
InputStreamReader(uc.getInputStream()));
-                                                       
+
                                                        realURL = in.readLine();
                                                        if(realURL == null)
                                                                throw new 
PluginNotFoundException("Initialization error: " + url +
@@ -428,21 +410,21 @@
                                                }
                                                assumeURLRedirect = 
!assumeURLRedirect;
                                        }
-                                       
+
                                        // Load the class inside file
                                        URL[] serverURLs = new URL[]{new 
URL(realURL)};
                                        ClassLoader cl = new 
URLClassLoader(serverURLs);
-                                       
-                                       
+
+
                                        // Handle automatic fetching of 
pluginclassname
                                        if (realClass.equals("*")) {
-                                               
+
                                                // Clean URL
                                                URI liburi = 
URIPreEncoder.encodeURI(realURL);
                                                if(logMINOR)
                                                        Logger.minor(this, 
"cleaned url: "+realURL+" -> "+liburi.toString());
                                                realURL = liburi.toString();
-                                               
+
                                                URL url = new 
URL("jar:"+realURL+"!/");
                                                JarURLConnection jarConnection 
= (JarURLConnection)url.openConnection();
                                                // Java seems to cache even 
file: urls...
@@ -450,12 +432,12 @@
                                                JarFile jf = 
jarConnection.getJarFile();
                                                //URLJarFile jf = new 
URLJarFile(new File(liburi));
                                                //is = 
jf.getInputStream(jf.getJarEntry("META-INF/MANIFEST.MF"));
-                                               
+
                                                //BufferedReader manifest = new 
BufferedReader(new 
InputStreamReader(cl.getResourceAsStream("/META-INF/MANIFEST.MF")));
-                                               
+
                                                //URL url = new URL(parts[1]);
                                                //URLConnection uc = 
cl.getResource("/META-INF/MANIFEST.MF").openConnection();
-                                               
+
                                                is = 
jf.getInputStream(jf.getJarEntry("META-INF/MANIFEST.MF"));
                                                in = new BufferedReader(new 
InputStreamReader(is));     
                                                String line;
@@ -468,14 +450,14 @@
                                                }
                                                //System.err.println("Real 
classname: " + realClass);
                                        }
-                                       
+
                                        cls = cl.loadClass(realClass);
-                                       
+
                                } catch (Exception e) {
                                        if (tries >= 5)
                                                throw new 
PluginNotFoundException("Initialization error:"
                                                                + filename, e);
-                                       
+
                                        try {
                                                Thread.sleep(100);
                                        } catch (Exception ee) {}
@@ -495,10 +477,10 @@
                                throw new PluginNotFoundException(filename);
                        }
                }
-               
+
                if(cls == null)
                        throw new PluginNotFoundException("Unknown error");
-               
+
                // Class loaded... Objectize it!
                Object o = null;
                try {
@@ -507,12 +489,16 @@
                        throw new PluginNotFoundException("Could not re-create 
plugin:" +
                                        filename, e);
                }
-               
+
                // See if we have the right type
                if (!(o instanceof FredPlugin)) {
                        throw new PluginNotFoundException("Not a plugin: " + 
filename);
                }
-               
+
                return (FredPlugin)o;
        }
+       
+       Ticker getTicker() {
+               return node.getTicker();
+       }
 }

Modified: 
branches/freenet-jfk/src/freenet/pluginmanager/RedirectPluginHTTPException.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/pluginmanager/RedirectPluginHTTPException.java 
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/pluginmanager/RedirectPluginHTTPException.java 
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -11,7 +11,7 @@
 public class RedirectPluginHTTPException extends PluginHTTPException {
        private static final long serialVersionUID = -1;

-       public final short code = 302; // Found
+       public static final short code = 302; // Found
        public final String newLocation;

        public RedirectPluginHTTPException(String message, String location, 
String newLocation) {

Modified: branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java
===================================================================
--- branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -32,6 +32,7 @@
 import com.sleepycat.je.SecondaryKeyCreator;
 import com.sleepycat.je.Transaction;
 import com.sleepycat.je.log.DbChecksumException;
+import com.sleepycat.je.log.LogFileNotFoundException;
 import com.sleepycat.je.util.DbLoad;

 import freenet.crypt.CryptFormatException;
@@ -182,23 +183,32 @@
                                System.err.println("Failed to reload database 
"+dbName+": "+e);
                                e.printStackTrace();
                        }
+                       
+                       // Should just open now, although it will need to 
reconstruct the secondary indexes.
                }

                try {
+                       // First try just opening it.
                        return new BerkeleyDBFreenetStore(storeEnvironment, 
newDBPrefix, newStoreFile, newFixSecondaryFile,
                                        maxStoreKeys, blockSize, headerSize, 
throwOnTooFewKeys, noCheck, wipe, storeShutdownHook, 
                                        reconstructFile);
                } catch (DatabaseException e) {

+                       // Try a reconstruct
+                       
                        System.err.println("Could not open store: "+e);
                        e.printStackTrace();

                        if(type == TYPE_SSK) {
-                               System.err.println("Cannot reconstruct SSK 
store/cache. Move the old store/cache out of the way, and report to 
developers.");
-                               throw e;
+                               System.err.println("Cannot reconstruct SSK 
store/cache! Sorry, your SSK store will now be deleted...");
+                               
BerkeleyDBFreenetStore.wipeOldDatabases(storeEnvironment, newDBPrefix);
+                               newStoreFile.delete();
+                               return new 
BerkeleyDBFreenetStore(storeEnvironment, newDBPrefix, newStoreFile, 
newFixSecondaryFile,
+                                               maxStoreKeys, blockSize, 
headerSize, throwOnTooFewKeys, noCheck, wipe, storeShutdownHook, 
+                                               reconstructFile);
                        }

-                       System.err.println("Attempting to reconstruct...");
+                       System.err.println("Attempting to reconstruct 
index...");
                        WrapperManager.signalStarting(5*60*60*1000);

                        // Reconstruct
@@ -246,7 +256,7 @@
                dbConfig.setTransactional(true);
                if(wipe) {
                        System.err.println("Wiping old database for "+prefix);
-                       wipeOldDatabases(prefix);
+                       wipeOldDatabases(environment, prefix);
                }

                chkDB = environment.openDatabase(null,prefix+"CHK",dbConfig);
@@ -410,7 +420,7 @@

                        boolean dontCheckForHolesShrinking = false;

-                       long chkBlocksInDatabase = chkDB.count();
+                       long chkBlocksInDatabase = 
highestBlockNumberInDatabase();
                        chkBlocksInStore = chkBlocksInDatabase;
                        long chkBlocksFromFile = countCHKBlocksFromFile();
                        lastRecentlyUsed = getMaxRecentlyUsed();
@@ -955,7 +965,7 @@
                this.reconstructFile = reconstructFile;
                name = prefix;

-               wipeOldDatabases(prefix);
+               wipeOldDatabases(environment, prefix);

                // Delete old database(s).

@@ -1022,22 +1032,22 @@
                storeShutdownHook.addEarlyJob(new ShutdownHook());
        }

-       private void wipeOldDatabases(String prefix) {
-               wipeDatabase(prefix+"CHK");
-               wipeDatabase(prefix+"CHK_accessTime");
-               wipeDatabase(prefix+"CHK_blockNum");
+       private static void wipeOldDatabases(Environment env, String prefix) {
+               wipeDatabase(env, prefix+"CHK");
+               wipeDatabase(env, prefix+"CHK_accessTime");
+               wipeDatabase(env, prefix+"CHK_blockNum");
                System.err.println("Removed old database "+prefix);
        }

-       private void wipeDatabase(String name) {
+       private static void wipeDatabase(Environment env, String name) {
                WrapperManager.signalStarting(5*60*60*1000);
-               Logger.normal(this, "Wiping database "+name);
+               Logger.normal(BerkeleyDBFreenetStore.class, "Wiping database 
"+name);
                try {
-                       environment.removeDatabase(null, name);
+                       env.removeDatabase(null, name);
                } catch (DatabaseNotFoundException e) {
                        System.err.println("Database "+name+" does not exist 
deleting it");
                } catch (DatabaseException e) {
-                       Logger.error(this, "Could not remove old database: 
"+name+": "+e, e);
+                       Logger.error(BerkeleyDBFreenetStore.class, "Could not 
remove old database: "+name+": "+e, e);
                        System.err.println("Could not remove old database: 
"+name+": "+e);
                        e.printStackTrace();
                }
@@ -1320,7 +1330,7 @@
                                }


-                               block = new SSKBlock(data,header,chk, true);
+                               block = new SSKBlock(data,header,chk, false);

                                if(!dontPromote) {
                                        storeBlock.updateRecentlyUsed();
@@ -1751,8 +1761,9 @@
                                chkDB_blockNum.get(t, blockNumEntry, found, 
LockMode.DEFAULT);

                        if(success == OperationStatus.KEYEXIST || success == 
OperationStatus.SUCCESS) {
-                               System.err.println("Trying to overwrite block 
"+blockNum+" but already used: "+getName());
-                               Logger.error(this, "Trying to overwrite block 
"+blockNum+" but already used: "+getName());
+                               System.err.println("Trying to overwrite block 
"+blockNum+" but already used: "+getName()+" for "+e);
+                               e.printStackTrace();
+                               Logger.error(this, "Trying to overwrite block 
"+blockNum+" but already used: "+getName()+" for "+e);
                                return false;
                        } else {
                                Logger.minor(this, "Key doesn't exist for block 
num "+blockNum+" but caught "+e, e);
@@ -1794,8 +1805,13 @@
                                }
                                Logger.error(this, "Corrupt secondary database 
("+getName()+"). Should be cleaned up on restart.");
                                System.err.println("Corrupt secondary database 
("+getName()+"). Should be cleaned up on restart.");
-                               
System.exit(freenet.node.Node.EXIT_DATABASE_REQUIRES_RESTART);
-                       } else if(ex instanceof DbChecksumException || ex 
instanceof RunRecoveryException) {
+                               WrapperManager.restart();
+                               
System.exit(freenet.node.NodeInitException.EXIT_DATABASE_REQUIRES_RESTART);
+                       } else if(ex instanceof DbChecksumException || ex 
instanceof RunRecoveryException || ex instanceof LogFileNotFoundException ||
+                                       // UGH! We really shouldn't have to do 
this ... :(
+                                       (msg != null && 
+                                                       
(msg.indexOf("LogFileNotFoundException") >= 0 || 
msg.indexOf("DbChecksumException") >= 0)
+                                                       || 
msg.indexOf("RunRecoveryException") >= 0)) {
                                System.err.println("Corrupt database! Will be 
reconstructed on restart");
                                try {
                                        reconstructFile.createNewFile();
@@ -1804,6 +1820,9 @@
                                        System.err.println("Corrupt database 
("+getName()+") but could not create flag file "+reconstructFile);
                                        return; // Not sure what else we can do
                                }
+                       } else {
+                               if(ex.getCause() != null)
+                                       
checkSecondaryDatabaseError(ex.getCause());
                        }
                }
        }
@@ -2063,29 +2082,37 @@
                                        if(chkStore != null)
                                                chkStore.close();
                                } catch (Throwable t) {
-                                       System.err.println("Caught closing 
database: "+t);
-                                       t.printStackTrace();
+                                       if(!(t instanceof RunRecoveryException 
|| t instanceof OutOfMemoryError)) {
+                                               System.err.println("Caught 
closing database: "+t);
+                                               t.printStackTrace();
+                                       }
                                }
                                try {
                                        if(chkDB_accessTime != null)
                                                chkDB_accessTime.close();
                                } catch (Throwable t) {
-                                       System.err.println("Caught closing 
database: "+t);
-                                       t.printStackTrace();
+                                       if(!(t instanceof RunRecoveryException 
|| t instanceof OutOfMemoryError)) {
+                                               System.err.println("Caught 
closing database: "+t);
+                                               t.printStackTrace();
+                                       }
                                }
                                try {
                                        if(chkDB_blockNum != null)
                                                chkDB_blockNum.close();
                                } catch (Throwable t) {
-                                       System.err.println("Caught closing 
database: "+t);
-                                       t.printStackTrace();
+                                       if(!(t instanceof RunRecoveryException 
|| t instanceof OutOfMemoryError)) {
+                                               System.err.println("Caught 
closing database: "+t);
+                                               t.printStackTrace();
+                                       }
                                }
                                try {   
                                        if(chkDB != null)
                                                chkDB.close();
                                } catch (Throwable t) {
-                                       System.err.println("Caught closing 
database: "+t);
-                                       t.printStackTrace();
+                                       if(!(t instanceof RunRecoveryException 
|| t instanceof OutOfMemoryError)) {
+                                               System.err.println("Caught 
closing database: "+t);
+                                               t.printStackTrace();
+                                       }
                                }
                                if(logMINOR) Logger.minor(this, "Closing 
database finished.");
                                System.err.println("Closed database");

Modified: branches/freenet-jfk/src/freenet/support/BitArray.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/BitArray.java      2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/BitArray.java      2007-08-21 
20:26:59 UTC (rev 14828)
@@ -43,8 +43,8 @@

        public BitArray(BitArray src) {
                this._size = src._size;
-               this._bits = new byte[src._size];
-               System.arraycopy(_bits, 0, src._bits, 0, _bits.length);
+               this._bits = new byte[src._bits.length];
+               System.arraycopy(src._bits, 0, _bits, 0, src._bits.length);
        }

        public void setBit(int pos, boolean f) {
@@ -123,8 +123,11 @@
                        if(b == 0) continue;
                        for(int j=0;j<8;j++) {
                                int mask = (1 << j);
-                               if((b & mask) != 0)
-                                       return i*8+j;
+                               if((b & mask) != 0) {
+                                       int x = i*8+j;
+                                       if(x >= _size) return -1;
+                                       return x;
+                               }
                        }
                }
                return -1;

Copied: branches/freenet-jfk/src/freenet/support/Executor.java (from rev 14796, 
trunk/freenet/src/freenet/support/Executor.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/Executor.java                      
        (rev 0)
+++ branches/freenet-jfk/src/freenet/support/Executor.java      2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,16 @@
+/**
+ * @author toad
+ * To the extent that this is copyrightable, it's part of Freenet and licensed 
+ * under GPL2 or later. However, it's a trivial interface taken from Sun JDK 
1.5,
+ * and we will use that when we migrate to 1.5.
+ */
+package freenet.support;
+
+public interface Executor {
+       
+       /** Execute a job. */
+       public void execute(Runnable job, String jobName);
+
+       /** Count the number of threads waiting for work */
+       public int waitingThreads();
+}

Modified: branches/freenet-jfk/src/freenet/support/Fields.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/Fields.java        2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/Fields.java        2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,6 +1,5 @@
 package freenet.support;

-import java.io.DataInputStream;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -146,6 +145,18 @@
        }

        /**
+        * Find the boolean value of the field. Throw if the string is neither 
"yes"/"true" nor "no"/"false".
+        * @param s
+        * @return
+        */
+       public static boolean stringToBool(String s) throws 
NumberFormatException {
+               if(s == null) throw new NumberFormatException("Null");
+               if(s.equalsIgnoreCase("false") || s.equalsIgnoreCase("no")) 
return false;
+               if(s.equalsIgnoreCase("true") || s.equalsIgnoreCase("yes")) 
return true;
+               throw new NumberFormatException("Invalid boolean: "+s);
+       }
+
+       /**
         * Converts a boolean to a String of either "true" or "false".
         * 
         * @param b

Modified: branches/freenet-jfk/src/freenet/support/FileLoggerHook.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/FileLoggerHook.java        
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/FileLoggerHook.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -693,7 +693,7 @@
                if (rotate) {
                        this.baseFilename = baseFilename;
                } else {
-                       logStream = new FileOutputStream(baseFilename, 
!logOverwrite);
+                       logStream = new BufferedOutputStream(new 
FileOutputStream(baseFilename, !logOverwrite), 65536);
                }
        }

@@ -822,18 +822,20 @@

                if (e != null) {

-                       // Convert the stack trace to a string.
-                       ByteArrayOutputStream bos = new 
ByteArrayOutputStream(350);
-                       PrintWriter bpw = new PrintWriter(bos);
-                       try {
-                               e.printStackTrace(bpw);
-                       } catch (NullPointerException ex) {
-                               log(this, getClass(), "Got evil 
NPE-in-stack-trace bug", null, ERROR);
-                               bpw.println("[ evil NPE-in-stack-trace 
triggered ]");
+                       sb.append(e.toString());
+                       
+                       StackTraceElement[] trace = e.getStackTrace();
+                       
+                       if(trace == null)
+                               sb.append("(null)");
+                       else {
+                               sb.append('\n');
+                               for(int i=0;i<trace.length;i++) {
+                                       sb.append("\tat ");
+                                       sb.append(trace[i].toString());
+                                       sb.append('\n');
+                               }
                        }
-                       bpw.flush();
-
-                       sb.append(bos.toString());
                }

                logString(sb.toString().getBytes());

Modified: branches/freenet-jfk/src/freenet/support/HTMLDecoder.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/HTMLDecoder.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/HTMLDecoder.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -3,7 +3,8 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.support;

-import java.util.HashMap;
+//import java.util.HashMap;
+import java.util.Map;

 /**
  * Description: Utility for converting character references e.g.: &lt; &gt;
@@ -13,8 +14,8 @@
  */
 public class HTMLDecoder {

-       public static final HashMap charTable;
-
+       static Map charTable = HTMLEntities.decodeMap;
+       
        public static String decode(String s) {
                String t;
                Character ch;
@@ -147,267 +148,19 @@

        // HTML is very particular about what constitutes white space.
        public static boolean isWhitespace(char ch) {
-               return (ch == '\u0020')
-                       || (ch == '\r')
-                       || (ch == '\n')
+               return 
+                       //space
+                   (ch == '\u0020')
+                       //Mac newline
+                   || (ch == '\r')
+                   //Unix newline
+                       || (ch == '\n')         
+                       //tab
                        || (ch == '\u0009')
+                       //Control
                        || (ch == '\u000c')
+                       //zero width space
                        || (ch == '\u200b');
        }

-       static {
-               charTable = new HashMap();
-               charTable.put("quot", new Character((char)34));
-               charTable.put("amp", new Character((char)38));
-               charTable.put("apos", new Character((char)39));
-               charTable.put("lt", new Character((char)60));
-               charTable.put("gt", new Character((char)62));
-               charTable.put("nbsp", new Character((char)160));
-               charTable.put("iexcl", new Character((char)161));
-               charTable.put("cent", new Character((char)162));
-               charTable.put("pound", new Character((char)163));
-               charTable.put("curren", new Character((char)164));
-               charTable.put("yen", new Character((char)165));
-               charTable.put("brvbar", new Character((char)166));
-               charTable.put("sect", new Character((char)167));
-               charTable.put("uml", new Character((char)168));
-               charTable.put("copy", new Character((char)169));
-               charTable.put("ordf", new Character((char)170));
-               charTable.put("laquo", new Character((char)171));
-               charTable.put("not", new Character((char)172));
-               charTable.put("shy", new Character((char)173));
-               charTable.put("reg", new Character((char)174));
-               charTable.put("macr", new Character((char)175));
-               charTable.put("deg", new Character((char)176));
-               charTable.put("plusmn", new Character((char)177));
-               charTable.put("sup2", new Character((char)178));
-               charTable.put("sup3", new Character((char)179));
-               charTable.put("acute", new Character((char)180));
-               charTable.put("micro", new Character((char)181));
-               charTable.put("para", new Character((char)182));
-               charTable.put("middot", new Character((char)183));
-               charTable.put("cedil", new Character((char)184));
-               charTable.put("sup1", new Character((char)185));
-               charTable.put("ordm", new Character((char)186));
-               charTable.put("raquo", new Character((char)187));
-               charTable.put("frac14", new Character((char)188));
-               charTable.put("frac12", new Character((char)189));
-               charTable.put("frac34", new Character((char)190));
-               charTable.put("iquest", new Character((char)191));
-               charTable.put("Agrave", new Character((char)192));
-               charTable.put("Aacute", new Character((char)193));
-               charTable.put("Acirc", new Character((char)194));
-               charTable.put("Atilde", new Character((char)195));
-               charTable.put("Auml", new Character((char)196));
-               charTable.put("Aring", new Character((char)197));
-               charTable.put("AElig", new Character((char)198));
-               charTable.put("Ccedil", new Character((char)199));
-               charTable.put("Egrave", new Character((char)200));
-               charTable.put("Eacute", new Character((char)201));
-               charTable.put("Ecirc", new Character((char)202));
-               charTable.put("Euml", new Character((char)203));
-               charTable.put("Igrave", new Character((char)204));
-               charTable.put("Iacute", new Character((char)205));
-               charTable.put("Icirc", new Character((char)206));
-               charTable.put("Iuml", new Character((char)207));
-               charTable.put("ETH", new Character((char)208));
-               charTable.put("Ntilde", new Character((char)209));
-               charTable.put("Ograve", new Character((char)210));
-               charTable.put("Oacute", new Character((char)211));
-               charTable.put("Ocirc", new Character((char)212));
-               charTable.put("Otilde", new Character((char)213));
-               charTable.put("Ouml", new Character((char)214));
-               charTable.put("times", new Character((char)215));
-               charTable.put("Oslash", new Character((char)216));
-               charTable.put("Ugrave", new Character((char)217));
-               charTable.put("Uacute", new Character((char)218));
-               charTable.put("Ucirc", new Character((char)219));
-               charTable.put("Uuml", new Character((char)220));
-               charTable.put("Yacute", new Character((char)221));
-               charTable.put("THORN", new Character((char)222));
-               charTable.put("szlig", new Character((char)223));
-               charTable.put("agrave", new Character((char)224));
-               charTable.put("aacute", new Character((char)225));
-               charTable.put("acirc", new Character((char)226));
-               charTable.put("atilde", new Character((char)227));
-               charTable.put("auml", new Character((char)228));
-               charTable.put("aring", new Character((char)229));
-               charTable.put("aelig", new Character((char)230));
-               charTable.put("ccedil", new Character((char)231));
-               charTable.put("egrave", new Character((char)232));
-               charTable.put("eacute", new Character((char)233));
-               charTable.put("ecirc", new Character((char)234));
-               charTable.put("euml", new Character((char)235));
-               charTable.put("igrave", new Character((char)236));
-               charTable.put("iacute", new Character((char)237));
-               charTable.put("icirc", new Character((char)238));
-               charTable.put("iuml", new Character((char)239));
-               charTable.put("eth", new Character((char)240));
-               charTable.put("ntilde", new Character((char)241));
-               charTable.put("ograve", new Character((char)242));
-               charTable.put("oacute", new Character((char)243));
-               charTable.put("ocirc", new Character((char)244));
-               charTable.put("otilde", new Character((char)245));
-               charTable.put("ouml", new Character((char)246));
-               charTable.put("divide", new Character((char)247));
-               charTable.put("oslash", new Character((char)248));
-               charTable.put("ugrave", new Character((char)249));
-               charTable.put("uacute", new Character((char)250));
-               charTable.put("ucirc", new Character((char)251));
-               charTable.put("uuml", new Character((char)252));
-               charTable.put("yacute", new Character((char)253));
-               charTable.put("thorn", new Character((char)254));
-               charTable.put("yuml", new Character((char)255));
-               charTable.put("OElig", new Character((char)338));
-               charTable.put("oelig", new Character((char)339));
-               charTable.put("Scaron", new Character((char)352));
-               charTable.put("scaron", new Character((char)353));
-               charTable.put("fnof", new Character((char)402));
-               charTable.put("circ", new Character((char)710));
-               charTable.put("tilde", new Character((char)732));
-               charTable.put("Alpha", new Character((char)913));
-               charTable.put("Beta", new Character((char)914));
-               charTable.put("Gamma", new Character((char)915));
-               charTable.put("Delta", new Character((char)916));
-               charTable.put("Epsilon", new Character((char)917));
-               charTable.put("Zeta", new Character((char)918));
-               charTable.put("Eta", new Character((char)919));
-               charTable.put("Theta", new Character((char)920));
-               charTable.put("Iota", new Character((char)921));
-               charTable.put("Kappa", new Character((char)922));
-               charTable.put("Lambda", new Character((char)923));
-               charTable.put("Mu", new Character((char)924));
-               charTable.put("Nu", new Character((char)925));
-               charTable.put("Xi", new Character((char)926));
-               charTable.put("Omicron", new Character((char)927));
-               charTable.put("Pi", new Character((char)928));
-               charTable.put("Rho", new Character((char)929));
-               charTable.put("Sigma", new Character((char)931));
-               charTable.put("Tau", new Character((char)932));
-               charTable.put("Upsilon", new Character((char)933));
-               charTable.put("Phi", new Character((char)934));
-               charTable.put("Chi", new Character((char)935));
-               charTable.put("Psi", new Character((char)936));
-               charTable.put("Omega", new Character((char)937));
-               charTable.put("alpha", new Character((char)945));
-               charTable.put("beta", new Character((char)946));
-               charTable.put("gamma", new Character((char)947));
-               charTable.put("delta", new Character((char)948));
-               charTable.put("epsilon", new Character((char)949));
-               charTable.put("zeta", new Character((char)950));
-               charTable.put("eta", new Character((char)951));
-               charTable.put("theta", new Character((char)952));
-               charTable.put("iota", new Character((char)953));
-               charTable.put("kappa", new Character((char)954));
-               charTable.put("lambda", new Character((char)955));
-               charTable.put("mu", new Character((char)956));
-               charTable.put("nu", new Character((char)957));
-               charTable.put("xi", new Character((char)958));
-               charTable.put("omicron", new Character((char)959));
-               charTable.put("pi", new Character((char)960));
-               charTable.put("rho", new Character((char)961));
-               charTable.put("sigmaf", new Character((char)962));
-               charTable.put("sigma", new Character((char)963));
-               charTable.put("tau", new Character((char)964));
-               charTable.put("upsilon", new Character((char)965));
-               charTable.put("phi", new Character((char)966));
-               charTable.put("chi", new Character((char)967));
-               charTable.put("psi", new Character((char)968));
-               charTable.put("omega", new Character((char)969));
-               charTable.put("thetasym", new Character((char)977));
-               charTable.put("upsih", new Character((char)978));
-               charTable.put("piv", new Character((char)982));
-               charTable.put("ensp", new Character((char)8194));
-               charTable.put("emsp", new Character((char)8195));
-               charTable.put("thinsp", new Character((char)8201));
-               charTable.put("zwnj", new Character((char)8204));
-               charTable.put("zwj", new Character((char)8205));
-               charTable.put("lrm", new Character((char)8206));
-               charTable.put("rlm", new Character((char)8207));
-               charTable.put("ndash", new Character((char)8211));
-               charTable.put("mdash", new Character((char)8212));
-               charTable.put("lsquo", new Character((char)8216));
-               charTable.put("rsquo", new Character((char)8217));
-               charTable.put("sbquo", new Character((char)8218));
-               charTable.put("ldquo", new Character((char)8220));
-               charTable.put("rdquo", new Character((char)8221));
-               charTable.put("bdquo", new Character((char)8222));
-               charTable.put("dagger", new Character((char)8224));
-               charTable.put("Dagger", new Character((char)8225));
-               charTable.put("bull", new Character((char)8226));
-               charTable.put("hellip", new Character((char)8230));
-               charTable.put("permil", new Character((char)8240));
-               charTable.put("prime", new Character((char)8242));
-               charTable.put("Prime", new Character((char)8243));
-               charTable.put("lsaquo", new Character((char)8249));
-               charTable.put("rsaquo", new Character((char)8250));
-               charTable.put("oline", new Character((char)8254));
-               charTable.put("frasl", new Character((char)8260));
-               charTable.put("euro", new Character((char)8364));
-               charTable.put("image", new Character((char)8465));
-               charTable.put("weierp", new Character((char)8472));
-               charTable.put("real", new Character((char)8476));
-               charTable.put("trade", new Character((char)8482));
-               charTable.put("alefsym", new Character((char)8501));
-               charTable.put("larr", new Character((char)8592));
-               charTable.put("uarr", new Character((char)8593));
-               charTable.put("rarr", new Character((char)8594));
-               charTable.put("darr", new Character((char)8595));
-               charTable.put("harr", new Character((char)8596));
-               charTable.put("crarr", new Character((char)8629));
-               charTable.put("lArr", new Character((char)8656));
-               charTable.put("uArr", new Character((char)8657));
-               charTable.put("rArr", new Character((char)8658));
-               charTable.put("dArr", new Character((char)8659));
-               charTable.put("hArr", new Character((char)8660));
-               charTable.put("forall", new Character((char)8704));
-               charTable.put("part", new Character((char)8706));
-               charTable.put("exist", new Character((char)8707));
-               charTable.put("empty", new Character((char)8709));
-               charTable.put("nabla", new Character((char)8711));
-               charTable.put("isin", new Character((char)8712));
-               charTable.put("notin", new Character((char)8713));
-               charTable.put("ni", new Character((char)8715));
-               charTable.put("prod", new Character((char)8719));
-               charTable.put("sum", new Character((char)8721));
-               charTable.put("minus", new Character((char)8722));
-               charTable.put("lowast", new Character((char)8727));
-               charTable.put("radic", new Character((char)8730));
-               charTable.put("prop", new Character((char)8733));
-               charTable.put("infin", new Character((char)8734));
-               charTable.put("ang", new Character((char)8736));
-               charTable.put("and", new Character((char)8743));
-               charTable.put("or", new Character((char)8744));
-               charTable.put("cap", new Character((char)8745));
-               charTable.put("cup", new Character((char)8746));
-               charTable.put("int", new Character((char)8747));
-               charTable.put("there4", new Character((char)8756));
-               charTable.put("sim", new Character((char)8764));
-               charTable.put("cong", new Character((char)8773));
-               charTable.put("asymp", new Character((char)8776));
-               charTable.put("ne", new Character((char)8800));
-               charTable.put("equiv", new Character((char)8801));
-               charTable.put("le", new Character((char)8804));
-               charTable.put("ge", new Character((char)8805));
-               charTable.put("sub", new Character((char)8834));
-               charTable.put("sup", new Character((char)8835));
-               charTable.put("nsub", new Character((char)8836));
-               charTable.put("sube", new Character((char)8838));
-               charTable.put("supe", new Character((char)8839));
-               charTable.put("oplus", new Character((char)8853));
-               charTable.put("otimes", new Character((char)8855));
-               charTable.put("perp", new Character((char)8869));
-               charTable.put("sdot", new Character((char)8901));
-               charTable.put("lceil", new Character((char)8968));
-               charTable.put("rceil", new Character((char)8969));
-               charTable.put("lfloor", new Character((char)8970));
-               charTable.put("rfloor", new Character((char)8971));
-               charTable.put("lang", new Character((char)9001));
-               charTable.put("rang", new Character((char)9002));
-               charTable.put("loz", new Character((char)9674));
-               charTable.put("spades", new Character((char)9824));
-               charTable.put("clubs", new Character((char)9827));
-               charTable.put("hearts", new Character((char)9829));
-               charTable.put("diams", new Character((char)9830));
-       }
 }

Modified: branches/freenet-jfk/src/freenet/support/HTMLEncoder.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/HTMLEncoder.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/HTMLEncoder.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -13,7 +13,8 @@
  * @author Unique Person at w3nO30p4p9L81xKTXbCaQBOvUww (via Frost)
  */
 public class HTMLEncoder {
-       public final static CharTable charTable;
+       public final static CharTable charTable = 
+               new CharTable(HTMLEntities.encodeMap);

        public static String encode(String s) {
                int n = s.length();
@@ -41,282 +42,7 @@
                }

        }
-       
-       static {
-               HashMap temp = new HashMap();
-               temp.put(new Character((char)0), "#0");
-               temp.put(new Character((char)34), "quot");
-               temp.put(new Character((char)38), "amp");
-               temp.put(new Character((char)39), "#39");
-               temp.put(new Character((char)60), "lt");
-               temp.put(new Character((char)62), "gt");
-               temp.put(new Character((char)160), "nbsp");
-               temp.put(new Character((char)161), "iexcl");
-               temp.put(new Character((char)162), "cent");
-               temp.put(new Character((char)163), "pound");
-               temp.put(new Character((char)164), "curren");
-               temp.put(new Character((char)165), "yen");
-               temp.put(new Character((char)166), "brvbar");
-               temp.put(new Character((char)167), "sect");
-               temp.put(new Character((char)168), "uml");
-               temp.put(new Character((char)169), "copy");
-               temp.put(new Character((char)170), "ordf");
-               temp.put(new Character((char)171), "laquo");
-               temp.put(new Character((char)172), "not");
-               temp.put(new Character((char)173), "shy");
-               temp.put(new Character((char)174), "reg");
-               temp.put(new Character((char)175), "macr");
-               temp.put(new Character((char)176), "deg");
-               temp.put(new Character((char)177), "plusmn");
-               temp.put(new Character((char)178), "sup2");
-               temp.put(new Character((char)179), "sup3");
-               temp.put(new Character((char)180), "acute");
-               temp.put(new Character((char)181), "micro");
-               temp.put(new Character((char)182), "para");
-               temp.put(new Character((char)183), "middot");
-               temp.put(new Character((char)184), "cedil");
-               temp.put(new Character((char)185), "sup1");
-               temp.put(new Character((char)186), "ordm");
-               temp.put(new Character((char)187), "raquo");
-               temp.put(new Character((char)188), "frac14");
-               temp.put(new Character((char)189), "frac12");
-               temp.put(new Character((char)190), "frac34");
-               temp.put(new Character((char)191), "iquest");
-               temp.put(new Character((char)192), "Agrave");
-               temp.put(new Character((char)193), "Aacute");
-               temp.put(new Character((char)194), "Acirc");
-               temp.put(new Character((char)195), "Atilde");
-               temp.put(new Character((char)196), "Auml");
-               temp.put(new Character((char)197), "Aring");
-               temp.put(new Character((char)198), "AElig");
-               temp.put(new Character((char)199), "Ccedil");
-               temp.put(new Character((char)200), "Egrave");
-               temp.put(new Character((char)201), "Eacute");
-               temp.put(new Character((char)202), "Ecirc");
-               temp.put(new Character((char)203), "Euml");
-               temp.put(new Character((char)204), "Igrave");
-               temp.put(new Character((char)205), "Iacute");
-               temp.put(new Character((char)206), "Icirc");
-               temp.put(new Character((char)207), "Iuml");
-               temp.put(new Character((char)208), "ETH");
-               temp.put(new Character((char)209), "Ntilde");
-               temp.put(new Character((char)210), "Ograve");
-               temp.put(new Character((char)211), "Oacute");
-               temp.put(new Character((char)212), "Ocirc");
-               temp.put(new Character((char)213), "Otilde");
-               temp.put(new Character((char)214), "Ouml");
-               temp.put(new Character((char)215), "times");
-               temp.put(new Character((char)216), "Oslash");
-               temp.put(new Character((char)217), "Ugrave");
-               temp.put(new Character((char)218), "Uacute");
-               temp.put(new Character((char)219), "Ucirc");
-               temp.put(new Character((char)220), "Uuml");
-               temp.put(new Character((char)221), "Yacute");
-               temp.put(new Character((char)222), "THORN");
-               temp.put(new Character((char)223), "szlig");
-               temp.put(new Character((char)224), "agrave");
-               temp.put(new Character((char)225), "aacute");
-               temp.put(new Character((char)226), "acirc");
-               temp.put(new Character((char)227), "atilde");
-               temp.put(new Character((char)228), "auml");
-               temp.put(new Character((char)229), "aring");
-               temp.put(new Character((char)230), "aelig");
-               temp.put(new Character((char)231), "ccedil");
-               temp.put(new Character((char)232), "egrave");
-               temp.put(new Character((char)233), "eacute");
-               temp.put(new Character((char)234), "ecirc");
-               temp.put(new Character((char)235), "euml");
-               temp.put(new Character((char)236), "igrave");
-               temp.put(new Character((char)237), "iacute");
-               temp.put(new Character((char)238), "icirc");
-               temp.put(new Character((char)239), "iuml");
-               temp.put(new Character((char)240), "eth");
-               temp.put(new Character((char)241), "ntilde");
-               temp.put(new Character((char)242), "ograve");
-               temp.put(new Character((char)243), "oacute");
-               temp.put(new Character((char)244), "ocirc");
-               temp.put(new Character((char)245), "otilde");
-               temp.put(new Character((char)246), "ouml");
-               temp.put(new Character((char)247), "divide");
-               temp.put(new Character((char)248), "oslash");
-               temp.put(new Character((char)249), "ugrave");
-               temp.put(new Character((char)250), "uacute");
-               temp.put(new Character((char)251), "ucirc");
-               temp.put(new Character((char)252), "uuml");
-               temp.put(new Character((char)253), "yacute");
-               temp.put(new Character((char)254), "thorn");
-               temp.put(new Character((char)255), "yuml");
-               temp.put(new Character((char)260), "#260");
-               temp.put(new Character((char)261), "#261");
-               temp.put(new Character((char)262), "#262");
-               temp.put(new Character((char)263), "#263");
-               temp.put(new Character((char)280), "#280");
-               temp.put(new Character((char)281), "#281");
-               temp.put(new Character((char)321), "#321");
-               temp.put(new Character((char)322), "#322");
-               temp.put(new Character((char)323), "#323");
-               temp.put(new Character((char)324), "#324");
-               temp.put(new Character((char)338), "OElig");
-               temp.put(new Character((char)339), "oelig");
-               temp.put(new Character((char)346), "#346");
-               temp.put(new Character((char)347), "#347");
-               temp.put(new Character((char)352), "Scaron");
-               temp.put(new Character((char)353), "scaron");
-               temp.put(new Character((char)376), "Yuml");
-               temp.put(new Character((char)377), "#377");
-               temp.put(new Character((char)378), "#378");
-               temp.put(new Character((char)379), "#379");
-               temp.put(new Character((char)380), "#380");
-               temp.put(new Character((char)402), "fnof");
-               temp.put(new Character((char)710), "circ");
-               temp.put(new Character((char)732), "tilde");
-               temp.put(new Character((char)913), "Alpha");
-               temp.put(new Character((char)914), "Beta");
-               temp.put(new Character((char)915), "Gamma");
-               temp.put(new Character((char)916), "Delta");
-               temp.put(new Character((char)917), "Epsilon");
-               temp.put(new Character((char)918), "Zeta");
-               temp.put(new Character((char)919), "Eta");
-               temp.put(new Character((char)920), "Theta");
-               temp.put(new Character((char)921), "Iota");
-               temp.put(new Character((char)922), "Kappa");
-               temp.put(new Character((char)923), "Lambda");
-               temp.put(new Character((char)924), "Mu");
-               temp.put(new Character((char)925), "Nu");
-               temp.put(new Character((char)926), "Xi");
-               temp.put(new Character((char)927), "Omicron");
-               temp.put(new Character((char)928), "Pi");
-               temp.put(new Character((char)929), "Rho");
-               temp.put(new Character((char)931), "Sigma");
-               temp.put(new Character((char)932), "Tau");
-               temp.put(new Character((char)933), "Upsilon");
-               temp.put(new Character((char)934), "Phi");
-               temp.put(new Character((char)935), "Chi");
-               temp.put(new Character((char)936), "Psi");
-               temp.put(new Character((char)937), "Omega");
-               temp.put(new Character((char)945), "alpha");
-               temp.put(new Character((char)946), "beta");
-               temp.put(new Character((char)947), "gamma");
-               temp.put(new Character((char)948), "delta");
-               temp.put(new Character((char)949), "epsilon");
-               temp.put(new Character((char)950), "zeta");
-               temp.put(new Character((char)951), "eta");
-               temp.put(new Character((char)952), "theta");
-               temp.put(new Character((char)953), "iota");
-               temp.put(new Character((char)954), "kappa");
-               temp.put(new Character((char)955), "lambda");
-               temp.put(new Character((char)956), "mu");
-               temp.put(new Character((char)957), "nu");
-               temp.put(new Character((char)958), "xi");
-               temp.put(new Character((char)959), "omicron");
-               temp.put(new Character((char)960), "pi");
-               temp.put(new Character((char)961), "rho");
-               temp.put(new Character((char)962), "sigmaf");
-               temp.put(new Character((char)963), "sigma");
-               temp.put(new Character((char)964), "tau");
-               temp.put(new Character((char)965), "upsilon");
-               temp.put(new Character((char)966), "phi");
-               temp.put(new Character((char)967), "chi");
-               temp.put(new Character((char)968), "psi");
-               temp.put(new Character((char)969), "omega");
-               temp.put(new Character((char)977), "thetasym");
-               temp.put(new Character((char)978), "upsih");
-               temp.put(new Character((char)982), "piv");
-               temp.put(new Character((char)8194), "ensp");
-               temp.put(new Character((char)8195), "emsp");
-               temp.put(new Character((char)8201), "thinsp");
-               temp.put(new Character((char)8204), "zwnj");
-               temp.put(new Character((char)8205), "zwj");
-               temp.put(new Character((char)8206), "lrm");
-               temp.put(new Character((char)8207), "rlm");
-               temp.put(new Character((char)8211), "ndash");
-               temp.put(new Character((char)8212), "mdash");
-               temp.put(new Character((char)8216), "lsquo");
-               temp.put(new Character((char)8217), "rsquo");
-               temp.put(new Character((char)8218), "sbquo");
-               temp.put(new Character((char)8220), "ldquo");
-               temp.put(new Character((char)8221), "rdquo");
-               temp.put(new Character((char)8222), "bdquo");
-               temp.put(new Character((char)8224), "dagger");
-               temp.put(new Character((char)8225), "Dagger");
-               temp.put(new Character((char)8226), "bull");
-               temp.put(new Character((char)8230), "hellip");
-               temp.put(new Character((char)8240), "permil");
-               temp.put(new Character((char)8242), "prime");
-               temp.put(new Character((char)8243), "Prime");
-               temp.put(new Character((char)8249), "lsaquo");
-               temp.put(new Character((char)8250), "rsaquo");
-               temp.put(new Character((char)8254), "oline");
-               temp.put(new Character((char)8260), "frasl");
-               temp.put(new Character((char)8364), "euro");
-               temp.put(new Character((char)8465), "image");
-               temp.put(new Character((char)8472), "weierp");
-               temp.put(new Character((char)8476), "real");
-               temp.put(new Character((char)8482), "trade");
-               temp.put(new Character((char)8501), "alefsym");
-               temp.put(new Character((char)8592), "larr");
-               temp.put(new Character((char)8593), "uarr");
-               temp.put(new Character((char)8594), "rarr");
-               temp.put(new Character((char)8595), "darr");
-               temp.put(new Character((char)8596), "harr");
-               temp.put(new Character((char)8629), "crarr");
-               temp.put(new Character((char)8656), "lArr");
-               temp.put(new Character((char)8657), "uArr");
-               temp.put(new Character((char)8658), "rArr");
-               temp.put(new Character((char)8659), "dArr");
-               temp.put(new Character((char)8660), "hArr");
-               temp.put(new Character((char)8704), "forall");
-               temp.put(new Character((char)8706), "part");
-               temp.put(new Character((char)8707), "exist");
-               temp.put(new Character((char)8709), "empty");
-               temp.put(new Character((char)8711), "nabla");
-               temp.put(new Character((char)8712), "isin");
-               temp.put(new Character((char)8713), "notin");
-               temp.put(new Character((char)8715), "ni");
-               temp.put(new Character((char)8719), "prod");
-               temp.put(new Character((char)8721), "sum");
-               temp.put(new Character((char)8722), "minus");
-               temp.put(new Character((char)8727), "lowast");
-               temp.put(new Character((char)8730), "radic");
-               temp.put(new Character((char)8733), "prop");
-               temp.put(new Character((char)8734), "infin");
-               temp.put(new Character((char)8736), "ang");
-               temp.put(new Character((char)8743), "and");
-               temp.put(new Character((char)8744), "or");
-               temp.put(new Character((char)8745), "cap");
-               temp.put(new Character((char)8746), "cup");
-               temp.put(new Character((char)8747), "int");
-               temp.put(new Character((char)8756), "there4");
-               temp.put(new Character((char)8764), "sim");
-               temp.put(new Character((char)8773), "cong");
-               temp.put(new Character((char)8776), "asymp");
-               temp.put(new Character((char)8800), "ne");
-               temp.put(new Character((char)8801), "equiv");
-               temp.put(new Character((char)8804), "le");
-               temp.put(new Character((char)8805), "ge");
-               temp.put(new Character((char)8834), "sub");
-               temp.put(new Character((char)8835), "sup");
-               temp.put(new Character((char)8836), "nsub");
-               temp.put(new Character((char)8838), "sube");
-               temp.put(new Character((char)8839), "supe");
-               temp.put(new Character((char)8853), "oplus");
-               temp.put(new Character((char)8855), "otimes");
-               temp.put(new Character((char)8869), "perp");
-               temp.put(new Character((char)8901), "sdot");
-               temp.put(new Character((char)8968), "lceil");
-               temp.put(new Character((char)8969), "rceil");
-               temp.put(new Character((char)8970), "lfloor");
-               temp.put(new Character((char)8971), "rfloor");
-               temp.put(new Character((char)9001), "lang");
-               temp.put(new Character((char)9002), "rang");
-               temp.put(new Character((char)9674), "loz");
-               temp.put(new Character((char)9824), "spades");
-               temp.put(new Character((char)9827), "clubs");
-               temp.put(new Character((char)9829), "hearts");
-               temp.put(new Character((char)9830), "diams");
-               charTable = new CharTable(temp);
-       }
-       
+               
        private final static class CharTable{
                private char[] chars;
                private String[] strings;

Copied: branches/freenet-jfk/src/freenet/support/HTMLEntities.java (from rev 
14796, trunk/freenet/src/freenet/support/HTMLEntities.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/HTMLEntities.java                  
        (rev 0)
+++ branches/freenet-jfk/src/freenet/support/HTMLEntities.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,328 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import java.util.HashMap;
+
+
+/**
+ * Class that provides data structures filled with
+ * HTML Entities and correspondant char value
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public final class HTMLEntities {
+       
+       /**
+        * a Map where the HTML Entity is the
+        * value and the correspondant char is
+        * the key
+        */
+       public static final HashMap encodeMap;
+       
+       /**
+        * a Map where the HTML Entity is the
+        * key and the correspondant char is
+        * the value
+        */
+       public static final HashMap decodeMap;
+       
+       private static final Object[][] charArray = {
+               {new Character((char)0), "#0"},
+               {new Character((char)34), "quot"},
+               {new Character((char)38), "amp"},
+               {new Character((char)39), "#39"},
+               {new Character((char)60), "lt"},
+               {new Character((char)62), "gt"},
+               {new Character((char)160), "nbsp"},
+               {new Character((char)161), "iexcl"},
+               {new Character((char)162), "cent"},
+               {new Character((char)163), "pound"},
+               {new Character((char)164), "curren"},
+               {new Character((char)165), "yen"},
+               {new Character((char)166), "brvbar"},
+               {new Character((char)167), "sect"},
+               {new Character((char)168), "uml"},
+               {new Character((char)169), "copy"},
+               {new Character((char)170), "ordf"},
+               {new Character((char)171), "laquo"},
+               {new Character((char)172), "not"},
+               {new Character((char)173), "shy"},
+               {new Character((char)174), "reg"},
+               {new Character((char)175), "macr"},
+               {new Character((char)176), "deg"},
+               {new Character((char)177), "plusmn"},
+               {new Character((char)178), "sup2"},
+               {new Character((char)179), "sup3"},
+               {new Character((char)180), "acute"},
+               {new Character((char)181), "micro"},
+               {new Character((char)182), "para"},
+               {new Character((char)183), "middot"},
+               {new Character((char)184), "cedil"},
+               {new Character((char)185), "sup1"},
+               {new Character((char)186), "ordm"},
+               {new Character((char)187), "raquo"},
+               {new Character((char)188), "frac14"},
+               {new Character((char)189), "frac12"},
+               {new Character((char)190), "frac34"},
+               {new Character((char)191), "iquest"},
+               {new Character((char)192), "Agrave"},
+               {new Character((char)193), "Aacute"},
+               {new Character((char)194), "Acirc"},
+               {new Character((char)195), "Atilde"},
+               {new Character((char)196), "Auml"},
+               {new Character((char)197), "Aring"},
+               {new Character((char)198), "AElig"},
+               {new Character((char)199), "Ccedil"},
+               {new Character((char)200), "Egrave"},
+               {new Character((char)201), "Eacute"},
+               {new Character((char)202), "Ecirc"},
+               {new Character((char)203), "Euml"},
+               {new Character((char)204), "Igrave"},
+               {new Character((char)205), "Iacute"},
+               {new Character((char)206), "Icirc"},
+               {new Character((char)207), "Iuml"},
+               {new Character((char)208), "ETH"},
+               {new Character((char)209), "Ntilde"},
+               {new Character((char)210), "Ograve"},
+               {new Character((char)211), "Oacute"},
+               {new Character((char)212), "Ocirc"},
+               {new Character((char)213), "Otilde"},
+               {new Character((char)214), "Ouml"},
+               {new Character((char)215), "times"},
+               {new Character((char)216), "Oslash"},
+               {new Character((char)217), "Ugrave"},
+               {new Character((char)218), "Uacute"},
+               {new Character((char)219), "Ucirc"},
+               {new Character((char)220), "Uuml"},
+               {new Character((char)221), "Yacute"},
+               {new Character((char)222), "THORN"},
+               {new Character((char)223), "szlig"},
+               {new Character((char)224), "agrave"},
+               {new Character((char)225), "aacute"},
+               {new Character((char)226), "acirc"},
+               {new Character((char)227), "atilde"},
+               {new Character((char)228), "auml"},
+               {new Character((char)229), "aring"},
+               {new Character((char)230), "aelig"},
+               {new Character((char)231), "ccedil"},
+               {new Character((char)232), "egrave"},
+               {new Character((char)233), "eacute"},
+               {new Character((char)234), "ecirc"},
+               {new Character((char)235), "euml"},
+               {new Character((char)236), "igrave"},
+               {new Character((char)237), "iacute"},
+               {new Character((char)238), "icirc"},
+               {new Character((char)239), "iuml"},
+               {new Character((char)240), "eth"},
+               {new Character((char)241), "ntilde"},
+               {new Character((char)242), "ograve"},
+               {new Character((char)243), "oacute"},
+               {new Character((char)244), "ocirc"},
+               {new Character((char)245), "otilde"},
+               {new Character((char)246), "ouml"},
+               {new Character((char)247), "divide"},
+               {new Character((char)248), "oslash"},
+               {new Character((char)249), "ugrave"},
+               {new Character((char)250), "uacute"},
+               {new Character((char)251), "ucirc"},
+               {new Character((char)252), "uuml"},
+               {new Character((char)253), "yacute"},
+               {new Character((char)254), "thorn"},
+               {new Character((char)255), "yuml"},
+               {new Character((char)260), "#260"},
+               {new Character((char)261), "#261"},
+               {new Character((char)262), "#262"},
+               {new Character((char)263), "#263"},
+               {new Character((char)280), "#280"},
+               {new Character((char)281), "#281"},
+               {new Character((char)321), "#321"},
+               {new Character((char)322), "#322"},
+               {new Character((char)323), "#323"},
+               {new Character((char)324), "#324"},
+               {new Character((char)338), "OElig"},
+               {new Character((char)339), "oelig"},
+               {new Character((char)346), "#346"},
+               {new Character((char)347), "#347"},
+               {new Character((char)352), "Scaron"},
+               {new Character((char)353), "scaron"},
+               {new Character((char)376), "Yuml"},
+               {new Character((char)377), "#377"},
+               {new Character((char)378), "#378"},
+               {new Character((char)379), "#379"},
+               {new Character((char)380), "#380"},
+               {new Character((char)402), "fnof"},
+               {new Character((char)710), "circ"},
+               {new Character((char)732), "tilde"},
+               {new Character((char)913), "Alpha"},
+               {new Character((char)914), "Beta"},
+               {new Character((char)915), "Gamma"},
+               {new Character((char)916), "Delta"},
+               {new Character((char)917), "Epsilon"},
+               {new Character((char)918), "Zeta"},
+               {new Character((char)919), "Eta"},
+               {new Character((char)920), "Theta"},
+               {new Character((char)921), "Iota"},
+               {new Character((char)922), "Kappa"},
+               {new Character((char)923), "Lambda"},
+               {new Character((char)924), "Mu"},
+               {new Character((char)925), "Nu"},
+               {new Character((char)926), "Xi"},
+               {new Character((char)927), "Omicron"},
+               {new Character((char)928), "Pi"},
+               {new Character((char)929), "Rho"},
+               {new Character((char)931), "Sigma"},
+               {new Character((char)932), "Tau"},
+               {new Character((char)933), "Upsilon"},
+               {new Character((char)934), "Phi"},
+               {new Character((char)935), "Chi"},
+               {new Character((char)936), "Psi"},
+               {new Character((char)937), "Omega"},
+               {new Character((char)945), "alpha"},
+               {new Character((char)946), "beta"},
+               {new Character((char)947), "gamma"},
+               {new Character((char)948), "delta"},
+               {new Character((char)949), "epsilon"},
+               {new Character((char)950), "zeta"},
+               {new Character((char)951), "eta"},
+               {new Character((char)952), "theta"},
+               {new Character((char)953), "iota"},
+               {new Character((char)954), "kappa"},
+               {new Character((char)955), "lambda"},
+               {new Character((char)956), "mu"},
+               {new Character((char)957), "nu"},
+               {new Character((char)958), "xi"},
+               {new Character((char)959), "omicron"},
+               {new Character((char)960), "pi"},
+               {new Character((char)961), "rho"},
+               {new Character((char)962), "sigmaf"},
+               {new Character((char)963), "sigma"},
+               {new Character((char)964), "tau"},
+               {new Character((char)965), "upsilon"},
+               {new Character((char)966), "phi"},
+               {new Character((char)967), "chi"},
+               {new Character((char)968), "psi"},
+               {new Character((char)969), "omega"},
+               {new Character((char)977), "thetasym"},
+               {new Character((char)978), "upsih"},
+               {new Character((char)982), "piv"},
+               {new Character((char)8194), "ensp"},
+               {new Character((char)8195), "emsp"},
+               {new Character((char)8201), "thinsp"},
+               {new Character((char)8204), "zwnj"},
+               {new Character((char)8205), "zwj"},
+               {new Character((char)8206), "lrm"},
+               {new Character((char)8207), "rlm"},
+               {new Character((char)8211), "ndash"},
+               {new Character((char)8212), "mdash"},
+               {new Character((char)8216), "lsquo"},
+               {new Character((char)8217), "rsquo"},
+               {new Character((char)8218), "sbquo"},
+               {new Character((char)8220), "ldquo"},
+               {new Character((char)8221), "rdquo"},
+               {new Character((char)8222), "bdquo"},
+               {new Character((char)8224), "dagger"},
+               {new Character((char)8225), "Dagger"},
+               {new Character((char)8226), "bull"},
+               {new Character((char)8230), "hellip"},
+               {new Character((char)8240), "permil"},
+               {new Character((char)8242), "prime"},
+               {new Character((char)8243), "Prime"},
+               {new Character((char)8249), "lsaquo"},
+               {new Character((char)8250), "rsaquo"},
+               {new Character((char)8254), "oline"},
+               {new Character((char)8260), "frasl"},
+               {new Character((char)8364), "euro"},
+               {new Character((char)8465), "image"},
+               {new Character((char)8472), "weierp"},
+               {new Character((char)8476), "real"},
+               {new Character((char)8482), "trade"},
+               {new Character((char)8501), "alefsym"},
+               {new Character((char)8592), "larr"},
+               {new Character((char)8593), "uarr"},
+               {new Character((char)8594), "rarr"},
+               {new Character((char)8595), "darr"},
+               {new Character((char)8596), "harr"},
+               {new Character((char)8629), "crarr"},
+               {new Character((char)8656), "lArr"},
+               {new Character((char)8657), "uArr"},
+               {new Character((char)8658), "rArr"},
+               {new Character((char)8659), "dArr"},
+               {new Character((char)8660), "hArr"},
+               {new Character((char)8704), "forall"},
+               {new Character((char)8706), "part"},
+               {new Character((char)8707), "exist"},
+               {new Character((char)8709), "empty"},
+               {new Character((char)8711), "nabla"},
+               {new Character((char)8712), "isin"},
+               {new Character((char)8713), "notin"},
+               {new Character((char)8715), "ni"},
+               {new Character((char)8719), "prod"},
+               {new Character((char)8721), "sum"},
+               {new Character((char)8722), "minus"},
+               {new Character((char)8727), "lowast"},
+               {new Character((char)8730), "radic"},
+               {new Character((char)8733), "prop"},
+               {new Character((char)8734), "infin"},
+               {new Character((char)8736), "ang"},
+               {new Character((char)8743), "and"},
+               {new Character((char)8744), "or"},
+               {new Character((char)8745), "cap"},
+               {new Character((char)8746), "cup"},
+               {new Character((char)8747), "int"},
+               {new Character((char)8756), "there4"},
+               {new Character((char)8764), "sim"},
+               {new Character((char)8773), "cong"},
+               {new Character((char)8776), "asymp"},
+               {new Character((char)8800), "ne"},
+               {new Character((char)8801), "equiv"},
+               {new Character((char)8804), "le"},
+               {new Character((char)8805), "ge"},
+               {new Character((char)8834), "sub"},
+               {new Character((char)8835), "sup"},
+               {new Character((char)8836), "nsub"},
+               {new Character((char)8838), "sube"},
+               {new Character((char)8839), "supe"},
+               {new Character((char)8853), "oplus"},
+               {new Character((char)8855), "otimes"},
+               {new Character((char)8869), "perp"},
+               {new Character((char)8901), "sdot"},
+               {new Character((char)8968), "lceil"},
+               {new Character((char)8969), "rceil"},
+               {new Character((char)8970), "lfloor"},
+               {new Character((char)8971), "rfloor"},
+               {new Character((char)9001), "lang"},
+               {new Character((char)9002), "rang"},
+               {new Character((char)9674), "loz"},
+               {new Character((char)9824), "spades"},
+               {new Character((char)9827), "clubs"},
+               {new Character((char)9829), "hearts"},
+               {new Character((char)9830), "diams"}
+       };
+       
+       
+       static {
+               encodeMap = new HashMap();
+               decodeMap = new HashMap();
+               
+               for(int i=0; i<charArray.length; i++) {
+                       encodeMap.put(charArray[i][0],charArray[i][1]);
+                       decodeMap.put(charArray[i][1],charArray[i][0]);
+               }
+               
+       }
+
+}

Modified: branches/freenet-jfk/src/freenet/support/HTMLNode.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/HTMLNode.java      2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/HTMLNode.java      2007-08-21 
20:26:59 UTC (rev 14828)
@@ -9,15 +9,13 @@
 import java.util.Map;
 import java.util.Set;

-import freenet.l10n.L10n;
-
 public class HTMLNode {

        protected final String name;

        private final String content;

-       protected final Map attributes = new HashMap();
+       private final Map attributes = new HashMap();

        protected final List children = new ArrayList();

@@ -37,10 +35,6 @@
                this(name, new String[] { attributeName }, new String[] { 
attributeValue }, content);
        }

-       public HTMLNode(String name, String[] attributeNames, String[] 
attributeValues) {
-               this(name, attributeNames, attributeValues, null);
-       }
-
        public HTMLNode(String name, String[] attributeNames, String[] 
attributeValues, String content) {
                this.name = name.toLowerCase(Locale.ENGLISH);
                if ((attributeNames != null) && (attributeValues != null)) {
@@ -51,36 +45,20 @@
                                attributes.put(attributeNames[attributeIndex], 
attributeValues[attributeIndex]);
                        }
                }
-               if (content != null) {
-                       if (!name.equals("#") && !name.equals("%")) {
-                               addChild(new HTMLNode("#", content));
-                               this.content = null;
-                       } else {
-                               this.content = content;
-                       }
-               } else {
+               if (content != null && !name.equals("#") && !name.equals("%")) {
+                       addChild(new HTMLNode("#", content));
                        this.content = null;
-               }
+               } else
+                       this.content = content;
        }

        /**
-        * @return the name
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
         * @return the content
         */
        public String getContent() {
                return content;
        }

-       public void addAttribute(String attributeName) {
-               addAttribute(attributeName, attributeName);
-       }
-
        public void addAttribute(String attributeName, String attributeValue) {
                attributes.put(attributeName, attributeValue);
        }
@@ -95,6 +73,13 @@

        public HTMLNode addChild(HTMLNode childNode) {
                if (childNode == null) throw new NullPointerException();
+               //since an efficient algorithm to check the loop presence 
+               //is not present, at least it checks if we are trying to
+               //addChild the node itself as a child
+               if (childNode.equals(this))     
+                       throw new IllegalArgumentException("A HTMLNode cannot 
be child of himself");
+               if (children.contains(childNode))
+                       throw new IllegalArgumentException("Cannot add twice 
the same HTMLNode as child");
                children.add(childNode);
                return childNode;
        }
@@ -190,7 +175,7 @@
         */
        public static class HTMLDoctype extends HTMLNode {

-               protected final String systemUri;
+               private final String systemUri;

                /**
                 * 
@@ -205,6 +190,9 @@
                 */
                public StringBuffer generate(StringBuffer tagBuffer) {
                        tagBuffer.append("<!DOCTYPE ").append(name).append(" 
PUBLIC \"").append(systemUri).append("\">\n");
+                       //TODO A meaningful exception should be raised 
+                       // when trying to call the method for a HTMLDoctype 
+                       // with number of child != 1 
                        return ((HTMLNode) children.get(0)).generate(tagBuffer);
                }


Modified: branches/freenet-jfk/src/freenet/support/HexUtil.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/HexUtil.java       2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/HexUtil.java       2007-08-21 
20:26:59 UTC (rev 14828)
@@ -21,7 +21,7 @@


        /**
-        * Converts a byte array into a string of upper case hex chars.
+        * Converts a byte array into a string of lower case hex chars.
         * 
         * @param bs
         *            A byte array
@@ -32,6 +32,8 @@
         * @return the string of hex chars.
         */
        public static final String bytesToHex(byte[] bs, int off, int length) {
+               if (bs.length <= off || bs.length < off+length)
+                       throw new IllegalArgumentException();
                StringBuffer sb = new StringBuffer(length * 2);
                bytesToHexAppend(bs, off, length, sb);
                return sb.toString();
@@ -42,8 +44,10 @@
                int off,
                int length,
                StringBuffer sb) {
+               if (bs.length <= off || bs.length < off+length)
+                       throw new IllegalArgumentException();
                sb.ensureCapacity(sb.length() + length * 2);
-               for (int i = off; (i < (off + length)) && (i < bs.length); i++) 
{
+               for (int i = off; i < (off + length); i++) {
                        sb.append(Character.forDigit((bs[i] >>> 4) & 0xf, 16));
                        sb.append(Character.forDigit(bs[i] & 0xf, 16));
                }
@@ -76,6 +80,7 @@
         */
        public static final void hexToBytes(String s, byte[] out, int off)
                throws NumberFormatException, IndexOutOfBoundsException {
+               
                int slen = s.length();
                if ((slen % 2) != 0) {
                        s = '0' + s;
@@ -105,6 +110,9 @@

        /**
         * Pack the bits in ba into a byte[].
+        *
+        * @param ba : the BitSet
+        * @param size : How many bits shall be taken into account starting 
from the LSB?
         */
        public final static byte[] bitsToBytes(BitSet ba, int size) {
                int bytesAlloc = countBytesForBits(size);
@@ -116,10 +124,10 @@
                        for(int j=0;j<8;j++) {
                                int idx = i*8+j;
                                boolean val = 
-                                       idx > size ? false :
+                                       idx > size - 1 ? false :
                                                ba.get(idx);
                                s |= val ? (1<<j) : 0;
-                               if(sb != null) sb.append(val ? '1' : '0');
+                               if(logDEBUG) sb.append(val ? '1' : '0');
                        }
                        if(s > 255) throw new IllegalStateException("WTF? s = 
"+s);
                        b[i] = (byte)s;

Modified: branches/freenet-jfk/src/freenet/support/LRUHashtable.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/LRUHashtable.java  2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/LRUHashtable.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -58,6 +58,14 @@
         }
     }

+       public final synchronized Object peekValue() {
+        if ( list.size() > 0 ) {
+            return ((QItem)hash.get(((QItem)list.tail()).obj)).value;
+        } else {
+            return null;
+        }
+       }
+
     public final int size() {
         return list.size();
     }
@@ -121,4 +129,8 @@
         }
     }

+       public boolean isEmpty() {
+               return list.isEmpty();
+       }
+
 }

Modified: branches/freenet-jfk/src/freenet/support/LRUQueue.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/LRUQueue.java      2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/LRUQueue.java      2007-08-21 
20:26:59 UTC (rev 14828)
@@ -34,6 +34,21 @@
     } 

     /**
+     * push to bottom (least recently used position)
+     */
+       public synchronized void pushLeast(Object obj) {
+        QItem insert = (QItem)hash.get(obj);        
+        if (insert == null) {
+            insert = new QItem(obj);
+            hash.put(obj,insert);
+        } else {
+            list.remove(insert);
+        }
+
+        list.push(insert);
+       }
+       
+    /**
      *  @return Least recently pushed Object.
      */
     public final synchronized Object pop() {
@@ -95,6 +110,34 @@
                return hash.keySet().toArray();
        }

+       public synchronized Object[] toArray(Object[] array) {
+               return hash.keySet().toArray(array);
+       }
+       
+       public synchronized Object[] toArrayOrdered() {
+               Object[] array = new Object[list.size()];
+               int x = 0;
+               for(Enumeration e = 
list.reverseElements();e.hasMoreElements();) {
+                       array[x++] = ((QItem)e.nextElement()).obj;
+               }
+               return array;
+       }
+
+       /**
+        * @param array The array to fill in. If it is too small a new array of 
the same type will be allocated.
+        */
+       public synchronized Object[] toArrayOrdered(Object[] array) {
+               array = toArray(array);
+               int listSize = list.size();
+               if(array.length != listSize)
+                       throw new 
IllegalStateException("array.length="+array.length+" but list.size="+listSize);
+               int x = 0;
+               for(Enumeration e = 
list.reverseElements();e.hasMoreElements();) {
+                       array[x++] = ((QItem)e.nextElement()).obj;
+               }
+               return array;
+       }
+       
        public synchronized boolean isEmpty() {
                return hash.isEmpty();
        }

Copied: branches/freenet-jfk/src/freenet/support/MutableBoolean.java (from rev 
14796, trunk/freenet/src/freenet/support/MutableBoolean.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/MutableBoolean.java                
                (rev 0)
+++ branches/freenet-jfk/src/freenet/support/MutableBoolean.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -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.support;
+
+/**
+ * Freely modifiable boolean wrapper.
+ * @author toad
+ *
+ */
+public class MutableBoolean {
+
+       public boolean value;
+       
+}

Copied: branches/freenet-jfk/src/freenet/support/PooledExecutor.java (from rev 
14796, trunk/freenet/src/freenet/support/PooledExecutor.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/PooledExecutor.java                
                (rev 0)
+++ branches/freenet-jfk/src/freenet/support/PooledExecutor.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,139 @@
+/* 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;
+
+import java.util.ArrayList;
+
+/**
+ * Pooled Executor implementation. Create a thread when we need one, let them 
die
+ * after 5 minutes of inactivity.
+ * @author toad
+ */
+public class PooledExecutor implements Executor {
+
+       private final ArrayList runningThreads /* <MyThread> */ = new 
ArrayList();
+       private final ArrayList waitingThreads /* <MyThread> */ = new 
ArrayList();
+       long threadCounter = 0;
+       private long jobCount;
+       private long jobMisses;
+       private static boolean logMINOR;
+       
+       /** Maximum time a thread will wait for a job */
+       static final int TIMEOUT = 5*60*1000;
+       
+       public void start() {
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       }
+       
+       public void execute(Runnable job, String jobName) {
+               while(true) {
+                       MyThread t;
+                       boolean mustStart = false;
+                       boolean miss = false;
+                       synchronized(this) {
+                               jobCount++;
+                               if(!waitingThreads.isEmpty()) {
+                                       t = (MyThread) 
waitingThreads.remove(waitingThreads.size()-1);
+                               } else {
+                                       // Will be coalesced by thread count 
listings if we use "@" or "for"
+                                       t = new MyThread("Pooled thread 
awaiting work @"+(threadCounter++));
+                                       t.setDaemon(true);
+                                       mustStart = true;
+                                       miss = true;
+                               }
+                       }
+                       synchronized(t) {
+                               if(!t.alive) continue;
+                               if(t.nextJob != null) continue;
+                               t.nextJob = job;
+                               if(!mustStart)
+                                       // It is possible that we could get a 
wierd race condition with
+                                       // notify()/wait() signalling on a 
thread being used by higher
+                                       // level code. So we'd best use 
notifyAll().
+                                       t.notifyAll();
+                       }
+                       t.setName(jobName);
+                       if(mustStart) {
+                               t.start();
+                               synchronized(this) {
+                                       runningThreads.add(t);
+                                       if(miss)
+                                               jobMisses++;
+                                       if(logMINOR)
+                                               Logger.minor(this, "Jobs: 
"+jobMisses+" misses of "+jobCount);
+                               }
+                       }
+                       return;
+               }
+       }
+
+       public synchronized int waitingThreads() {
+               return waitingThreads.size();
+       }
+       
+       class MyThread extends Thread {
+               
+               final String defaultName;
+               boolean alive = true;
+               Runnable nextJob;
+               
+               public MyThread(String defaultName) {
+                       super(defaultName);
+                       this.defaultName = defaultName;
+               }
+
+               public void run() {
+                       long ranJobs = 0;
+                       while(true) {
+                               Runnable job;
+                               
+                               synchronized(this) {
+                                       job = nextJob;
+                                       nextJob = null;
+                               }
+                               
+                               if(job == null) {
+                                       synchronized(PooledExecutor.this) {
+                                               waitingThreads.add(this);
+                                       }
+                                       synchronized(this) {
+                                               if(nextJob == null) {
+                                                       
this.setName(defaultName);
+                                                       try {
+                                                               wait(TIMEOUT);
+                                                       } catch 
(InterruptedException e) {
+                                                               // Ignore
+                                                       }
+                                               }
+                                               job = nextJob;
+                                               nextJob = null;
+                                               if(job == null) {
+                                                       alive = false;
+                                                       // execute() won't give 
us another job if alive = false
+                                               }
+                                       }
+                                       synchronized(PooledExecutor.this) {
+                                               waitingThreads.remove(this);
+                                               if(!alive) {
+                                                       
runningThreads.remove(this);
+                                                       if(logMINOR)
+                                                               
Logger.minor(this, "Exiting having executed "+ranJobs+" jobs : "+this);
+                                                       return;
+                                               }
+                                       }
+                               }
+                               
+                               // Run the job
+                               try {
+                                       job.run();
+                               } catch (Throwable t) {
+                                       Logger.error(this, "Caught "+t+" 
running job "+job, t);
+                               }
+                               ranJobs++;
+                       }
+               }
+               
+       }
+       
+}

Modified: branches/freenet-jfk/src/freenet/support/RandomGrabArray.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/RandomGrabArray.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/RandomGrabArray.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -66,7 +66,6 @@
                                oret = ret;
                                if(ret.isCancelled()) ret = null;
                                if(ret != null && !ret.canRemove()) {
-                                       ret.setParentGrabArray(null);
                                        return ret;
                                }
                                do {

Modified: branches/freenet-jfk/src/freenet/support/ShortBuffer.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/ShortBuffer.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/ShortBuffer.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -56,7 +56,7 @@
        public ShortBuffer(byte[] data) {
                _start = 0;
                if(data.length > Short.MAX_VALUE)
-                   throw new IllegalArgumentException();
+                   throw new IllegalArgumentException("Too big: "+data.length);
                _length = (short)data.length;
                _data = data;
        }

Modified: branches/freenet-jfk/src/freenet/support/SimpleFieldSet.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/SimpleFieldSet.java        
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/SimpleFieldSet.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -33,8 +33,23 @@
     private String endMarker;
     private final boolean shortLived;
     static public final char MULTI_LEVEL_CHAR = '.';
+    static public final char MULTI_VALUE_CHAR = ';';
+    static public final char KEYVALUE_SEPARATOR_CHAR = '=';
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];

     /**
+     * Create a SimpleFieldSet.
+     * @param shortLived If false, strings will be interned to ensure that 
they use as
+     * little memory as possible. Only set to true if the SFS will be 
short-lived or
+     * small.
+     */
+    public SimpleFieldSet(boolean shortLived) {
+        values = new HashMap();
+               subsets = null;
+               this.shortLived = shortLived;
+    }
+    
+    /**
      * Construct a SimpleFieldSet from reading a BufferedReader.
      * @param br
      * @param allowMultiple If true, multiple lines with the same field name 
will be
@@ -46,9 +61,7 @@
      * problem.
      */
     public SimpleFieldSet(BufferedReader br, boolean allowMultiple, boolean 
shortLived) throws IOException {
-        values = new HashMap();
-               subsets = null;
-               this.shortLived = shortLived;
+        this(shortLived);
         read(br, allowMultiple);
     }

@@ -61,35 +74,23 @@
     }

     public SimpleFieldSet(LineReader lis, int maxLineLength, int 
lineBufferSize, boolean tolerant, boolean utf8OrIso88591, boolean 
allowMultiple, boolean shortLived) throws IOException {
-       values = new HashMap();
-               subsets = null;
-               this.shortLived = shortLived;
+       this(shortLived);
        read(lis, maxLineLength, lineBufferSize, tolerant, utf8OrIso88591, 
allowMultiple);
     }
-    
-    /**
-     * Create a SimpleFieldSet.
-     * @param shortLived If false, strings will be interned to ensure that 
they use as
-     * little memory as possible. Only set to true if the SFS will be 
short-lived or
-     * small.
-     */
-    public SimpleFieldSet(boolean shortLived) {
-        values = new HashMap();
-               subsets = null;
-               this.shortLived = shortLived;
-    }

     /**
      * Construct from a string.
+     * String format:
+     * blah=blah
+     * blah=blah
+     * End
      * @param shortLived If false, strings will be interned to ensure that 
they use as
      * little memory as possible. Only set to true if the SFS will be 
short-lived or
      * small.
      * @throws IOException if the string is too short or invalid.
      */
     public SimpleFieldSet(String content, boolean allowMultiple, boolean 
shortLived) throws IOException {
-       values = new HashMap();
-       subsets = null;
-       this.shortLived = shortLived;
+       this(shortLived);
         StringReader sr = new StringReader(content);
         BufferedReader br = new BufferedReader(sr);
            read(br, allowMultiple);
@@ -112,7 +113,7 @@
                 throw new IOException(); // No end marker!
             }
             firstLine = false;
-            int index = line.indexOf('=');
+            int index = line.indexOf(KEYVALUE_SEPARATOR_CHAR);
             if(index >= 0) {
                 // Mapping
                 String before = line.substring(0, index);
@@ -149,7 +150,7 @@
             }
             if((line.length() == 0) && tolerant) continue; // ignore
             firstLine = false;
-            int index = line.indexOf('=');
+            int index = line.indexOf(KEYVALUE_SEPARATOR_CHAR);
             if(index >= 0) {
                 // Mapping
                 String before = line.substring(0, index);
@@ -169,8 +170,8 @@
                if(idx == -1)
                        return (String) values.get(key);
                else if(idx == 0)
-                       return null;
-               else {
+                       return (subset("") == null) ? null : 
subset("").get(key.substring(1));
+               else {
                        if(subsets == null) return null;
                        String before = key.substring(0, idx);
                        String after = key.substring(idx+1);
@@ -181,12 +182,14 @@
     }

     public String[] getAll(String key) {
-       return split(get(key));
+       String k = get(key);
+       if(k == null) return null;
+       return split(k);
     }

     private static final String[] split(String string) {
        if(string == null) return new String[0];
-       return string.split(";"); // slower???
+       return string.split(String.valueOf(MULTI_VALUE_CHAR)); // slower???
 //     int index = string.indexOf(';');
 //     if(index == -1) return null;
 //     Vector v=new Vector();
@@ -203,7 +206,40 @@
 //     return (String[]) v.toArray();
        }

+    private static final String unsplit(String[] strings) {
+       StringBuffer sb = new StringBuffer();
+       for(int i=0;i<strings.length;i++) {
+               if(i != 0) sb.append(MULTI_VALUE_CHAR);
+               sb.append(strings[i]);
+       }
+       return sb.toString();
+    }
+    
     /**
+     * Put contents of a fieldset, overwrite old values.
+     */
+    public void putAllOverwrite(SimpleFieldSet fs) {
+       Iterator i = fs.values.keySet().iterator();
+       while(i.hasNext()) {
+               String key = (String) i.next();
+               String hisVal = (String) fs.values.get(key);
+               values.put(key, hisVal); // overwrite old
+       }
+       if(fs.subsets == null) return;
+       i = fs.subsets.keySet().iterator();
+       while(i.hasNext()) {
+               String key = (String) i.next();
+               SimpleFieldSet hisFS = (SimpleFieldSet) fs.subsets.get(key);
+               SimpleFieldSet myFS = (SimpleFieldSet) subsets.get(key);
+               if(myFS != null) {
+                       myFS.putAllOverwrite(hisFS);
+               } else {
+                       subsets.put(key, hisFS);
+               }
+       }
+    }
+    
+    /**
      * Set a key to a value. If the value already exists, throw 
IllegalStateException.
      * @param key The key.
      * @param value The value.
@@ -250,7 +286,7 @@
        private synchronized final boolean put(String key, String value, 
boolean allowMultiple, boolean overwrite) {
                int idx;
                if(value == null) return true; // valid no-op
-               if(value.indexOf('\n') != -1) throw new 
IllegalArgumentException("A simplefieldSet can't accept \n !");
+               if(value.indexOf('\n') != -1) throw new 
IllegalArgumentException("A simplefieldSet can't accept newlines !");
                if((idx = key.indexOf(MULTI_LEVEL_CHAR)) == -1) {
                        String x = (String) values.get(key);

@@ -259,7 +295,7 @@
                                values.put(key, value);
                        } else {
                                if(!allowMultiple) return false;
-                               values.put(key, ((String)values.get(key))+ ';' 
+value);
+                               values.put(key, ((String)values.get(key))+ 
MULTI_VALUE_CHAR +value);
                        }
                } else {
                        String before = key.substring(0, idx);
@@ -325,7 +361,7 @@
             String value = (String) entry.getValue();
             w.write(prefix);
             w.write(key);
-            w.write('=');
+            w.write(KEYVALUE_SEPARATOR_CHAR);
             w.write(value);
             w.write('\n');
        }
@@ -361,7 +397,7 @@

        // Output
        for(i=0; i < keys.length; i++)
-               w.write(prefix+keys[i]+'='+get(keys[i])+'\n');
+               
w.write(prefix+keys[i]+KEYVALUE_SEPARATOR_CHAR+get(keys[i])+'\n');

        if(subsets != null) {
                String[] orderedPrefixes = (String[]) 
subsets.keySet().toArray(new String[subsets.size()]);
@@ -438,6 +474,15 @@
        KeyIterator subIterator;
        String prefix;

+       /**
+        * It provides an iterator for the SimpleSetField
+        * which passes through every key.
+        * (e.g. for key1=value1 key2.sub2=value2 key1.sub=value3
+        * it will provide key1,key2.sub2,key1.sub)
+        * @param a prefix to put BEFORE every key
+        * (e.g. for key1=value, if the iterator is created with prefix 
"aPrefix",
+        * it will provide aPrefixkey1
+        */
        public KeyIterator(String prefix) {
                synchronized(SimpleFieldSet.this) {
                        valuesIterator = values.keySet().iterator();
@@ -463,10 +508,18 @@

                public boolean hasNext() {
                        synchronized(SimpleFieldSet.this) {
-                               if(valuesIterator.hasNext()) return true;
-                               if((subIterator != null) && 
subIterator.hasNext()) return true;
-                               if(subIterator != null) subIterator = null;
-                               return false;
+                               while(true) {
+                                       if(valuesIterator.hasNext()) return 
true;
+                                       if((subIterator != null) && 
subIterator.hasNext()) return true;
+                                       if(subIterator != null) subIterator = 
null;
+                                       if(subsetIterator != null && 
subsetIterator.hasNext()) {
+                                               String key = (String) 
subsetIterator.next();
+                                               SimpleFieldSet fs = 
(SimpleFieldSet) subsets.get(key);
+                                               String newPrefix = prefix + key 
+ MULTI_LEVEL_CHAR;
+                                               subIterator = 
fs.keyIterator(newPrefix);
+                                       } else
+                                               return false;
+                               }
                        }
                }

@@ -551,6 +604,16 @@
                }
        }

+       /**
+        * It removes the specified subset.
+        * For example, in a SimpleFieldSet like this:
+        * foo=bar
+        * foo.bar=foobar
+        * foo.bar.boo=foobarboo
+        * calling it with the parameter "foo"
+        * means to drop the second and the third line.
+        * @param is the subset to remove
+        */
        public synchronized void removeSubset(String key) {
                if(subsets == null) return;
                int idx;
@@ -578,11 +641,11 @@
        }

        public Iterator directSubsetNameIterator() {
-               return subsets.keySet().iterator();
+               return (subsets == null) ? null : subsets.keySet().iterator();
        }

        public String[] namesOfDirectSubsets() {
-               return (String[]) subsets.keySet().toArray(new 
String[subsets.size()]);
+               return (subsets == null) ? EMPTY_STRING_ARRAY : (String[]) 
subsets.keySet().toArray(new String[subsets.size()]);
        }

        public static SimpleFieldSet readFrom(InputStream is, boolean 
allowMultiple, boolean shortLived) throws IOException {
@@ -645,6 +708,16 @@
                }
        }

+       public double getDouble(String key) throws FSParseException {
+               String s = get(key);
+               if(s == null) throw new FSParseException("No key "+key);
+               try {
+                       return Double.parseDouble(s);
+               } catch (NumberFormatException e) {
+                       throw new FSParseException("Cannot parse "+s+" for 
integer "+key);
+               }
+       }
+       
        public long getLong(String key, long def) {
                String s = get(key);
                if(s == null) return def;
@@ -664,6 +737,44 @@
                        throw new FSParseException("Cannot parse "+s+" for long 
"+key);
                }
        }
+       
+       public short getShort(String key) throws FSParseException {
+               String s = get(key);
+               if(s == null) throw new FSParseException("No key "+key);
+               try {
+                       return Short.parseShort(s);
+               } catch (NumberFormatException e) {
+                       throw new FSParseException("Cannot parse "+s+" for 
short "+key);
+               }
+       }
+       
+       public short getShort(String key, short def) {
+               String s = get(key);
+               if(s == null) return def;
+               try {
+                       return Short.parseShort(s);
+               } catch (NumberFormatException e) {
+                       return def;
+               }
+       }
+       
+       public char getChar(String key) throws FSParseException {
+               String s = get(key);
+               if(s == null) throw new FSParseException("No key "+key);
+                       if (s.length() == 1)
+                               return s.charAt(0);
+                       else
+                               throw new FSParseException("Cannot parse "+s+" 
for char "+key);
+       } 
+       
+       public char getChar(String key, char def) {
+               String s = get(key);
+               if(s == null) return def;
+                       if (s.length() == 1)
+                               return s.charAt(0);
+                       else
+                               return def;
+       } 

        public boolean getBoolean(String key, boolean def) {
                return Fields.stringToBool(get(key), def);
@@ -678,6 +789,7 @@

        public int[] getIntArray(String key) {
                String[] strings = getAll(key);
+               if(strings == null) return null;
                int[] ret = new int[strings.length];
                for(int i=0;i<strings.length;i++) {
                        try {
@@ -690,4 +802,8 @@
                return ret;
        }

+       public void putOverwrite(String key, String[] strings) {
+               putOverwrite(key, unsplit(strings));
+       }
+
 }

Modified: branches/freenet-jfk/src/freenet/support/StringArray.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/StringArray.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/StringArray.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -20,7 +20,7 @@
                if((array != null) && (array.length > 0)){
                        StringBuffer sb = new StringBuffer();
                        for(int i=0; i<array.length; i++)
-                               sb.append(array.toString()+'|');
+                               sb.append(array[i].toString()+'|');
                        return '[' + sb.substring(0, sb.length() - 
1).toString() + ']';
                }else
                        return "";
@@ -83,5 +83,19 @@
        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/freenet-jfk/src/freenet/support/TimeUtil.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/TimeUtil.java      2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/TimeUtil.java      2007-08-21 
20:26:59 UTC (rev 14828)
@@ -25,72 +25,102 @@
  * Formats milliseconds into a week/day/hour/second/milliseconds string.
  */
 public class TimeUtil {
-       public static String formatTime(long timeInterval, int maxTerms, 
boolean withSecondFractions) {
-               StringBuffer sb = new StringBuffer(64);
-               long l = timeInterval;
-               int termCount = 0;
-               //
-               int weeks = (int)(l / ((long)7*24*60*60*1000));
-               if (weeks > 0) {
+       
+       /**
+        * It converts a given time interval into a 
+        * week/day/hour/second.milliseconds string.
+        * @param timeInterval interval to convert
+        * @param maxTerms the terms number to display
+        * (e.g. 2 means "h" and "m" if the time could be expressed in hour,
+        * 3 means "h","m","s" in the same example).
+        * The maximum terms number available is 6
+        * @param withSecondFractions if true it displays seconds.milliseconds
+        * @return the formatted String
+        */
+    public static String formatTime(long timeInterval, int maxTerms, boolean 
withSecondFractions) {
+        
+       if (maxTerms > 6 )
+               throw new IllegalArgumentException();
+        
+       StringBuffer sb = new StringBuffer(64);
+        long l = timeInterval;
+        int termCount = 0;
+        //
+        if(l < 0) {
+            sb.append('-');
+            l = l * -1;
+        }
+        if( !withSecondFractions && l < 1000 ) {
+            return "";
+        }
+        if(termCount >= maxTerms) {
+            return sb.toString();
+        }
+        //
+        long weeks = (long)(l / ((long)7*24*60*60*1000));
+        if (weeks > 0) {
             sb.append(weeks).append('w');
-                 termCount++;
-                 l = l - ((long)weeks * ((long)7*24*60*60*1000));
-               }
-               //
-               int days = (int)(l / ((long)24*60*60*1000));
-               if (days > 0) {
+            termCount++;
+            l = l - ((long)weeks * ((long)7*24*60*60*1000));
+        }
+        if(termCount >= maxTerms) {
+            return sb.toString();
+        }
+        //
+        long days = (long)(l / ((long)24*60*60*1000));
+        if (days > 0) {
             sb.append(days).append('d');
-                 termCount++;
-                 l = l - ((long)days * ((long)24*60*60*1000));
-               }
-               if(termCount >= maxTerms) {
-                 return sb.toString();
-               }
-               //
-               int hours = (int)(l / ((long)60*60*1000));
-               if (hours > 0) {
+            termCount++;
+            l = l - ((long)days * ((long)24*60*60*1000));
+        }
+        if(termCount >= maxTerms) {
+          return sb.toString();
+        }
+        //
+        long hours = (long)(l / ((long)60*60*1000));
+        if (hours > 0) {
             sb.append(hours).append('h');
-                 termCount++;
-                 l = l - ((long)hours * ((long)60*60*1000));
-               }
-               if(termCount >= maxTerms) {
-                 return sb.toString();
-               }
-               //
-               int minutes = (int)(l / ((long)60*1000));
-               if (minutes > 0) {
+            termCount++;
+            l = l - ((long)hours * ((long)60*60*1000));
+        }
+        if(termCount >= maxTerms) {
+            return sb.toString();
+        }
+        //
+        long minutes = (long)(l / ((long)60*1000));
+        if (minutes > 0) {
             sb.append(minutes).append('m');
-                 termCount++;
-                 l = l - ((long)minutes * ((long)60*1000));
-               }
-               if(termCount >= maxTerms) {
-                 return sb.toString();
-               }
-               if(withSecondFractions && ((maxTerms - termCount) >= 2)) {
-                       if (l > 0) {
-                               double fractionalSeconds = ((double) l) / 
((double) 1000.0);
-                               DecimalFormat fix3 = new DecimalFormat("0.000");
+            termCount++;
+            l = l - ((long)minutes * ((long)60*1000));
+        }
+        if(termCount >= maxTerms) {
+            return sb.toString();
+        }
+        if(withSecondFractions && ((maxTerms - termCount) >= 2)) {
+            if (l > 0) {
+                double fractionalSeconds = ((double) l) / ((double) 1000.0);
+                DecimalFormat fix3 = new DecimalFormat("0.000");
                 sb.append(fix3.format(fractionalSeconds)).append('s');
-                               termCount++;
-                               //l = l - ((long)fractionalSeconds * 
(long)1000);
-                       }
-               } else {
-                       int seconds = (int)(l / (long)1000);
-                       if (seconds > 0) {
+                termCount++;
+                //l = l - ((long)fractionalSeconds * (long)1000);
+            }
+        } else {
+            long seconds = (long)(l / (long)1000);
+            if (seconds > 0) {
                 sb.append(seconds).append('s');
-                               termCount++;
-                               //l = l - ((long)seconds * (long)1000);
-                       }
-               }
-               //
-               return sb.toString();
-       }
-       
-       public static String formatTime(long timeInterval) {
-               return formatTime(timeInterval, 2, false);
-       }
-       
-       public static String formatTime(long timeInterval, int maxTerms) {
-               return formatTime(timeInterval, maxTerms, false);
-       }
+                termCount++;
+                //l = l - ((long)seconds * (long)1000);
+            }
+        }
+        //
+        return sb.toString();
+    }
+    
+    public static String formatTime(long timeInterval) {
+        return formatTime(timeInterval, 2, false);
+    }
+    
+    public static String formatTime(long timeInterval, int maxTerms) {
+        return formatTime(timeInterval, maxTerms, false);
+    }
 }

Modified: branches/freenet-jfk/src/freenet/support/TokenBucket.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/TokenBucket.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/TokenBucket.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -34,7 +34,11 @@
         * @return True if we could acquire the tokens.
         */
        public synchronized boolean instantGrab(long tokens) {
+               if(logMINOR)
+                       Logger.minor(this, "instant grab: "+tokens+" 
current="+current+" max="+max);
                addTokens();
+               if(logMINOR)
+                       Logger.minor(this, "instant grab: "+tokens+" 
current="+current+" max="+max);
                if(current > tokens) {
                        current -= tokens;
                        if(current > max) current = max;
@@ -173,6 +177,8 @@
        public synchronized void addTokens() {
                addTokensNoClip();
                if(current > max) current = max;
+               if(logMINOR)
+                       Logger.minor(this, "addTokens: Clipped, 
current="+current);
        }

        /**
@@ -182,6 +188,8 @@
                long add = tokensToAdd();
                current += add;
                timeLastTick += add * nanosPerTick;
+               if(logMINOR)
+                       Logger.minor(this, "addTokensNoClip: Added "+add+" 
tokens, current="+current);
                // Deliberately do not clip to size at this point; caller must 
do this, but it is usually beneficial for the caller to do so.
        }


Modified: branches/freenet-jfk/src/freenet/support/URLDecoder.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/URLDecoder.java    2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/URLDecoder.java    2007-08-21 
20:26:59 UTC (rev 14828)
@@ -3,9 +3,8 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.support;

-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
+import java.io.StringWriter;

 /**
  * Decode encoded URLs (or parts of URLs). @see URLEncoder.
@@ -41,13 +40,13 @@
                if (s.length() == 0)
                        return "";
                int len = s.length();
-               ByteArrayOutputStream decodedBytes = new 
ByteArrayOutputStream();
+               StringWriter decoded = new StringWriter();
                boolean hasDecodedSomething = false;

                for (int i = 0; i < len; i++) {
                        char c = s.charAt(i);
                        if (Character.isLetterOrDigit(c))
-                               decodedBytes.write(c);
+                               decoded.write(c);
                        else if (c == '%') {
                                if (i >= len - 2) {
                                        throw new URLEncodedFormatException(s);
@@ -62,34 +61,25 @@
                                        long read = Fields.hexToLong(hexval);
                                        if (read == 0)
                                                throw new 
URLEncodedFormatException("Can't encode" + " 00");
-                                       decodedBytes.write((int) read);
+                                       decoded.write((int) read);
                                        hasDecodedSomething = true;
                                } catch (NumberFormatException nfe) {
                                        // Not encoded?
                                        if(tolerant && !hasDecodedSomething) {
-                                               try {
-                                                       byte[] buf = 
('%'+hexval).getBytes("UTF-8");
-                                                       decodedBytes.write(buf, 
0, buf.length);
-                                                       continue;
-                                               } catch 
(UnsupportedEncodingException e) {
-                                                       throw new Error(e);
-                                               }
+                                               decoded.write('%');
+                                               decoded.write(hexval);
+                                               continue;
                                        }

                                        throw new 
URLEncodedFormatException("Not a two character hex % escape: "+hexval+" in "+s);
                                }
                        } else {
-                               try {
-                                       byte[] encoded = 
(""+c).getBytes("UTF-8");
-                                       decodedBytes.write(encoded, 0, 
encoded.length);
-                               } catch (UnsupportedEncodingException e) {
-                                       throw new Error(e);
-                               }
+                               decoded.write(c);
                        }
                }
                try {
-                       decodedBytes.close();
-                       return new String(decodedBytes.toByteArray(), "utf-8");
+                       decoded.close();
+                       return decoded.toString();
                } catch (IOException ioe1) {
                        /* if this throws something's wrong */
                }

Copied: branches/freenet-jfk/src/freenet/support/WeakHashSet.java (from rev 
14796, trunk/freenet/src/freenet/support/WeakHashSet.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/WeakHashSet.java                   
        (rev 0)
+++ branches/freenet-jfk/src/freenet/support/WeakHashSet.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,80 @@
+package freenet.support;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+public class WeakHashSet implements Set {
+       
+       private final WeakHashMap map;
+       
+       public WeakHashSet() {
+               map = new WeakHashMap();
+       }
+
+       public boolean add(Object key) {
+               return map.put(key, null) == null;
+       }
+
+       public boolean addAll(Collection arg0) {
+               boolean changed = false;
+               for(Iterator i=arg0.iterator();i.hasNext();) {
+                       changed |= add(i.next());
+               }
+               return changed;
+       }
+
+       public void clear() {
+               map.clear();
+       }
+
+       public boolean contains(Object key) {
+               return map.containsKey(key);
+       }
+
+       public boolean containsAll(Collection arg0) {
+               for(Iterator i=arg0.iterator();i.hasNext();) {
+                       if(!map.containsKey(i.next())) return false;
+               }
+               return true;
+       }
+
+       public boolean isEmpty() {
+               return map.isEmpty();
+       }
+
+       public Iterator iterator() {
+               return map.keySet().iterator();
+       }
+
+       public boolean remove(Object key) {
+               return map.remove(key) != null;
+       }
+
+       public boolean removeAll(Collection arg0) {
+               boolean changed = false;
+               for(Iterator i=arg0.iterator();i.hasNext();) {
+                       changed |= remove(i.next());
+               }
+               return changed;
+       }
+
+       public boolean retainAll(Collection arg0) {
+               // FIXME
+               throw new UnsupportedOperationException();
+       }
+
+       public int size() {
+               return map.size();
+       }
+
+       public Object[] toArray() {
+               return map.keySet().toArray();
+       }
+
+       public Object[] toArray(Object[] arg0) {
+               return map.keySet().toArray(arg0);
+       }
+
+}

Modified: branches/freenet-jfk/src/freenet/support/compress/GzipCompressor.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/compress/GzipCompressor.java       
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/compress/GzipCompressor.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -24,7 +24,9 @@
                        os = output.getOutputStream();
                        gos = new GZIPOutputStream(os);
                        long written = 0;
-                       byte[] buffer = new byte[4096];
+                       // Bigger input buffer, so can compress all at once.
+                       // Won't hurt on I/O either, although most OSs will 
only return a page at a time.
+                       byte[] buffer = new byte[32768];
                        while(true) {
                                int l = (int) Math.min(buffer.length, maxLength 
- written);
                                int x = is.read(buffer, 0, buffer.length);
@@ -65,14 +67,17 @@
                byte[] buffer = new byte[4096];
                while(true) {
                        int l = (int) Math.min(buffer.length, maxLength - 
written);
-                       int x = gis.read(buffer, 0, 4096);
+                       // We can over-read to determine whether we have 
over-read.
+                       // We enforce maximum size this way.
+                       // FIXME there is probably a better way to do this!
+                       int x = gis.read(buffer, 0, buffer.length);
                        if(l < x) {
                                Logger.normal(this, "l="+l+", x="+x+", 
written="+written+", maxLength="+maxLength+" throwing a 
CompressionOutputSizeException");
                                if(maxCheckSizeBytes > 0) {
                                        written += x;
                                        while(true) {
                                                l = (int) 
Math.min(buffer.length, maxLength + maxCheckSizeBytes - written);
-                                               x = gis.read(buffer, 0, 4096);
+                                               x = gis.read(buffer, 0, l);
                                                if(x <= -1) throw new 
CompressionOutputSizeException(written);
                                                if(x == 0) throw new 
IOException("Returned zero from read()");
                                                written += x;

Modified: branches/freenet-jfk/src/freenet/support/io/ArrayBucket.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/ArrayBucket.java        
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/ArrayBucket.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -32,7 +32,7 @@
                data.add(initdata);
        }

-       public ArrayBucket(String name) {
+       ArrayBucket(String name) {
                data = new ArrayList();
                this.name = name;
        }

Copied: branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java (from 
rev 14796, trunk/freenet/src/freenet/support/io/BaseFileBucket.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java             
                (rev 0)
+++ branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,464 @@
+package freenet.support.io;
+
+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.util.Vector;
+
+import org.tanukisoftware.wrapper.WrapperManager;
+
+import freenet.support.Logger;
+import freenet.support.SimpleFieldSet;
+import freenet.support.StringArray;
+import freenet.support.api.Bucket;
+
+public abstract class BaseFileBucket implements Bucket, 
SerializableToFieldSetBucket {
+
+       // JVM caches File.size() and there is no way to flush the cache, so we
+       // need to track it ourselves
+       protected long length;
+       protected long fileRestartCounter;
+       /** Has the bucket been freed? If so, no further operations may be done 
*/
+       private boolean freed;
+       /** Vector of streams (FileBucketInputStream or FileBucketOutputStream) 
which 
+        * are open to this file. So we can be sure they are all closed when we 
free it. 
+        * Can be null. */
+       private Vector streams;
+
+       protected static String tempDir = null;
+
+       public BaseFileBucket(File file) {
+               if(file == null) throw new NullPointerException();
+               this.length = file.length();
+               if(deleteOnExit()) {
+                       try {
+                               file.deleteOnExit();
+                       } catch (NullPointerException e) {
+                               
if(WrapperManager.hasShutdownHookBeenTriggered()) {
+                                       Logger.normal(this, 
"NullPointerException setting deleteOnExit while shutting down - buggy JVM 
code: "+e, e);
+                               } else {
+                                       Logger.error(this, "Caught "+e+" doing 
deleteOnExit() for "+file+" - JVM bug ????");
+                               }
+                       }
+               }
+       }
+
+       public OutputStream getOutputStream() throws IOException {
+               synchronized (this) {
+                       File file = getFile();
+                       if(freed)
+                               throw new IOException("File already freed");
+                       if(isReadOnly())
+                               throw new IOException("Bucket is read-only: 
"+this);
+                       
+                       if(createFileOnly() && file.exists())
+                               throw new FileExistsException(file);
+                       
+                       if(streams != null && !streams.isEmpty())
+                               Logger.error(this, "Streams open on "+this+" 
while opening an output stream!: "+streams);
+                       
+                       File tempfile = createFileOnly() ? getTempfile() : file;
+                       long streamNumber = ++fileRestartCounter;
+                       
+                       FileBucketOutputStream os = 
+                               new FileBucketOutputStream(tempfile, 
streamNumber);
+                       
+                       if(Logger.shouldLog(Logger.DEBUG, this))
+                               Logger.debug(this, "Creating "+os, new 
Exception("debug"));
+                       
+                       addStream(os);
+                       return os;
+               }
+       }
+
+       private synchronized void addStream(Object stream) {
+               // BaseFileBucket is a very common object, and often very long 
lived,
+               // so we need to minimize memory usage even at the cost of 
frequent allocations.
+               if(streams == null)
+                       streams = new Vector(1,1);
+               streams.add(stream);
+       }
+       
+       private synchronized void removeStream(Object stream) {
+               // Race condition is possible
+               if(streams == null) return;
+               streams.remove(stream);
+               if(streams.isEmpty()) streams = null;
+       }
+
+       protected abstract boolean createFileOnly();
+       
+       protected abstract boolean deleteOnExit();
+       
+       protected abstract boolean deleteOnFinalize();
+       
+       protected abstract boolean deleteOnFree();
+
+       /**
+        * Create a temporary file in the same directory as this file.
+        */
+       protected File getTempfile() throws IOException {
+               File file = getFile();
+               File f = File.createTempFile(file.getName(), ".freenet-tmp", 
file.getParentFile());
+               if(deleteOnExit()) f.deleteOnExit();
+               return f;
+       }
+       
+       protected synchronized void resetLength() {
+               length = 0;
+       }
+
+       /**
+        * Internal OutputStream impl.
+        * If createFileOnly is set, we won't overwrite an existing file, and 
we write to a temp file
+        * then rename over the target. Note that we can't use createNewFile 
then new FOS() because while
+        * createNewFile is atomic, the combination is not, so if we do it we 
are vulnerable to symlink
+        * attacks.
+        * @author toad
+        */
+       class FileBucketOutputStream extends FileOutputStream {
+
+               private long restartCount;
+               private File tempfile;
+               private boolean closed;
+               
+               protected FileBucketOutputStream(
+                       File tempfile, long restartCount)
+                       throws FileNotFoundException {
+                       super(tempfile, false);
+                       if(Logger.shouldLog(Logger.MINOR, this))
+                               Logger.minor(this, "Writing to "+tempfile+" for 
"+getFile());
+                       this.tempfile = tempfile;
+                       resetLength();
+                       this.restartCount = restartCount;
+                       closed = false;
+               }
+               
+               protected void confirmWriteSynchronized() throws IOException {
+                       synchronized(BaseFileBucket.this) {
+                               if (fileRestartCounter > restartCount)
+                                       throw new 
IllegalStateException("writing to file after restart");
+                               if(freed)
+                                       throw new IOException("writing to file 
after it has been freed");
+                       }
+                       if(isReadOnly())
+                               throw new IOException("File is read-only");
+                               
+               }
+               
+               public void write(byte[] b) throws IOException {
+                       synchronized (BaseFileBucket.this) {
+                               confirmWriteSynchronized();
+                               super.write(b);
+                               length += b.length;
+                       }
+               }
+
+               public void write(byte[] b, int off, int len) throws 
IOException {
+                       synchronized (BaseFileBucket.this) {
+                               confirmWriteSynchronized();
+                               super.write(b, off, len);
+                               length += len;
+                       }
+               }
+
+               public void write(int b) throws IOException {
+                       synchronized (BaseFileBucket.this) {
+                               confirmWriteSynchronized();
+                               super.write(b);
+                               length++;
+                       }
+               }
+               
+               public void close() throws IOException {
+                       File file;
+                       synchronized(this) {
+                               if(closed) return;
+                               closed = true;
+                               file = getFile();
+                       }
+                       removeStream(this);
+                       boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
+                       if(logMINOR)
+                               Logger.minor(this, "Closing 
"+BaseFileBucket.this);
+                       try {
+                               super.close();
+                       } catch (IOException e) {
+                               if(logMINOR)
+                                       Logger.minor(this, "Failed closing 
"+BaseFileBucket.this+" : "+e, e);
+                               if(createFileOnly()) tempfile.delete();
+                               throw e;
+                       }
+                       if(createFileOnly()) {
+                               if(file.exists()) {
+                                       if(logMINOR)
+                                               Logger.minor(this, "File exists 
creating file for "+this);
+                                       tempfile.delete();
+                                       throw new FileExistsException(file);
+                               }
+                               if(!tempfile.renameTo(file)) {
+                                       if(logMINOR)
+                                               Logger.minor(this, "Cannot 
rename file for "+this);
+                                       if(file.exists()) throw new 
FileExistsException(file);
+                                       tempfile.delete();
+                                       if(logMINOR)
+                                               Logger.minor(this, "Deleted, 
cannot rename file for "+this);
+                                       throw new IOException("Cannot rename 
file");
+                               }
+                       }
+               }
+               
+               public String toString() {
+                       return 
super.toString()+":"+BaseFileBucket.this.toString();
+               }
+       }
+
+       class FileBucketInputStream extends FileInputStream {
+               Exception e;
+               boolean closed;
+
+               public FileBucketInputStream(File f) throws IOException {
+                       super(f);
+                       if (Logger.shouldLog(Logger.DEBUG, this))
+                               e = new Exception("debug");
+               }
+               
+               public void close() throws IOException {
+                       synchronized(this) {
+                               if(closed) return;
+                               closed = true;
+                       }
+                       removeStream(this);
+                       super.close();
+               }
+               
+               public String toString() {
+                       return 
super.toString()+":"+BaseFileBucket.this.toString();
+               }
+       }
+
+       public synchronized InputStream getInputStream() throws IOException {
+               if(freed)
+                       throw new IOException("File already freed");
+               File file = getFile();
+               if(!file.exists()) {
+                       Logger.normal(this, "File does not exist: "+file+" for 
"+this);
+                       return new NullInputStream();
+               } else {
+                       FileBucketInputStream is =
+                               new FileBucketInputStream(file);
+                       addStream(is);
+                       if(Logger.shouldLog(Logger.DEBUG, this))
+                               Logger.debug(this, "Creating "+is, new 
Exception("debug"));
+                       return is;
+               }
+       }
+
+       /**
+        * @return the name of the file.
+        */
+       public synchronized String getName() {
+               return getFile().getName();
+       }
+
+       public synchronized long size() {
+               return length;
+       }
+
+       /**
+        * Actually delete the underlying file. Called by finalizer, will not be
+        * called twice. But length must still be valid when calling it.
+        */
+       protected synchronized void deleteFile() {
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, "Deleting "+getFile()+" for "+this, 
new Exception("debug"));
+               getFile().delete();
+       }
+
+       public void finalize() {
+               if(deleteOnFinalize())
+                       free(true);
+       }
+
+       /**
+        * Return directory used for temp files.
+        */
+       public final synchronized static String getTempDir() {
+               return tempDir;  // **FIXME**/TODO: locking on tempDir needs to 
be checked by a Java guru for consistency
+       }
+
+       /**
+        * Set temp file directory.
+        * <p>
+        * The directory must exist.
+        */
+       public final synchronized static void setTempDir(String dirName) {
+               File dir = new File(dirName);
+               if (!(dir.exists() && dir.isDirectory() && dir.canWrite())) {
+                       throw new IllegalArgumentException(
+                               "Bad Temp Directory: " + dir.getAbsolutePath());
+               }
+               tempDir = dirName;  // **FIXME**/TODO: locking on tempDir needs 
to be checked by a Java guru for consistency
+       }
+
+       // determine the temp directory in one of several ways
+
+       static {
+               // Try the Java property (1.2 and above)
+               tempDir = System.getProperty("java.io.tmpdir");
+
+               // Deprecated calls removed.
+
+               // Try TEMP and TMP
+               //      if (tempDir == null) {
+               //          tempDir = System.getenv("TEMP");
+               //      }
+
+               //      if (tempDir == null) {
+               //          tempDir = System.getenv("TMP");
+               //      }
+
+               // make some semi-educated guesses based on OS.
+
+               if (tempDir == null) {
+                       String os = System.getProperty("os.name");
+                       if (os != null) {
+
+                               String[] candidates = null;
+
+                               // XXX: Add more possible OSes here.
+                               if (os.equalsIgnoreCase("Linux")
+                                       || os.equalsIgnoreCase("FreeBSD")) {
+                                       String[] linuxCandidates = { "/tmp", 
"/var/tmp" };
+                                       candidates = linuxCandidates;
+                               } else if (os.equalsIgnoreCase("Windows")) {
+                                       String[] windowsCandidates =
+                                               { "C:\\TEMP", 
"C:\\WINDOWS\\TEMP" };
+                                       candidates = windowsCandidates;
+                               }
+
+                               if (candidates != null) {
+                                       for (int i = 0; i < candidates.length; 
i++) {
+                                               File path = new 
File(candidates[i]);
+                                               if (path.exists()
+                                                       && path.isDirectory()
+                                                       && path.canWrite()) {
+                                                       tempDir = candidates[i];
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // last resort -- use current working directory
+
+               if (tempDir == null) {
+                       // This can be null -- but that's OK, null => cwd for 
File
+                       // constructor, anyways.
+                       tempDir = System.getProperty("user.dir");
+               }
+       }
+
+       public synchronized Bucket[] split(int splitSize) {
+               if(length > ((long)Integer.MAX_VALUE) * splitSize)
+                       throw new IllegalArgumentException("Way too big!: 
"+length+" for "+splitSize);
+               int bucketCount = (int) (length / splitSize);
+               if(length % splitSize > 0) bucketCount++;
+               Bucket[] buckets = new Bucket[bucketCount];
+               File file = getFile();
+               for(int i=0;i<buckets.length;i++) {
+                       long startAt = i * splitSize * 1L;
+                       long endAt = Math.min(startAt + splitSize * 1L, length);
+                       long len = endAt - startAt;
+                       buckets[i] = new ReadOnlyFileSliceBucket(file, startAt, 
len);
+               }
+               return buckets;
+       }
+
+       public void free() {
+               free(false);
+       }
+       
+       public void free(boolean forceFree) {
+               Object[] toClose;
+               synchronized(this) {
+                       if(freed) return;
+                       freed = true;
+                       toClose = streams == null ? null : streams.toArray();
+                       streams = null;
+               }
+               
+               if(toClose != null) {
+                       Logger.error(this, "Streams open free()ing "+this+" : 
"+StringArray.toString(toClose), new Exception("debug"));
+                       for(int i=0;i<toClose.length;i++) {
+                               try {
+                                       if(toClose[i] instanceof 
FileBucketOutputStream) {
+                                               ((FileBucketOutputStream) 
toClose[i]).close();
+                                       } else {
+                                               ((FileBucketInputStream) 
toClose[i]).close();
+                                       }
+                               } catch (IOException e) {
+                                       Logger.error(this, "Caught closing 
stream in free(): "+e, e);
+                               } catch (Throwable t) {
+                                       Logger.error(this, "Caught closing 
stream in free(): "+t, t);
+                               }
+                       }
+               }
+               
+               File file = getFile();
+               if ((deleteOnFree() || forceFree) && file.exists()) {
+                       Logger.debug(this,
+                               "Deleting bucket " + file.getName());
+                       deleteFile();
+                       if (file.exists())
+                               Logger.error(this,
+                                       "Delete failed on bucket " + 
file.getName());
+               }
+       }
+       
+       public synchronized String toString() {
+               return super.toString()+ ':' 
+getFile().getPath()+":streams="+(streams == null ? 0 : streams.size());
+       }
+
+       /**
+        * Returns the file object this buckets data is kept in.
+        */
+       public abstract File getFile();
+       
+       public synchronized SimpleFieldSet toFieldSet() {
+               if(deleteOnFinalize()) return null;
+               SimpleFieldSet fs = new SimpleFieldSet(false);
+               fs.putSingle("Type", "FileBucket");
+               fs.putSingle("Filename", getFile().toString());
+               fs.put("Length", size());
+               return fs;
+       }
+
+       public static Bucket create(SimpleFieldSet fs, PersistentFileTracker f) 
throws CannotCreateFromFieldSetException {
+               String tmp = fs.get("Filename");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
filename");
+               File file = FileUtil.getCanonicalFile(new File(tmp));
+               if(f.matches(file)) {
+                       return PersistentTempFileBucket.create(fs, f);
+               }
+               tmp = fs.get("Length");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
length");
+               try {
+                       long length = Long.parseLong(tmp);
+                       if(length !=  file.length())
+                               throw new 
CannotCreateFromFieldSetException("Invalid length: should be "+length+" 
actually "+file.length()+" on "+file);
+               } catch (NumberFormatException e) {
+                       throw new CannotCreateFromFieldSetException("Corrupt 
length "+tmp, e);
+               }
+               FileBucket bucket = new FileBucket(file, false, true, false, 
false, false);
+               if(file.exists()) // no point otherwise!
+                       f.register(file);
+               return bucket;
+       }
+
+}

Modified: branches/freenet-jfk/src/freenet/support/io/FileBucket.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/FileBucket.java 2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/FileBucket.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -4,16 +4,7 @@
 package freenet.support.io;

 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 freenet.crypt.RandomSource;
-import freenet.support.Logger;
-import freenet.support.SimpleFieldSet;
 import freenet.support.api.Bucket;

 /**
@@ -21,7 +12,7 @@
  * 
  * @author oskar
  */
-public class FileBucket implements Bucket, SerializableToFieldSetBucket {
+public class FileBucket extends BaseFileBucket implements Bucket, 
SerializableToFieldSetBucket {

        protected final File file;
        protected boolean readOnly;
@@ -29,7 +20,6 @@
        protected boolean deleteOnFree;
        protected final boolean deleteOnExit;
        protected final boolean createFileOnly;
-       protected long length;
        // JVM caches File.size() and there is no way to flush the cache, so we
        // need to track it ourselves
        protected long fileRestartCounter;
@@ -52,6 +42,7 @@
         * @param deleteOnExit If true, delete the file on a clean exit of the 
JVM. Irreversible - use with care!
         */
        public FileBucket(File file, boolean readOnly, boolean createFileOnly, 
boolean deleteOnFinalize, boolean deleteOnExit, boolean deleteOnFree) {
+               super(file);
                if(file == null) throw new NullPointerException();
                file = file.getAbsoluteFile();
                this.readOnly = readOnly;
@@ -60,323 +51,20 @@
                this.deleteOnFinalize = deleteOnFinalize;
                this.deleteOnFree = deleteOnFree;
                this.deleteOnExit = deleteOnExit;
-               if(deleteOnExit) {
-                       try {
-                               file.deleteOnExit();
-                       } catch (NullPointerException e) {
-                               Logger.error(this, "Impossible: "+e, e);
-                               System.err.println("Impossible: "+e);
-                               e.printStackTrace();
-                       }
-               }
                // Useful for finding temp file leaks.
                // System.err.println("-- FileBucket.ctr(0) -- " +
                // file.getAbsolutePath());
                // (new Exception("get stack")).printStackTrace();
                fileRestartCounter = 0;
-               if(file.exists()) {
-                       length = file.length();
-                       if(!file.canWrite())
-                               readOnly = true;
-               }
-               else length = 0;
        }

        /**
-        * Creates a new FileBucket in a random temporary file in the temporary
-        * directory.
-        * @throws IOException 
-        */
-       public FileBucket(RandomSource random) throws IOException {
-               // **FIXME**/TODO: locking on tempDir needs to be checked by a 
Java guru for consistency
-               file = File.createTempFile(tempDir, ".freenet.tmp");
-               createFileOnly = true;
-               // Useful for finding temp file leaks.
-               //System.err.println("-- FileBucket.ctr(1) -- " +
-               // file.getAbsolutePath());
-               //(new Exception("get stack")).printStackTrace();
-               deleteOnFinalize = true;
-               deleteOnExit = true;
-               length = 0;
-               file.deleteOnExit();
-       }
-
-       public FileBucket(SimpleFieldSet fs, PersistentFileTracker f) throws 
CannotCreateFromFieldSetException {
-               createFileOnly = true;
-               deleteOnExit = false;
-               String tmp = fs.get("Filename");
-               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
filename");
-               this.file = FileUtil.getCanonicalFile(new File(tmp));
-               tmp = fs.get("Length");
-               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
length");
-               try {
-                       length = Long.parseLong(tmp);
-                       if(length !=  file.length())
-                               throw new 
CannotCreateFromFieldSetException("Invalid length: should be "+length+" 
actually "+file.length()+" on "+file);
-               } catch (NumberFormatException e) {
-                       throw new CannotCreateFromFieldSetException("Corrupt 
length "+tmp, e);
-               }
-               if(file.exists()) // no point otherwise!
-                       f.register(file);
-       }
-
-       public OutputStream getOutputStream() throws IOException {
-               synchronized (this) {
-                       if(readOnly)
-                               throw new IOException("Bucket is read-only");
-                       
-                       if(createFileOnly && file.exists())
-                               throw new FileExistsException(file);
-                       
-                       // FIXME: behaviour depends on UNIX semantics, to 
totally abstract
-                       // it out we would have to kill the old write streams 
here
-                       // FIXME: what about existing streams? Will ones on 
append append
-                       // to the new truncated file? Do we want them to? What 
about
-                       // truncated ones? We should kill old streams here, 
right?
-                       return newFileBucketOutputStream(createFileOnly ? 
getTempfile() : file, file.getPath(), ++fileRestartCounter);
-               }
-       }
-
-       /**
-        * Create a temporary file in the same directory as this file.
-        */
-       protected File getTempfile() throws IOException {
-               File f = File.createTempFile(file.getName(), ".freenet-tmp", 
file.getParentFile());
-               if(deleteOnExit) f.deleteOnExit();
-               return f;
-       }
-       
-       protected FileBucketOutputStream newFileBucketOutputStream(
-               File tempfile, String s, long streamNumber) throws IOException {
-               return new FileBucketOutputStream(tempfile, s, streamNumber);
-       }
-
-       protected synchronized void resetLength() {
-               length = 0;
-       }
-
-       /**
-        * Internal OutputStream impl.
-        * If createFileOnly is set, we won't overwrite an existing file, and 
we write to a temp file
-        * then rename over the target. Note that we can't use createNewFile 
then new FOS() because while
-        * createNewFile is atomic, the combination is not, so if we do it we 
are vulnerable to symlink
-        * attacks.
-        * @author toad
-        */
-       class FileBucketOutputStream extends FileOutputStream {
-
-               private long restartCount;
-               private File tempfile;
-               private boolean closed;
-               
-               protected FileBucketOutputStream(
-                       File tempfile, String s, long restartCount)
-                       throws FileNotFoundException {
-                       super(tempfile, false);
-                       if(Logger.shouldLog(Logger.MINOR, this))
-                               Logger.minor(this, "Writing to "+tempfile+" for 
"+file);
-                       this.tempfile = tempfile;
-                       resetLength();
-                       this.restartCount = restartCount;
-                       closed = false;
-               }
-               
-               protected void confirmWriteSynchronized() throws IOException {
-                       if (fileRestartCounter > restartCount)
-                               throw new IllegalStateException("writing to 
file after restart");
-                       if(readOnly)
-                               throw new IOException("File is read-only");
-               }
-               
-               public void write(byte[] b) throws IOException {
-                       synchronized (FileBucket.this) {
-                               confirmWriteSynchronized();
-                               super.write(b);
-                               length += b.length;
-                       }
-               }
-
-               public void write(byte[] b, int off, int len) throws 
IOException {
-                       synchronized (FileBucket.this) {
-                               confirmWriteSynchronized();
-                               super.write(b, off, len);
-                               length += len;
-                       }
-               }
-
-               public void write(int b) throws IOException {
-                       synchronized (FileBucket.this) {
-                               confirmWriteSynchronized();
-                               super.write(b);
-                               length++;
-                       }
-               }
-               
-               public void close() throws IOException {
-                       synchronized(this) {
-                               if(closed) return;
-                               closed = true;
-                       }
-                       boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
-                       if(logMINOR)
-                               Logger.minor(this, "Closing "+FileBucket.this);
-                       try {
-                               super.close();
-                       } catch (IOException e) {
-                               if(logMINOR)
-                                       Logger.minor(this, "Failed closing 
"+FileBucket.this+" : "+e, e);
-                               if(createFileOnly) tempfile.delete();
-                               throw e;
-                       }
-                       if(createFileOnly) {
-                               if(file.exists()) {
-                                       if(logMINOR)
-                                               Logger.minor(this, "File exists 
creating file for "+this);
-                                       tempfile.delete();
-                                       throw new FileExistsException(file);
-                               }
-                               if(!tempfile.renameTo(file)) {
-                                       if(logMINOR)
-                                               Logger.minor(this, "Cannot 
rename file for "+this);
-                                       if(file.exists()) throw new 
FileExistsException(file);
-                                       tempfile.delete();
-                                       if(logMINOR)
-                                               Logger.minor(this, "Deleted, 
cannot rename file for "+this);
-                                       throw new IOException("Cannot rename 
file");
-                               }
-                       }
-               }
-       }
-
-       class FileBucketInputStream extends FileInputStream {
-               Exception e;
-
-               public FileBucketInputStream(File f) throws IOException {
-                       super(f);
-                       if (Logger.shouldLog(Logger.DEBUG, this))
-                               e = new Exception("debug");
-               }
-       }
-
-       public synchronized InputStream getInputStream() throws IOException {
-               if(!file.exists()) {
-                       Logger.normal(this, "File does not exist: "+file+" for 
"+this);
-                       return new NullInputStream();
-               } else 
-                       return new FileBucketInputStream(file);
-       }
-
-       /**
-        * @return the name of the file.
-        */
-       public synchronized String getName() {
-               return file.getName();
-       }
-
-       public synchronized long size() {
-               return length;
-       }
-
-       /**
         * Returns the file object this buckets data is kept in.
         */
        public synchronized File getFile() {
                return file;
        }

-       /**
-        * Actually delete the underlying file. Called by finalizer, will not be
-        * called twice. But length must still be valid when calling it.
-        */
-       protected synchronized void deleteFile() {
-               file.delete();
-       }
-
-       public void finalize() {
-               if(deleteOnFinalize)
-                       free(deleteOnFinalize);
-       }
-
-       /**
-        * Return directory used for temp files.
-        */
-       public final synchronized static String getTempDir() {
-               return tempDir;  // **FIXME**/TODO: locking on tempDir needs to 
be checked by a Java guru for consistency
-       }
-
-       /**
-        * Set temp file directory.
-        * <p>
-        * The directory must exist.
-        */
-       public final synchronized static void setTempDir(String dirName) {
-               File dir = new File(dirName);
-               if (!(dir.exists() && dir.isDirectory() && dir.canWrite())) {
-                       throw new IllegalArgumentException(
-                               "Bad Temp Directory: " + dir.getAbsolutePath());
-               }
-               tempDir = dirName;  // **FIXME**/TODO: locking on tempDir needs 
to be checked by a Java guru for consistency
-       }
-
-       // determine the temp directory in one of several ways
-
-       static {
-               // Try the Java property (1.2 and above)
-               tempDir = System.getProperty("java.io.tmpdir");
-
-               // Deprecated calls removed.
-
-               // Try TEMP and TMP
-               //      if (tempDir == null) {
-               //          tempDir = System.getenv("TEMP");
-               //      }
-
-               //      if (tempDir == null) {
-               //          tempDir = System.getenv("TMP");
-               //      }
-
-               // make some semi-educated guesses based on OS.
-
-               if (tempDir == null) {
-                       String os = System.getProperty("os.name");
-                       if (os != null) {
-
-                               String[] candidates = null;
-
-                               // XXX: Add more possible OSes here.
-                               if (os.equalsIgnoreCase("Linux")
-                                       || os.equalsIgnoreCase("FreeBSD")) {
-                                       String[] linuxCandidates = { "/tmp", 
"/var/tmp" };
-                                       candidates = linuxCandidates;
-                               } else if (os.equalsIgnoreCase("Windows")) {
-                                       String[] windowsCandidates =
-                                               { "C:\\TEMP", 
"C:\\WINDOWS\\TEMP" };
-                                       candidates = windowsCandidates;
-                               }
-
-                               if (candidates != null) {
-                                       for (int i = 0; i < candidates.length; 
i++) {
-                                               File path = new 
File(candidates[i]);
-                                               if (path.exists()
-                                                       && path.isDirectory()
-                                                       && path.canWrite()) {
-                                                       tempDir = candidates[i];
-                                                       break;
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               // last resort -- use current working directory
-
-               if (tempDir == null) {
-                       // This can be null -- but that's OK, null => cwd for 
File
-                       // constructor, anyways.
-                       tempDir = System.getProperty("user.dir");
-               }
-       }
-
        public synchronized boolean isReadOnly() {
                return readOnly;
        }
@@ -394,46 +82,19 @@
                deleteOnFinalize = false;
        }

-       public synchronized Bucket[] split(int splitSize) {
-               if(length > ((long)Integer.MAX_VALUE) * splitSize)
-                       throw new IllegalArgumentException("Way too big!: 
"+length+" for "+splitSize);
-               int bucketCount = (int) (length / splitSize);
-               if(length % splitSize > 0) bucketCount++;
-               Bucket[] buckets = new Bucket[bucketCount];
-               for(int i=0;i<buckets.length;i++) {
-                       long startAt = i * splitSize * 1L;
-                       long endAt = Math.min(startAt + splitSize * 1L, length);
-                       long len = endAt - startAt;
-                       buckets[i] = new ReadOnlyFileSliceBucket(file, startAt, 
len);
-               }
-               return buckets;
+       protected boolean createFileOnly() {
+               return createFileOnly;
        }

-       public void free() {
-               free(false);
+       protected boolean deleteOnExit() {
+               return deleteOnExit;
        }
-       
-       public synchronized void free(boolean forceFree) {
-               if ((deleteOnFree || forceFree) && file.exists()) {
-                       Logger.debug(this,
-                               "Deleting bucket " + file.getName());
-                       deleteFile();
-                       if (file.exists())
-                               Logger.error(this,
-                                       "Delete failed on bucket " + 
file.getName());
-               }
+
+       protected boolean deleteOnFinalize() {
+               return deleteOnFinalize;
        }
-       
-       public synchronized String toString() {
-               return super.toString()+ ':' +file.getPath();
-       }

-       public synchronized SimpleFieldSet toFieldSet() {
-               if(deleteOnFinalize) return null;
-               SimpleFieldSet fs = new SimpleFieldSet(false);
-               fs.putSingle("Type", "FileBucket");
-               fs.putSingle("Filename", file.toString());
-               fs.put("Length", length);
-               return fs;
+       protected boolean deleteOnFree() {
+               return deleteOnFree;
        }
 }

Deleted: branches/freenet-jfk/src/freenet/support/io/FileBucketFactory.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/FileBucketFactory.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/FileBucketFactory.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,39 +0,0 @@
-package freenet.support.io;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Vector;
-
-import freenet.support.Logger;
-import freenet.support.api.Bucket;
-import freenet.support.api.BucketFactory;
-
-public class FileBucketFactory implements BucketFactory {
-    
-    private Vector files = new Vector();
-    
-    // Must have trailing "/"
-    public final File rootDir;
-
-    public FileBucketFactory(File rootDir) {
-        this.rootDir = rootDir;
-    }
-
-    public Bucket makeBucket(long size) throws IOException {
-       File f = File.createTempFile("bf_", ".freenet-tmp", rootDir);
-        Bucket b = new FileBucket(f, false, true, true, false, true);
-        files.addElement(f);
-        return b;
-    }
-
-    public void freeBucket(Bucket b) throws IOException {
-        if (!(b instanceof FileBucket)) throw new IOException("not a 
FileBucket!");
-        File f = ((FileBucket) b).getFile();
-        //System.err.println("FREEING: " + f.getName());
-        if (files.removeElement(f)) {
-            if (!f.delete())
-                Logger.error(this, "Delete failed on bucket "+f.getName(), new 
Exception());
-           files.trimToSize();
-       }
-    }
-}

Modified: branches/freenet-jfk/src/freenet/support/io/FileUtil.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/FileUtil.java   2007-08-21 
19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/FileUtil.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -4,10 +4,14 @@
 package freenet.support.io;

 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
 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.InputStreamReader;

 import freenet.client.DefaultMIMETypes;
@@ -72,6 +76,7 @@
                return result;
        }

+       // FIXME: this is called readUTF but it reads in the default charset 
... eh??
        public static String readUTF(File file) throws FileNotFoundException, 
IOException {
                StringBuffer result = new StringBuffer();
                FileInputStream fis = null;
@@ -85,9 +90,8 @@

                        char[] buf = new char[4096];
                        int length = 0;
-                       
-                       while(length != -1) {
-                               length = isr.read(buf);
+
+                       while((length = isr.read(buf)) > 0) {
                                result.append(buf, 0, length);
                        }

@@ -100,7 +104,37 @@
                }
                return result.toString();
        }
+       
+       public static boolean writeTo(InputStream input, File target) throws 
FileNotFoundException, IOException {
+               BufferedInputStream bis = null;
+               DataInputStream dis = null;
+               FileOutputStream fos = null;
+               BufferedOutputStream bos = null;
+               File file = File.createTempFile("temp", ".tmp");
+               
+               try {
+                       bis = new BufferedInputStream(input);
+                       dis = new DataInputStream(bis);
+                       fos = new FileOutputStream(file);
+                       bos= new BufferedOutputStream(fos);

+                       int len = 0;
+                       byte[] buffer = new byte[4096];
+                       while ((len = dis.read(buffer)) > 0) {
+                               bos.write(buffer, 0, len);
+                       }
+               } catch (IOException e) {
+                       throw e;
+               } finally {
+                       if(dis != null) dis.close();
+                       if(bis != null) bis.close();
+                       if(fos != null) fos.close();
+                       if(bos != null) bos.close();    
+               }
+               
+               return file.renameTo(target);
+       }
+
        public static String sanitize(String s) {
                StringBuffer sb = new StringBuffer(s.length());
                for(int i=0;i<s.length();i++) {
@@ -125,8 +159,10 @@
                if(filename.indexOf('.') >= 0) {
                        String oldExt = 
filename.substring(filename.lastIndexOf('.'));
                        if(DefaultMIMETypes.isValidExt(mimeType, oldExt)) 
return filename;
-               } 
-               return filename + '.' + DefaultMIMETypes.getExtension(filename);
+               }
+               String defaultExt = DefaultMIMETypes.getExtension(filename);
+               if(defaultExt == null) return filename;
+               else return filename + '.' + defaultExt;
        }

 }

Modified: branches/freenet-jfk/src/freenet/support/io/FilenameGenerator.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/FilenameGenerator.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/FilenameGenerator.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -6,7 +6,7 @@
 import org.tanukisoftware.wrapper.WrapperManager;

 import freenet.crypt.RandomSource;
-import freenet.support.HexUtil;
+import freenet.support.Fields;
 import freenet.support.Logger;
 import freenet.support.TimeUtil;

@@ -27,15 +27,14 @@
                this.random = random;
                this.prefix = prefix;
                if (dir == null)
-                       tmpDir = new File(System.getProperty("java.io.tmpdir"));
+                       tmpDir = FileUtil.getCanonicalFile(new 
File(System.getProperty("java.io.tmpdir")));
                else
-                       tmpDir = dir;
+                       tmpDir = FileUtil.getCanonicalFile(dir);
         if(!tmpDir.exists()) {
             tmpDir.mkdir();
                }
                if(!(tmpDir.isDirectory() && tmpDir.canRead() && 
tmpDir.canWrite()))
                        throw new IOException("Not a directory or cannot 
read/write: "+tmpDir);
-               
                if(wipeFiles) {
                        long wipedFiles = 0;
                        long wipeableFiles = 0;
@@ -64,18 +63,45 @@
                }
        }

-       public File makeRandomFilename() {
-               byte[] randomFilename = new byte[8]; // should be plenty
+       public long makeRandomFilename() {
+               long randomFilename; // should be plenty
                while(true) {
-                       random.nextBytes(randomFilename);
-                       String filename = prefix + 
HexUtil.bytesToHex(randomFilename);
+                       randomFilename = random.nextLong();
+                       if(randomFilename == -1) continue; // Disallowed as 
used for error reporting
+                       String filename = prefix + 
Long.toHexString(randomFilename);
                        File ret = new File(tmpDir, filename);
                        if(!ret.exists()) {
                                if(Logger.shouldLog(Logger.MINOR, this))
                                        Logger.minor(this, "Made random 
filename: "+ret, new Exception("debug"));
-                               return ret;
+                               return randomFilename;
                        }
                }
        }

+       public File getFilename(long id) {
+               return new File(tmpDir, prefix + Long.toHexString(id));
+       }
+
+       public boolean matches(File file) {
+               return getID(file) != -1;
+       }
+
+       public long getID(File file) {
+               
if(!(FileUtil.getCanonicalFile(file.getParentFile()).equals(tmpDir))) {
+                       Logger.error(this, "Not the same dir: 
parent="+FileUtil.getCanonicalFile(file.getParentFile())+" but tmpDir="+tmpDir);
+                       return -1;
+               }
+               String name = file.getName();
+               if(!name.startsWith(prefix)) {
+                       Logger.error(this, "Does not start with prefix: 
"+name+" prefix "+prefix);
+                       return -1;
+               }
+               try {
+                       return 
Fields.hexToLong(name.substring(prefix.length()));
+               } catch (NumberFormatException e) {
+                       Logger.error(this, "Cannot getID: "+e+" from 
"+(name.substring(prefix.length())), e);
+                       return -1;
+               }
+       }
+
 }

Copied: branches/freenet-jfk/src/freenet/support/io/MultiReaderBucket.java 
(from rev 14796, trunk/freenet/src/freenet/support/io/MultiReaderBucket.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/MultiReaderBucket.java          
                (rev 0)
+++ branches/freenet-jfk/src/freenet/support/io/MultiReaderBucket.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,129 @@
+/* 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 java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+import freenet.support.api.Bucket;
+
+/**
+ * A wrapper for a read-only bucket providing for multiple readers. The data 
is 
+ * only freed when all of the readers have freed it.
+ * @author toad
+ */
+public class MultiReaderBucket {
+       
+       private final Bucket bucket;
+       
+       // Assume there will be relatively few readers
+       private ArrayList readers;
+       
+       private boolean closed;
+       
+       public MultiReaderBucket(Bucket underlying) {
+               bucket = underlying;
+       }
+
+       /** Get a reader bucket */
+       public Bucket getReaderBucket() {
+               synchronized(this) {
+                       if(closed) return null;
+                       Bucket d = new ReaderBucket();
+                       if(readers == null) readers = new ArrayList();
+                       readers.add(d);
+                       return d;
+               }
+       }
+
+       class ReaderBucket implements Bucket {
+               
+               private boolean freed;
+
+               public void free() {
+                       synchronized(MultiReaderBucket.this) {
+                               if(freed) return;
+                               freed = true;
+                               readers.remove(this);
+                               if(!readers.isEmpty()) return;
+                               readers = null;
+                               if(closed) return;
+                               closed = true;
+                       }
+                       bucket.free();
+               }
+
+               public InputStream getInputStream() throws IOException {
+                       synchronized(MultiReaderBucket.this) {
+                               if(freed || closed) {
+                                       throw new IOException("Already freed");
+                               }
+                       }
+                       return new ReaderBucketInputStream();
+               }
+               
+               private class ReaderBucketInputStream extends InputStream {
+                       
+                       InputStream is;
+                       
+                       ReaderBucketInputStream() throws IOException {
+                               is = bucket.getInputStream();
+                       }
+                       
+                       public final int read() throws IOException {
+                               synchronized(MultiReaderBucket.this) {
+                                       if(freed || closed) throw new 
IOException("Already closed");
+                               }
+                               return is.read();
+                       }
+                       
+                       public final int read(byte[] data, int offset, int 
length) throws IOException {
+                               synchronized(MultiReaderBucket.this) {
+                                       if(freed || closed) throw new 
IOException("Already closed");
+                               }
+                               return is.read(data, offset, length);
+                       }
+                       
+                       public final int read(byte[] data) throws IOException {
+                               synchronized(MultiReaderBucket.this) {
+                                       if(freed || closed) throw new 
IOException("Already closed");
+                               }
+                               return is.read(data);
+                       }
+                       
+                       public final void close() throws IOException {
+                               is.close();
+                       }
+                       
+               }
+               
+               public String getName() {
+                       return bucket.getName();
+               }
+
+               public OutputStream getOutputStream() throws IOException {
+                       throw new IOException("Read only");
+               }
+
+               public boolean isReadOnly() {
+                       return true;
+               }
+
+               public void setReadOnly() {
+                       // Already read only
+               }
+
+               public long size() {
+                       return bucket.size();
+               }
+               
+               public void finalize() {
+                       free();
+               }
+               
+       }
+       
+}

Modified: 
branches/freenet-jfk/src/freenet/support/io/NullPersistentFileTracker.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/NullPersistentFileTracker.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/NullPersistentFileTracker.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -22,4 +22,16 @@
                return new File(".");
        }

+       public boolean matches(File file) {
+               return false;
+       }
+
+       public FilenameGenerator getGenerator() {
+               return null;
+       }
+
+       public long getID(File file) {
+               return 0;
+       }
+
 }

Modified: 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
   2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
   2007-08-21 20:26:59 UTC (rev 14828)
@@ -6,6 +6,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.ref.SoftReference;

 import org.spaceroots.mantissa.random.MersenneTwister;

@@ -28,8 +29,8 @@
        private final Bucket bucket;
        private final int minPaddedSize;
        private final RandomSource origRandom;
-       private final Rijndael aes;
-       /** The decryption key. May be null. */
+       private SoftReference /* <Rijndael> */ aesRef;
+       /** The decryption key. */
        private final byte[] key;
        /** Broken (old) encryption? */
        private final boolean brokenEncryption;
@@ -45,26 +46,14 @@
         * @param origRandom Hard random number generator from which to obtain 
a seed for padding.
         * @throws UnsupportedCipherException 
         */
-       public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize, 
RandomSource origRandom, boolean forgetKey) {
+       public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize, 
RandomSource origRandom) {
                this.origRandom = origRandom;
                this.bucket = bucket;
                if(bucket.size() != 0) throw new 
IllegalArgumentException("Bucket must be empty");
-               try {
-                       aes = new Rijndael(256, 256, false);
-               } catch (UnsupportedCipherException e) {
-                       throw new Error(e);
-               }
                brokenEncryption = false;
                byte[] tempKey = new byte[32];
                origRandom.nextBytes(tempKey);
-               aes.initialize(tempKey);
-               if(forgetKey) {
-                       // Might as well blank it
-                       for(int i=0;i<tempKey.length;i++) tempKey[i] = 0;
-                       this.key = null;
-               } else {
-                       this.key = tempKey;
-               }
+               this.key = tempKey;
                this.minPaddedSize = minSize;
                readOnly = false;
                lastOutputStream = 0;
@@ -88,12 +77,7 @@
                this.origRandom = origRandom;
                this.bucket = bucket;
                brokenEncryption = oldCrypto;
-               try {
-                       aes = new Rijndael(256, 256, oldCrypto);
-               } catch (UnsupportedCipherException e) {
-                       throw new Error(e);
-               }
-               aes.initialize(key);
+               if(key.length != 32) throw new IllegalArgumentException("Key 
wrong length: "+key.length);
                this.key = key;
                this.minPaddedSize = minSize;
                readOnly = false;
@@ -119,12 +103,7 @@
                        throw new CannotCreateFromFieldSetException("No key");
                brokenEncryption = fs.get("CryptoType") == null;
                key = HexUtil.hexToBytes(tmp);
-               try {
-                       aes = new Rijndael(256, 256, brokenEncryption);
-               } catch (UnsupportedCipherException e) {
-                       throw new Error(e);
-               }
-               aes.initialize(key);
+               if(key.length != 32) throw new IllegalArgumentException("Key 
wrong length: "+key.length);
                tmp = fs.get("MinPaddedSize");
                if(tmp == null)
                        minPaddedSize = 1024; // FIXME throw! back 
compatibility hack
@@ -136,7 +115,7 @@
                        }
                }
                if(dataLength > bucket.size())
-                       throw new CannotCreateFromFieldSetException("Underlying 
bucket is too small: should be "+dataLength+" actually "+bucket.size());
+                       throw new CannotCreateFromFieldSetException("Underlying 
bucket "+bucket+" is too small: should be "+dataLength+" actually 
"+bucket.size());
        }

        public OutputStream getOutputStream() throws IOException {
@@ -159,6 +138,7 @@
                        this.out = out;
                        dataLength = 0;
                        this.streamNumber = streamNumber;
+                       Rijndael aes = getRijndael();
                        pcfb = PCFBMode.create(aes);
                }

@@ -235,6 +215,7 @@

                public PaddedEphemerallyEncryptedInputStream(InputStream in) {
                        this.in = in;
+                       Rijndael aes = getRijndael();
                        pcfb = PCFBMode.create(aes);
                        ptr = 0;
                }
@@ -310,6 +291,22 @@
                }
        }

+       private synchronized Rijndael getRijndael() {
+               Rijndael aes;
+               if(aesRef != null) {
+                       aes = (Rijndael) aesRef.get();
+                       if(aes != null) return aes;
+               }
+               try {
+                       aes = new Rijndael(256, 256, false);
+               } catch (UnsupportedCipherException e) {
+                       throw new Error(e);
+               }
+               aes.initialize(key);
+               aesRef = new SoftReference(aes);
+               return aes;
+       }
+
        public String getName() {
                return "Encrypted:"+bucket.getName();
        }
@@ -342,7 +339,7 @@
        }

        /**
-        * Get the decryption key. May have been blanked out.
+        * Get the decryption key.
         */
        public byte[] getKey() {
                return key;

Modified: 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucketFactory.java
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -23,6 +23,6 @@
        }

        public Bucket makeBucket(long size) throws IOException {
-               return new 
PaddedEphemerallyEncryptedBucket(baseFactory.makeBucket(size), minSize, random, 
true);
+               return new 
PaddedEphemerallyEncryptedBucket(baseFactory.makeBucket(size), minSize, random);
        }
 }

Modified: branches/freenet-jfk/src/freenet/support/io/PersistentFileTracker.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/PersistentFileTracker.java      
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/PersistentFileTracker.java      
2007-08-21 20:26:59 UTC (rev 14828)
@@ -21,5 +21,14 @@
         * Get the persistent temp files directory.
         */
        public File getDir();
+
+       /**
+        * Is the file in question one of our persistent temp files?
+        */
+       public boolean matches(File file);
+
+       public FilenameGenerator getGenerator();
+
+       public long getID(File file);

 }

Modified: 
branches/freenet-jfk/src/freenet/support/io/PersistentTempBucketFactory.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/io/PersistentTempBucketFactory.java    
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/support/io/PersistentTempBucketFactory.java    
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -91,22 +91,24 @@
                Iterator i = originalFiles.iterator();
                while(i.hasNext()) {
                        File f = (File) (i.next());
+                       if(Logger.shouldLog(Logger.MINOR, this))
+                               Logger.minor(this, "Deleting old tempfile "+f);
                        f.delete();
                }
        }

        private Bucket makeRawBucket(long size) throws IOException {
-               return new FileBucket(fg.makeRandomFilename(), false, true, 
false, false, true);
+               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, rand, false));
+               return new DelayedFreeBucket(this, new 
PaddedEphemerallyEncryptedBucket(b, 1024, rand));
        }

        public Bucket makeEncryptedBucket() throws IOException {
                Bucket b = makeRawBucket(-1);
-               return new DelayedFreeBucket(this, new 
PaddedEphemerallyEncryptedBucket(b, 1024, rand, false));
+               return new DelayedFreeBucket(this, new 
PaddedEphemerallyEncryptedBucket(b, 1024, rand));
        }

        /**
@@ -130,4 +132,16 @@
                return dir;
        }

+       public FilenameGenerator getGenerator() {
+               return fg;
+       }
+
+       public boolean matches(File file) {
+               return fg.matches(file);
+       }
+
+       public long getID(File file) {
+               return fg.getID(file);
+       }
+
 }

Copied: 
branches/freenet-jfk/src/freenet/support/io/PersistentTempFileBucket.java (from 
rev 14796, trunk/freenet/src/freenet/support/io/PersistentTempFileBucket.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/PersistentTempFileBucket.java   
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/support/io/PersistentTempFileBucket.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,55 @@
+package freenet.support.io;
+
+import java.io.File;
+
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+public class PersistentTempFileBucket extends TempFileBucket {
+
+       protected PersistentTempFileBucket(long id, FilenameGenerator 
generator) {
+               super(id, generator);
+       }
+
+       protected boolean deleteOnFinalize() {
+               // Do not delete on finalize
+               return false;
+       }
+       
+       protected boolean deleteOnExit() {
+               // DO NOT DELETE ON EXIT !!!!
+               return false;
+       }
+       
+       public static Bucket create(SimpleFieldSet fs, PersistentFileTracker f) 
throws CannotCreateFromFieldSetException {
+               String tmp = fs.get("Filename");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
filename");
+               File file = FileUtil.getCanonicalFile(new File(tmp));
+               long id = f.getID(file);
+               if(id == -1)
+                       throw new CannotCreateFromFieldSetException("Cannot 
derive persistent temp file id from filename "+file);
+               tmp = fs.get("Length");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
length");
+               long length;
+               try {
+                       length = Long.parseLong(tmp);
+                       if(length !=  file.length())
+                               throw new 
CannotCreateFromFieldSetException("Invalid length: should be "+length+" 
actually "+file.length()+" on "+file);
+               } catch (NumberFormatException e) {
+                       throw new CannotCreateFromFieldSetException("Corrupt 
length "+tmp, e);
+               }
+               Bucket bucket = new PersistentTempFileBucket(id, 
f.getGenerator());
+               if(file.exists()) // no point otherwise!
+                       f.register(file);
+               return bucket;
+       }
+       
+       public SimpleFieldSet toFieldSet() {
+               if(deleteOnFinalize()) return null;
+               SimpleFieldSet fs = super.toFieldSet();
+               fs.putOverwrite("Type", "PersistentTempFileBucket");
+               fs.put("FilenameID", filenameID);
+               return fs;
+       }
+       
+}

Deleted: branches/freenet-jfk/src/freenet/support/io/RandomAccessFileBucket.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/RandomAccessFileBucket.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/RandomAccessFileBucket.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,453 +0,0 @@
-// REDFLAG: test and javadoc
-package freenet.support.io;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.util.Vector;
-
-import freenet.support.Logger;
-import freenet.support.SimpleFieldSet;
-import freenet.support.api.Bucket;
-
-/**
- * Bucket implementation that can efficiently access any arbitrary byte-range
- * of a file.
- *
- **/
-public class RandomAccessFileBucket implements Bucket, 
SerializableToFieldSetBucket {
-
-    private final File file;
-    private final long offset;
-    private final long len;
-    private boolean readOnly = false;
-    private boolean released = false;
-    private Vector streams = new Vector();
-    
-    public RandomAccessFileBucket(File file, long offset, long len, boolean 
readOnly)
-        throws IOException {
-        if (!(file.exists() && file.canRead())) {
-            throw new IOException("Can't read file: " + 
file.getAbsolutePath());
-        } 
-        
-        if ((!file.canWrite()) && (!readOnly)) {
-            throw new IOException("Can't write to file: " + 
file.getAbsolutePath());
-        } 
-
-        this.file = file;
-        this.readOnly = readOnly;
-        this.offset = offset;
-        this.len = len;
-    }
-
-    public RandomAccessFileBucket(SimpleFieldSet fs, PersistentFileTracker f) 
throws CannotCreateFromFieldSetException {
-               String tmp = fs.get("Filename");
-               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
filename");
-               this.file = new File(tmp);
-               tmp = fs.get("Length");
-               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
length");
-               try {
-                       len = Long.parseLong(tmp);
-               } catch (NumberFormatException e) {
-                       throw new CannotCreateFromFieldSetException("Corrupt 
length "+tmp, e);
-               }
-               tmp = fs.get("Offset");
-               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
offset");
-               try {
-                       offset = Long.parseLong(tmp);
-               } catch (NumberFormatException e) {
-                       throw new CannotCreateFromFieldSetException("Corrupt 
offset "+tmp, e);
-               }
-       }
-
-    public static class Range {
-        Range(long offset, long len) {
-            this.offset = offset;
-            this.len = len;
-        }
-
-        public long offset;
-        public long len;
-    }
-
-    public final synchronized Range getRange() {
-        return new Range(offset, len);
-    }
-    
-    // hmmm make protected???
-    public final synchronized boolean hasOpenStreams() {
-        return streams.size() > 0;
-    }
-
-    // Wrap non-const members so we can tell
-    // when code touches the Bucket after it
-    // has been released.
-    public synchronized InputStream getInputStream() throws IOException {
-        if (isReleased()) {
-            throw new IOException("Attempt to use a released 
RandomAccessFileBucket: " + getName() );
-        }
-
-        InputStream newIn = new RAInputStream(file.getAbsolutePath());
-        streams.addElement(newIn);
-        return newIn;
-    }
-
-    public synchronized OutputStream getOutputStream() throws IOException {
-        if (isReleased()) {
-            throw new IOException("Attempt to use a released 
RandomAccessBucket: " + getName() );
-        }
-
-        if (readOnly) {
-            throw new IOException("Tried to write a read-only Bucket.");
-        }
-
-        OutputStream newOut = new RAOutputStream(file.getAbsolutePath());
-        streams.addElement(newOut);
-        return newOut;
-    }
-
-    public String getName() {
-        return file.getAbsolutePath() + " [" + offset + ", " + 
-            (offset + len - 1) + ']';
-    }
-        
-    public long size() { return len; }
-
-    public synchronized boolean release() {
-        if (released) {
-            return true;
-        }
-
-        // Force all open streams closed. 
-        // Windows won't let us delete the file unless we
-        // do this.
-        for (int i =0; i < streams.size(); i++) {
-            try {
-                if (streams.elementAt(i) instanceof InputStream) {
-                    ((InputStream)streams.elementAt(i)).close();
-
-                    if(Logger.shouldLog(Logger.DEBUG, this))
-                       Logger.debug(this, "closed open InputStream !: " + 
-                                       file.getAbsolutePath());
-                }
-                else if (streams.elementAt(i) instanceof OutputStream) {
-                    ((OutputStream)streams.elementAt(i)).close();
-                    if(Logger.shouldLog(Logger.DEBUG, this))
-                       Logger.debug(this, "closed open OutputStream !: " + 
-                                       file.getAbsolutePath());
-                }
-            }
-            catch (IOException ioe) {
-            }
-        }
-        streams.removeAllElements();
-        streams.trimToSize();
-        // We don't delete anything because we don't own anything.
-        released = true;
-        return true;
-    }
-
-    public synchronized final boolean isReleased() { return released; }
-
-    public void finalize() throws Throwable {
-       synchronized(this) {
-               if(released) return;
-       }
-       release();
-    }
-
-    // REDFLAG: RETEST
-    // set blocks = -1 for until end.
-    // last block may have length < blockSize
-    public static Bucket[] segment(File file, int blockSize, 
-                                   long offset, int blocks,  boolean readOnly) 
-        throws IOException {
-        
-        if (!(file.exists() && file.canRead())) {
-            throw new IOException("Can't read file: " + 
file.getAbsolutePath());
-        } 
-        
-        if ((!file.canWrite()) && (!readOnly)) {
-            throw new IOException("Can't write to file: " + 
file.getAbsolutePath());
-        } 
-        
-        if ((offset < 0) || (offset >= file.length() - 1)) {
-            throw new IllegalArgumentException("offset: " + offset);
-        }
-
-        long length = file.length() - offset;
-        int nBlocks = (int) (length / blockSize);
-        if ((length % blockSize) != 0) {
-            nBlocks++;
-        }        
-        
-        if (blocks == -1) {
-            blocks = nBlocks;
-        }
-        else if ((blocks > nBlocks) || (blocks < 1)) {
-            throw new IllegalArgumentException("blocks: " + blocks);
-        } 
-
-        Bucket[] ret = new Bucket[blocks];
-        
-        for (int i = 0; i < blocks; i++) {
-            final long localOffset = i * blockSize * 1L + offset;
-            int blockLen = blockSize;
-            if (i == nBlocks - 1) {
-                blockLen = (int) (length - (nBlocks - 1) * blockSize * 1L);
-            }
-            ret[i] = new RandomAccessFileBucket(file, localOffset, blockLen, 
readOnly);
-        }
-        
-        return ret;
-    }
-
-    ////////////////////////////////////////////////////////////
-    // InputStream and OutputStream implementations
-    //
-    private final static boolean vociferous = false;
-
-    class RAInputStream extends InputStream  {
-        public RAInputStream(String prefix) throws IOException {
-            raf = new RandomAccessFile(file, "r");
-            raf.seek(offset);
-            println(" -- Created new InputStream [" + offset + 
-                    ", " + (offset + len -1) + ']');
-        }
-        
-        ////////////////////////////////////////////////////////////
-        // FilterInput implementation
-
-        private final int bytesLeft() throws IOException {
-                       synchronized (RandomAccessFileBucket.this) {
-                               return (int)(offset + len - 
raf.getFilePointer());
-                       }
-        }
-
-        public int read() throws java.io.IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".read()");
-                checkValid();
-                if (bytesLeft() < 1) {
-                    return -1; // EOF
-                } 
-                return raf.read();
-            }
-        }
-        
-        public int read(byte[] bytes) throws java.io.IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".read(byte[])");
-                checkValid();
-                int nAvailable = bytesLeft();
-                if (nAvailable < 1) {
-                    return -1; // EOF
-                } 
-                if (nAvailable > bytes.length) {
-                    nAvailable = bytes.length;
-                }
-                return raf.read(bytes, 0, nAvailable);
-            }
-        }
-        
-        public int read(byte[] bytes, int a, int b) throws java.io.IOException 
{
-            synchronized (RandomAccessFileBucket.this) {
-                println(".read(byte[], int, int)");
-                checkValid();
-                int nAvailable = bytesLeft();
-                if (nAvailable < 1) {
-                    return -1; // EOF
-                } 
-                if (nAvailable > b) {
-                    nAvailable = b;
-                }
-                return raf.read(bytes, a, nAvailable);
-            }
-        }
-        
-        public long skip(long a) throws java.io.IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".skip(long)");
-                checkValid();
-                int nAvailable = bytesLeft();
-                if (nAvailable < 1) {
-                    return -1; // EOF
-                } 
-                if (nAvailable > a) {
-                    nAvailable = (int)a;
-                }
-
-                return raf.skipBytes(nAvailable);
-            }
-        }
-        
-        public int available() throws java.io.IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".available()");
-                checkValid();
-                return bytesLeft();
-            }
-        }
-        
-        public void close() throws java.io.IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".close()");
-                checkValid();       
-                raf.close();
-                if (streams.contains(RAInputStream.this)) {
-                    streams.removeElement(RAInputStream.this);
-                }
-                streams.trimToSize();
-            }
-        }
-        
-        // LATER: support if really needed.
-        public  void mark(int a) {
-            // NOP
-        }
-
-        public void reset() {
-            // NOP
-        }
-        
-        public boolean markSupported() {
-            return false;
-        }
-
-        private final void println(String text) {
-            if (vociferous) {
-                if(Logger.shouldLog(Logger.DEBUG, this))
-                       Logger.debug(this, text);
-            }
-        }
-        
-        private final void checkValid() throws IOException {
-                       synchronized(RandomAccessFileBucket.this) {
-                               if (released) {
-                                       throw new IOException("Attempt to use a 
released RandomAccessFileBucket: " + prefix);
-                               }
-                       }
-        }
-
-        ////////////////////////////////////////////////////////////
-        private RandomAccessFile raf = null;
-        private String prefix = "";
-    }
-
-    private class RAOutputStream extends OutputStream {
-        public RAOutputStream(String pref) throws IOException {
-            raf = new RandomAccessFile(file, "rw");
-            raf.seek(offset);
-            println(" -- Created new OutputStream [" + offset + ", " 
-                    + (offset + len -1) + ']');
-        }
-    
-        ////////////////////////////////////////////////////////////
-        // OutputStream implementation
-        public void write(int b) throws IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".write(b)");
-                checkValid();
-                int nAvailable = bytesLeft();
-                if (nAvailable < 1) {
-                    throw new IOException("Attempt to write past end of 
Bucket.");
-                }
-                raf.write(b);
-            }
-        }
-    
-        public void write(byte[] buf) throws IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".write(buf)");
-                checkValid();
-                int nAvailable = bytesLeft();
-                if (nAvailable < buf.length) {
-                    throw new IOException("Attempt to write past end of 
Bucket.");
-                }
-                raf.write(buf);
-            }
-        }
-    
-        public void write(byte[] buf, int off, int len) throws IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".write(buf,off,len)");
-                checkValid();
-                int nAvailable = bytesLeft();
-                if (nAvailable < len) {
-                    throw new IOException("Attempt to write past end of 
Bucket.");
-                }
-                raf.write(buf, off, len);
-            }
-        }
-    
-        public void flush() throws IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".flush()");
-                checkValid();
-                // NOP? Bytes written immediately?
-                // REDFLAG: double check.
-            }
-        }
-    
-        public void close() throws IOException {
-            synchronized (RandomAccessFileBucket.this) {
-                println(".close()");
-                checkValid();
-                if (streams.contains(RAOutputStream.this)) {
-                    streams.removeElement(RAOutputStream.this);
-                }
-                streams.trimToSize();
-                raf.close();
-            }
-        }
-
-        ////////////////////////////////////////////////////////////
-        private void println(String text) {
-            if (vociferous) {
-                if(Logger.shouldLog(Logger.DEBUG, this))
-                       Logger.debug(this, text);
-            }
-        }
-
-        private final void checkValid() throws IOException {
-                       synchronized (RandomAccessFileBucket.this) {
-                               if (isReleased()) {
-                                       throw new IOException("Attempt to use a 
released RandomAccessFileBucket: " + prefix);
-                               }
-                       }
-        }
-        private final int bytesLeft() throws IOException {
-                       synchronized (RandomAccessFileBucket.this) {
-                               return (int)(offset + len - 
raf.getFilePointer());
-                       }
-        }
-
-        private RandomAccessFile raf = null;
-        private String prefix = "";
-
-    }
-    ////////////////////////////////////////////////////////////
-
-    public synchronized boolean isReadOnly() {
-       return readOnly;
-    }
-    
-    public synchronized void setReadOnly() {
-       readOnly = true;
-    }
-
-       public void free() {
-               release();
-       }
-
-       public synchronized SimpleFieldSet toFieldSet() {
-               SimpleFieldSet fs = new SimpleFieldSet(false);
-               fs.putSingle("Type", "RandomAccessFileBucket");
-               fs.putSingle("Filename", file.toString());
-               fs.put("Offset", offset);
-               fs.put("Length", len);
-               return fs;
-       }
-}

Modified: 
branches/freenet-jfk/src/freenet/support/io/RandomAccessFileWrapper.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/RandomAccessFileWrapper.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/RandomAccessFileWrapper.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -5,6 +5,8 @@
 import java.io.IOException;
 import java.io.RandomAccessFile;

+import freenet.support.Logger;
+
 public class RandomAccessFileWrapper implements RandomAccessThing {

        // FIXME maybe we should avoid opening these until we are ready to use 
them
@@ -38,4 +40,12 @@
                return raf.length();
        }

+       public void close() {
+               try {
+                       raf.close();
+               } catch (IOException e) {
+                       Logger.error(this, "Could not close "+raf+" : "+e+" for 
"+this, e);
+               }
+       }
+
 }

Modified: branches/freenet-jfk/src/freenet/support/io/RandomAccessThing.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/RandomAccessThing.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/RandomAccessThing.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -16,5 +16,7 @@
        public void pread(long fileOffset, byte[] buf, int bufOffset, int 
length) throws IOException;

        public void pwrite(long fileOffset, byte[] buf, int bufOffset, int 
length) throws IOException;
+
+       public void close();

 }

Modified: 
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
   2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
   2007-08-21 20:26:59 UTC (rev 14828)
@@ -19,7 +19,7 @@
        public static Bucket create(SimpleFieldSet fs, RandomSource random, 
PersistentFileTracker f) throws CannotCreateFromFieldSetException {
                if(fs == null) {
                        if(Logger.shouldLog(Logger.MINOR, 
SerializableToFieldSetBucketUtil.class))
-                               
Logger.minor(SerializableToFieldSetBucketUtil.class, "fs = null");
+                               
Logger.minor(SerializableToFieldSetBucketUtil.class, "fs = null", new 
Exception("debug"));
                        return null;
                }
                String type = fs.get("Type");
@@ -56,17 +56,17 @@

                        throw new CannotCreateFromFieldSetException("No type");
                } else if(type.equals("FileBucket")) {
-                       return new FileBucket(fs, f);
+                       return BaseFileBucket.create(fs, f);
                } else if(type.equals("PaddedEphemerallyEncryptedBucket")) {
                        return new PaddedEphemerallyEncryptedBucket(fs, random, 
f);
                } else if(type.equals("NullBucket")) {
                        return new NullBucket();
-               } else if(type.equals("RandomAccessFileBucket")) {
-                       return new RandomAccessFileBucket(fs, f);
                } else if(type.equals("ReadOnlyFileSliceBucket")) {
                        return new ReadOnlyFileSliceBucket(fs);
                } else if(type.equals("DelayedFreeBucket")) {
                        return new DelayedFreeBucket(fs, random, f);
+               } else if(type.equals("PersistentTempFileBucket")) {
+                       return PersistentTempFileBucket.create(fs, f);
                } else
                        throw new 
CannotCreateFromFieldSetException("Unrecognized type "+type);
        }

Deleted: branches/freenet-jfk/src/freenet/support/io/SpyInputStream.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/SpyInputStream.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/SpyInputStream.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,148 +0,0 @@
-package freenet.support.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/*
- * This code is part of FProxy, an HTTP proxy server for Freenet. It is
- * distributed under the GNU Public Licence (GPL) version 2. See
- * http://www.gnu.org/ for further details of the GPL.
- */
-
-/**
- * The purpose of all of this gobbledeygook is to keep rude FCPClient
- * implementations from writing to temp files after the requests that own them
- * have been canceled.
- * 
- * <p>
- * These could be removed once FCPClient implementation deficiencies have been
- * corrected but sanity checks are always useful.
- * </p>
- * 
- * @author ian
- * @see freenet.support.SpyOutputStream
- */
-class SpyInputStream extends java.io.FilterInputStream {
-
-    private String prefix = "";
-       private TempFileBucket tfb = null;
-
-       private final void println(String text) {
-       }
-
-       private final void checkValid() throws IOException {
-               synchronized (tfb) {
-                       if (tfb.isReleased()) {
-                               throw new IOException(
-                                       "Attempt to use a released 
TempFileBucket: " + prefix);
-                       }
-               }
-       }
-
-       /**
-        * Constructor for the SpyInputStream object
-        * 
-        * @param tfb
-        * @param prefix
-        * @exception IOException
-        */
-       public SpyInputStream(TempFileBucket tfb, String prefix)
-               throws IOException {
-               super(null);
-               InputStream tmpIn = null;
-               try {
-                       this.prefix = prefix;
-                       this.tfb = tfb;
-                       checkValid();
-                       tmpIn = tfb.getRealInputStream();
-                       in = tmpIn;
-               } catch (IOException ioe) {
-                       try {
-                               if (tmpIn != null) {
-                                       tmpIn.close();
-                               }
-                       } catch (Exception e) {
-                               // NOP
-                       }
-                       throw ioe;
-               }
-               println("Created new InputStream");
-       }
-
-       public int read() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".read()");
-                       checkValid();
-                       return in.read();
-               }
-       }
-
-       public int read(byte[] bytes) throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".read(byte[])");
-                       checkValid();
-                       return in.read(bytes);
-               }
-       }
-
-    public int read(byte[] bytes, int a, int b) throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".read(byte[], int, int)");
-                       checkValid();
-                       // FIXME remove debugging
-                       if((a+b > bytes.length) || (a < 0) || (b < 0))
-                               throw new 
ArrayIndexOutOfBoundsException("a="+a+", b="+b+", length "+bytes.length);
-                       return in.read(bytes, a, b);
-               }
-       }
-    
-       public long skip(long a) throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".skip(long)");
-                       checkValid();
-                       return in.skip(a);
-               }
-       }
-
-    public int available() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".available()");
-                       checkValid();
-                       return in.available();
-               }
-       }
-       
-       public void close() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".close()");
-                       checkValid();
-                       in.close();
-                       if (tfb.streams.contains(in)) {
-                               tfb.streams.removeElement(in);
-                       }
-               }
-       }
-
-       
-       public void mark(int a) {
-               synchronized (tfb) {
-                       println(".mark(int)");
-                       in.mark(a);
-               }
-       }
-
-       public void reset() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".reset()");
-                       checkValid();
-                       in.reset();
-               }
-       }
-
-    public boolean markSupported() {
-               synchronized (tfb) {
-                       println(".markSupported()");
-                       return in.markSupported();
-               }
-       }
-}

Deleted: branches/freenet-jfk/src/freenet/support/io/SpyOutputStream.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/SpyOutputStream.java    
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/SpyOutputStream.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,116 +0,0 @@
-package freenet.support.io;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import freenet.support.Logger;
-
-/*
- * This code is part of FProxy, an HTTP proxy server for Freenet. It is
- * distributed under the GNU Public Licence (GPL) version 2. See
- * http://www.gnu.org/ for further details of the GPL.
- */
-
-/**
- * @author ian
- * @see freenet.support.SpyInputStream
- */
-public class SpyOutputStream extends FilterOutputStream {
-
-       private String prefix = "";
-       private TempFileBucket tfb = null;
-
-       private void debugPrintLn(String text) {
-               if (Logger.shouldLog(Logger.DEBUG,this))
-                       Logger.debug(this, text);
-       }
-
-       private final void checkValid() throws IOException {
-               synchronized (tfb) {
-                       if (tfb.isReleased()) {
-                               throw new IOException(
-                                       "Attempt to use a released 
TempFileBucket: " + prefix);
-                       }
-               }
-       }
-
-       /**
-        * Constructor for the SpyOutputStream object
-        * 
-        * @param tfb
-        * @param pref
-        * @exception IOException
-        */
-       public SpyOutputStream(TempFileBucket tfb, String pref)
-               throws IOException {
-               super(null);
-               OutputStream tmpOut = null;
-               try {
-                       this.prefix = pref;
-                       this.tfb = tfb;
-                       checkValid();
-                       tmpOut = tfb.getRealOutputStream();
-                       out = tmpOut;
-               } catch (IOException ioe) {
-                       //ioe.printStackTrace();
-                       debugPrintLn("SpyOutputStream ctr failed!: " + 
ioe.toString());
-                       try {
-                               if (tmpOut != null) {
-                                       tmpOut.close();
-                               }
-                       } catch (Exception e0) {
-                               // NOP
-                       }
-                       debugPrintLn("SpyOutputStream ctr failed!: " + 
ioe.toString());
-                       throw ioe;
-               }
-               debugPrintLn("Created new OutputStream");
-       }
-
-       ////////////////////////////////////////////////////////////
-       // FilterOutputStream implementation
-
-       public void write(int b) throws IOException {
-               synchronized (tfb) {
-                       //       println(".write(b)");
-                       checkValid();
-                       out.write(b);
-               }
-       }
-
-       public void write(byte[] buf) throws IOException {
-               synchronized (tfb) {
-                       //       println(".write(buf)");
-                       checkValid();
-                       out.write(buf);
-               }
-       }
-
-       public void write(byte[] buf, int off, int len) throws IOException {
-               synchronized (tfb) {
-                       //       println(".write(buf,off,len)");
-                       checkValid();
-                       out.write(buf, off, len);
-               }
-       }
-
-       public void flush() throws IOException {
-               synchronized (tfb) {
-                       debugPrintLn(".flush()");
-                       checkValid();
-                       out.flush();
-               }
-       }
-
-       public void close() throws IOException {
-               synchronized (tfb) {
-                       debugPrintLn(".close()");
-                       checkValid();
-                       out.close();
-                       if (tfb.streams.contains(out)) {
-                               tfb.streams.removeElement(out);
-                       }
-               }
-       }
-}

Modified: branches/freenet-jfk/src/freenet/support/io/TempBucketFactory.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/TempBucketFactory.java  
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/TempBucketFactory.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,9 +1,7 @@
 package freenet.support.io;

-import java.io.File;
 import java.io.IOException;

-import freenet.support.Logger;
 import freenet.support.api.Bucket;
 import freenet.support.api.BucketFactory;

@@ -20,33 +18,19 @@
  */
 public class TempBucketFactory implements BucketFactory {

-       private static class NOPHook implements TempBucketHook {
-               public void enlargeFile(long curLength, long finalLength) {
-               }
-               public void shrinkFile(long curLength, long finalLength) {
-               }
-               public void deleteFile(long curLength) {
-               }
-               public void createFile(long curLength) {
-               }
-       }
-
-       private final static TempBucketHook DONT_HOOK = new NOPHook();
-       private static TempBucketHook hook = DONT_HOOK;
-       private static boolean logDebug=true;
-       
        private final FilenameGenerator filenameGenerator;

        public static long defaultIncrement = 4096;
+       
+       public static float DEFAULT_FACTOR = 1.25F;

        // Storage accounting disabled by default.
        public TempBucketFactory(FilenameGenerator filenameGenerator) {
-               logDebug = Logger.shouldLog(Logger.DEBUG,this);
                this.filenameGenerator = filenameGenerator;
        }

        public Bucket makeBucket(long size) throws IOException {
-               return makeBucket(size, 1.25F, defaultIncrement);
+               return makeBucket(size, DEFAULT_FACTOR, defaultIncrement);
        }

        public Bucket makeBucket(long size, float factor) throws IOException {
@@ -67,61 +51,9 @@
         */
        public TempFileBucket makeBucket(long size, float factor, long 
increment)
                throws IOException {
-               logDebug = Logger.shouldLog(Logger.DEBUG,this);
-               File f = filenameGenerator.makeRandomFilename();
-               if(f == null) throw new NullPointerException();
+               long id = filenameGenerator.makeRandomFilename();

-               return new TempFileBucket(f, hook, size, increment, factor);
+               return new TempFileBucket(id, filenameGenerator);
        }

-       /**
-        * Free bucket
-        * 
-        * @param b
-        *            Description of the Parameter
-        */
-       public void freeBucket(Bucket b) {
-               if (b instanceof TempFileBucket) {
-                       if (logDebug)
-                               Logger.debug(
-                                       this,
-                                       "Temp bucket released: "
-                                               + ((TempFileBucket) 
b).getFile().getAbsolutePath(),
-                                       new Exception("debug"));
-                       if (!((TempFileBucket) b).release()) {
-                               System.err.println("Could not release temp 
bucket" + b);
-                               Logger.error(
-                                       this,
-                                       "Could not release temp bucket " + b,
-                                        new Exception("Failed to release 
tempbucket"));
-                       }
-               }
-       }
-
-       /**
-        * Sets the storage accounting hook.
-        * 
-        * @param t
-        *            The hook object to use to keep track of the amount of 
storage
-        *            used. It is legal for t to be null. In this case storage
-        *            accounting is disabled.
-        */
-       public static void setHook(TempBucketHook t) {
-               if (logDebug)
-                       Logger.debug(
-                               TempBucketFactory.class,
-                               "Set TempBucketHook to " + t);
-               hook = t;
-               if (hook == null) {
-                       // Allow hooks to be disabled w/o sprinkling
-                       // if (hook != null) {/*blah blah */} calls
-                       // throughout the code.
-                       hook = DONT_HOOK;
-                       if (logDebug) {
-                               Logger.debug(
-                                       TempBucketHook.class,
-                                       "TempBucketHook file usage management 
was disabled.");
-                       }
-               }
-       }
 }

Deleted: branches/freenet-jfk/src/freenet/support/io/TempBucketHook.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/TempBucketHook.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/TempBucketHook.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,34 +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;
-
-public interface TempBucketHook {
-    
-    /** Allocate space for a write making the file larger
-     * Call this before writing a file, make sure you call shrinkFile if the 
write fails
-     * @param curLength the length of the file before the write
-     * @param finalLength the length of the file after the write
-     * @throws IOException if insufficient space
-     */
-    void enlargeFile(long curLength, long finalLength) throws IOException;
-    
-    /** Deallocate space after a write enlarging the file fails
-     * Call this if enlargeFile was called but the write failed.
-     * Also call it if you want to truncate a file
-     * @param curLength original length before write
-     * @param finalLength length the file would have been if the write had 
succeeded
-     */
-    void shrinkFile(long curLength, long finalLength);
-    
-    /** Deallocate space for a temp file, AFTER successful delete completed
-     */
-    void deleteFile(long curLength);
-    
-    /** Allocate space for a temp file, before actually creating it
-     */
-    void createFile(long curLength) throws IOException;
-
-}

Modified: branches/freenet-jfk/src/freenet/support/io/TempFileBucket.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/TempFileBucket.java     
2007-08-21 19:57:05 UTC (rev 14827)
+++ branches/freenet-jfk/src/freenet/support/io/TempFileBucket.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,13 +1,10 @@
 package freenet.support.io;

 import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Vector;

 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;

 /*
  *  This code is part of FProxy, an HTTP proxy server for Freenet.
@@ -19,423 +16,70 @@
  *
  * @author     giannij
  */
-public class TempFileBucket extends FileBucket {
-       TempBucketHook hook = null;
-       // How much we have asked the Hook to allocate for us
-       long fakeLength = 0;
-       long minAlloc;
-       float factor;
+public class TempFileBucket extends BaseFileBucket implements Bucket, 
SerializableToFieldSetBucket {
+       long filenameID;
+       final FilenameGenerator generator;
        private static boolean logDebug = true;
+       private boolean readOnly;
        /**
         * Constructor for the TempFileBucket object
         *
         * @param  f  File
         */
-       protected TempFileBucket(
-               File f,
-               TempBucketHook hook,
-               long startLength,
-               long minAlloc,
-               float factor)
-               throws IOException {
-               super(f, false, false, true, true, true);
+       public TempFileBucket(
+               long id,
+               FilenameGenerator generator) {
+               super(generator.getFilename(id));
+               this.filenameID = id;
+               this.generator = generator;
                synchronized(this) {
                        logDebug = Logger.shouldLog(Logger.DEBUG, this);
                }
-               if (minAlloc > 0)
-                       this.minAlloc = minAlloc;
-               else
-                       this.minAlloc = 1024;
-               this.factor = factor;
-               if (factor < 1.0)
-                       throw new IllegalArgumentException("factor must be >= 
1.0");

-               // Make sure finalize wacks temp file 
-               // if it is not explictly freed.
-               deleteOnFinalize = true;
-
                //System.err.println("FProxyServlet.TempFileBucket -- created: 
" +
                //         f.getAbsolutePath());
-               this.hook = hook;
-               long x = startLength <= 0 ? minAlloc : startLength;
-               hook.createFile(x);
-               this.fakeLength = x;
                synchronized(this) {
                        if (logDebug)
                                Logger.debug(
                                        this,
-                                       "Initializing TempFileBucket(" + f + 
',' + hook + ')');
+                                       "Initializing TempFileBucket(" + 
getFile());
                }
        }

-       /**
-        *  Gets the realInputStream attribute of the TempFileBucket object
-        *
-        * @return                  The realInputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       synchronized InputStream getRealInputStream() throws IOException {
-               if (released)
-                       throw new IllegalStateException(
-                               "Trying to getInputStream on " + "released 
TempFileBucket!");
-               if (logDebug)
-                       Logger.debug(
-                               this,
-                               "getRealInputStream() for " + file,
-                               new Exception("debug"));
-               if (!file.exists())
-                       return new NullInputStream();
-               else
-                       return new HookedFileBucketInputStream(file);
+       protected boolean deleteOnFinalize() {
+               // Make sure finalize wacks temp file 
+               // if it is not explictly freed.
+               return true;
        }
-
-       /**
-        *  Gets the realOutputStream attribute of the TempFileBucket object
-        *
-        * @return                  The realOutputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       OutputStream getRealOutputStream() throws IOException {
-               synchronized(this) {
-                       if (logDebug)
-                               Logger.debug(
-                                       this,
-                                       "getRealOutputStream() for " + file,
-                                       new Exception("debug"));
-               }
-               return super.getOutputStream();
+       
+       public SimpleFieldSet toFieldSet() {
+               if(deleteOnFinalize())
+                       return null; // Not persistent
+               // For subclasses i.e. PersistentTempFileBucket
+               return super.toFieldSet();
        }

-       // Wrap non-const members so we can tell
-       // when code touches the Bucket after it
-       // has been released.
-       /**
-        *  Gets the inputStream attribute of the TempFileBucket object
-        *
-        * @return                  The inputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       public synchronized InputStream getInputStream() throws IOException {
-               logDebug = Logger.shouldLog(Logger.DEBUG, this);
-               if (logDebug)
-                       Logger.debug(this, "getInputStream for " + file);
-               InputStream newIn = new SpyInputStream(this, 
file.getAbsolutePath());
-               return newIn;
+       protected boolean createFileOnly() {
+               return false;
        }

-       /**
-        *  Gets the outputStream attribute of the TempFileBucket object
-        *
-        * @return                  The outputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       public synchronized OutputStream getOutputStream() throws IOException {
-               logDebug = Logger.shouldLog(Logger.DEBUG, this);
-               if (logDebug)
-                       Logger.debug(this, "getOutputStream for " + file);
-               return new SpyOutputStream(this, file.getAbsolutePath());
-       }
-
-       /**
-        *  Release
-        *
-        * @return    Success
-        */
-       public synchronized boolean release() {
-               if(Logger.shouldLog(Logger.MINOR, this))
-                       Logger.minor(this, "Releasing bucket: "+file, new 
Exception("debug"));
-               //System.err.println("FProxyServlet.TempFileBucket -- release: 
" +                      // file.getAbsolutePath());
-
-               //System.err.println("CALL STACK: ");
-               //(new Exception()).printStackTrace();
-
-               // Force all open streams closed.
-               // Windows won't let us delete the file unless we
-               // do this.
-               logDebug = Logger.shouldLog(Logger.DEBUG, this);
-               if (logDebug)
-                       Logger.debug(this, "Releasing TempFileBucket " + file);
-               for (int i = 0; i < streams.size(); i++) {
-                       try {
-                               if (streams.elementAt(i) instanceof 
InputStream) {
-                                       InputStream is = (InputStream) 
streams.elementAt(i);
-                                       is.close();
-
-                                       if (logDebug) {
-                                               Logger.debug(
-                                                       this,
-                                                       "closed open 
InputStream !: "
-                                                               + 
file.getAbsolutePath(),
-                                                       new Exception("debug"));
-                                               if (is instanceof 
FileBucketInputStream) {
-                                                       Logger.debug(
-                                                               this,
-                                                               "Open 
InputStream created: ",
-                                                               
((FileBucketInputStream) is).e);
-                                               }
-                                       }
-                               } else if (streams.elementAt(i) instanceof 
OutputStream) {
-                                       OutputStream os = (OutputStream) 
(streams.elementAt(i));
-                                       os.close();
-                                       if (logDebug) {
-                                               Logger.debug(
-                                                       this,
-                                                       "closed open 
OutputStream !: "
-                                                               + 
file.getAbsolutePath(),
-                                                       new Exception("debug"));
-//                                             if (os instanceof 
FileBucketOutputStream) {
-//                                                     Logger.debug(
-//                                                             this,
-//                                                             "Open 
OutputStream created: ",
-//                                                             
((FileBucketOutputStream) os).e);
-//                                             }
-
-                                       }
-                               }
-                       } catch (IOException ioe) {
-                       }
-               }
-               if (logDebug)
-                       Logger.debug(this, "Closed streams for " + file);
-               if (released) {
-                       if(Logger.shouldLog(Logger.MINOR, this))
-                               Logger.minor(this,
-                                               "Already released file: " + 
file.getName());
-                       if (file.exists())
-                               throw new IllegalStateException(
-                                       "already released file "
-                                               + file.getName()
-                                               + " BUT IT STILL EXISTS!");
-                       return true;
-               }
-               if (logDebug)
-                       Logger.debug(
-                               this,
-                               "Checked for released for " + file);
-               released = true;
-               if (file.exists()) {
-                       if (logDebug)
-                               Logger.debug(
-                                       this,
-                                       "Deleting bucket " + file.getName());
-                       if (!file.delete()) {
-                               Logger.error(
-                                       this,
-                                       "Delete failed on bucket " + 
file.getName(),
-                                       new Exception());
-                               // Nonrecoverable; even though the user can't 
fix it it's still very serious
-                               return false;
-                       } else {
-                               if (hook != null)
-                                       hook.deleteFile(fakeLength);
-                       }
-               } else {
-                       if (hook != null)
-                               hook.deleteFile(fakeLength);
-               }
-               if (logDebug)
-                       Logger.debug(
-                               this,
-                               "release() returning true for " + file);
+       protected boolean deleteOnFree() {
                return true;
        }

-       /**
-        *  Gets the released attribute of the TempFileBucket object
-        *
-        * @return    The released value
-        */
-       public final synchronized boolean isReleased() {
-               return released;
+       public File getFile() {
+               return generator.getFilename(filenameID);
        }

-//     /**
-//      *  Finalize
-//      *
-//      * @exception  Throwable  Description of the Exception
-//      */
-//     public void finalize() throws Throwable {
-//             if (logDebug)
-//                     Logger.debug(this, "Finalizing TempFileBucket for " + 
file);
-//             super.finalize();
-//     }
-
-       protected Vector streams = new Vector();
-       private boolean released;
-
-       protected synchronized FileBucketOutputStream newFileBucketOutputStream(
-               String s,
-               boolean append,
-               long restartCount)
-               throws IOException {
-               if (logDebug)
-                       Logger.debug(this,
-                               "Creating new HookedFileBucketOutputStream for 
" + file);
-               if (hook != null)
-                       return new HookedFileBucketOutputStream(s, 
restartCount);
-               else
-                       return super.newFileBucketOutputStream(new File(s), s, 
restartCount);
+       public boolean isReadOnly() {
+               return readOnly;
        }

-       protected synchronized void deleteFile() {
-               if (logDebug)
-                       Logger.debug(this, "Deleting " + file);
-               file.delete();
-               if (hook != null)
-                       hook.deleteFile(fakeLength);
+       public void setReadOnly() {
+               readOnly = true;
        }

-       protected synchronized void resetLength() {
-               if (logDebug)
-                       Logger.debug(this, "Resetting length for " + file);
-               if (length != 0) {
-                       if (hook != null) {
-                               hook.shrinkFile(0, fakeLength);
-                               fakeLength = 0;
-                       }
-                       super.resetLength();
-               }
+       protected boolean deleteOnExit() {
+               return true;
        }
-
-       protected final synchronized void getLengthSynchronized(long len) 
throws IOException {
-               //       Core.logger.log(this, "getLengthSynchronized("+len+
-               //                    "); fakeLength = "+fakeLength, 
Logger.DEBUG);
-               long l = fakeLength;
-               long ol = l;
-               while (len > l) {
-                       l = (long) (l * factor);
-                       if (minAlloc > 0)
-                               l = l + minAlloc - (l % minAlloc);
-                       if (l <= fakeLength)
-                               throw new IllegalStateException("Bucket 
extension error!");
-                       //        Core.logger.log(this, "l now "+l, 
Logger.DEBUG);
-                       if (ol == l)
-                               throw new IllegalStateException("infinite 
loop");
-                       ol = l;
-               }
-               if (fakeLength != l) {
-                       if (Logger.shouldLog(Logger.DEBUG, this))
-                               Logger.debug(
-                                       this,
-                                       "getLengthSynchronized("
-                                               + len
-                                               + "): increasing "
-                                               + fakeLength
-                                               + " to: "
-                                               + l
-                                               + " (real length: "
-                                               + length
-                                               + ')');
-                       hook.enlargeFile(fakeLength, l);
-               }
-               fakeLength = l;
-       }
-
-       public synchronized String toString(){
-               return "TempFileBucket (File: 
'"+getFile().getAbsolutePath()+"', streams: "+streams.size()+", hook: "+hook+ 
')';
-       }
-
-       class HookedFileBucketInputStream extends FileBucketInputStream {
-               HookedFileBucketInputStream(File f) throws IOException {
-                       super(f);
-                       streams.addElement(this);
-               }
-
-               public void close() throws IOException {
-                       super.close();
-                       while (streams.remove(this));
-               }
-       }
-
-       class HookedFileBucketOutputStream extends FileBucketOutputStream {
-
-               protected HookedFileBucketOutputStream(
-                       String s,
-                       long restartCount)
-                       throws IOException {
-                       super(new File(s), s, restartCount);
-                       streams.addElement(this);
-                       if (Logger.shouldLog(Logger.DEBUG, this))
-                               Logger.debug(
-                                       this,
-                                       "Created HookedFileBucketOutputStream("
-                                               + s
-                                               + ','
-                            + restartCount
-                                               + ')');
-               }
-
-               public void close() throws IOException {
-                       super.close();
-                       while (streams.remove(this));
-               }
-
-               public void write(byte[] b) throws IOException {
-                       //        Core.logger.log(this, 
"HookedFileBucketOutputStream.write(byte[] len "+
-                       //                        b.length+") for "+file, 
Logger.DEBUG);
-                       synchronized (TempFileBucket.this) {
-                               //            Core.logger.log(this, 
"Synchronized on TempFileBucket", 
-                               //                            Logger.DEBUG);
-                               super.confirmWriteSynchronized();
-                               //            Core.logger.log(this, 
"confirmWriteSynchronized()", Logger.DEBUG);
-                               long finalLength = length + b.length;
-                               //            Core.logger.log(this, 
"length="+length+", finalLength="+finalLength,
-                               //                            Logger.DEBUG);
-                               long realStartLen = fakeLength;
-                               getLengthSynchronized(finalLength);
-                               //            Core.logger.log(this, "Called 
hook.enlargeFile()", Logger.DEBUG);
-                               try {
-                                       super.write(b);
-                                       //                Core.logger.log(this, 
"Written", Logger.DEBUG);
-                               } catch (IOException e) {
-                                       //                Core.logger.log(this, 
"Write failed", Logger.DEBUG);
-                                       hook.shrinkFile(realStartLen, 
fakeLength);
-                                       fakeLength = realStartLen;
-                                       //                Core.logger.log(this, 
"Shrank file", Logger.DEBUG);
-                                       throw e;
-                               }
-                       }
-               }
-
-               public void write(byte[] b, int off, int len) throws 
IOException {
-                       //        Core.logger.log(this, 
"HookedFileBucketOutputStream.write(byte[], "+off+
-                       //                        ","+len+") for "+file, 
Logger.DEBUG);
-                       synchronized (TempFileBucket.this) {
-                               long finalLength = length + len;
-                               long realStartLen = fakeLength;
-                               getLengthSynchronized(finalLength);
-                               try {
-                                       super.write(b, off, len);
-                               } catch (IOException e) {
-                                       hook.shrinkFile(realStartLen, 
fakeLength);
-                                       fakeLength = realStartLen;
-                                       throw e;
-                               }
-                       }
-               }
-
-               public void write(int b) throws IOException {
-                       //        Core.logger.log(this, 
"HookedFileBucketOutputStream.write(int) for "+file,
-                       //                        Logger.DEBUG);
-                       synchronized (TempFileBucket.this) {
-                               long finalLength = length + 1;
-                               long realStartLen = fakeLength;
-                               getLengthSynchronized(finalLength);
-                               try {
-                                       super.write(b);
-                               } catch (IOException e) {
-                                       hook.shrinkFile(realStartLen, 
fakeLength);
-                                       fakeLength = realStartLen;
-                                       throw e;
-                               }
-                       }
-               }
-
-       }
-
-       public SimpleFieldSet toFieldSet() {
-               SimpleFieldSet fs = super.toFieldSet();
-               fs.putSingle("Type", "TempFileBucket");
-               return fs;
-       }
 }

Modified: 
branches/freenet-jfk/src/freenet/support/math/TimeDecayingRunningAverage.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/math/TimeDecayingRunningAverage.java   
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/freenet/support/math/TimeDecayingRunningAverage.java   
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -7,6 +7,7 @@
 import java.io.DataOutputStream;
 import java.io.IOException;

+import freenet.node.TimeSkewDetectorCallback;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;

@@ -40,6 +41,7 @@
     double minReport;
     double maxReport;
     boolean logDEBUG;
+    private final TimeSkewDetectorCallback timeSkewCallback;

     public String toString() {
                long now = System.currentTimeMillis();
@@ -53,7 +55,7 @@
     }

     public TimeDecayingRunningAverage(double defaultValue, long halfLife,
-            double min, double max) {
+            double min, double max, TimeSkewDetectorCallback callback) {
        curValue = defaultValue;
         this.defaultValue = defaultValue;
         started = false;
@@ -66,10 +68,11 @@
         if(logDEBUG)
                Logger.debug(this, "Created "+this,
                                new Exception("debug"));
+        this.timeSkewCallback = callback;
     }

     public TimeDecayingRunningAverage(double defaultValue, long halfLife,
-            double min, double max, SimpleFieldSet fs) {
+            double min, double max, SimpleFieldSet fs, 
TimeSkewDetectorCallback callback) {
        curValue = defaultValue;
         this.defaultValue = defaultValue;
         started = false;
@@ -87,7 +90,7 @@
                started = fs.getBoolean("Started", false);
                if(started) {
                        curValue = fs.getDouble("CurrentValue", curValue);
-                       if(curValue > maxReport || curValue < minReport) {
+                       if(curValue > maxReport || curValue < minReport || 
Double.isNaN(curValue)) {
                                curValue = defaultValue;
                                totalReports = 0;
                                createdTime = System.currentTimeMillis();
@@ -98,9 +101,10 @@
                        }
                }
         }
+        this.timeSkewCallback = callback;
     }

-    public TimeDecayingRunningAverage(double defaultValue, double halfLife, 
double min, double max, DataInputStream dis) throws IOException {
+    public TimeDecayingRunningAverage(double defaultValue, double halfLife, 
double min, double max, DataInputStream dis, TimeSkewDetectorCallback callback) 
throws IOException {
         int m = dis.readInt();
         if(m != MAGIC) throw new IOException("Invalid magic "+m);
         int v = dis.readInt();
@@ -120,6 +124,7 @@
         lastReportTime = -1;
         createdTime = System.currentTimeMillis() - priorExperienceTime;
         totalReports = dis.readLong();
+        this.timeSkewCallback = callback;
     }

     public TimeDecayingRunningAverage(TimeDecayingRunningAverage a) {
@@ -132,6 +137,7 @@
         this.started = a.started;
         this.totalReports = a.totalReports;
         this.curValue = a.curValue;
+        this.timeSkewCallback = a.timeSkewCallback;
     }

     public synchronized double currentValue() {
@@ -165,13 +171,17 @@
                                         now - lastReportTime;
                                long uptime = now - createdTime;
                                if(thisInterval < 0) {
-                                       Logger.error(this, "Clock (reporting) 
went back in time, ignoring report: "+now+" was "+lastReportTime+" (back 
"+(-thisInterval)+"ms");
+                                       Logger.error(this, "Clock (reporting) 
went back in time, ignoring report: "+now+" was "+lastReportTime+" (back 
"+(-thisInterval)+"ms)");
                                        lastReportTime = now;
+                                       if(timeSkewCallback != null)
+                                               
timeSkewCallback.setTimeSkewDetectedUserAlert();
                                        return;
                                }
                                double thisHalfLife = halfLife;
                                if(uptime < 0) {
-                                       Logger.error(this, "Clock (uptime) went 
back in time, ignoring report: "+now+" was "+createdTime+" (back 
"+(-uptime)+"ms");
+                                       Logger.error(this, "Clock (uptime) went 
back in time, ignoring report: "+now+" was "+createdTime+" (back 
"+(-uptime)+"ms)");
+                                       if(timeSkewCallback != null)
+                                               
timeSkewCallback.setTimeSkewDetectedUserAlert();
                                        return;
                                } else {
                                        if((uptime / 4) < thisHalfLife) 
thisHalfLife = (uptime / 4);

Copied: branches/freenet-jfk/src/freenet/tools/MergeSFS.java (from rev 14796, 
trunk/freenet/src/freenet/tools/MergeSFS.java)
===================================================================
--- branches/freenet-jfk/src/freenet/tools/MergeSFS.java                        
        (rev 0)
+++ branches/freenet-jfk/src/freenet/tools/MergeSFS.java        2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,26 @@
+package freenet.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import freenet.support.SimpleFieldSet;
+
+public class MergeSFS {
+
+       /**
+        * @param args
+        * @throws IOException 
+        */
+       public static void main(String[] args) throws IOException {
+               File f1 = new File(args[0]);
+               File f2 = new File(args[1]);
+               SimpleFieldSet fs1 = SimpleFieldSet.readFrom(f1, false, true);
+               SimpleFieldSet fs2 = SimpleFieldSet.readFrom(f2, false, true);
+               fs1.putAllOverwrite(fs2);
+               PrintWriter pw = new PrintWriter(System.out);
+               fs1.writeToOrdered(pw);
+               pw.flush();
+       }
+
+}

Modified: 
branches/freenet-jfk/src/org/spaceroots/mantissa/random/MersenneTwister.java
===================================================================
--- 
branches/freenet-jfk/src/org/spaceroots/mantissa/random/MersenneTwister.java    
    2007-08-21 19:57:05 UTC (rev 14827)
+++ 
branches/freenet-jfk/src/org/spaceroots/mantissa/random/MersenneTwister.java    
    2007-08-21 20:26:59 UTC (rev 14828)
@@ -68,193 +68,187 @@
  * @version $Id: MersenneTwister.java,v 1.1 2005/02/07 23:03:14 amphibian Exp $

  */
-public class MersenneTwister
-  extends Random {
-
+public class MersenneTwister extends Random {
        private static final long serialVersionUID = -1;
-       
-  /** Creates a new random number generator.
-   * <p>The instance is initialized using the current time as the
-   * seed.</p>
-   */
-  public MersenneTwister() {
-    mt = new int[N];
-    setSeed(System.currentTimeMillis());
-  }

-  /** Creates a new random number generator using a single int seed.
-   * @param seed the initial seed (32 bits integer)
-   */
-  public MersenneTwister(int seed) {
-    mt = new int[N];
-    setSeed(seed);
-  }
+       private static final int   N     = 624;
+       private static final int   M     = 397;
+       private static final int[] MAG01 = { 0x0, 0x9908b0df };

-  /** Creates a new random number generator using an int array seed.
-   * @param seed the initial seed (32 bits integers array), if null
-   * the seed of the generator will be related to the current time
-   */
-  public MersenneTwister(int[] seed) {
-    mt = new int[N];
-    setSeed(seed);
-  }
+       private final int[] mt;
+       private int   mti;

-  /** Seed from byte[] */
-  public MersenneTwister(byte[] seed) {
-       mt = new int[N];
-       setSeed(seed);
-  }
-  
-  /** Creates a new random number generator using a single long seed.
-   * @param seed the initial seed (64 bits integer)
-   */
-  public MersenneTwister(long seed) {
-    mt = new int[N];
-    setSeed(seed);
-  }
+       /** Creates a new random number generator.
+        * <p>The instance is initialized using the current time as the
+        * seed.</p>
+        */
+       public MersenneTwister() {
+               mt = new int[N];
+               setSeed(System.currentTimeMillis());
+       }

-  /** Reinitialize the generator as if just built with the given int seed.
-   * <p>The state of the generator is exactly the same as a new
-   * generator built with the same seed.</p>
-   * @param seed the initial seed (32 bits integer)
-   */
-  public void setSeed(int seed) {
-    // we use a long masked by 0xffffffffL as a poor man unsigned int
-    long longMT = seed;
-    mt[0]= (int) longMT;
-    for (mti = 1; mti < N; ++mti) {
-      // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
-      // initializer from the 2002-01-09 C version by Makoto Matsumoto
-      longMT = (1812433253l * (longMT ^ (longMT >> 30)) + mti) & 0xffffffffL; 
-      mt[mti]= (int) longMT;
-    }
-  }
+       /** Creates a new random number generator using a single int seed.
+        * @param seed the initial seed (32 bits integer)
+        */
+       public MersenneTwister(int seed) {
+               mt = new int[N];
+               setSeed(seed);
+       }

-  /**
-   * Seed from byte[].
-   */
-  public void setSeed(byte[] seed) {
-         int[] seeds = new int[seed.length/4];
-         for(int i=0;i<seeds.length;i+=4) {
-                 seeds[i] = Fields.bytesToInt(seed, i);
-         }
-         setSeed(seeds);
-  }
-  
-  /** Reinitialize the generator as if just built with the given int array 
seed.
-   * <p>The state of the generator is exactly the same as a new
-   * generator built with the same seed.</p>
-   * @param seed the initial seed (32 bits integers array), if null
-   * the seed of the generator will be related to the current time
-   */
-  public void setSeed(int[] seed) {
+       /** Creates a new random number generator using an int array seed.
+        * @param seed the initial seed (32 bits integers array), if null
+        * the seed of the generator will be related to the current time
+        */
+       public MersenneTwister(int[] seed) {
+               mt = new int[N];
+               setSeed(seed);
+       }

-    if (seed == null) {
-      setSeed(System.currentTimeMillis());
-      return;
-    }
+       /** Seed from byte[] */
+       public MersenneTwister(byte[] seed) {
+               mt = new int[N];
+               setSeed(seed);
+       }

-    setSeed(19650218);
-    int i = 1;
-    int j = 0;
+       /** Creates a new random number generator using a single long seed.
+        * @param seed the initial seed (64 bits integer)
+        */
+       public MersenneTwister(long seed) {
+               mt = new int[N];
+               setSeed(seed);
+       }

-    for (int k = Math.max(N, seed.length); k != 0; k--) {
-      long l0 = (mt[i] & 0x7fffffffl)   | ((mt[i]   < 0) ? 0x80000000l : 0x0l);
-      long l1 = (mt[i-1] & 0x7fffffffl) | ((mt[i-1] < 0) ? 0x80000000l : 0x0l);
-      long l  = (l0 ^ ((l1 ^ (l1 >> 30)) * 1664525l)) + seed[j] + j; // non 
linear
-      mt[i]   = (int) (l & 0xffffffffl);
-      i++; j++;
-      if (i >= N) {
-        mt[0] = mt[N - 1];
-        i = 1;
-      }
-      if (j >= seed.length) {
-        j = 0;
-      }
-    }
+       /** Reinitialize the generator as if just built with the given int seed.
+        * <p>The state of the generator is exactly the same as a new
+        * generator built with the same seed.</p>
+        * @param seed the initial seed (32 bits integer)
+        */
+       public synchronized void setSeed(int seed) {
+               // we use a long masked by 0xffffffffL as a poor man unsigned 
int
+               long longMT = seed;
+               mt[0]= (int) longMT;
+               for (mti = 1; mti < N; ++mti) {
+                       // See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier.
+                       // initializer from the 2002-01-09 C version by Makoto 
Matsumoto
+                       longMT = (1812433253l * (longMT ^ (longMT >> 30)) + 
mti) & 0xffffffffL; 
+                       mt[mti]= (int) longMT;
+               }
+       }

-    for (int k = N - 1; k != 0; k--) {
-      long l0 = (mt[i] & 0x7fffffffl)   | ((mt[i]   < 0) ? 0x80000000l : 0x0l);
-      long l1 = (mt[i-1] & 0x7fffffffl) | ((mt[i-1] < 0) ? 0x80000000l : 0x0l);
-      long l  = (l0 ^ ((l1 ^ (l1 >> 30)) * 1566083941l)) - i; // non linear
-      mt[i]   = (int) (l & 0xffffffffL);
-      i++;
-      if (i >= N) {
-        mt[0] = mt[N - 1];
-        i = 1;
-      }
-    }
+       /**
+        * Seed from byte[].
+        */
+       public synchronized void setSeed(byte[] seed) {
+               int[] seeds = new int[seed.length/4];
+               for(int i=0;i<seeds.length;i+=4) {
+                       seeds[i] = Fields.bytesToInt(seed, i);
+               }
+               setSeed(seeds);
+       }

-    mt[0] = 0x80000000; // MSB is 1; assuring non-zero initial array
+       /** Reinitialize the generator as if just built with the given int 
array seed.
+        * <p>The state of the generator is exactly the same as a new
+        * generator built with the same seed.</p>
+        * @param seed the initial seed (32 bits integers array), if null
+        * the seed of the generator will be related to the current time
+        */
+       public synchronized void setSeed(int[] seed) {
+               if (seed == null) {
+                       setSeed(System.currentTimeMillis());
+                       return;
+               }

-  }
+               setSeed(19650218);
+               int i = 1;
+               int j = 0;

-  /** Reinitialize the generator as if just built with the given long seed.
-   * <p>The state of the generator is exactly the same as a new
-   * generator built with the same seed.</p>
-   * @param seed the initial seed (64 bits integer)
-   */
-  public void setSeed(long seed) {
-    if (mt == null) {
-      // this is probably a spurious call from base class constructor,
-      // we do nothing and wait for the setSeed in our own
-      // constructors after array allocation
-      return;
-    }
-    setSeed(new int[] { (int) (seed >>> 32), (int) (seed & 0xffffffffl) });
-  }
+               for (int k = Math.max(N, seed.length); k != 0; k--) {
+                       long l0 = (mt[i] & 0x7fffffffl)   | ((mt[i]   < 0) ? 
0x80000000l : 0x0l);
+                       long l1 = (mt[i-1] & 0x7fffffffl) | ((mt[i-1] < 0) ? 
0x80000000l : 0x0l);
+                       long l  = (l0 ^ ((l1 ^ (l1 >> 30)) * 1664525l)) + 
seed[j] + j; // non linear
+                       mt[i]   = (int) (l & 0xffffffffl);
+                       i++; j++;
+                       if (i >= N) {
+                               mt[0] = mt[N - 1];
+                               i = 1;
+                       }
+                       if (j >= seed.length) {
+                               j = 0;
+                       }
+               }

-  /** Generate next pseudorandom number.
-   * <p>This method is the core generation algorithm. As per
-   * <code>java.util.Random</code> contract, it is used by all the
-   * public generation methods for the various primitive types {@link
-   * #nextBoolean nextBoolean}, {@link #nextBytes nextBytes}, {@link
-   * #nextDouble nextDouble}, {@link #nextFloat nextFloat}, {@link
-   * #nextGaussian nextGaussian}, {@link #nextInt nextInt} and {@link
-   * #nextLong nextLong}.</p>
-   * @param bits number of random bits to produce
-   */
-  protected int next(int bits) {
+               for (int k = N - 1; k != 0; k--) {
+                       long l0 = (mt[i] & 0x7fffffffl)   | ((mt[i]   < 0) ? 
0x80000000l : 0x0l);
+                       long l1 = (mt[i-1] & 0x7fffffffl) | ((mt[i-1] < 0) ? 
0x80000000l : 0x0l);
+                       long l  = (l0 ^ ((l1 ^ (l1 >> 30)) * 1566083941l)) - i; 
// non linear
+                       mt[i]   = (int) (l & 0xffffffffL);
+                       i++;
+                       if (i >= N) {
+                               mt[0] = mt[N - 1];
+                               i = 1;
+                       }
+               }

-    int y;
+               mt[0] = 0x80000000; // MSB is 1; assuring non-zero initial array

-    if (mti >= N) { // generate N words at one time
-      int mtNext = mt[0];
-      for (int k = 0; k < N - M; ++k) {
-        int mtCurr = mtNext;
-        mtNext = mt[k + 1];
-        y = (mtCurr & 0x80000000) | (mtNext & 0x7fffffff);
-        mt[k] = mt[k + M] ^ (y >>> 1) ^ MAG01[y & 0x1];
-      }
-      for (int k = N - M; k < N - 1; ++k) {
-        int mtCurr = mtNext;
-        mtNext = mt[k + 1];
-        y = (mtCurr & 0x80000000) | (mtNext & 0x7fffffff);
-        mt[k] = mt[k + (M - N)] ^ (y >>> 1) ^ MAG01[y & 0x1];
-      }
-      y = (mtNext & 0x80000000) | (mt[0] & 0x7fffffff);
-      mt[N - 1] = mt[M - 1] ^ (y >>> 1) ^ MAG01[y & 0x1];
+       }

-      mti = 0;
-    }
-  
-    y = mt[mti++];
+       /** Reinitialize the generator as if just built with the given long 
seed.
+        * <p>The state of the generator is exactly the same as a new
+        * generator built with the same seed.</p>
+        * @param seed the initial seed (64 bits integer)
+        */
+       public synchronized void setSeed(long seed) {
+               if (mt == null) {
+                       // this is probably a spurious call from base class 
constructor,
+                       // we do nothing and wait for the setSeed in our own
+                       // constructors after array allocation
+                       return;
+               }
+               setSeed(new int[] { (int) (seed >>> 32), (int) (seed & 
0xffffffffl) });
+       }

-    // tempering
-    y ^= (y >>> 11);
-    y ^= (y <<   7) & 0x9d2c5680;
-    y ^= (y <<  15) & 0xefc60000;
-    y ^= (y >>> 18);
+       /** Generate next pseudorandom number.
+        * <p>This method is the core generation algorithm. As per
+        * <code>java.util.Random</code> contract, it is used by all the
+        * public generation methods for the various primitive types {@link
+        * #nextBoolean nextBoolean}, {@link #nextBytes nextBytes}, {@link
+        * #nextDouble nextDouble}, {@link #nextFloat nextFloat}, {@link
+        * #nextGaussian nextGaussian}, {@link #nextInt nextInt} and {@link
+        * #nextLong nextLong}.</p>
+        * @param bits number of random bits to produce
+        */
+       protected synchronized int next(int bits) {
+               int y;

-    return y >>> (32 - bits);
+               if (mti >= N) { // generate N words at one time
+                       int mtNext = mt[0];
+                       for (int k = 0; k < N - M; ++k) {
+                               int mtCurr = mtNext;
+                               mtNext = mt[k + 1];
+                               y = (mtCurr & 0x80000000) | (mtNext & 
0x7fffffff);
+                               mt[k] = mt[k + M] ^ (y >>> 1) ^ MAG01[y & 0x1];
+                       }
+                       for (int k = N - M; k < N - 1; ++k) {
+                               int mtCurr = mtNext;
+                               mtNext = mt[k + 1];
+                               y = (mtCurr & 0x80000000) | (mtNext & 
0x7fffffff);
+                               mt[k] = mt[k + (M - N)] ^ (y >>> 1) ^ MAG01[y & 
0x1];
+                       }
+                       y = (mtNext & 0x80000000) | (mt[0] & 0x7fffffff);
+                       mt[N - 1] = mt[M - 1] ^ (y >>> 1) ^ MAG01[y & 0x1];

-  }
+                       mti = 0;
+               }

-  private static final int   N     = 624;
-  private static final int   M     = 397;
-  private static final int[] MAG01 = { 0x0, 0x9908b0df };
+               y = mt[mti++];

-  private int[] mt;
-  private int   mti;
+               // tempering
+               y ^= (y >>> 11);
+               y ^= (y <<   7) & 0x9d2c5680;
+               y ^= (y <<  15) & 0xefc60000;
+               y ^= (y >>> 18);

+               return y >>> (32 - bits);
+       }
 }

Copied: branches/freenet-jfk/test/freenet/support (from rev 14796, 
trunk/freenet/test/freenet/support)

Deleted: branches/freenet-jfk/test/freenet/support/Base64Test.java
===================================================================
--- trunk/freenet/test/freenet/support/Base64Test.java  2007-08-18 19:36:17 UTC 
(rev 14796)
+++ branches/freenet-jfk/test/freenet/support/Base64Test.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,159 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package freenet.support;
-
-import junit.framework.TestCase;
-import java.util.Arrays;
-
-/**
- * Test case for {@link freenet.support.Base64} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class Base64Test extends TestCase {
-       
-       /**
-        * Test the encode(byte[]) method
-        * against a well-known example
-        * (see http://en.wikipedia.org/wiki/Base_64 as reference)
-        * to verify if it encode works correctly.
-        */
-       public void testEncode() {
-               String toEncode = "Man is distinguished, not only by his 
reason, but by this singular " +
-                               "passion from other animals, which is a lust of 
the mind, that by a perseverance " +
-                               "of delight in the continued and indefatigable 
generation of knowledge, exceeds " +
-                               "the short vehemence of any carnal pleasure.";
-               String expectedResult = 
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ" +
-                               
"1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
 +
-                               
"x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY"
 +
-                               
"29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRz"
 +
-                               
"IHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4";
-               byte[] aByteArrayToEncode = toEncode.getBytes();
-               assertEquals(Base64.encode(aByteArrayToEncode),expectedResult);
-       }
-       
-       /**
-        * Test the decode(String) method
-        * against a well-known example
-        * (see http://en.wikipedia.org/wiki/Base_64 as reference)
-        * to verify if it decode an already encoded string correctly.
-        */     
-       public void testDecode() {
-               String toDecode = 
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ" +
-                               
"1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
 +
-                               
"x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY"
 +
-                               
"29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRz"
 +
-                               
"IHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=";
-               String expectedResult = "Man is distinguished, not only by his 
reason, but by this singular " +
-                               "passion from other animals, which is a lust of 
the mind, that by a perseverance " +
-                               "of delight in the continued and indefatigable 
generation of knowledge, exceeds " +
-                               "the short vehemence of any carnal pleasure.";
-               try {
-                       String decodedString = new 
String(Base64.decode(toDecode));
-                       assertEquals(decodedString,expectedResult);
-               } catch (IllegalBase64Exception aException) {
-                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
-       }
-       
-       /**
-        * Test encode(byte[] in)
-        * and decode(String inStr) methods,
-        * to verify if they work correctly together.
-        * It compares the string before encoding
-        * and with the one after decoding.
-        */
-       public void testEncodeDecode() {
-               byte[] bytesDecoded;
-               byte[] bytesToEncode = new byte[5];
-               
-               //byte upper bound
-               bytesToEncode[0] = 127;
-               bytesToEncode[1] = 64;
-               bytesToEncode[2] = 0;
-               bytesToEncode[3] = -64;
-               //byte lower bound
-               bytesToEncode[4] = -128;        
-               
-               String aBase64EncodedString = Base64.encode(bytesToEncode);
-               
-               try {
-                       bytesDecoded = Base64.decode(aBase64EncodedString);
-                       assertTrue(Arrays.equals(bytesToEncode,bytesDecoded)); 
} 
-               catch (IllegalBase64Exception aException) {
-                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
-       }
-       
-       /**
-        * Test the encode(String,boolean)
-        * method to verify if the padding
-        * character '=' is correctly placed.
-        */
-       public void testEncodePadding() {
-               byte[][] methodBytesArray = {
-                               //three byte Array -> no padding char expected
-                               {4,4,4},                
-                               //two byte Array -> one padding char expected
-                               {4,4},          
-                               //one byte Array -> two padding-chars expected  
-                               {4}};           
-               String encoded;
-               
-               for (int i = 0; i<methodBytesArray.length; i++) {
-                       encoded = Base64.encode(methodBytesArray[i],true);
-                       if (i == 0)
-                               //no occurrences expected
-                               assertEquals(encoded.indexOf('='),-1);
-                       else
-                               
assertEquals(encoded.indexOf('='),encoded.length()-i);
-               }
-       }
-       
-       /**
-        * Test if the decode(String) method
-        * raise correctly an exception when
-        * providing a string with non-Base64
-        * characters.
-        */
-       public void testIllegalBaseCharacter() {
-//             TODO: check many other possibile cases!
-               String illegalCharString = "abcd=fghilmn";
-               try {
-                       Base64.decode(illegalCharString);
-                       fail("Expected IllegalBase64Exception not thrown"); }
-               catch (IllegalBase64Exception exception) {
-                       assertSame("illegal Base64 
character",exception.getMessage()); }
-       }
-       
-       /**
-        * Test if the decode(String) method
-        * raise correctly an exception when
-        * providing a string with a 
-        * wrong Base64 length.
-        * (as we can consider not-padded strings too,
-        *  the only wrong lengths are the ones
-        *  where -> number MOD 4 = 1).
-        */
-       public void testIllegalBaseLength() {
-               //most interesting case
-               String illegalLengthString = "a";
-               try {
-                       Base64.decode(illegalLengthString);
-                       fail("Expected IllegalBase64Exception not thrown"); }
-               catch (IllegalBase64Exception exception) {
-                       assertSame("illegal Base64 
length",exception.getMessage()); }
-       }
-}
\ No newline at end of file

Copied: branches/freenet-jfk/test/freenet/support/Base64Test.java (from rev 
14796, trunk/freenet/test/freenet/support/Base64Test.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/Base64Test.java                   
        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/Base64Test.java   2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,159 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package freenet.support;
+
+import junit.framework.TestCase;
+import java.util.Arrays;
+
+/**
+ * Test case for {@link freenet.support.Base64} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class Base64Test extends TestCase {
+       
+       /**
+        * Test the encode(byte[]) method
+        * against a well-known example
+        * (see http://en.wikipedia.org/wiki/Base_64 as reference)
+        * to verify if it encode works correctly.
+        */
+       public void testEncode() {
+               String toEncode = "Man is distinguished, not only by his 
reason, but by this singular " +
+                               "passion from other animals, which is a lust of 
the mind, that by a perseverance " +
+                               "of delight in the continued and indefatigable 
generation of knowledge, exceeds " +
+                               "the short vehemence of any carnal pleasure.";
+               String expectedResult = 
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ" +
+                               
"1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
 +
+                               
"x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY"
 +
+                               
"29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRz"
 +
+                               
"IHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4";
+               byte[] aByteArrayToEncode = toEncode.getBytes();
+               assertEquals(Base64.encode(aByteArrayToEncode),expectedResult);
+       }
+       
+       /**
+        * Test the decode(String) method
+        * against a well-known example
+        * (see http://en.wikipedia.org/wiki/Base_64 as reference)
+        * to verify if it decode an already encoded string correctly.
+        */     
+       public void testDecode() {
+               String toDecode = 
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ" +
+                               
"1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIG"
 +
+                               
"x1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY"
 +
+                               
"29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRz"
 +
+                               
"IHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=";
+               String expectedResult = "Man is distinguished, not only by his 
reason, but by this singular " +
+                               "passion from other animals, which is a lust of 
the mind, that by a perseverance " +
+                               "of delight in the continued and indefatigable 
generation of knowledge, exceeds " +
+                               "the short vehemence of any carnal pleasure.";
+               try {
+                       String decodedString = new 
String(Base64.decode(toDecode));
+                       assertEquals(decodedString,expectedResult);
+               } catch (IllegalBase64Exception aException) {
+                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
+       }
+       
+       /**
+        * Test encode(byte[] in)
+        * and decode(String inStr) methods,
+        * to verify if they work correctly together.
+        * It compares the string before encoding
+        * and with the one after decoding.
+        */
+       public void testEncodeDecode() {
+               byte[] bytesDecoded;
+               byte[] bytesToEncode = new byte[5];
+               
+               //byte upper bound
+               bytesToEncode[0] = 127;
+               bytesToEncode[1] = 64;
+               bytesToEncode[2] = 0;
+               bytesToEncode[3] = -64;
+               //byte lower bound
+               bytesToEncode[4] = -128;        
+               
+               String aBase64EncodedString = Base64.encode(bytesToEncode);
+               
+               try {
+                       bytesDecoded = Base64.decode(aBase64EncodedString);
+                       assertTrue(Arrays.equals(bytesToEncode,bytesDecoded)); 
} 
+               catch (IllegalBase64Exception aException) {
+                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
+       }
+       
+       /**
+        * Test the encode(String,boolean)
+        * method to verify if the padding
+        * character '=' is correctly placed.
+        */
+       public void testEncodePadding() {
+               byte[][] methodBytesArray = {
+                               //three byte Array -> no padding char expected
+                               {4,4,4},                
+                               //two byte Array -> one padding char expected
+                               {4,4},          
+                               //one byte Array -> two padding-chars expected  
+                               {4}};           
+               String encoded;
+               
+               for (int i = 0; i<methodBytesArray.length; i++) {
+                       encoded = Base64.encode(methodBytesArray[i],true);
+                       if (i == 0)
+                               //no occurrences expected
+                               assertEquals(encoded.indexOf('='),-1);
+                       else
+                               
assertEquals(encoded.indexOf('='),encoded.length()-i);
+               }
+       }
+       
+       /**
+        * Test if the decode(String) method
+        * raise correctly an exception when
+        * providing a string with non-Base64
+        * characters.
+        */
+       public void testIllegalBaseCharacter() {
+//             TODO: check many other possibile cases!
+               String illegalCharString = "abcd=fghilmn";
+               try {
+                       Base64.decode(illegalCharString);
+                       fail("Expected IllegalBase64Exception not thrown"); }
+               catch (IllegalBase64Exception exception) {
+                       assertSame("illegal Base64 
character",exception.getMessage()); }
+       }
+       
+       /**
+        * Test if the decode(String) method
+        * raise correctly an exception when
+        * providing a string with a 
+        * wrong Base64 length.
+        * (as we can consider not-padded strings too,
+        *  the only wrong lengths are the ones
+        *  where -> number MOD 4 = 1).
+        */
+       public void testIllegalBaseLength() {
+               //most interesting case
+               String illegalLengthString = "a";
+               try {
+                       Base64.decode(illegalLengthString);
+                       fail("Expected IllegalBase64Exception not thrown"); }
+               catch (IllegalBase64Exception exception) {
+                       assertSame("illegal Base64 
length",exception.getMessage()); }
+       }
+}
\ No newline at end of file

Deleted: branches/freenet-jfk/test/freenet/support/BitArrayTest.java
===================================================================
--- trunk/freenet/test/freenet/support/BitArrayTest.java        2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/BitArrayTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,184 +0,0 @@
-/* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.BitArray} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class BitArrayTest extends TestCase {
-
-       private final int sampleBitsNumber = 10;
-       private final int oneByteBits = 8;
-       
-       /**
-        * Creates a BitArray with all values set to the
-        * boolean argument
-        * @param arraySize the size of the BitArray
-        * @param value the value for each bit
-        * @return the set BitArray
-        */
-       private BitArray createAllEqualsBitArray(int arraySize, boolean value) {
-               BitArray methodBitArray = new BitArray(arraySize);
-               //setting all bits true
-               for (int i=0; i<methodBitArray.getSize();i++)
-                       methodBitArray.setBit(i,value);
-               return methodBitArray;
-       }
-       
-       /**
-        * Creates a String of toRepeat String as long as needed
-        * @param stringSize length requested
-        * @param toRepeat String to repeat stringSize times
-        * @return the String of toRepeat
-        */
-       private String createAllOneString(int stringSize, String toRepeat) {
-               StringBuffer methodStringBuffer = new StringBuffer();
-               for (int i=0;i<stringSize;i++)
-                       methodStringBuffer.append(toRepeat);
-               return methodStringBuffer.toString();
-       }
-
-       /**
-        * Tests BitArray(int) constructor
-        * and verifies if the instance is
-        * well created (all values must be
-        * readables and false, and the length
-        * has to be correct)
-        */
-       public void testBitArray_int() {
-               BitArray methodBitArray = new BitArray(sampleBitsNumber);
-               for(int i=0;i<sampleBitsNumber;i++)
-                       assertFalse(methodBitArray.bitAt(i));
-               assertEquals(methodBitArray.getSize(),sampleBitsNumber);
-       }
-       
-       /**
-        * Tests toString() method
-        * creating BitArrays with same value bits.
-        */
-       public void testToStringAllEquals() {
-               BitArray methodBitArray = 
createAllEqualsBitArray(sampleBitsNumber,true);
-               String expectedString = 
createAllOneString(sampleBitsNumber,"1");
-               assertEquals(methodBitArray.toString(),expectedString);
-               methodBitArray = 
createAllEqualsBitArray(sampleBitsNumber,false);
-               expectedString = createAllOneString(sampleBitsNumber,"0");
-               assertEquals(methodBitArray.toString(),expectedString);
-       }
-       
-       /**
-        * Tests toString() method
-        * with a BitArray with size zero.
-        */
-       public void testToStringEmpty() {
-               BitArray methodBitArray = new BitArray(0);
-               assertEquals(methodBitArray.toString().length(),0);
-       }
-       
-       /**
-        * Tests setBit(int,boolean) method
-        * trying to set a bit out of bounds
-        */
-       public void testSetBit_OutOfBounds() {
-               BitArray methodBitArray = new BitArray(sampleBitsNumber);
-               try {
-                       methodBitArray.setBit(sampleBitsNumber,true); 
-                       //fail("Expected Exception Error Not Thrown!");
-                       } 
-               catch (ArrayIndexOutOfBoundsException anException) { 
-                       assertNotNull(anException); }
-       }
-
-       /**
-        * Tests setBit(int,boolean) method
-        * using getAt(int) to verify if they are
-        * consistent.
-        */
-       public void testSetAndGetBit() {
-               BitArray methodBitArray = new BitArray(sampleBitsNumber);
-               //setting true even bits
-               for (int i=0; i<methodBitArray.getSize();i=i+2)
-                       methodBitArray.setBit(i,true);
-               //checking even bits
-               for (int i=0; i<methodBitArray.getSize();i=i+2)
-                       assertTrue(methodBitArray.bitAt(i));
-               //checking odd bits
-               for (int i=1; i<methodBitArray.getSize();i=i+2)
-                       assertFalse(methodBitArray.bitAt(i));
-       }
-
-       /**
-        * Tests unsignedByteToInt(byte) method
-        * trying it correctness for every possible (i.e. 256) 
-        * byte value
-        */
-       public void testUnsignedByteToInt() {
-               byte sampleByte;
-               for (int i =0; i<256; i++) {
-                       sampleByte = (byte)i;
-                       assertEquals(i,BitArray.unsignedByteToInt(sampleByte)); 
}
-       }
-
-       /**
-        * Tests getSize() method
-        */
-       public void testGetSize() {
-               BitArray methodBitArray = new BitArray(0);
-               assertEquals(methodBitArray.getSize(),0);
-               methodBitArray = createAllEqualsBitArray(sampleBitsNumber,true);
-               assertEquals(methodBitArray.getSize(),sampleBitsNumber);
-       }
-
-       /**
-        * Tests setAllOnes() method
-        * comparing the result to
-        * a BitArray with already all ones
-        * set.
-        */
-       public void testSetAllOnes() {
-               BitArray methodBitArray = 
createAllEqualsBitArray(sampleBitsNumber,true);
-               BitArray methodBitArrayToVerify = new 
BitArray(sampleBitsNumber);
-               methodBitArrayToVerify.setAllOnes();
-               assertEquals(methodBitArray,methodBitArrayToVerify);
-       }
-
-       /**
-        * Tests firstOne() method
-        * far all possible first-one-position
-        * in a BitArray with as many bits as in
-        * a single byte
-        */
-       public void testFirstOne() {
-               BitArray methodBitArray = new BitArray(oneByteBits);
-               //only one "1"
-               for(int i=0; i<oneByteBits; i++) {
-                       methodBitArray = new BitArray(oneByteBits);
-                       methodBitArray.setBit(i,true);
-                       assertEquals(methodBitArray.firstOne(),i);}
-               
-               methodBitArray.setAllOnes();
-               //augmenting zeros
-               for(int i=0; i<oneByteBits-1; i++) {
-                       methodBitArray.setBit(i,false);
-                       assertEquals(methodBitArray.firstOne(),i+1);}
-               //all zeros
-               methodBitArray.setBit(oneByteBits-1,false);
-               assertEquals(methodBitArray.firstOne(),-1);
-       }
-
-}

Copied: branches/freenet-jfk/test/freenet/support/BitArrayTest.java (from rev 
14796, trunk/freenet/test/freenet/support/BitArrayTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/BitArrayTest.java                 
        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/BitArrayTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,184 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.BitArray} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class BitArrayTest extends TestCase {
+
+       private final int sampleBitsNumber = 10;
+       private final int oneByteBits = 8;
+       
+       /**
+        * Creates a BitArray with all values set to the
+        * boolean argument
+        * @param arraySize the size of the BitArray
+        * @param value the value for each bit
+        * @return the set BitArray
+        */
+       private BitArray createAllEqualsBitArray(int arraySize, boolean value) {
+               BitArray methodBitArray = new BitArray(arraySize);
+               //setting all bits true
+               for (int i=0; i<methodBitArray.getSize();i++)
+                       methodBitArray.setBit(i,value);
+               return methodBitArray;
+       }
+       
+       /**
+        * Creates a String of toRepeat String as long as needed
+        * @param stringSize length requested
+        * @param toRepeat String to repeat stringSize times
+        * @return the String of toRepeat
+        */
+       private String createAllOneString(int stringSize, String toRepeat) {
+               StringBuffer methodStringBuffer = new StringBuffer();
+               for (int i=0;i<stringSize;i++)
+                       methodStringBuffer.append(toRepeat);
+               return methodStringBuffer.toString();
+       }
+
+       /**
+        * Tests BitArray(int) constructor
+        * and verifies if the instance is
+        * well created (all values must be
+        * readables and false, and the length
+        * has to be correct)
+        */
+       public void testBitArray_int() {
+               BitArray methodBitArray = new BitArray(sampleBitsNumber);
+               for(int i=0;i<sampleBitsNumber;i++)
+                       assertFalse(methodBitArray.bitAt(i));
+               assertEquals(methodBitArray.getSize(),sampleBitsNumber);
+       }
+       
+       /**
+        * Tests toString() method
+        * creating BitArrays with same value bits.
+        */
+       public void testToStringAllEquals() {
+               BitArray methodBitArray = 
createAllEqualsBitArray(sampleBitsNumber,true);
+               String expectedString = 
createAllOneString(sampleBitsNumber,"1");
+               assertEquals(methodBitArray.toString(),expectedString);
+               methodBitArray = 
createAllEqualsBitArray(sampleBitsNumber,false);
+               expectedString = createAllOneString(sampleBitsNumber,"0");
+               assertEquals(methodBitArray.toString(),expectedString);
+       }
+       
+       /**
+        * Tests toString() method
+        * with a BitArray with size zero.
+        */
+       public void testToStringEmpty() {
+               BitArray methodBitArray = new BitArray(0);
+               assertEquals(methodBitArray.toString().length(),0);
+       }
+       
+       /**
+        * Tests setBit(int,boolean) method
+        * trying to set a bit out of bounds
+        */
+       public void testSetBit_OutOfBounds() {
+               BitArray methodBitArray = new BitArray(sampleBitsNumber);
+               try {
+                       methodBitArray.setBit(sampleBitsNumber,true); 
+                       //fail("Expected Exception Error Not Thrown!");
+                       } 
+               catch (ArrayIndexOutOfBoundsException anException) { 
+                       assertNotNull(anException); }
+       }
+
+       /**
+        * Tests setBit(int,boolean) method
+        * using getAt(int) to verify if they are
+        * consistent.
+        */
+       public void testSetAndGetBit() {
+               BitArray methodBitArray = new BitArray(sampleBitsNumber);
+               //setting true even bits
+               for (int i=0; i<methodBitArray.getSize();i=i+2)
+                       methodBitArray.setBit(i,true);
+               //checking even bits
+               for (int i=0; i<methodBitArray.getSize();i=i+2)
+                       assertTrue(methodBitArray.bitAt(i));
+               //checking odd bits
+               for (int i=1; i<methodBitArray.getSize();i=i+2)
+                       assertFalse(methodBitArray.bitAt(i));
+       }
+
+       /**
+        * Tests unsignedByteToInt(byte) method
+        * trying it correctness for every possible (i.e. 256) 
+        * byte value
+        */
+       public void testUnsignedByteToInt() {
+               byte sampleByte;
+               for (int i =0; i<256; i++) {
+                       sampleByte = (byte)i;
+                       assertEquals(i,BitArray.unsignedByteToInt(sampleByte)); 
}
+       }
+
+       /**
+        * Tests getSize() method
+        */
+       public void testGetSize() {
+               BitArray methodBitArray = new BitArray(0);
+               assertEquals(methodBitArray.getSize(),0);
+               methodBitArray = createAllEqualsBitArray(sampleBitsNumber,true);
+               assertEquals(methodBitArray.getSize(),sampleBitsNumber);
+       }
+
+       /**
+        * Tests setAllOnes() method
+        * comparing the result to
+        * a BitArray with already all ones
+        * set.
+        */
+       public void testSetAllOnes() {
+               BitArray methodBitArray = 
createAllEqualsBitArray(sampleBitsNumber,true);
+               BitArray methodBitArrayToVerify = new 
BitArray(sampleBitsNumber);
+               methodBitArrayToVerify.setAllOnes();
+               assertEquals(methodBitArray,methodBitArrayToVerify);
+       }
+
+       /**
+        * Tests firstOne() method
+        * far all possible first-one-position
+        * in a BitArray with as many bits as in
+        * a single byte
+        */
+       public void testFirstOne() {
+               BitArray methodBitArray = new BitArray(oneByteBits);
+               //only one "1"
+               for(int i=0; i<oneByteBits; i++) {
+                       methodBitArray = new BitArray(oneByteBits);
+                       methodBitArray.setBit(i,true);
+                       assertEquals(methodBitArray.firstOne(),i);}
+               
+               methodBitArray.setAllOnes();
+               //augmenting zeros
+               for(int i=0; i<oneByteBits-1; i++) {
+                       methodBitArray.setBit(i,false);
+                       assertEquals(methodBitArray.firstOne(),i+1);}
+               //all zeros
+               methodBitArray.setBit(oneByteBits-1,false);
+               assertEquals(methodBitArray.firstOne(),-1);
+       }
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/HTMLEncoderDecoderTest.java
===================================================================
--- trunk/freenet/test/freenet/support/HTMLEncoderDecoderTest.java      
2007-08-18 19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/HTMLEncoderDecoderTest.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,129 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-import freenet.utils.UTFUtil;
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.HTMLEncoder} and 
- * {@link freenet.support.HTMLDecoder} classes.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class HTMLEncoderDecoderTest extends TestCase {
-       
-       private static final char WHITESPACE = '\u0020';
-       private static final char TAB = '\t';
-       private static final char UNIX_NEWLINE = '\n';
-       private static final char MAC_NEWLINE = '\r';
-       private static final char CONTROL = '\u000c';
-       private static final char ZEROWIDTHSPACE = '\u200b';
-       
-       /**
-        * Tests decode(String) method
-        * trying to decode entity by entity
-        */
-       public void testDecodeSingleEntities() {
-               for (int i =0; i<UTFUtil.HTML_ENTITIES_UTF.length; i++)
-                       
assertEquals(HTMLDecoder.decode(UTFUtil.HTML_ENTITIES_UTF[i][1]),UTFUtil.HTML_ENTITIES_UTF[i][0]);
-       }
-       
-       /**
-        * Tests decode(String) method
-        * trying to decode a long String
-        * with all possible entity appended
-        */
-       public void testDecodeAppendedEntities() {
-               StringBuffer toDecode = new StringBuffer();
-               StringBuffer expected = new StringBuffer();
-               for (int i =0; i<UTFUtil.HTML_ENTITIES_UTF.length; i++) {
-                       toDecode.append(UTFUtil.HTML_ENTITIES_UTF[i][1]);
-                       expected.append(UTFUtil.HTML_ENTITIES_UTF[i][0]);
-               }
-               
assertEquals(HTMLDecoder.decode(toDecode.toString()),expected.toString());
-       }
-       
-       /**
-        * Tests decode(String) method
-        * trying to decode incomplete entities.
-        * The incomplete entity must remain
-        * the same as before encoding
-        */
-       public void testDecodeIncomplete() {
-               //without ending semicolon
-               assertEquals(HTMLDecoder.decode("&Phi"),"&Phi");
-               //an Entity without a char, 
-               //which means a not existing Entity 
-               assertEquals(HTMLDecoder.decode("&Ph;"),"&Ph;");
-               //without ash
-               assertEquals(HTMLDecoder.decode("&1234;"),"&1234;");
-               //without ampersand
-               assertEquals(HTMLDecoder.decode("Phi;"),"Phi;");
-               //emtpy String
-               assertEquals(HTMLDecoder.decode(""),"");
-       }
-       
-       /**
-        * Tests compact(String) method
-        * trying to compact String with
-        * repeated whitespaces of every kind
-        * (e.g. tabs,newline,space).
-        */
-       public void testCompactRepeated(){
-               StringBuffer strBuffer[] = new StringBuffer[6];
-               for (int i = 0; i < strBuffer.length; i++)
-                       strBuffer[i] = new StringBuffer();
-               
-               for (int i=0;i<100;i++) {
-                       //adding different "whitespaces"
-                       strBuffer[0].append(WHITESPACE);
-                       strBuffer[1].append(TAB);
-                       strBuffer[2].append(UNIX_NEWLINE);
-                       strBuffer[3].append(MAC_NEWLINE);
-                       strBuffer[4].append(CONTROL);
-                       strBuffer[5].append(ZEROWIDTHSPACE);
-                       
-                       for (int j = 0; j < strBuffer.length; j++)
-                               assertEquals(" ",
-                                               
HTMLDecoder.compact(strBuffer[j].toString()));
-               }
-       }
-       
-       /**
-        * Tests compact(String) method
-        * with each kind of "whitespace"
-        */
-       public void testCompactMixed(){
-               String toCompact = "\u0020"+"\t"+"\n"+"\r"+"\u200b"+"\u000c";
-               assertEquals(HTMLDecoder.compact(toCompact)," ");
-       }
-       
-       /**
-        * Tests isWhiteSpace() method
-        * against all possible HTML white space
-        * type
-        */
-       public void testIsWhiteSpace() {
-               assertTrue(HTMLDecoder.isWhitespace(WHITESPACE));
-               assertTrue(HTMLDecoder.isWhitespace(TAB));
-               assertTrue(HTMLDecoder.isWhitespace(UNIX_NEWLINE));
-               assertTrue(HTMLDecoder.isWhitespace(MAC_NEWLINE));
-               assertTrue(HTMLDecoder.isWhitespace(CONTROL));
-               assertTrue(HTMLDecoder.isWhitespace(ZEROWIDTHSPACE));
-       }
-
-
-}

Copied: branches/freenet-jfk/test/freenet/support/HTMLEncoderDecoderTest.java 
(from rev 14796, trunk/freenet/test/freenet/support/HTMLEncoderDecoderTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/HTMLEncoderDecoderTest.java       
                        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/HTMLEncoderDecoderTest.java       
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,129 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+import freenet.utils.UTFUtil;
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.HTMLEncoder} and 
+ * {@link freenet.support.HTMLDecoder} classes.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class HTMLEncoderDecoderTest extends TestCase {
+       
+       private static final char WHITESPACE = '\u0020';
+       private static final char TAB = '\t';
+       private static final char UNIX_NEWLINE = '\n';
+       private static final char MAC_NEWLINE = '\r';
+       private static final char CONTROL = '\u000c';
+       private static final char ZEROWIDTHSPACE = '\u200b';
+       
+       /**
+        * Tests decode(String) method
+        * trying to decode entity by entity
+        */
+       public void testDecodeSingleEntities() {
+               for (int i =0; i<UTFUtil.HTML_ENTITIES_UTF.length; i++)
+                       
assertEquals(HTMLDecoder.decode(UTFUtil.HTML_ENTITIES_UTF[i][1]),UTFUtil.HTML_ENTITIES_UTF[i][0]);
+       }
+       
+       /**
+        * Tests decode(String) method
+        * trying to decode a long String
+        * with all possible entity appended
+        */
+       public void testDecodeAppendedEntities() {
+               StringBuffer toDecode = new StringBuffer();
+               StringBuffer expected = new StringBuffer();
+               for (int i =0; i<UTFUtil.HTML_ENTITIES_UTF.length; i++) {
+                       toDecode.append(UTFUtil.HTML_ENTITIES_UTF[i][1]);
+                       expected.append(UTFUtil.HTML_ENTITIES_UTF[i][0]);
+               }
+               
assertEquals(HTMLDecoder.decode(toDecode.toString()),expected.toString());
+       }
+       
+       /**
+        * Tests decode(String) method
+        * trying to decode incomplete entities.
+        * The incomplete entity must remain
+        * the same as before encoding
+        */
+       public void testDecodeIncomplete() {
+               //without ending semicolon
+               assertEquals(HTMLDecoder.decode("&Phi"),"&Phi");
+               //an Entity without a char, 
+               //which means a not existing Entity 
+               assertEquals(HTMLDecoder.decode("&Ph;"),"&Ph;");
+               //without ash
+               assertEquals(HTMLDecoder.decode("&1234;"),"&1234;");
+               //without ampersand
+               assertEquals(HTMLDecoder.decode("Phi;"),"Phi;");
+               //emtpy String
+               assertEquals(HTMLDecoder.decode(""),"");
+       }
+       
+       /**
+        * Tests compact(String) method
+        * trying to compact String with
+        * repeated whitespaces of every kind
+        * (e.g. tabs,newline,space).
+        */
+       public void testCompactRepeated(){
+               StringBuffer strBuffer[] = new StringBuffer[6];
+               for (int i = 0; i < strBuffer.length; i++)
+                       strBuffer[i] = new StringBuffer();
+               
+               for (int i=0;i<100;i++) {
+                       //adding different "whitespaces"
+                       strBuffer[0].append(WHITESPACE);
+                       strBuffer[1].append(TAB);
+                       strBuffer[2].append(UNIX_NEWLINE);
+                       strBuffer[3].append(MAC_NEWLINE);
+                       strBuffer[4].append(CONTROL);
+                       strBuffer[5].append(ZEROWIDTHSPACE);
+                       
+                       for (int j = 0; j < strBuffer.length; j++)
+                               assertEquals(" ",
+                                               
HTMLDecoder.compact(strBuffer[j].toString()));
+               }
+       }
+       
+       /**
+        * Tests compact(String) method
+        * with each kind of "whitespace"
+        */
+       public void testCompactMixed(){
+               String toCompact = "\u0020"+"\t"+"\n"+"\r"+"\u200b"+"\u000c";
+               assertEquals(HTMLDecoder.compact(toCompact)," ");
+       }
+       
+       /**
+        * Tests isWhiteSpace() method
+        * against all possible HTML white space
+        * type
+        */
+       public void testIsWhiteSpace() {
+               assertTrue(HTMLDecoder.isWhitespace(WHITESPACE));
+               assertTrue(HTMLDecoder.isWhitespace(TAB));
+               assertTrue(HTMLDecoder.isWhitespace(UNIX_NEWLINE));
+               assertTrue(HTMLDecoder.isWhitespace(MAC_NEWLINE));
+               assertTrue(HTMLDecoder.isWhitespace(CONTROL));
+               assertTrue(HTMLDecoder.isWhitespace(ZEROWIDTHSPACE));
+       }
+
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java
===================================================================
--- trunk/freenet/test/freenet/support/HTMLNodeTest.java        2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,309 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.HTMLNode} class.
- *
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class HTMLNodeTest extends TestCase {
-       
-       private HTMLNode exampleNode;
-       
-       //example node name that includes a not ASCII char [greek alpha]
-       private static final String SAMPLE_NODE_NAME = "s\u03b1mpleNode";
-       
-       //example node attribute that includes a not ASCII char [greek beta]
-       private static final String SAMPLE_ATTRIBUTE_NAME = 
"sampleAttri\u03b2uteName";
-       
-       //example node attribute value that includes a not ASCII char [greek 
epsilon]
-       private static final String SAMPLE_ATTRIBUTE_VALUE = 
"sampleAttribut\u03b5Value";
-       
-       //example node content that includes a not ASCII char [greek omicron]
-       private static final String SAMPLE_NODE_CONTENT = 
"sampleNodeC\u03bfntent";
-       
-       protected void setUp() throws Exception {
-               super.setUp();
-               exampleNode = new HTMLNode(SAMPLE_NODE_NAME);
-       }
-       
-       /**
-        * Test HTMLNode(String,String,String,String) constructor
-        * using non-ASCII chars
-        */
-       public void testNotAsciiHTMLNode_StringStringStringString() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
-                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
-                               SAMPLE_NODE_CONTENT);
-               assertFalse(exampleNode.children.contains(methodHTMLNode));
-               exampleNode.addChild(methodHTMLNode);
-               assertTrue(exampleNode.children.contains(methodHTMLNode));
-       }
-       
-       /**
-        * Tests addAttribute(String,String) method
-        * adding the same attribute many times
-        * and verifying it keeps only one
-        * reference to it.
-        */
-       public void testSameAttributeManyTimes() {
-               int times = 100;
-               String methodAttributeName = "exampleAttributeName";
-               String methodAttributeValue = "exampleAttributeValue";
-               for (int i = 0; i < times; i++) {
-                       
exampleNode.addAttribute(methodAttributeName,methodAttributeValue);
-                       assertEquals(exampleNode.getAttributes().size(),1);
-               }
-       }
-       
-       /**
-        * Tests addChild(HTMLNode) method
-        * adding the Node itself as its
-        * child. The method should rise an exception
-        */
-       public void testAddChildUsingTheNodeItselfAsChild() {
-               try {
-                       exampleNode.addChild(exampleNode);
-               fail("Expected Exception Error Not Thrown!"); } 
-               catch (IllegalArgumentException anException) {
-                       assertNotNull(anException); }
-       }
-       
-       /**
-        * Tests addChildren(HTMLNode[]) method
-        * adding the Node itself as its
-        * child. The method should rise an exception
-        */
-       public void testAddChildrenUsingTheNodeItselfAsChild() {
-               HTMLNode[] methodHTMLNodesArray = {new 
HTMLNode(SAMPLE_NODE_NAME),
-                                                                               
   exampleNode,
-                                                                               
   new HTMLNode(SAMPLE_NODE_NAME+"1")};
-               try {
-                       exampleNode.addChildren(methodHTMLNodesArray);
-                       fail("Expected Exception Error Not Thrown!"); } 
-               catch (IllegalArgumentException anException) {
-                       assertNotNull(anException); }
-       }
-       
-       /**
-        * Tests addChild(String) method
-        * using the same name every time
-        * and verifying that a real new 
-        * HTML is always added.
-        */
-       public void testAddChildSameName() {
-               int times = 100;
-               for (int i = 1; i<=times; i++) {
-                       exampleNode.addChild(SAMPLE_NODE_NAME);
-                       assertEquals(exampleNode.children.size(),i);
-               }
-       }
-       
-       /**
-        * Tests addChild(HTMLNode) method
-        * verifying the behavior when adding
-        * the same HTMLNode instance two times.
-        * It should raise an IllegalArgument exception.
-        */
-       public void testAddChildSameObject() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
-               exampleNode.addChild(methodHTMLNode);
-               try {
-                       exampleNode.addChild(methodHTMLNode);
-                       fail("Expected Exception Error Not Thrown!"); } 
-               catch (IllegalArgumentException anException) {
-                       assertNotNull(anException); }
-       }
-       
-       /**
-        * Tests addChildren(HTMLNode[]) method
-        * verifying the behavior when adding
-        * the same HTMLNode instance two times.
-        */
-       public void testAddChildrenSameObject() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
-               HTMLNode[] methodHTMLNodesArray = {methodHTMLNode,
-                                                                               
   methodHTMLNode};
-               try {
-                       exampleNode.addChildren(methodHTMLNodesArray);
-                       fail("Expected Exception Error Not Thrown!"); } 
-               catch (IllegalArgumentException anException) {
-                       assertNotNull(anException); }
-       }
-       
-       /**
-        * Tests getContent() method using
-        * common sample HTMLNode, and "#"
-        * "%" named nodes
-        */
-       public void testGetContent() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
-               assertNull(methodHTMLNode.getContent());
-               
-               methodHTMLNode = new 
HTMLNode(SAMPLE_NODE_NAME,SAMPLE_NODE_CONTENT);
-               //since the HTMLNode name is not "#", or "%",
-               //the content will be a new child with the "#" name
-               assertEquals(SAMPLE_NODE_CONTENT,
-                               
((HTMLNode)(methodHTMLNode.children.get(0))).getContent());
-               assertNull(methodHTMLNode.getContent());
-               
-               methodHTMLNode = new HTMLNode("#",SAMPLE_NODE_CONTENT);
-               assertEquals(SAMPLE_NODE_CONTENT,
-                               methodHTMLNode.getContent());
-               methodHTMLNode = new HTMLNode("%",SAMPLE_NODE_CONTENT);
-               assertEquals(SAMPLE_NODE_CONTENT,
-                               methodHTMLNode.getContent());
-       }
-       
-       /**
-        * Fetches the first line of a String
-        * @param aString the String to consider
-        * @return the first line of the String
-        */
-       private String readFirstLine(String aString) {
-               int newLineIndex = aString.indexOf("\n");
-               if ( newLineIndex == -1)
-                       return aString;
-               return aString.substring(0,newLineIndex);
-       }
-       
-       /**
-        * Tests generate() method with a
-        * HTMLNode with only the name.
-        * The resulting string should be in the form:
-        * <node_name />
-        */
-       public void testGenerate_fromHTMLNode_String() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
-               assertEquals(("<"+SAMPLE_NODE_NAME+" />").toLowerCase(),
-                                       methodHTMLNode.generate());
-       }
-       
-       /**
-        * Tests generate() method with a
-        * HTMLNode with the name and content.
-        * The resulting string should be in the form:
-        * <node_name>Node_Content</node_name>
-        */
-       public void testGenerate_fromHTMLNode_StringString() {
-               HTMLNode methodHTMLNode = new 
HTMLNode(SAMPLE_NODE_NAME,SAMPLE_NODE_CONTENT);
-               assertEquals(("<"+SAMPLE_NODE_NAME+">").toLowerCase() + 
-                                        SAMPLE_NODE_CONTENT +
-                                        
("</"+SAMPLE_NODE_NAME+">").toLowerCase(),
-                                       methodHTMLNode.generate());
-       }
-       
-       /**
-        * Tests generate() method with a
-        * HTMLNode with the name, an attribute and its value.
-        * The resulting string should be in the form:
-        * <node_name Attribute_Name="Attribute_Value" />
-        */
-       public void testGenerate_fromHTMLNode_StringStringString() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
-                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE);
-               assertEquals(("<"+SAMPLE_NODE_NAME+" ").toLowerCase() + 
-                                        SAMPLE_ATTRIBUTE_NAME + "=" +
-                                        "\""+SAMPLE_ATTRIBUTE_VALUE+"\""+
-                                        " />",
-                                       methodHTMLNode.generate());
-       }
-       
-       /**
-        * Tests generate() method with a
-        * HTMLNode with the name, an attribute and its value.
-        * The resulting string should be in the form:
-        * <node_name Attribute_Name="Attribute_Value">Node_Content</node_name>
-        */
-       public void testGenerate_fromHTMLNode_StringStringStringString() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
-                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
-                               SAMPLE_NODE_CONTENT);
-               assertEquals(generateFullNodeOutput(SAMPLE_NODE_NAME,
-                               SAMPLE_ATTRIBUTE_NAME, SAMPLE_ATTRIBUTE_VALUE, 
-                               SAMPLE_NODE_CONTENT),
-                                       methodHTMLNode.generate());
-       }
-       
-       /**
-        * Generates the correct output for the HTMLNode.generate() method
-        * when called from a single node having the specified parameters
-        * @param aName the HTMLNode name
-        * @param aAttributeName the HTMLNode attribute name
-        * @param aAttributeValue the HTMLNode attribute value
-        * @param aContent the HTMLNode content
-        * @return the correct output expected by HTMLNode.generate() method
-        */
-       private String generateFullNodeOutput(String aName, String 
aAttributeName, String aAttributeValue, String aContent) {
-               return ("<"+aName+" ").toLowerCase() + 
-               aAttributeName + "=" +
-                "\""+aAttributeValue+"\">" +
-                aContent +
-                ("</"+aName+">").toLowerCase();
-       }
-       
-       /**
-        * Tests generate() method with a
-        * HTMLNode that has a child.
-        * <node_name Attribute_Name="Attribute_Value">Node_Content
-        * <child_node_name 
child_Attribute_Name="child_Attribute_Value">child_Node_Content</child_node_name>
-        * </node_name>
-        */
-       public void testGenerate_HTMLNode_withChild() {
-               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
-                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
-                               SAMPLE_NODE_CONTENT);
-               HTMLNode methodHTMLNodeChild = new HTMLNode(SAMPLE_NODE_NAME,
-                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
-                               SAMPLE_NODE_CONTENT);
-               
-               methodHTMLNode.addChild(methodHTMLNodeChild);
-               
-               assertEquals(("<"+SAMPLE_NODE_NAME+" ").toLowerCase() + 
-                                SAMPLE_ATTRIBUTE_NAME + "=" +
-                                "\""+SAMPLE_ATTRIBUTE_VALUE+"\">" +
-                                SAMPLE_NODE_CONTENT +
-                                
-                                //child
-                                generateFullNodeOutput(SAMPLE_NODE_NAME,
-                                                       SAMPLE_ATTRIBUTE_NAME, 
SAMPLE_ATTRIBUTE_VALUE, 
-                                                       SAMPLE_NODE_CONTENT) +
-                                
-                                ("</"+SAMPLE_NODE_NAME+">").toLowerCase(),
-                                methodHTMLNode.generate());
-       }
-       
-       /**
-        * Tests HTMLDoctype.generate() method
-        * comparing the result with the expected
-        * String. It is useful for regression tests.
-        */
-       public void testHTMLDoctype_generate() {
-               String sampleDocType = "html";
-               String sampleSystemUri = "-//W3C//DTD XHTML 1.1//EN";
-               HTMLNode methodHTMLNodeDoc = new 
HTMLNode.HTMLDoctype(sampleDocType,sampleSystemUri);
-               methodHTMLNodeDoc.addChild(SAMPLE_NODE_NAME);
-               String generatedString = methodHTMLNodeDoc.generate();
-               //consider only the HTMLDocType generated text
-               assertEquals("<!DOCTYPE "+sampleDocType+" PUBLIC 
\""+sampleSystemUri+"\">",     
-                               readFirstLine(generatedString));
-               
-       }
-
-}

Copied: branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java (from rev 
14796, trunk/freenet/test/freenet/support/HTMLNodeTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java                 
        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,309 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.HTMLNode} class.
+ *
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class HTMLNodeTest extends TestCase {
+       
+       private HTMLNode exampleNode;
+       
+       //example node name that includes a not ASCII char [greek alpha]
+       private static final String SAMPLE_NODE_NAME = "s\u03b1mpleNode";
+       
+       //example node attribute that includes a not ASCII char [greek beta]
+       private static final String SAMPLE_ATTRIBUTE_NAME = 
"sampleAttri\u03b2uteName";
+       
+       //example node attribute value that includes a not ASCII char [greek 
epsilon]
+       private static final String SAMPLE_ATTRIBUTE_VALUE = 
"sampleAttribut\u03b5Value";
+       
+       //example node content that includes a not ASCII char [greek omicron]
+       private static final String SAMPLE_NODE_CONTENT = 
"sampleNodeC\u03bfntent";
+       
+       protected void setUp() throws Exception {
+               super.setUp();
+               exampleNode = new HTMLNode(SAMPLE_NODE_NAME);
+       }
+       
+       /**
+        * Test HTMLNode(String,String,String,String) constructor
+        * using non-ASCII chars
+        */
+       public void testNotAsciiHTMLNode_StringStringStringString() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_NODE_CONTENT);
+               assertFalse(exampleNode.children.contains(methodHTMLNode));
+               exampleNode.addChild(methodHTMLNode);
+               assertTrue(exampleNode.children.contains(methodHTMLNode));
+       }
+       
+       /**
+        * Tests addAttribute(String,String) method
+        * adding the same attribute many times
+        * and verifying it keeps only one
+        * reference to it.
+        */
+       public void testSameAttributeManyTimes() {
+               int times = 100;
+               String methodAttributeName = "exampleAttributeName";
+               String methodAttributeValue = "exampleAttributeValue";
+               for (int i = 0; i < times; i++) {
+                       
exampleNode.addAttribute(methodAttributeName,methodAttributeValue);
+                       assertEquals(exampleNode.getAttributes().size(),1);
+               }
+       }
+       
+       /**
+        * Tests addChild(HTMLNode) method
+        * adding the Node itself as its
+        * child. The method should rise an exception
+        */
+       public void testAddChildUsingTheNodeItselfAsChild() {
+               try {
+                       exampleNode.addChild(exampleNode);
+               fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests addChildren(HTMLNode[]) method
+        * adding the Node itself as its
+        * child. The method should rise an exception
+        */
+       public void testAddChildrenUsingTheNodeItselfAsChild() {
+               HTMLNode[] methodHTMLNodesArray = {new 
HTMLNode(SAMPLE_NODE_NAME),
+                                                                               
   exampleNode,
+                                                                               
   new HTMLNode(SAMPLE_NODE_NAME+"1")};
+               try {
+                       exampleNode.addChildren(methodHTMLNodesArray);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests addChild(String) method
+        * using the same name every time
+        * and verifying that a real new 
+        * HTML is always added.
+        */
+       public void testAddChildSameName() {
+               int times = 100;
+               for (int i = 1; i<=times; i++) {
+                       exampleNode.addChild(SAMPLE_NODE_NAME);
+                       assertEquals(exampleNode.children.size(),i);
+               }
+       }
+       
+       /**
+        * Tests addChild(HTMLNode) method
+        * verifying the behavior when adding
+        * the same HTMLNode instance two times.
+        * It should raise an IllegalArgument exception.
+        */
+       public void testAddChildSameObject() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               exampleNode.addChild(methodHTMLNode);
+               try {
+                       exampleNode.addChild(methodHTMLNode);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests addChildren(HTMLNode[]) method
+        * verifying the behavior when adding
+        * the same HTMLNode instance two times.
+        */
+       public void testAddChildrenSameObject() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               HTMLNode[] methodHTMLNodesArray = {methodHTMLNode,
+                                                                               
   methodHTMLNode};
+               try {
+                       exampleNode.addChildren(methodHTMLNodesArray);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests getContent() method using
+        * common sample HTMLNode, and "#"
+        * "%" named nodes
+        */
+       public void testGetContent() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               assertNull(methodHTMLNode.getContent());
+               
+               methodHTMLNode = new 
HTMLNode(SAMPLE_NODE_NAME,SAMPLE_NODE_CONTENT);
+               //since the HTMLNode name is not "#", or "%",
+               //the content will be a new child with the "#" name
+               assertEquals(SAMPLE_NODE_CONTENT,
+                               
((HTMLNode)(methodHTMLNode.children.get(0))).getContent());
+               assertNull(methodHTMLNode.getContent());
+               
+               methodHTMLNode = new HTMLNode("#",SAMPLE_NODE_CONTENT);
+               assertEquals(SAMPLE_NODE_CONTENT,
+                               methodHTMLNode.getContent());
+               methodHTMLNode = new HTMLNode("%",SAMPLE_NODE_CONTENT);
+               assertEquals(SAMPLE_NODE_CONTENT,
+                               methodHTMLNode.getContent());
+       }
+       
+       /**
+        * Fetches the first line of a String
+        * @param aString the String to consider
+        * @return the first line of the String
+        */
+       private String readFirstLine(String aString) {
+               int newLineIndex = aString.indexOf("\n");
+               if ( newLineIndex == -1)
+                       return aString;
+               return aString.substring(0,newLineIndex);
+       }
+       
+       /**
+        * Tests generate() method with a
+        * HTMLNode with only the name.
+        * The resulting string should be in the form:
+        * <node_name />
+        */
+       public void testGenerate_fromHTMLNode_String() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               assertEquals(("<"+SAMPLE_NODE_NAME+" />").toLowerCase(),
+                                       methodHTMLNode.generate());
+       }
+       
+       /**
+        * Tests generate() method with a
+        * HTMLNode with the name and content.
+        * The resulting string should be in the form:
+        * <node_name>Node_Content</node_name>
+        */
+       public void testGenerate_fromHTMLNode_StringString() {
+               HTMLNode methodHTMLNode = new 
HTMLNode(SAMPLE_NODE_NAME,SAMPLE_NODE_CONTENT);
+               assertEquals(("<"+SAMPLE_NODE_NAME+">").toLowerCase() + 
+                                        SAMPLE_NODE_CONTENT +
+                                        
("</"+SAMPLE_NODE_NAME+">").toLowerCase(),
+                                       methodHTMLNode.generate());
+       }
+       
+       /**
+        * Tests generate() method with a
+        * HTMLNode with the name, an attribute and its value.
+        * The resulting string should be in the form:
+        * <node_name Attribute_Name="Attribute_Value" />
+        */
+       public void testGenerate_fromHTMLNode_StringStringString() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE);
+               assertEquals(("<"+SAMPLE_NODE_NAME+" ").toLowerCase() + 
+                                        SAMPLE_ATTRIBUTE_NAME + "=" +
+                                        "\""+SAMPLE_ATTRIBUTE_VALUE+"\""+
+                                        " />",
+                                       methodHTMLNode.generate());
+       }
+       
+       /**
+        * Tests generate() method with a
+        * HTMLNode with the name, an attribute and its value.
+        * The resulting string should be in the form:
+        * <node_name Attribute_Name="Attribute_Value">Node_Content</node_name>
+        */
+       public void testGenerate_fromHTMLNode_StringStringStringString() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_NODE_CONTENT);
+               assertEquals(generateFullNodeOutput(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME, SAMPLE_ATTRIBUTE_VALUE, 
+                               SAMPLE_NODE_CONTENT),
+                                       methodHTMLNode.generate());
+       }
+       
+       /**
+        * Generates the correct output for the HTMLNode.generate() method
+        * when called from a single node having the specified parameters
+        * @param aName the HTMLNode name
+        * @param aAttributeName the HTMLNode attribute name
+        * @param aAttributeValue the HTMLNode attribute value
+        * @param aContent the HTMLNode content
+        * @return the correct output expected by HTMLNode.generate() method
+        */
+       private String generateFullNodeOutput(String aName, String 
aAttributeName, String aAttributeValue, String aContent) {
+               return ("<"+aName+" ").toLowerCase() + 
+               aAttributeName + "=" +
+                "\""+aAttributeValue+"\">" +
+                aContent +
+                ("</"+aName+">").toLowerCase();
+       }
+       
+       /**
+        * Tests generate() method with a
+        * HTMLNode that has a child.
+        * <node_name Attribute_Name="Attribute_Value">Node_Content
+        * <child_node_name 
child_Attribute_Name="child_Attribute_Value">child_Node_Content</child_node_name>
+        * </node_name>
+        */
+       public void testGenerate_HTMLNode_withChild() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_NODE_CONTENT);
+               HTMLNode methodHTMLNodeChild = new HTMLNode(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_NODE_CONTENT);
+               
+               methodHTMLNode.addChild(methodHTMLNodeChild);
+               
+               assertEquals(("<"+SAMPLE_NODE_NAME+" ").toLowerCase() + 
+                                SAMPLE_ATTRIBUTE_NAME + "=" +
+                                "\""+SAMPLE_ATTRIBUTE_VALUE+"\">" +
+                                SAMPLE_NODE_CONTENT +
+                                
+                                //child
+                                generateFullNodeOutput(SAMPLE_NODE_NAME,
+                                                       SAMPLE_ATTRIBUTE_NAME, 
SAMPLE_ATTRIBUTE_VALUE, 
+                                                       SAMPLE_NODE_CONTENT) +
+                                
+                                ("</"+SAMPLE_NODE_NAME+">").toLowerCase(),
+                                methodHTMLNode.generate());
+       }
+       
+       /**
+        * Tests HTMLDoctype.generate() method
+        * comparing the result with the expected
+        * String. It is useful for regression tests.
+        */
+       public void testHTMLDoctype_generate() {
+               String sampleDocType = "html";
+               String sampleSystemUri = "-//W3C//DTD XHTML 1.1//EN";
+               HTMLNode methodHTMLNodeDoc = new 
HTMLNode.HTMLDoctype(sampleDocType,sampleSystemUri);
+               methodHTMLNodeDoc.addChild(SAMPLE_NODE_NAME);
+               String generatedString = methodHTMLNodeDoc.generate();
+               //consider only the HTMLDocType generated text
+               assertEquals("<!DOCTYPE "+sampleDocType+" PUBLIC 
\""+sampleSystemUri+"\">",     
+                               readFirstLine(generatedString));
+               
+       }
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/HexUtilTest.java
===================================================================
--- trunk/freenet/test/freenet/support/HexUtilTest.java 2007-08-18 19:36:17 UTC 
(rev 14796)
+++ branches/freenet-jfk/test/freenet/support/HexUtilTest.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,371 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package freenet.support;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.util.Arrays;
-import java.util.BitSet;
-
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.HexUtil} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class HexUtilTest extends TestCase {
-
-       /**
-        * Test the bytesToHex(byte[]) method
-        * against every possible single byte value.
-        */
-       public void testBytesToHex_byte() {
-               byte[] methodByteArray = new byte[1];
-               String expectedResult;
-               for (int i = 255; i >= 0; i--) {
-                       methodByteArray[0] = (byte)i;
-                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
-                          For this reason we need the next "switch case". */
-                       expectedResult = (i <= 15?
-                                       "0" + (Integer.toHexString(i)):
-                                       (Integer.toHexString(i)));
-                       
assertEquals(expectedResult,HexUtil.bytesToHex(methodByteArray));}
-       }
-       
-       /**
-        * Test the hexToBytes(String) method
-        * against the hex representation of
-        * every possible single byte.
-        * The starting offset is always 0.
-        */
-       public void testHexToBytes_String() {
-               byte[] expectedByteArray = new byte[1];
-               String methodHexString;
-               for (int i = 255; i >= 0; i--) {
-                       expectedByteArray[0] = (byte)i;
-                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
-                          For this reason we need the next "switch case". */
-                       methodHexString = (i <= 15?
-                                       "0" + (Integer.toHexString(i)):
-                                       (Integer.toHexString(i)));
-                       
assertTrue(Arrays.equals(expectedByteArray,HexUtil.hexToBytes(methodHexString)));}
-       }
-       
-       /**
-        * Test the hexToBytes(String,int) method
-        * against the hex representation of
-        * every possible single byte.
-        * The starting offset is always 0.
-        */
-       public void testHexToBytes_StringInt() {
-               byte[] expectedByteArray = new byte[1];
-               String methodHexString;
-               for (int i = 255; i >= 0; i--) {
-                       expectedByteArray[0] = (byte)i;
-                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
-                          For this reason we need the next "switch case". */
-                       methodHexString = (i <= 15?
-                                       "0" + (Integer.toHexString(i)):
-                                       (Integer.toHexString(i)));
-                       
assertTrue(Arrays.equals(expectedByteArray,HexUtil.hexToBytes(methodHexString,0)));}
-       }
-       
-       /**
-        * Test the hexToBytes(String,byte[],int) method
-        * against the hex representation of
-        * every possible single byte.
-        */
-       public void testHexToBytes_StringByteInt() {
-               byte[] expectedByteArray = new byte[1];
-               byte[] outputArray = new byte[1];
-               String methodHexString;
-               for (int i = 255; i >= 0; i--) {
-                       expectedByteArray[0] = (byte)i;
-                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
-                          For this reason we need the next "switch case". */
-                       methodHexString = (i <= 15?
-                                       "0" + (Integer.toHexString(i)):
-                                       (Integer.toHexString(i)));
-                       HexUtil.hexToBytes(methodHexString,outputArray,0);
-                       
assertTrue(Arrays.equals(expectedByteArray,outputArray));}
-       }
-       
-       /**
-        * Test the bitsToByte(BitSet,int) method
-        * against the bit representation of
-        * every possible single byte.
-        */
-       public void testBitsToBytes_BitSetInt() {
-               byte[] expectedByteArray = new byte[1];
-               byte[] outputArray = new byte[1];
-               BitSet methodBitSet = new BitSet(8);
-               for (int i = 0; i < 256; i++) {
-                       outputArray = HexUtil.bitsToBytes(methodBitSet, 8);
-                       expectedByteArray[0] = (byte)i;
-                       
assertTrue(Arrays.equals(expectedByteArray,outputArray));
-                       addOne(methodBitSet);
-               }
-       }
-       
-       /**
-        * It adds 1 to a given BitSet 
-        * @param aBitSet
-        */
-       private void addOne(BitSet aBitSet) {
-               int bitSetIndex = 0;
-               while (aBitSet.get(bitSetIndex)==true) {
-                       aBitSet.flip(bitSetIndex);
-                       bitSetIndex++;
-               }
-               aBitSet.flip(bitSetIndex);
-       }
-       
-       /**
-        * Test countBytesForBits(int) method
-        * against all possible values until 256 bytes
-        */
-       public void testCountBytesForBits_int() {
-               //border case
-               assertEquals(HexUtil.countBytesForBits(0),0);
-               for (int expectedBytesCount = 1; expectedBytesCount < 256; 
expectedBytesCount++)
-                       for (int bits = (expectedBytesCount-1)*8+1; bits <= 
(expectedBytesCount)*8; bits++)
-                               
assertEquals(HexUtil.countBytesForBits(bits),expectedBytesCount);
-       }
-       
-       /**
-        * Test bytesToBits(byte[],BitSet,int) method
-        * against all possible single byte value.
-        * It uses HexUtil.bitsToBytes() method for the check,
-        * so be sure that method works correctly!
-        */
-       public void testBytesToBits_byteBitSetInt() {
-               byte[] methodByteArray = new byte[1];
-               BitSet methodBitSet = new BitSet(8);
-               for (int i = 0; i < 255; i++) {
-                       methodByteArray[0] = (byte)i;
-                       HexUtil.bytesToBits(methodByteArray,methodBitSet,7);
-                       
assertTrue(Arrays.equals(methodByteArray,HexUtil.bitsToBytes(methodBitSet,8)));}
-       }
-       
-       /**
-        * Test biToHex(BigInteger) method
-        * comparing its results to results provided
-        * by different scientific valid calculators.
-        */
-       public void testBiToHex_BigInteger() {
-               BigInteger methodBigInteger = new BigInteger("999999999999999");
-               String expectedHexValue = "038d7ea4c67fff";
-               
assertEquals(HexUtil.biToHex(methodBigInteger),expectedHexValue);
-               methodBigInteger = new BigInteger("0");
-               expectedHexValue = "00";
-               
assertEquals(HexUtil.biToHex(methodBigInteger),expectedHexValue);
-               methodBigInteger = new BigInteger("72057594037927935");
-               expectedHexValue = "00ffffffffffffff";
-               
assertEquals(HexUtil.biToHex(methodBigInteger),expectedHexValue);
-       }
-       
-       /**
-        * Test bitsToHexString(BitSet,int) method
-        * comparing its results to results provided
-        * by different scientific valid calculators.
-        */
-       public void testBitsToHexString() {
-               BitSet methodBitSet = new BitSet(8);
-               String expectedString = "00";
-               
assertEquals(HexUtil.bitsToHexString(methodBitSet,8),expectedString);
-               methodBitSet.set(0,7,true); /*0x7f*/
-               expectedString = "7f";
-               
assertEquals(HexUtil.bitsToHexString(methodBitSet,8),expectedString);
-               methodBitSet.set(0,9,true); /*0xff*/
-               expectedString = "ff";
-               
assertEquals(HexUtil.bitsToHexString(methodBitSet,8),expectedString);
-       }
-       
-       /**
-        * Tests hexToBits(String,BitSet,int) method
-        */
-       public void testHexToBits() {
-               String methodStringToStore = "00";
-               BitSet methodBitSet = new BitSet(8);
-               
HexUtil.hexToBits(methodStringToStore,methodBitSet,methodBitSet.size());
-               assertTrue(methodBitSet.cardinality()==0);              
-               BitSet expectedBitSet = new BitSet(8);
-               expectedBitSet.set(0,7,true); /*0x7f*/
-               methodStringToStore = "7f";
-               methodBitSet = new BitSet(8);
-               
HexUtil.hexToBits(methodStringToStore,methodBitSet,methodBitSet.size());
-               assertTrue(methodBitSet.intersects(expectedBitSet));
-               expectedBitSet.set(0,9,true); /*0xff*/
-               methodStringToStore = "ff";
-               methodBitSet = new BitSet(8);
-               
HexUtil.hexToBits(methodStringToStore,methodBitSet,methodBitSet.size());
-               assertTrue(methodBitSet.intersects(expectedBitSet));
-       }
-       
-       /**
-        * Tests writeBigInteger(BigInteger,DataOutputStream)
-        * and readBigInteger(DataInputStream) comparing a
-        * BigInteger after writing it to a Stream and
-        * reading it from the writing result.
-        */
-       public void testWriteAndReadBigInteger() {
-               BigInteger methodBigInteger = new BigInteger("999999999999999");
-               ByteArrayOutputStream methodByteArrayOutStream = new 
ByteArrayOutputStream();
-               DataOutputStream methodDataOutStream = new 
DataOutputStream(methodByteArrayOutStream);
-               try {
-                       
HexUtil.writeBigInteger(methodBigInteger,methodDataOutStream);
-                       ByteArrayInputStream methodByteArrayInStream = 
-                               new 
ByteArrayInputStream(methodByteArrayOutStream.toByteArray());
-                       DataInputStream methodDataInStream = new 
DataInputStream(methodByteArrayInStream);
-                       
assertTrue(methodBigInteger.compareTo(HexUtil.readBigInteger(methodDataInStream))==0);
-               } catch (IOException aException) {
-                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
-       }
-       
-       /**
-        * Test bytesToHex(byte[],int,int) method
-        * with a too long starting offset. The tested
-        * method should raise an exception.
-        */
-       public void testBytesToHex_byteIntInt_WithLongOffset() {
-        try {
-               int arrayLength = 3;
-               byte[] methodBytesArray = new byte[arrayLength];
-               HexUtil.bytesToHex(methodBytesArray,arrayLength+1,1);
-            fail("Expected Exception Error Not Thrown!"); } 
-        catch (IllegalArgumentException anException) {
-            assertNotNull(anException); }
-    }
-       
-       /**
-        * Test bytesToHex(byte[],int,int) method
-        * with asking to read too many bytes. The tested
-        * method should raise an exception.
-        */
-       public void testBytesToHex_byteIntInt_WithLongReading() {
-        try {
-               int arrayLength = 3;
-               byte[] methodBytesArray = new byte[arrayLength];
-               HexUtil.bytesToHex(methodBytesArray,0,arrayLength+1);
-            fail("Expected Exception Error Not Thrown!"); } 
-        catch (IllegalArgumentException anException) {
-            assertNotNull(anException); }
-    }
-       
-       /**
-        * Test bytesToHex(byte[],int,int) method
-        * with a 0 length.
-        */
-       public void testBytesToHex_byteIntInt_WithZeroLength() {
-               int length = 0;
-               byte[] methodBytesArray = {1,2,3};              //a non-zero 
bytes array
-               assertEquals("",HexUtil.bytesToHex(methodBytesArray,0,length));
-       }
-       
-       /**
-        * Test hexToBytes(String,byte[],int) method
-        * with a too long offset.
-        * The method should raise an exception.
-        */
-       public void testHexToBytes_StringByteInt_WithLongOffset() {
-        try {
-               String methodString = "0";
-               byte[] methodByteArray = new byte[1];
-               
HexUtil.hexToBytes(methodString,methodByteArray,methodByteArray.length);
-            fail("Expected Exception Error Not Thrown!"); } 
-        catch (ArrayIndexOutOfBoundsException anException) {
-            assertNotNull(anException); }
-    }
-       
-       /**
-        * Test hexToBytes(String,byte[],int) method
-        * with a too short byte[] to put the result.
-        * The method should raise an exception.
-        */
-       public void testHexToBytes_StringByteInt_WithShortArray() {
-        try {
-               String methodString = "0000";
-               byte[] methodByteArray = new byte[1];
-               HexUtil.hexToBytes(methodString,methodByteArray,0);
-            fail("Expected Exception Error Not Thrown!"); } 
-        catch (IndexOutOfBoundsException anException) {
-            assertNotNull(anException); }
-    }
-       
-       /**
-        * Test all hexToBytes() methods
-        * with a not valid character.
-        * The method should raise an exception. 
-        */
-       public void testHexToBytes_WithBadDigit() {
-               String methodString = "00%0";
-               try {
-               byte[] methodByteArray = new byte[methodString.length()];
-               HexUtil.hexToBytes(methodString,methodByteArray,0);
-            fail("Expected Exception Error Not Thrown!"); } 
-        catch (NumberFormatException anException) {
-            assertNotNull(anException); }
-        try {
-               HexUtil.hexToBytes(methodString,0);
-            fail("Expected Exception Error Not Thrown!"); } 
-        catch (NumberFormatException anException) {
-            assertNotNull(anException); }
-        try {
-               HexUtil.hexToBytes(methodString);
-            fail("Expected Exception Error Not Thrown!"); } 
-        catch (NumberFormatException anException) {
-            assertNotNull(anException); }
-    }
-       
-       /**
-        * Test the bitsToByte(BitSet,int) method
-        * using a size smaller than the actual number
-        * of bits in the BitSet.
-        */
-       public void testBitsToBytes_WithShortSize() {
-               byte[] expectedByteArray = new byte[1];
-               byte[] outputArray = new byte[1];
-               BitSet methodBitSet = new BitSet(8);
-
-               /* 0x01 */
-               methodBitSet.flip(0);
-               expectedByteArray[0] = (byte)1;
-               /* 0x01 & 0x00 == 0x01 */
-               outputArray = HexUtil.bitsToBytes(methodBitSet,0);
-               assertFalse(Arrays.equals(expectedByteArray,outputArray));
-               /* 0x01 & 0x01 == 0x01 */
-               outputArray = HexUtil.bitsToBytes(methodBitSet,1);
-               assertTrue(Arrays.equals(expectedByteArray,outputArray));
-               
-               /* 0x80 */
-               methodBitSet.flip(7);
-               /* 0x08 */
-               methodBitSet.flip(3);
-               expectedByteArray[0] = (byte)128+8+1;
-               /* 0x89 & 0x08 == 0x89 */
-               outputArray = HexUtil.bitsToBytes(methodBitSet,3);
-               assertFalse(Arrays.equals(expectedByteArray,outputArray));
-               /* 0x89 & 0xff == 0x89 */
-               outputArray = HexUtil.bitsToBytes(methodBitSet,8);
-               assertTrue(Arrays.equals(expectedByteArray,outputArray));
-       }
-}

Copied: branches/freenet-jfk/test/freenet/support/HexUtilTest.java (from rev 
14796, trunk/freenet/test/freenet/support/HexUtilTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/HexUtilTest.java                  
        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/HexUtilTest.java  2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,371 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package freenet.support;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.BitSet;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.HexUtil} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class HexUtilTest extends TestCase {
+
+       /**
+        * Test the bytesToHex(byte[]) method
+        * against every possible single byte value.
+        */
+       public void testBytesToHex_byte() {
+               byte[] methodByteArray = new byte[1];
+               String expectedResult;
+               for (int i = 255; i >= 0; i--) {
+                       methodByteArray[0] = (byte)i;
+                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
+                          For this reason we need the next "switch case". */
+                       expectedResult = (i <= 15?
+                                       "0" + (Integer.toHexString(i)):
+                                       (Integer.toHexString(i)));
+                       
assertEquals(expectedResult,HexUtil.bytesToHex(methodByteArray));}
+       }
+       
+       /**
+        * Test the hexToBytes(String) method
+        * against the hex representation of
+        * every possible single byte.
+        * The starting offset is always 0.
+        */
+       public void testHexToBytes_String() {
+               byte[] expectedByteArray = new byte[1];
+               String methodHexString;
+               for (int i = 255; i >= 0; i--) {
+                       expectedByteArray[0] = (byte)i;
+                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
+                          For this reason we need the next "switch case". */
+                       methodHexString = (i <= 15?
+                                       "0" + (Integer.toHexString(i)):
+                                       (Integer.toHexString(i)));
+                       
assertTrue(Arrays.equals(expectedByteArray,HexUtil.hexToBytes(methodHexString)));}
+       }
+       
+       /**
+        * Test the hexToBytes(String,int) method
+        * against the hex representation of
+        * every possible single byte.
+        * The starting offset is always 0.
+        */
+       public void testHexToBytes_StringInt() {
+               byte[] expectedByteArray = new byte[1];
+               String methodHexString;
+               for (int i = 255; i >= 0; i--) {
+                       expectedByteArray[0] = (byte)i;
+                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
+                          For this reason we need the next "switch case". */
+                       methodHexString = (i <= 15?
+                                       "0" + (Integer.toHexString(i)):
+                                       (Integer.toHexString(i)));
+                       
assertTrue(Arrays.equals(expectedByteArray,HexUtil.hexToBytes(methodHexString,0)));}
+       }
+       
+       /**
+        * Test the hexToBytes(String,byte[],int) method
+        * against the hex representation of
+        * every possible single byte.
+        */
+       public void testHexToBytes_StringByteInt() {
+               byte[] expectedByteArray = new byte[1];
+               byte[] outputArray = new byte[1];
+               String methodHexString;
+               for (int i = 255; i >= 0; i--) {
+                       expectedByteArray[0] = (byte)i;
+                       /* Integer.toHexString works with int so it doesn't 
return always a two digit hex. 
+                          For this reason we need the next "switch case". */
+                       methodHexString = (i <= 15?
+                                       "0" + (Integer.toHexString(i)):
+                                       (Integer.toHexString(i)));
+                       HexUtil.hexToBytes(methodHexString,outputArray,0);
+                       
assertTrue(Arrays.equals(expectedByteArray,outputArray));}
+       }
+       
+       /**
+        * Test the bitsToByte(BitSet,int) method
+        * against the bit representation of
+        * every possible single byte.
+        */
+       public void testBitsToBytes_BitSetInt() {
+               byte[] expectedByteArray = new byte[1];
+               byte[] outputArray = new byte[1];
+               BitSet methodBitSet = new BitSet(8);
+               for (int i = 0; i < 256; i++) {
+                       outputArray = HexUtil.bitsToBytes(methodBitSet, 8);
+                       expectedByteArray[0] = (byte)i;
+                       
assertTrue(Arrays.equals(expectedByteArray,outputArray));
+                       addOne(methodBitSet);
+               }
+       }
+       
+       /**
+        * It adds 1 to a given BitSet 
+        * @param aBitSet
+        */
+       private void addOne(BitSet aBitSet) {
+               int bitSetIndex = 0;
+               while (aBitSet.get(bitSetIndex)==true) {
+                       aBitSet.flip(bitSetIndex);
+                       bitSetIndex++;
+               }
+               aBitSet.flip(bitSetIndex);
+       }
+       
+       /**
+        * Test countBytesForBits(int) method
+        * against all possible values until 256 bytes
+        */
+       public void testCountBytesForBits_int() {
+               //border case
+               assertEquals(HexUtil.countBytesForBits(0),0);
+               for (int expectedBytesCount = 1; expectedBytesCount < 256; 
expectedBytesCount++)
+                       for (int bits = (expectedBytesCount-1)*8+1; bits <= 
(expectedBytesCount)*8; bits++)
+                               
assertEquals(HexUtil.countBytesForBits(bits),expectedBytesCount);
+       }
+       
+       /**
+        * Test bytesToBits(byte[],BitSet,int) method
+        * against all possible single byte value.
+        * It uses HexUtil.bitsToBytes() method for the check,
+        * so be sure that method works correctly!
+        */
+       public void testBytesToBits_byteBitSetInt() {
+               byte[] methodByteArray = new byte[1];
+               BitSet methodBitSet = new BitSet(8);
+               for (int i = 0; i < 255; i++) {
+                       methodByteArray[0] = (byte)i;
+                       HexUtil.bytesToBits(methodByteArray,methodBitSet,7);
+                       
assertTrue(Arrays.equals(methodByteArray,HexUtil.bitsToBytes(methodBitSet,8)));}
+       }
+       
+       /**
+        * Test biToHex(BigInteger) method
+        * comparing its results to results provided
+        * by different scientific valid calculators.
+        */
+       public void testBiToHex_BigInteger() {
+               BigInteger methodBigInteger = new BigInteger("999999999999999");
+               String expectedHexValue = "038d7ea4c67fff";
+               
assertEquals(HexUtil.biToHex(methodBigInteger),expectedHexValue);
+               methodBigInteger = new BigInteger("0");
+               expectedHexValue = "00";
+               
assertEquals(HexUtil.biToHex(methodBigInteger),expectedHexValue);
+               methodBigInteger = new BigInteger("72057594037927935");
+               expectedHexValue = "00ffffffffffffff";
+               
assertEquals(HexUtil.biToHex(methodBigInteger),expectedHexValue);
+       }
+       
+       /**
+        * Test bitsToHexString(BitSet,int) method
+        * comparing its results to results provided
+        * by different scientific valid calculators.
+        */
+       public void testBitsToHexString() {
+               BitSet methodBitSet = new BitSet(8);
+               String expectedString = "00";
+               
assertEquals(HexUtil.bitsToHexString(methodBitSet,8),expectedString);
+               methodBitSet.set(0,7,true); /*0x7f*/
+               expectedString = "7f";
+               
assertEquals(HexUtil.bitsToHexString(methodBitSet,8),expectedString);
+               methodBitSet.set(0,9,true); /*0xff*/
+               expectedString = "ff";
+               
assertEquals(HexUtil.bitsToHexString(methodBitSet,8),expectedString);
+       }
+       
+       /**
+        * Tests hexToBits(String,BitSet,int) method
+        */
+       public void testHexToBits() {
+               String methodStringToStore = "00";
+               BitSet methodBitSet = new BitSet(8);
+               
HexUtil.hexToBits(methodStringToStore,methodBitSet,methodBitSet.size());
+               assertTrue(methodBitSet.cardinality()==0);              
+               BitSet expectedBitSet = new BitSet(8);
+               expectedBitSet.set(0,7,true); /*0x7f*/
+               methodStringToStore = "7f";
+               methodBitSet = new BitSet(8);
+               
HexUtil.hexToBits(methodStringToStore,methodBitSet,methodBitSet.size());
+               assertTrue(methodBitSet.intersects(expectedBitSet));
+               expectedBitSet.set(0,9,true); /*0xff*/
+               methodStringToStore = "ff";
+               methodBitSet = new BitSet(8);
+               
HexUtil.hexToBits(methodStringToStore,methodBitSet,methodBitSet.size());
+               assertTrue(methodBitSet.intersects(expectedBitSet));
+       }
+       
+       /**
+        * Tests writeBigInteger(BigInteger,DataOutputStream)
+        * and readBigInteger(DataInputStream) comparing a
+        * BigInteger after writing it to a Stream and
+        * reading it from the writing result.
+        */
+       public void testWriteAndReadBigInteger() {
+               BigInteger methodBigInteger = new BigInteger("999999999999999");
+               ByteArrayOutputStream methodByteArrayOutStream = new 
ByteArrayOutputStream();
+               DataOutputStream methodDataOutStream = new 
DataOutputStream(methodByteArrayOutStream);
+               try {
+                       
HexUtil.writeBigInteger(methodBigInteger,methodDataOutStream);
+                       ByteArrayInputStream methodByteArrayInStream = 
+                               new 
ByteArrayInputStream(methodByteArrayOutStream.toByteArray());
+                       DataInputStream methodDataInStream = new 
DataInputStream(methodByteArrayInStream);
+                       
assertTrue(methodBigInteger.compareTo(HexUtil.readBigInteger(methodDataInStream))==0);
+               } catch (IOException aException) {
+                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
+       }
+       
+       /**
+        * Test bytesToHex(byte[],int,int) method
+        * with a too long starting offset. The tested
+        * method should raise an exception.
+        */
+       public void testBytesToHex_byteIntInt_WithLongOffset() {
+        try {
+               int arrayLength = 3;
+               byte[] methodBytesArray = new byte[arrayLength];
+               HexUtil.bytesToHex(methodBytesArray,arrayLength+1,1);
+            fail("Expected Exception Error Not Thrown!"); } 
+        catch (IllegalArgumentException anException) {
+            assertNotNull(anException); }
+    }
+       
+       /**
+        * Test bytesToHex(byte[],int,int) method
+        * with asking to read too many bytes. The tested
+        * method should raise an exception.
+        */
+       public void testBytesToHex_byteIntInt_WithLongReading() {
+        try {
+               int arrayLength = 3;
+               byte[] methodBytesArray = new byte[arrayLength];
+               HexUtil.bytesToHex(methodBytesArray,0,arrayLength+1);
+            fail("Expected Exception Error Not Thrown!"); } 
+        catch (IllegalArgumentException anException) {
+            assertNotNull(anException); }
+    }
+       
+       /**
+        * Test bytesToHex(byte[],int,int) method
+        * with a 0 length.
+        */
+       public void testBytesToHex_byteIntInt_WithZeroLength() {
+               int length = 0;
+               byte[] methodBytesArray = {1,2,3};              //a non-zero 
bytes array
+               assertEquals("",HexUtil.bytesToHex(methodBytesArray,0,length));
+       }
+       
+       /**
+        * Test hexToBytes(String,byte[],int) method
+        * with a too long offset.
+        * The method should raise an exception.
+        */
+       public void testHexToBytes_StringByteInt_WithLongOffset() {
+        try {
+               String methodString = "0";
+               byte[] methodByteArray = new byte[1];
+               
HexUtil.hexToBytes(methodString,methodByteArray,methodByteArray.length);
+            fail("Expected Exception Error Not Thrown!"); } 
+        catch (ArrayIndexOutOfBoundsException anException) {
+            assertNotNull(anException); }
+    }
+       
+       /**
+        * Test hexToBytes(String,byte[],int) method
+        * with a too short byte[] to put the result.
+        * The method should raise an exception.
+        */
+       public void testHexToBytes_StringByteInt_WithShortArray() {
+        try {
+               String methodString = "0000";
+               byte[] methodByteArray = new byte[1];
+               HexUtil.hexToBytes(methodString,methodByteArray,0);
+            fail("Expected Exception Error Not Thrown!"); } 
+        catch (IndexOutOfBoundsException anException) {
+            assertNotNull(anException); }
+    }
+       
+       /**
+        * Test all hexToBytes() methods
+        * with a not valid character.
+        * The method should raise an exception. 
+        */
+       public void testHexToBytes_WithBadDigit() {
+               String methodString = "00%0";
+               try {
+               byte[] methodByteArray = new byte[methodString.length()];
+               HexUtil.hexToBytes(methodString,methodByteArray,0);
+            fail("Expected Exception Error Not Thrown!"); } 
+        catch (NumberFormatException anException) {
+            assertNotNull(anException); }
+        try {
+               HexUtil.hexToBytes(methodString,0);
+            fail("Expected Exception Error Not Thrown!"); } 
+        catch (NumberFormatException anException) {
+            assertNotNull(anException); }
+        try {
+               HexUtil.hexToBytes(methodString);
+            fail("Expected Exception Error Not Thrown!"); } 
+        catch (NumberFormatException anException) {
+            assertNotNull(anException); }
+    }
+       
+       /**
+        * Test the bitsToByte(BitSet,int) method
+        * using a size smaller than the actual number
+        * of bits in the BitSet.
+        */
+       public void testBitsToBytes_WithShortSize() {
+               byte[] expectedByteArray = new byte[1];
+               byte[] outputArray = new byte[1];
+               BitSet methodBitSet = new BitSet(8);
+
+               /* 0x01 */
+               methodBitSet.flip(0);
+               expectedByteArray[0] = (byte)1;
+               /* 0x01 & 0x00 == 0x01 */
+               outputArray = HexUtil.bitsToBytes(methodBitSet,0);
+               assertFalse(Arrays.equals(expectedByteArray,outputArray));
+               /* 0x01 & 0x01 == 0x01 */
+               outputArray = HexUtil.bitsToBytes(methodBitSet,1);
+               assertTrue(Arrays.equals(expectedByteArray,outputArray));
+               
+               /* 0x80 */
+               methodBitSet.flip(7);
+               /* 0x08 */
+               methodBitSet.flip(3);
+               expectedByteArray[0] = (byte)128+8+1;
+               /* 0x89 & 0x08 == 0x89 */
+               outputArray = HexUtil.bitsToBytes(methodBitSet,3);
+               assertFalse(Arrays.equals(expectedByteArray,outputArray));
+               /* 0x89 & 0xff == 0x89 */
+               outputArray = HexUtil.bitsToBytes(methodBitSet,8);
+               assertTrue(Arrays.equals(expectedByteArray,outputArray));
+       }
+}

Deleted: branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java
===================================================================
--- trunk/freenet/test/freenet/support/LRUHashtableTest.java    2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,358 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import java.util.Enumeration;
-
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.LRUHashtable} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class LRUHashtableTest extends TestCase {
-
-       private final int sampleElemsNumber = 100;
-       
-       /**
-        * Creates a double array of objects with a specified size
-        * where Object[i][0] is the key, and is an Integer,
-        * and Object[i][1] is the value 
-        * @param size the array size
-        * @return the objects double array
-        */
-       private Object[][] createSampleKeyVal(int size) {
-               Object[][] sampleObjects = new Object[size][2];
-               for (int i=0; i<sampleObjects.length;i++) {
-                       //key
-                       sampleObjects[i][0] = new Integer(i);
-                       //value
-                       sampleObjects[i][1] = new Object(); }           
-               return sampleObjects;
-       }
-       
-       /**
-        * Creates a LRUHashtable filled with the specified objects number
-        * @param size HashTable size
-        * @return the created LRUHashtable
-        */
-       private LRUHashtable createSampleHashTable(int size) {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               Object[][] sampleObjects = createSampleKeyVal(size);
-               for (int i=0;i<sampleObjects.length;i++)
-                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
-               return methodLRUht;
-       }
-       
-       /**
-        * It verifies if a key-value pair is present in
-        * a LRUHashtable
-        * @param aLRUht a LRUHashtable to check in
-        * @param aKey a key to find
-        * @param aValue the correspondent value
-        * @return true if the key is present and returned value is the same as 
in the argument
-        */
-       private boolean verifyKeyValPresence(LRUHashtable aLRUht, Object aKey, 
Object aValue) {
-               if (aLRUht.containsKey(aKey))
-                       return aLRUht.get(aKey).equals(aValue);
-               return false;
-       }
-       
-       /**
-        * Tests push(Object,Object) method
-        * providing null object as arguments 
-        * (after setting up a sample HashTable) 
-        * and verifying if the correct exception
-        * is raised
-        */
-       public void testPushNull() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               try {
-                       //a null value is admitted
-                       methodLRUht.push(new Object(),null);}           
-               catch (NullPointerException anException) { 
-                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
-               try {
-                       methodLRUht.push(null,null);
-                       fail("Expected Exception Error Not Thrown!"); }
-               catch (NullPointerException anException) { 
assertNotNull(anException); }
-               try {
-                       methodLRUht.push(null,new Object());
-                       fail("Expected Exception Error Not Thrown!"); }
-               catch (NullPointerException anException) { 
assertNotNull(anException); }
-               
-       }
-       
-       /**
-        * Tests push(Object,Object) method
-        * and verifies the behaviour when
-        * pushing the same object more than one
-        * time.
-        */
-       public void testPushSameObjTwice() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               Object[][] sampleObj = {
-                               { new Integer(sampleElemsNumber), new Object() 
}, 
-                               { new Integer(sampleElemsNumber+1), new 
Object() } };
-               
-               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
-               methodLRUht.push(sampleObj[1][0],sampleObj[1][1]);
-               
-               //check presence
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));  
        
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));
-               //check size
-               assertTrue(methodLRUht.size()==sampleElemsNumber+2);            
                
-               
-               //push the same object another time
-               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));
-               assertTrue(methodLRUht.size()==sampleElemsNumber+2);
-       }
-       
-       /**
-        * Tests push(Object,Object) method
-        * and verifies the behaviour when
-        * pushing the same key with two different
-        * values.
-        */
-       public void testPushSameKey() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               Object[][] sampleObj = {
-                               { new Integer(sampleElemsNumber), new Object() 
}, 
-                               { new Integer(sampleElemsNumber+1), new 
Object() } };
-               
-               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
-               methodLRUht.push(sampleObj[1][0],sampleObj[1][1]);
-               
-               //check presence
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));  
        
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));  
        
-               //check size
-               assertTrue(methodLRUht.size()==sampleElemsNumber+2);
-               
-               //creating and pushing a different value
-               sampleObj[0][1] = new Object();         
-               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));  
        
-               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));
-               assertTrue(methodLRUht.size()==sampleElemsNumber+2);
-       }
-
-       /**
-        * Tests popKey() method pushing
-        * and popping objects and
-        * verifying if their keys are correctly 
-        * (in a FIFO manner) fetched and the
-        * HashTable entry deleted
-        */
-       public void testPopKey() {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)              
-                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
-               //getting keys
-               for (int i=0; i<sampleObjects.length; i++)              
-                       assertEquals(sampleObjects[i][0],methodLRUht.popKey());
-               //the HashTable must be empty
-               assertNull(methodLRUht.popKey());
-       }
-       
-       /**
-        * Tests popKey() method pushing
-        * and popping objects and
-        * verifying if their values are correctly 
-        * (in a FIFO manner) fetched and the
-        * HashTable entry deleted
-        */
-       public void testPopValue() {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)
-                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
-               //getting values
-               for (int i=0; i<sampleObjects.length; i++)
-                       
assertEquals(sampleObjects[i][1],methodLRUht.popValue());
-               //the HashTable must be empty
-               assertNull(methodLRUht.popKey());
-       }
-
-       /**
-        * Tests peekValue() method pushing
-        * and popping objects and
-        * verifying if their peekValue is correct
-        */
-       public void testPeekValue() {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)
-                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
-               //getting values
-               for (int i=0; i<sampleObjects.length; i++) {
-                       
assertEquals(sampleObjects[i][1],methodLRUht.peekValue());
-                       methodLRUht.popKey(); }
-               //the HashTable must be empty
-               assertNull(methodLRUht.peekValue());
-               //insert and fetch a null value
-               methodLRUht.push(new Object(),null);
-               assertNull(methodLRUht.peekValue());
-       }
-
-       /**
-        * Tests size() method
-        * pushing and popping elements into
-        * the LRUHashTable
-        */
-       public void testSize() {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
-               assertTrue(methodLRUht.size()==0);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++) {            
-                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
-                       assertTrue(methodLRUht.size()==i+1); }
-               //popping keys
-               for (int i=sampleObjects.length-1; i>=0; i--) {
-                       methodLRUht.popKey(); 
-                       assertTrue(methodLRUht.size()==i); }
-       }
-
-       /**
-        * Tests removeKey(Object) method
-        * verifies if all elements are correctly
-        * removed checking the method return value,
-        * if the element is still contained and
-        * the HashTable size.
-        */
-       public void testRemoveKey() {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)
-                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
-               //popping keys
-               for (int i=sampleObjects.length-1; i>=0; i--) {
-                       assertTrue(methodLRUht.removeKey(sampleObjects[i][0]));
-                       
assertFalse(methodLRUht.containsKey(sampleObjects[i][0]));
-                       assertTrue(methodLRUht.size()==i); }
-       }
-       
-       /**
-        * Tests removeKey(Object) providing a null
-        * key and trying to remove it after 
-        * setting up a sample queue.
-        */
-       public void testRemoveNullKey() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               try {
-                       methodLRUht.removeKey(null);
-                       fail("Expected Exception Error Not Thrown!"); }
-               catch (NullPointerException anException) { 
-                       assertNotNull(anException); }
-       }
-       
-       /**
-        * Tests removeKey(Object) method
-        * trying to remove a not present key after 
-        * setting up a sample LRUHashtable.
-        */
-       public void testRemoveNotPresent() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               assertFalse(methodLRUht.removeKey(new Object()));
-       }
-
-       /**
-        * Tests containsKey(Object) method
-        * trying to find a not present key after 
-        * setting up a sample queue.
-        * Then it search for a present one.
-        */
-       public void testContainsKey() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               assertFalse(methodLRUht.containsKey(new Object()));
-               Object methodSampleObj = new Object();
-               methodLRUht.push(methodSampleObj,null);
-               assertTrue(methodLRUht.containsKey(methodSampleObj));
-       }
-
-       /**
-        * Tests get(Object) method
-        * trying to find a not present key after 
-        * setting up a sample HashTable,
-        * then it search a present key.
-        */
-       public void testGet() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               assertNull(methodLRUht.get(new Object()));
-               Object methodSampleKey = new Object();
-               Object methodSampleValue = new Object();
-               methodLRUht.push(methodSampleKey,methodSampleValue);
-               
assertEquals(methodLRUht.get(methodSampleKey),methodSampleValue);
-       }
-       
-       /**
-        * Tests get(Object) trying to fetch 
-        * a null key.
-        */
-       public void testGetNullKey() {
-               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
-               try {
-                       methodLRUht.get(null);
-                       fail("Expected Exception Error Not Thrown!"); }
-               catch (NullPointerException anException) { 
-                       assertNotNull(anException); }
-       }
-
-       /**
-        * Tests keys() method
-        * verifying if the Enumeration provided
-        * is correct
-        */
-       public void testKeys() {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)
-                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
-               Enumeration methodEnumeration = methodLRUht.keys();
-               int j=0;
-               while(methodEnumeration.hasMoreElements()) {                    
-                       
assertEquals(methodEnumeration.nextElement(),sampleObjects[j][0]);
-                       j++; }
-       }
-
-       /**
-        * Tests isEmpty() method
-        * trying it with a new generated
-        * HashTable and after popping
-        * out all keys in a sample LRUHashTable
-        */
-       public void testIsEmpty() {
-               LRUHashtable methodLRUht = new LRUHashtable();
-               assertTrue(methodLRUht.isEmpty());
-               methodLRUht = createSampleHashTable(sampleElemsNumber);
-               //popping keys
-               for (int i=0; i<sampleElemsNumber;i++)
-                       methodLRUht.popKey();
-               assertTrue(methodLRUht.isEmpty());
-       }
-
-}

Copied: branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java (from 
rev 14796, trunk/freenet/test/freenet/support/LRUHashtableTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java             
                (rev 0)
+++ branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java     
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,358 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import java.util.Enumeration;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.LRUHashtable} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class LRUHashtableTest extends TestCase {
+
+       private final int sampleElemsNumber = 100;
+       
+       /**
+        * Creates a double array of objects with a specified size
+        * where Object[i][0] is the key, and is an Integer,
+        * and Object[i][1] is the value 
+        * @param size the array size
+        * @return the objects double array
+        */
+       private Object[][] createSampleKeyVal(int size) {
+               Object[][] sampleObjects = new Object[size][2];
+               for (int i=0; i<sampleObjects.length;i++) {
+                       //key
+                       sampleObjects[i][0] = new Integer(i);
+                       //value
+                       sampleObjects[i][1] = new Object(); }           
+               return sampleObjects;
+       }
+       
+       /**
+        * Creates a LRUHashtable filled with the specified objects number
+        * @param size HashTable size
+        * @return the created LRUHashtable
+        */
+       private LRUHashtable createSampleHashTable(int size) {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               Object[][] sampleObjects = createSampleKeyVal(size);
+               for (int i=0;i<sampleObjects.length;i++)
+                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
+               return methodLRUht;
+       }
+       
+       /**
+        * It verifies if a key-value pair is present in
+        * a LRUHashtable
+        * @param aLRUht a LRUHashtable to check in
+        * @param aKey a key to find
+        * @param aValue the correspondent value
+        * @return true if the key is present and returned value is the same as 
in the argument
+        */
+       private boolean verifyKeyValPresence(LRUHashtable aLRUht, Object aKey, 
Object aValue) {
+               if (aLRUht.containsKey(aKey))
+                       return aLRUht.get(aKey).equals(aValue);
+               return false;
+       }
+       
+       /**
+        * Tests push(Object,Object) method
+        * providing null object as arguments 
+        * (after setting up a sample HashTable) 
+        * and verifying if the correct exception
+        * is raised
+        */
+       public void testPushNull() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               try {
+                       //a null value is admitted
+                       methodLRUht.push(new Object(),null);}           
+               catch (NullPointerException anException) { 
+                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
+               try {
+                       methodLRUht.push(null,null);
+                       fail("Expected Exception Error Not Thrown!"); }
+               catch (NullPointerException anException) { 
assertNotNull(anException); }
+               try {
+                       methodLRUht.push(null,new Object());
+                       fail("Expected Exception Error Not Thrown!"); }
+               catch (NullPointerException anException) { 
assertNotNull(anException); }
+               
+       }
+       
+       /**
+        * Tests push(Object,Object) method
+        * and verifies the behaviour when
+        * pushing the same object more than one
+        * time.
+        */
+       public void testPushSameObjTwice() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               Object[][] sampleObj = {
+                               { new Integer(sampleElemsNumber), new Object() 
}, 
+                               { new Integer(sampleElemsNumber+1), new 
Object() } };
+               
+               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
+               methodLRUht.push(sampleObj[1][0],sampleObj[1][1]);
+               
+               //check presence
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));  
        
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));
+               //check size
+               assertTrue(methodLRUht.size()==sampleElemsNumber+2);            
                
+               
+               //push the same object another time
+               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));
+               assertTrue(methodLRUht.size()==sampleElemsNumber+2);
+       }
+       
+       /**
+        * Tests push(Object,Object) method
+        * and verifies the behaviour when
+        * pushing the same key with two different
+        * values.
+        */
+       public void testPushSameKey() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               Object[][] sampleObj = {
+                               { new Integer(sampleElemsNumber), new Object() 
}, 
+                               { new Integer(sampleElemsNumber+1), new 
Object() } };
+               
+               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
+               methodLRUht.push(sampleObj[1][0],sampleObj[1][1]);
+               
+               //check presence
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));  
        
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));  
        
+               //check size
+               assertTrue(methodLRUht.size()==sampleElemsNumber+2);
+               
+               //creating and pushing a different value
+               sampleObj[0][1] = new Object();         
+               methodLRUht.push(sampleObj[0][0],sampleObj[0][1]);
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[0][0],sampleObj[0][1]));  
        
+               
assertTrue(verifyKeyValPresence(methodLRUht,sampleObj[1][0],sampleObj[1][1]));
+               assertTrue(methodLRUht.size()==sampleElemsNumber+2);
+       }
+
+       /**
+        * Tests popKey() method pushing
+        * and popping objects and
+        * verifying if their keys are correctly 
+        * (in a FIFO manner) fetched and the
+        * HashTable entry deleted
+        */
+       public void testPopKey() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)              
+                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
+               //getting keys
+               for (int i=0; i<sampleObjects.length; i++)              
+                       assertEquals(sampleObjects[i][0],methodLRUht.popKey());
+               //the HashTable must be empty
+               assertNull(methodLRUht.popKey());
+       }
+       
+       /**
+        * Tests popKey() method pushing
+        * and popping objects and
+        * verifying if their values are correctly 
+        * (in a FIFO manner) fetched and the
+        * HashTable entry deleted
+        */
+       public void testPopValue() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)
+                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
+               //getting values
+               for (int i=0; i<sampleObjects.length; i++)
+                       
assertEquals(sampleObjects[i][1],methodLRUht.popValue());
+               //the HashTable must be empty
+               assertNull(methodLRUht.popKey());
+       }
+
+       /**
+        * Tests peekValue() method pushing
+        * and popping objects and
+        * verifying if their peekValue is correct
+        */
+       public void testPeekValue() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)
+                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
+               //getting values
+               for (int i=0; i<sampleObjects.length; i++) {
+                       
assertEquals(sampleObjects[i][1],methodLRUht.peekValue());
+                       methodLRUht.popKey(); }
+               //the HashTable must be empty
+               assertNull(methodLRUht.peekValue());
+               //insert and fetch a null value
+               methodLRUht.push(new Object(),null);
+               assertNull(methodLRUht.peekValue());
+       }
+
+       /**
+        * Tests size() method
+        * pushing and popping elements into
+        * the LRUHashTable
+        */
+       public void testSize() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
+               assertTrue(methodLRUht.size()==0);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++) {            
+                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
+                       assertTrue(methodLRUht.size()==i+1); }
+               //popping keys
+               for (int i=sampleObjects.length-1; i>=0; i--) {
+                       methodLRUht.popKey(); 
+                       assertTrue(methodLRUht.size()==i); }
+       }
+
+       /**
+        * Tests removeKey(Object) method
+        * verifies if all elements are correctly
+        * removed checking the method return value,
+        * if the element is still contained and
+        * the HashTable size.
+        */
+       public void testRemoveKey() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)
+                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
+               //popping keys
+               for (int i=sampleObjects.length-1; i>=0; i--) {
+                       assertTrue(methodLRUht.removeKey(sampleObjects[i][0]));
+                       
assertFalse(methodLRUht.containsKey(sampleObjects[i][0]));
+                       assertTrue(methodLRUht.size()==i); }
+       }
+       
+       /**
+        * Tests removeKey(Object) providing a null
+        * key and trying to remove it after 
+        * setting up a sample queue.
+        */
+       public void testRemoveNullKey() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               try {
+                       methodLRUht.removeKey(null);
+                       fail("Expected Exception Error Not Thrown!"); }
+               catch (NullPointerException anException) { 
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests removeKey(Object) method
+        * trying to remove a not present key after 
+        * setting up a sample LRUHashtable.
+        */
+       public void testRemoveNotPresent() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               assertFalse(methodLRUht.removeKey(new Object()));
+       }
+
+       /**
+        * Tests containsKey(Object) method
+        * trying to find a not present key after 
+        * setting up a sample queue.
+        * Then it search for a present one.
+        */
+       public void testContainsKey() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               assertFalse(methodLRUht.containsKey(new Object()));
+               Object methodSampleObj = new Object();
+               methodLRUht.push(methodSampleObj,null);
+               assertTrue(methodLRUht.containsKey(methodSampleObj));
+       }
+
+       /**
+        * Tests get(Object) method
+        * trying to find a not present key after 
+        * setting up a sample HashTable,
+        * then it search a present key.
+        */
+       public void testGet() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               assertNull(methodLRUht.get(new Object()));
+               Object methodSampleKey = new Object();
+               Object methodSampleValue = new Object();
+               methodLRUht.push(methodSampleKey,methodSampleValue);
+               
assertEquals(methodLRUht.get(methodSampleKey),methodSampleValue);
+       }
+       
+       /**
+        * Tests get(Object) trying to fetch 
+        * a null key.
+        */
+       public void testGetNullKey() {
+               LRUHashtable methodLRUht = 
createSampleHashTable(sampleElemsNumber);
+               try {
+                       methodLRUht.get(null);
+                       fail("Expected Exception Error Not Thrown!"); }
+               catch (NullPointerException anException) { 
+                       assertNotNull(anException); }
+       }
+
+       /**
+        * Tests keys() method
+        * verifying if the Enumeration provided
+        * is correct
+        */
+       public void testKeys() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               Object[][] sampleObjects = 
createSampleKeyVal(sampleElemsNumber);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)
+                       
methodLRUht.push(sampleObjects[i][0],sampleObjects[i][1]);
+               Enumeration methodEnumeration = methodLRUht.keys();
+               int j=0;
+               while(methodEnumeration.hasMoreElements()) {                    
+                       
assertEquals(methodEnumeration.nextElement(),sampleObjects[j][0]);
+                       j++; }
+       }
+
+       /**
+        * Tests isEmpty() method
+        * trying it with a new generated
+        * HashTable and after popping
+        * out all keys in a sample LRUHashTable
+        */
+       public void testIsEmpty() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               assertTrue(methodLRUht.isEmpty());
+               methodLRUht = createSampleHashTable(sampleElemsNumber);
+               //popping keys
+               for (int i=0; i<sampleElemsNumber;i++)
+                       methodLRUht.popKey();
+               assertTrue(methodLRUht.isEmpty());
+       }
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/LRUQueueTest.java
===================================================================
--- trunk/freenet/test/freenet/support/LRUQueueTest.java        2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/LRUQueueTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,291 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import java.util.Enumeration;
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.LRUQueue} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class LRUQueueTest extends TestCase {
-       
-       private final int sampleElemsNumber = 100;
-
-       /**
-        * Creates an array of objects with a specified size
-        * @param size the array size
-        * @return the objects array
-        */
-       private Object[] createSampleObjects(int size) {
-               Object[] sampleObjects = new Object[size];
-               for (int i=0; i<sampleObjects.length;i++)
-                       sampleObjects[i] = new Object();
-               return sampleObjects;
-       }
-       
-       /**
-        * Creates a LRUQueue filled with the specified objects number
-        * @param size queue size
-        * @return the created LRUQueue
-        */
-       private LRUQueue createSampleQueue(int size) {
-               LRUQueue methodLRUQueue = new LRUQueue();
-               Object[] sampleObjects = createSampleObjects(size);
-               for (int i=0;i<sampleObjects.length;i++)
-                       methodLRUQueue.push(sampleObjects[i]);
-               return methodLRUQueue;
-       }
-       
-       /**
-        * Verifies if an element is present in an array
-        * @param anArray the array to search into
-        * @param aElementToSearch the object that must be found
-        * @return true if there is at least one reference to the object
-        */
-       private boolean isPresent(Object[] anArray, Object aElementToSearch) {
-               for(int i=0; i<anArray.length; i++)
-                       if (anArray[i].equals(aElementToSearch))
-                               return true;
-               return false;
-       }
-       
-       /**
-        * Verifies if the order of the last two elements in the
-        * queue is correct
-        * @param aLRUQueue the LRUQueue to check
-        * @param nextToLast the next-to-last element expected
-        * @param last the last element expected
-        * @return true if the order is correct
-        */
-       private boolean verifyLastElemsOrder(LRUQueue aLRUQueue, Object 
nextToLast, Object last ) {
-               boolean retVal = true;
-               int size = aLRUQueue.size();
-               Enumeration methodEnum = aLRUQueue.elements();
-               int counter = 0;
-               while (methodEnum.hasMoreElements()) {
-                       //next-to-last object
-                       if (counter == size-2)
-                               retVal &= 
(methodEnum.nextElement()).equals(nextToLast);
-                       //last object
-                       else if (counter == size-1)
-                               retVal &= 
(methodEnum.nextElement()).equals(last);
-                       else
-                               methodEnum.nextElement();
-                       counter++; }
-               return retVal;
-       }
-       
-       /**
-        * Tests push(Object) method
-        * providing a null object as argument 
-        * (after setting up a sample queue) 
-        * and verifying if the correct exception
-        * is raised
-        */
-       public void testPushNull() {
-               LRUQueue methodLRUQueue = 
this.createSampleQueue(sampleElemsNumber);
-               try {
-                       methodLRUQueue.push(null);
-                       fail("Expected Exception Error Not Thrown!"); }
-               catch (NullPointerException anException) {
-                       assertNotNull(anException);     }
-       }
-       
-       /**
-        * Tests push(Object) method
-        * and verifies the behaviour when
-        * pushing the same object more than one
-        * time.
-        */
-       public void testPushSameObjTwice() {
-               LRUQueue methodLRUQueue = 
this.createSampleQueue(sampleElemsNumber);
-               Object[] sampleObj = {new Object(), new Object()};
-               
-               methodLRUQueue.push(sampleObj[0]);
-               methodLRUQueue.push(sampleObj[1]);
-               
-               //check size
-               assertTrue(methodLRUQueue.size()==sampleElemsNumber+2);         
        
-               //check order
-               assertTrue(verifyLastElemsOrder(methodLRUQueue, sampleObj[0], 
sampleObj[1]));           
-               
-               methodLRUQueue.push(sampleObj[0]);
-               //check size
-               assertTrue(methodLRUQueue.size()==sampleElemsNumber+2);         
        
-               //check order
-               assertTrue(verifyLastElemsOrder(methodLRUQueue, sampleObj[1], 
sampleObj[0]));           
-       }
-
-       /**
-        * Tests pop() method pushing
-        * and popping objects and
-        * verifying if they are correctly (in a FIFO manner)
-        * fetched and deleted
-        */
-       public void testPop() {
-               LRUQueue methodLRUQueue = new LRUQueue();
-               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)              
-                       methodLRUQueue.push(sampleObjects[i]);
-               //getting objects
-               for (int i=0; i<sampleObjects.length; i++)              
-                       assertEquals(sampleObjects[i],methodLRUQueue.pop());
-               //the queue must be empty
-               assertNull(methodLRUQueue.pop());                               
-       }
-
-       /**
-        * Tests size() method checking size
-        * when empty, when putting each object
-        * and when popping each object.
-        */
-       public void testSize() {
-               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
-               LRUQueue methodLRUQueue = new LRUQueue();
-               assertTrue(methodLRUQueue.size()==0);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++) {
-                       methodLRUQueue.push(sampleObjects[i]);
-                       assertTrue(methodLRUQueue.size()==i+1); }
-               //getting all objects
-               for (int i=sampleObjects.length-1; i>=0; i--) {
-                       methodLRUQueue.pop();
-                       assertTrue(methodLRUQueue.size()==i); }
-               assertTrue(methodLRUQueue.size()==0);
-       }
-
-       /**
-        * Tests remove(Object) method
-        * verifies if all objects are correctly
-        * removed checking the method return value,
-        * if the object is still contained and
-        * the queue size.
-        */
-       public void testRemove() {
-               LRUQueue methodLRUQueue = new LRUQueue();
-               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
-               for (int i=0;i<sampleObjects.length;i++)
-                       methodLRUQueue.push(sampleObjects[i]);
-               //removing all objects in the opposite way used by pop() method
-               for(int i=sampleObjects.length-1;i>=0;i--) {
-                       assertTrue(methodLRUQueue.remove(sampleObjects[i]));
-                       assertFalse(methodLRUQueue.contains(sampleObjects[i])); 
-                       assertTrue(methodLRUQueue.size()==i); }
-       }
-       
-       /**
-        * Tests remove(Object) providing a null
-        * argument and trying to remove it after 
-        * setting up a sample queue.
-        */
-       public void testRemoveNull() {
-               LRUQueue methodLRUQueue = createSampleQueue(sampleElemsNumber);
-               try {
-                       methodLRUQueue.remove(null);
-                       fail("Expected Exception Error Not Thrown!"); }
-               catch (NullPointerException anException) {
-                       assertNotNull(anException);     }
-       }
-       
-       /**
-        * Tests remove(Object) method
-        * trying to remove a not present object after 
-        * setting up a sample queue.
-        */
-       public void testRemoveNotPresent() {
-               LRUQueue methodLRUQueue = createSampleQueue(sampleElemsNumber);
-               assertFalse(methodLRUQueue.remove(new Object()));
-       }
-
-       /**
-        * Tests contains(Object) method
-        * trying to find a not present object after 
-        * setting up a sample queue.
-        * Then it search a present object.
-        */
-       public void testContains() {
-               LRUQueue methodLRUQueue = createSampleQueue(sampleElemsNumber);
-               assertFalse(methodLRUQueue.contains(new Object()));
-               Object methodSampleObj = new Object();
-               methodLRUQueue.push(methodSampleObj);
-               assertTrue(methodLRUQueue.contains(methodSampleObj));
-       }
-
-       
-       /**
-        * Tests elements() method
-        * verifying if the Enumeration provided
-        * is correct
-        */
-       public void testElements() {
-               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
-               LRUQueue methodLRUQueue = new LRUQueue();
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)
-                       methodLRUQueue.push(sampleObjects[i]);
-               Enumeration methodEnumeration = methodLRUQueue.elements();
-               int j=0;
-               while(methodEnumeration.hasMoreElements()) {                    
-                       
assertEquals(methodEnumeration.nextElement(),sampleObjects[j]);
-                       j++; }
-       }
-
-       /**
-        * Tests toArray() method
-        * verifying if the array generated has the same object
-        * that are put into the created LRUQueue
-        */
-       public void testToArray() {
-               LRUQueue methodLRUQueue = new LRUQueue();
-               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
-               //pushing objects
-               for (int i=0; i<sampleObjects.length; i++)
-                       methodLRUQueue.push(sampleObjects[i]);
-               Object[] resultingArray = methodLRUQueue.toArray();
-               assertTrue(resultingArray.length==sampleObjects.length);
-               for(int i=0;i<sampleObjects.length;i++)
-                       assertTrue(isPresent(resultingArray,sampleObjects[i])); 
-       }
-       
-       /**
-        * Tests toArray() method
-        * when the queue is empty
-        */
-       public void testToArrayEmptyQueue() {
-               LRUQueue methodLRUQueue = new LRUQueue();
-               assertTrue(methodLRUQueue.toArray().length==0);
-       }
-
-       /**
-        * Tests isEmpty() method
-        * trying it with an empty queue
-        * and then with a sample queue.
-        */
-       public void testIsEmpty() {
-               LRUQueue methodLRUQueue = new LRUQueue();
-               assertTrue(methodLRUQueue.isEmpty());
-               methodLRUQueue = createSampleQueue(sampleElemsNumber);
-               assertFalse(methodLRUQueue.isEmpty());
-               //emptying the queue...
-               for(int i=0;i<sampleElemsNumber;i++)            
-                       methodLRUQueue.pop();
-               assertTrue(methodLRUQueue.isEmpty());
-       }
-}
\ No newline at end of file

Copied: branches/freenet-jfk/test/freenet/support/LRUQueueTest.java (from rev 
14796, trunk/freenet/test/freenet/support/LRUQueueTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/LRUQueueTest.java                 
        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/LRUQueueTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,291 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import java.util.Enumeration;
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.LRUQueue} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class LRUQueueTest extends TestCase {
+       
+       private final int sampleElemsNumber = 100;
+
+       /**
+        * Creates an array of objects with a specified size
+        * @param size the array size
+        * @return the objects array
+        */
+       private Object[] createSampleObjects(int size) {
+               Object[] sampleObjects = new Object[size];
+               for (int i=0; i<sampleObjects.length;i++)
+                       sampleObjects[i] = new Object();
+               return sampleObjects;
+       }
+       
+       /**
+        * Creates a LRUQueue filled with the specified objects number
+        * @param size queue size
+        * @return the created LRUQueue
+        */
+       private LRUQueue createSampleQueue(int size) {
+               LRUQueue methodLRUQueue = new LRUQueue();
+               Object[] sampleObjects = createSampleObjects(size);
+               for (int i=0;i<sampleObjects.length;i++)
+                       methodLRUQueue.push(sampleObjects[i]);
+               return methodLRUQueue;
+       }
+       
+       /**
+        * Verifies if an element is present in an array
+        * @param anArray the array to search into
+        * @param aElementToSearch the object that must be found
+        * @return true if there is at least one reference to the object
+        */
+       private boolean isPresent(Object[] anArray, Object aElementToSearch) {
+               for(int i=0; i<anArray.length; i++)
+                       if (anArray[i].equals(aElementToSearch))
+                               return true;
+               return false;
+       }
+       
+       /**
+        * Verifies if the order of the last two elements in the
+        * queue is correct
+        * @param aLRUQueue the LRUQueue to check
+        * @param nextToLast the next-to-last element expected
+        * @param last the last element expected
+        * @return true if the order is correct
+        */
+       private boolean verifyLastElemsOrder(LRUQueue aLRUQueue, Object 
nextToLast, Object last ) {
+               boolean retVal = true;
+               int size = aLRUQueue.size();
+               Enumeration methodEnum = aLRUQueue.elements();
+               int counter = 0;
+               while (methodEnum.hasMoreElements()) {
+                       //next-to-last object
+                       if (counter == size-2)
+                               retVal &= 
(methodEnum.nextElement()).equals(nextToLast);
+                       //last object
+                       else if (counter == size-1)
+                               retVal &= 
(methodEnum.nextElement()).equals(last);
+                       else
+                               methodEnum.nextElement();
+                       counter++; }
+               return retVal;
+       }
+       
+       /**
+        * Tests push(Object) method
+        * providing a null object as argument 
+        * (after setting up a sample queue) 
+        * and verifying if the correct exception
+        * is raised
+        */
+       public void testPushNull() {
+               LRUQueue methodLRUQueue = 
this.createSampleQueue(sampleElemsNumber);
+               try {
+                       methodLRUQueue.push(null);
+                       fail("Expected Exception Error Not Thrown!"); }
+               catch (NullPointerException anException) {
+                       assertNotNull(anException);     }
+       }
+       
+       /**
+        * Tests push(Object) method
+        * and verifies the behaviour when
+        * pushing the same object more than one
+        * time.
+        */
+       public void testPushSameObjTwice() {
+               LRUQueue methodLRUQueue = 
this.createSampleQueue(sampleElemsNumber);
+               Object[] sampleObj = {new Object(), new Object()};
+               
+               methodLRUQueue.push(sampleObj[0]);
+               methodLRUQueue.push(sampleObj[1]);
+               
+               //check size
+               assertTrue(methodLRUQueue.size()==sampleElemsNumber+2);         
        
+               //check order
+               assertTrue(verifyLastElemsOrder(methodLRUQueue, sampleObj[0], 
sampleObj[1]));           
+               
+               methodLRUQueue.push(sampleObj[0]);
+               //check size
+               assertTrue(methodLRUQueue.size()==sampleElemsNumber+2);         
        
+               //check order
+               assertTrue(verifyLastElemsOrder(methodLRUQueue, sampleObj[1], 
sampleObj[0]));           
+       }
+
+       /**
+        * Tests pop() method pushing
+        * and popping objects and
+        * verifying if they are correctly (in a FIFO manner)
+        * fetched and deleted
+        */
+       public void testPop() {
+               LRUQueue methodLRUQueue = new LRUQueue();
+               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)              
+                       methodLRUQueue.push(sampleObjects[i]);
+               //getting objects
+               for (int i=0; i<sampleObjects.length; i++)              
+                       assertEquals(sampleObjects[i],methodLRUQueue.pop());
+               //the queue must be empty
+               assertNull(methodLRUQueue.pop());                               
+       }
+
+       /**
+        * Tests size() method checking size
+        * when empty, when putting each object
+        * and when popping each object.
+        */
+       public void testSize() {
+               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
+               LRUQueue methodLRUQueue = new LRUQueue();
+               assertTrue(methodLRUQueue.size()==0);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++) {
+                       methodLRUQueue.push(sampleObjects[i]);
+                       assertTrue(methodLRUQueue.size()==i+1); }
+               //getting all objects
+               for (int i=sampleObjects.length-1; i>=0; i--) {
+                       methodLRUQueue.pop();
+                       assertTrue(methodLRUQueue.size()==i); }
+               assertTrue(methodLRUQueue.size()==0);
+       }
+
+       /**
+        * Tests remove(Object) method
+        * verifies if all objects are correctly
+        * removed checking the method return value,
+        * if the object is still contained and
+        * the queue size.
+        */
+       public void testRemove() {
+               LRUQueue methodLRUQueue = new LRUQueue();
+               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
+               for (int i=0;i<sampleObjects.length;i++)
+                       methodLRUQueue.push(sampleObjects[i]);
+               //removing all objects in the opposite way used by pop() method
+               for(int i=sampleObjects.length-1;i>=0;i--) {
+                       assertTrue(methodLRUQueue.remove(sampleObjects[i]));
+                       assertFalse(methodLRUQueue.contains(sampleObjects[i])); 
+                       assertTrue(methodLRUQueue.size()==i); }
+       }
+       
+       /**
+        * Tests remove(Object) providing a null
+        * argument and trying to remove it after 
+        * setting up a sample queue.
+        */
+       public void testRemoveNull() {
+               LRUQueue methodLRUQueue = createSampleQueue(sampleElemsNumber);
+               try {
+                       methodLRUQueue.remove(null);
+                       fail("Expected Exception Error Not Thrown!"); }
+               catch (NullPointerException anException) {
+                       assertNotNull(anException);     }
+       }
+       
+       /**
+        * Tests remove(Object) method
+        * trying to remove a not present object after 
+        * setting up a sample queue.
+        */
+       public void testRemoveNotPresent() {
+               LRUQueue methodLRUQueue = createSampleQueue(sampleElemsNumber);
+               assertFalse(methodLRUQueue.remove(new Object()));
+       }
+
+       /**
+        * Tests contains(Object) method
+        * trying to find a not present object after 
+        * setting up a sample queue.
+        * Then it search a present object.
+        */
+       public void testContains() {
+               LRUQueue methodLRUQueue = createSampleQueue(sampleElemsNumber);
+               assertFalse(methodLRUQueue.contains(new Object()));
+               Object methodSampleObj = new Object();
+               methodLRUQueue.push(methodSampleObj);
+               assertTrue(methodLRUQueue.contains(methodSampleObj));
+       }
+
+       
+       /**
+        * Tests elements() method
+        * verifying if the Enumeration provided
+        * is correct
+        */
+       public void testElements() {
+               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
+               LRUQueue methodLRUQueue = new LRUQueue();
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)
+                       methodLRUQueue.push(sampleObjects[i]);
+               Enumeration methodEnumeration = methodLRUQueue.elements();
+               int j=0;
+               while(methodEnumeration.hasMoreElements()) {                    
+                       
assertEquals(methodEnumeration.nextElement(),sampleObjects[j]);
+                       j++; }
+       }
+
+       /**
+        * Tests toArray() method
+        * verifying if the array generated has the same object
+        * that are put into the created LRUQueue
+        */
+       public void testToArray() {
+               LRUQueue methodLRUQueue = new LRUQueue();
+               Object[] sampleObjects = createSampleObjects(sampleElemsNumber);
+               //pushing objects
+               for (int i=0; i<sampleObjects.length; i++)
+                       methodLRUQueue.push(sampleObjects[i]);
+               Object[] resultingArray = methodLRUQueue.toArray();
+               assertTrue(resultingArray.length==sampleObjects.length);
+               for(int i=0;i<sampleObjects.length;i++)
+                       assertTrue(isPresent(resultingArray,sampleObjects[i])); 
+       }
+       
+       /**
+        * Tests toArray() method
+        * when the queue is empty
+        */
+       public void testToArrayEmptyQueue() {
+               LRUQueue methodLRUQueue = new LRUQueue();
+               assertTrue(methodLRUQueue.toArray().length==0);
+       }
+
+       /**
+        * Tests isEmpty() method
+        * trying it with an empty queue
+        * and then with a sample queue.
+        */
+       public void testIsEmpty() {
+               LRUQueue methodLRUQueue = new LRUQueue();
+               assertTrue(methodLRUQueue.isEmpty());
+               methodLRUQueue = createSampleQueue(sampleElemsNumber);
+               assertFalse(methodLRUQueue.isEmpty());
+               //emptying the queue...
+               for(int i=0;i<sampleElemsNumber;i++)            
+                       methodLRUQueue.pop();
+               assertTrue(methodLRUQueue.isEmpty());
+       }
+}
\ No newline at end of file

Deleted: branches/freenet-jfk/test/freenet/support/MultiValueTableTest.java
===================================================================
--- trunk/freenet/test/freenet/support/MultiValueTableTest.java 2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/MultiValueTableTest.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,345 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-import java.util.Arrays;
-
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.MultiValueTable} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class MultiValueTableTest extends TestCase {
-       
-       private final int sampleKeyNumber = 100;
-       private final int sampleMaxValueNumber = 3;
-       private final boolean sampleIsRandom = true;
-       
-       /**
-        * Create a Object[][] filled with increasing Integers as keys
-        * and a List of generic Objects as values.
-        * @param keysNumber the number of keys to create
-        * @param valueNumber the maximum value number per key
-        * @param isRandom if true each key could have [1,valuesNumber] values
-        * chosen randomly, if false each key will have valuesNumber values
-        * @return the Object[][] created
-        */
-       private Object[][] createSampleKeyMultiVal(int keysNumber, int 
valuesNumber, boolean isRandom) {
-               Object[][] sampleObjects = new Object[keysNumber][valuesNumber];
-               int methodValuesNumber = valuesNumber;
-               Random rnd = new Random();
-               for (int i=0; i<sampleObjects.length;i++) {
-                       if (isRandom) 
-                               methodValuesNumber = 
1+rnd.nextInt(valuesNumber);
-                       sampleObjects[i][0] = new Integer(i);
-                       sampleObjects[i][1] = 
fillSampleValuesList(methodValuesNumber); }
-               return sampleObjects;
-       }
-       
-       /**
-        * Create a sample List filled
-        * with the specified number of
-        * generic objects
-        * @param valuesNumber number of objects to create
-        * @return the sample List
-        */
-       private List fillSampleValuesList(int valuesNumber) {
-               List sampleValues = new LinkedList();
-               for(int i=0; i<valuesNumber;i++)
-                       sampleValues.add(new Object());
-               return sampleValues;
-       }
-       
-       /**
-        * Create a sample MultiValueTable
-        * @param keyNumber the number of key to insert in the MultiValueTable
-        * @param maxValueNumber the maximum number of value for each key
-        * @param isRandom true if the maxValueNumber is an upper bound, false 
if it is the actual value
-        * @return the sample MultiValueTable created
-        */
-       private MultiValueTable createSampleMultiValueTable(int keyNumber, int 
maxValueNumber, boolean isRandom) {
-               Object[][] sampleObjects = 
createSampleKeyMultiVal(keyNumber,maxValueNumber,isRandom);
-               return fillMultiValueTable(sampleObjects);
-       }
-       
-       /**
-        * Given an Enumeration it returns the number of present objects
-        * @param anEnumeration
-        * @return the number of present objects
-        */
-       private int enumerationSize(Enumeration anEnumeration) {
-               int counter = 0;
-               while(anEnumeration.hasMoreElements()) {
-                       anEnumeration.nextElement();
-                       counter++;}
-               return counter;
-       }
-       
-       /**
-        * Fill a new MultiValueTable from a Object[][] provided.
-        * The Object[][] must be in the same form generated by
-        * createSampleKeyMultiVal method.
-        * @param sampleObjects Object[][] array, with [i][0] as key and [i][1] 
as list of values
-        * @return the created MultiValueTable
-        */
-       private MultiValueTable fillMultiValueTable(Object[][] sampleObjects) {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               Iterator itr;
-               for(int i=0;i<sampleKeyNumber;i++) {
-                       itr = ((List)(sampleObjects[i][1])).iterator();
-                       while( itr.hasNext())
-                               methodMVTable.put(sampleObjects[i][0], 
itr.next());
-               }
-               return methodMVTable;
-       }
-
-       /**
-        * Tests if there are problems when
-        * putting values in a sample
-        * MultiValueTable
-        */
-       public void testPut() {
-               assertNotNull(
-                               
createSampleMultiValueTable(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom));
-       }
-
-       /**
-        * Tests get(Object) method with both
-        * present keys and not present
-        */
-       public void testGet() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               assertNull(methodMVTable.get(new Object()));
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               for(int i=0;i<sampleObjects.length;i++)
-                       
assertEquals(methodMVTable.get(sampleObjects[i][0]),((List)sampleObjects[i][1]).get(0));
-       }
-
-       /**
-        * Tests containsKey(Object) method verifying
-        * if all keys inserted are correctly found.
-        * It verifies the correct behavior with empty
-        * MultiValueTable and not present keys, too.
-        */
-       public void testContainsKey() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               assertFalse(methodMVTable.containsKey(new Object()));
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               for(int i=0;i<sampleObjects.length;i++)
-                       
assertTrue(methodMVTable.containsKey(sampleObjects[i][0]));
-               assertFalse(methodMVTable.containsKey(new Object()));
-       }
-
-       /**
-        * Tests containsElement(Object,Object) method
-        * verifying if all values inserted are correctly
-        * found.
-        * It verifies the correct behavior with empty
-        * MultiValueTable and not present Elements, too.
-        */
-       public void testContainsElement() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               assertFalse(methodMVTable.containsElement(new Object(),new 
Object()));
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               Iterator iter;
-               for(int i=0;i<sampleObjects.length;i++) {
-                       iter = ((List)(sampleObjects[i][1])).iterator();
-                       
assertFalse(methodMVTable.containsElement(sampleObjects[i][0],new Object()));
-                       while(iter.hasNext())
-                               
assertTrue(methodMVTable.containsElement(sampleObjects[i][0],iter.next()));
-               }
-       }
-
-       /**
-        * Tests getAll() method
-        */
-       public void testGetAll() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               //TODO: verifies if an Exception is necessary
-               methodMVTable.getAll(new Object());
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               Iterator iter;
-               Enumeration methodEnumeration;
-               for(int i=0;i<sampleObjects.length;i++) {
-                       iter = ((List)(sampleObjects[i][1])).iterator();
-                       methodEnumeration = 
methodMVTable.getAll(sampleObjects[i][0]);
-                       while(iter.hasNext())
-                               
assertEquals(methodEnumeration.nextElement(),iter.next());
-               }
-       }
-
-       /**
-        * Tests countAll() method
-        */
-       public void testCountAll() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               assertEquals(methodMVTable.countAll(new Object()),0);
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               for(int i=0;i<sampleObjects.length;i++)
-                       
assertEquals(((List)(sampleObjects[i][1])).size(),methodMVTable.countAll(sampleObjects[i][0]));
-       }
-
-       /**
-        * Tests getSync(Object) method fetching
-        * both present and not present keys
-        */
-       public void testGetSync() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               assertNull(methodMVTable.getSync(new Object()));
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               for(int i=0;i<sampleObjects.length;i++)
-                       
assertEquals(methodMVTable.getSync(sampleObjects[i][0]),((List)sampleObjects[i][1]));
-       }
-
-       /**
-        * Tests getArray(Object) method both
-        * with a present key and a not present key
-        */
-       public void testGetArray() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               assertNull(methodMVTable.getArray(new Object()));
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               for(int i=0;i<sampleObjects.length;i++)
-                       
assertTrue(Arrays.equals(((List)(sampleObjects[i][1])).toArray(),methodMVTable.getArray(sampleObjects[i][0])));
-       }
-
-       /**
-        * Tests remove(Object) method trying
-        * to remove all keys inserted in a MultiValueTable.
-        * It verifies the behavior when removing a not present
-        * key, too.
-        */
-       public void testRemove() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               //TODO: shouldn't it raise an exception?
-               methodMVTable.remove(new Object());
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               for(int i=0;i<sampleObjects.length;i++)
-                       methodMVTable.remove(sampleObjects[i][0]);
-               assertTrue(methodMVTable.isEmpty());
-       }
-
-       /**
-        * Tests isEmpty() method with an empty MultiValueTable,
-        * after putting objects and after removing all of them.
-        */
-       public void testIsEmpty() {
-               MultiValueTable methodMVTable = new MultiValueTable();
-               assertTrue(methodMVTable.isEmpty());
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               methodMVTable = fillMultiValueTable(sampleObjects);
-               assertFalse(methodMVTable.isEmpty());
-               for(int i=0;i<sampleObjects.length;i++)
-                       methodMVTable.remove(sampleObjects[i][0]);
-               assertTrue(methodMVTable.isEmpty());
-       }
-
-       /**
-        * Tests clear() method filling a MultiValueTable
-        * and verifying if all keys are correctly removed.
-        * Finally it verifies the result of isEmpty() method.
-        */
-       public void testClear() {
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               MultiValueTable methodMVTable = 
fillMultiValueTable(sampleObjects);
-               methodMVTable.clear();
-               for(int i=0;i<sampleObjects.length;i++)
-                       
assertFalse(methodMVTable.containsKey(sampleObjects[i][0]));
-               assertTrue(methodMVTable.isEmpty());
-       }
-
-       /**
-        * Tests removeElement(Object,Object) removing all elements from
-        * a sample MultiValueTable, and verifying if they are correctly
-        * removed and if the result of isEmpty() method is correct.
-        */
-       public void testRemoveElement() {
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               MultiValueTable methodMVTable = 
fillMultiValueTable(sampleObjects);
-               Object methodValue;
-               Iterator iter;
-               for(int i=0;i<sampleObjects.length;i++) {
-                       iter = ((List)(sampleObjects[i][1])).iterator();
-                       
assertFalse(methodMVTable.removeElement(sampleObjects[i][0],new Object()));
-                       while(iter.hasNext()) {
-                               methodValue = iter.next();
-                               
assertTrue(methodMVTable.removeElement(sampleObjects[i][0],methodValue));
-                               
assertFalse(methodMVTable.containsElement(sampleObjects[i][0],methodValue));
-                       }
-               }
-               assertTrue(methodMVTable.isEmpty());
-       }
-
-       /**
-        * Tests keys() method verifying if all keys inserted are
-        * correctly present in the resulting Enumeration
-        */
-       public void testKeys() {
-               Object[][] sampleObjects = 
-                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
-               MultiValueTable methodMVTable = 
fillMultiValueTable(sampleObjects);
-               //TODO: shouldn't it respect keys order?
-               int j = sampleObjects.length-1;
-               Enumeration methodEnumeration = methodMVTable.keys();
-               while(methodEnumeration.hasMoreElements()) {
-                       
assertEquals(sampleObjects[j][0],methodEnumeration.nextElement());
-                       j--;}
-       }
-       
-       /**
-        * Tests elements() and keys() method
-        * verifying their behavior when putting the same
-        * value for different keys.
-        */
-       public void testDifferentKeysSameElement() {
-               int keysNumber = 2;
-               MultiValueTable methodMVTable = new MultiValueTable();
-               String sampleValue = "sampleValue";
-               //putting the same value for different keys
-               for(int i=0;i<keysNumber;i++)
-                       methodMVTable.put(new Object(),sampleValue);
-
-               assertEquals(enumerationSize(methodMVTable.elements()),1);
-               assertEquals(enumerationSize(methodMVTable.keys()),keysNumber);
-       }
-
-}

Copied: branches/freenet-jfk/test/freenet/support/MultiValueTableTest.java 
(from rev 14796, trunk/freenet/test/freenet/support/MultiValueTableTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/MultiValueTableTest.java          
                (rev 0)
+++ branches/freenet-jfk/test/freenet/support/MultiValueTableTest.java  
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,345 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.MultiValueTable} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class MultiValueTableTest extends TestCase {
+       
+       private final int sampleKeyNumber = 100;
+       private final int sampleMaxValueNumber = 3;
+       private final boolean sampleIsRandom = true;
+       
+       /**
+        * Create a Object[][] filled with increasing Integers as keys
+        * and a List of generic Objects as values.
+        * @param keysNumber the number of keys to create
+        * @param valueNumber the maximum value number per key
+        * @param isRandom if true each key could have [1,valuesNumber] values
+        * chosen randomly, if false each key will have valuesNumber values
+        * @return the Object[][] created
+        */
+       private Object[][] createSampleKeyMultiVal(int keysNumber, int 
valuesNumber, boolean isRandom) {
+               Object[][] sampleObjects = new Object[keysNumber][valuesNumber];
+               int methodValuesNumber = valuesNumber;
+               Random rnd = new Random();
+               for (int i=0; i<sampleObjects.length;i++) {
+                       if (isRandom) 
+                               methodValuesNumber = 
1+rnd.nextInt(valuesNumber);
+                       sampleObjects[i][0] = new Integer(i);
+                       sampleObjects[i][1] = 
fillSampleValuesList(methodValuesNumber); }
+               return sampleObjects;
+       }
+       
+       /**
+        * Create a sample List filled
+        * with the specified number of
+        * generic objects
+        * @param valuesNumber number of objects to create
+        * @return the sample List
+        */
+       private List fillSampleValuesList(int valuesNumber) {
+               List sampleValues = new LinkedList();
+               for(int i=0; i<valuesNumber;i++)
+                       sampleValues.add(new Object());
+               return sampleValues;
+       }
+       
+       /**
+        * Create a sample MultiValueTable
+        * @param keyNumber the number of key to insert in the MultiValueTable
+        * @param maxValueNumber the maximum number of value for each key
+        * @param isRandom true if the maxValueNumber is an upper bound, false 
if it is the actual value
+        * @return the sample MultiValueTable created
+        */
+       private MultiValueTable createSampleMultiValueTable(int keyNumber, int 
maxValueNumber, boolean isRandom) {
+               Object[][] sampleObjects = 
createSampleKeyMultiVal(keyNumber,maxValueNumber,isRandom);
+               return fillMultiValueTable(sampleObjects);
+       }
+       
+       /**
+        * Given an Enumeration it returns the number of present objects
+        * @param anEnumeration
+        * @return the number of present objects
+        */
+       private int enumerationSize(Enumeration anEnumeration) {
+               int counter = 0;
+               while(anEnumeration.hasMoreElements()) {
+                       anEnumeration.nextElement();
+                       counter++;}
+               return counter;
+       }
+       
+       /**
+        * Fill a new MultiValueTable from a Object[][] provided.
+        * The Object[][] must be in the same form generated by
+        * createSampleKeyMultiVal method.
+        * @param sampleObjects Object[][] array, with [i][0] as key and [i][1] 
as list of values
+        * @return the created MultiValueTable
+        */
+       private MultiValueTable fillMultiValueTable(Object[][] sampleObjects) {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               Iterator itr;
+               for(int i=0;i<sampleKeyNumber;i++) {
+                       itr = ((List)(sampleObjects[i][1])).iterator();
+                       while( itr.hasNext())
+                               methodMVTable.put(sampleObjects[i][0], 
itr.next());
+               }
+               return methodMVTable;
+       }
+
+       /**
+        * Tests if there are problems when
+        * putting values in a sample
+        * MultiValueTable
+        */
+       public void testPut() {
+               assertNotNull(
+                               
createSampleMultiValueTable(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom));
+       }
+
+       /**
+        * Tests get(Object) method with both
+        * present keys and not present
+        */
+       public void testGet() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               assertNull(methodMVTable.get(new Object()));
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               for(int i=0;i<sampleObjects.length;i++)
+                       
assertEquals(methodMVTable.get(sampleObjects[i][0]),((List)sampleObjects[i][1]).get(0));
+       }
+
+       /**
+        * Tests containsKey(Object) method verifying
+        * if all keys inserted are correctly found.
+        * It verifies the correct behavior with empty
+        * MultiValueTable and not present keys, too.
+        */
+       public void testContainsKey() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               assertFalse(methodMVTable.containsKey(new Object()));
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               for(int i=0;i<sampleObjects.length;i++)
+                       
assertTrue(methodMVTable.containsKey(sampleObjects[i][0]));
+               assertFalse(methodMVTable.containsKey(new Object()));
+       }
+
+       /**
+        * Tests containsElement(Object,Object) method
+        * verifying if all values inserted are correctly
+        * found.
+        * It verifies the correct behavior with empty
+        * MultiValueTable and not present Elements, too.
+        */
+       public void testContainsElement() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               assertFalse(methodMVTable.containsElement(new Object(),new 
Object()));
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               Iterator iter;
+               for(int i=0;i<sampleObjects.length;i++) {
+                       iter = ((List)(sampleObjects[i][1])).iterator();
+                       
assertFalse(methodMVTable.containsElement(sampleObjects[i][0],new Object()));
+                       while(iter.hasNext())
+                               
assertTrue(methodMVTable.containsElement(sampleObjects[i][0],iter.next()));
+               }
+       }
+
+       /**
+        * Tests getAll() method
+        */
+       public void testGetAll() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               //TODO: verifies if an Exception is necessary
+               methodMVTable.getAll(new Object());
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               Iterator iter;
+               Enumeration methodEnumeration;
+               for(int i=0;i<sampleObjects.length;i++) {
+                       iter = ((List)(sampleObjects[i][1])).iterator();
+                       methodEnumeration = 
methodMVTable.getAll(sampleObjects[i][0]);
+                       while(iter.hasNext())
+                               
assertEquals(methodEnumeration.nextElement(),iter.next());
+               }
+       }
+
+       /**
+        * Tests countAll() method
+        */
+       public void testCountAll() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               assertEquals(methodMVTable.countAll(new Object()),0);
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               for(int i=0;i<sampleObjects.length;i++)
+                       
assertEquals(((List)(sampleObjects[i][1])).size(),methodMVTable.countAll(sampleObjects[i][0]));
+       }
+
+       /**
+        * Tests getSync(Object) method fetching
+        * both present and not present keys
+        */
+       public void testGetSync() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               assertNull(methodMVTable.getSync(new Object()));
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               for(int i=0;i<sampleObjects.length;i++)
+                       
assertEquals(methodMVTable.getSync(sampleObjects[i][0]),((List)sampleObjects[i][1]));
+       }
+
+       /**
+        * Tests getArray(Object) method both
+        * with a present key and a not present key
+        */
+       public void testGetArray() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               assertNull(methodMVTable.getArray(new Object()));
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               for(int i=0;i<sampleObjects.length;i++)
+                       
assertTrue(Arrays.equals(((List)(sampleObjects[i][1])).toArray(),methodMVTable.getArray(sampleObjects[i][0])));
+       }
+
+       /**
+        * Tests remove(Object) method trying
+        * to remove all keys inserted in a MultiValueTable.
+        * It verifies the behavior when removing a not present
+        * key, too.
+        */
+       public void testRemove() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               //TODO: shouldn't it raise an exception?
+               methodMVTable.remove(new Object());
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               for(int i=0;i<sampleObjects.length;i++)
+                       methodMVTable.remove(sampleObjects[i][0]);
+               assertTrue(methodMVTable.isEmpty());
+       }
+
+       /**
+        * Tests isEmpty() method with an empty MultiValueTable,
+        * after putting objects and after removing all of them.
+        */
+       public void testIsEmpty() {
+               MultiValueTable methodMVTable = new MultiValueTable();
+               assertTrue(methodMVTable.isEmpty());
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               methodMVTable = fillMultiValueTable(sampleObjects);
+               assertFalse(methodMVTable.isEmpty());
+               for(int i=0;i<sampleObjects.length;i++)
+                       methodMVTable.remove(sampleObjects[i][0]);
+               assertTrue(methodMVTable.isEmpty());
+       }
+
+       /**
+        * Tests clear() method filling a MultiValueTable
+        * and verifying if all keys are correctly removed.
+        * Finally it verifies the result of isEmpty() method.
+        */
+       public void testClear() {
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               MultiValueTable methodMVTable = 
fillMultiValueTable(sampleObjects);
+               methodMVTable.clear();
+               for(int i=0;i<sampleObjects.length;i++)
+                       
assertFalse(methodMVTable.containsKey(sampleObjects[i][0]));
+               assertTrue(methodMVTable.isEmpty());
+       }
+
+       /**
+        * Tests removeElement(Object,Object) removing all elements from
+        * a sample MultiValueTable, and verifying if they are correctly
+        * removed and if the result of isEmpty() method is correct.
+        */
+       public void testRemoveElement() {
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               MultiValueTable methodMVTable = 
fillMultiValueTable(sampleObjects);
+               Object methodValue;
+               Iterator iter;
+               for(int i=0;i<sampleObjects.length;i++) {
+                       iter = ((List)(sampleObjects[i][1])).iterator();
+                       
assertFalse(methodMVTable.removeElement(sampleObjects[i][0],new Object()));
+                       while(iter.hasNext()) {
+                               methodValue = iter.next();
+                               
assertTrue(methodMVTable.removeElement(sampleObjects[i][0],methodValue));
+                               
assertFalse(methodMVTable.containsElement(sampleObjects[i][0],methodValue));
+                       }
+               }
+               assertTrue(methodMVTable.isEmpty());
+       }
+
+       /**
+        * Tests keys() method verifying if all keys inserted are
+        * correctly present in the resulting Enumeration
+        */
+       public void testKeys() {
+               Object[][] sampleObjects = 
+                       
createSampleKeyMultiVal(sampleKeyNumber,sampleMaxValueNumber,sampleIsRandom);
+               MultiValueTable methodMVTable = 
fillMultiValueTable(sampleObjects);
+               //TODO: shouldn't it respect keys order?
+               int j = sampleObjects.length-1;
+               Enumeration methodEnumeration = methodMVTable.keys();
+               while(methodEnumeration.hasMoreElements()) {
+                       
assertEquals(sampleObjects[j][0],methodEnumeration.nextElement());
+                       j--;}
+       }
+       
+       /**
+        * Tests elements() and keys() method
+        * verifying their behavior when putting the same
+        * value for different keys.
+        */
+       public void testDifferentKeysSameElement() {
+               int keysNumber = 2;
+               MultiValueTable methodMVTable = new MultiValueTable();
+               String sampleValue = "sampleValue";
+               //putting the same value for different keys
+               for(int i=0;i<keysNumber;i++)
+                       methodMVTable.put(new Object(),sampleValue);
+
+               assertEquals(enumerationSize(methodMVTable.elements()),1);
+               assertEquals(enumerationSize(methodMVTable.keys()),keysNumber);
+       }
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/SimpleFieldSetTest.java
===================================================================
--- trunk/freenet/test/freenet/support/SimpleFieldSetTest.java  2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/SimpleFieldSetTest.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,701 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package freenet.support;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Arrays;
-import java.util.Iterator;
-
-import freenet.node.FSParseException;
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.SimpleFieldSet} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class SimpleFieldSetTest extends TestCase {
-
-       private static final char KEY_VALUE_SEPARATOR = '=';
-       
-       /* A double string array used across all tests 
-        * it must not be changed in order to perform tests
-        * correctly */
-       private static final String[][] SAMPLE_STRING_PAIRS = {  
-               //directSubset
-               {"foo","bar"},                  
-               {"foo.bar","foobar"},   
-               {"foo.bar.boo.far","foobar"},
-               {"foo2","foobar.fooboo.foofar.foofoo"},
-               {"foo3",KEY_VALUE_SEPARATOR+"bar"} };
-       
-       private static final String SAMPLE_END_MARKER = "END";
-       
-       /**
-        * Tests putSingle(String,String) method
-        * trying to store a key with two paired
-        * multi_level_chars (i.e. "..").
-        */
-       public void 
testSimpleFieldSetPutSingle_StringString_WithTwoPairedMultiLevelChars() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               String methodKey = "foo..bar.";
-               String methodValue = "foobar";
-               methodSFS.putSingle(methodKey,methodValue);
-               
assertEquals(methodSFS.subset("foo").subset("").subset("bar").get(""),methodValue);
-               assertEquals(methodSFS.get(methodKey),methodValue);
-       }
-       
-       /**
-        * Tests putAppend(String,String) method
-        * trying to store a key with two paired
-        * multi_level_chars (i.e. "..").
-        */
-       public void 
testSimpleFieldSetPutAppend_StringString_WithTwoPairedMultiLevelChars() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               String methodKey = "foo..bar";
-               String methodValue = "foobar";
-               methodSFS.putAppend(methodKey,methodValue);
-               assertEquals(methodSFS.get(methodKey),methodValue);
-       }
-       
-       /**
-        * Tests put() and get() methods
-        * using a normal Map behaviour
-        * and without MULTI_LEVEL_CHARs
-        */
-       public void testSimpleFieldSetPutAndGet_NoMultiLevel(){
-               String[][] methodPairsArray = { 
-                               
{"A","a"},{"B","b"},{"C","c"},{"D","d"},{"E","e"},{"F","f"} };
-               assertTrue(checkPutAndGetPairs(methodPairsArray));
-       }
-       
-       /**
-        * Tests put() and get() methods
-        * using a normal Map behaviour
-        * and with MULTI_LEVEL_CHARs
-        */
-       public void testSimpleFieldSetPutAndGet_MultiLevel(){
-               String[][] methodPairsArray_DoubleLevel = { 
-                               {"A.A","aa"},
-                               {"A.B","ab"},
-                               {"A.C","ac"},
-                               {"A.D","ad"},
-                               {"A.E","ae"},
-                               {"A.F","af"} };
-               String[][] methodPairsArray_MultiLevel = { 
-                               {"A.A.A.A","aa"},
-                               {"A.B.A","ab"},
-                               {"A.C.Cc","ac"},
-                               {"A.D.F","ad"},
-                               {"A.E.G","ae"},
-                               {"A.F.J.II.UI.BOO","af"} };
-               assertTrue(checkPutAndGetPairs(methodPairsArray_DoubleLevel));
-               assertTrue(checkPutAndGetPairs(methodPairsArray_MultiLevel));
-       }
-       
-       
-       /**
-        * It puts key-value pairs in a SimpleFieldSet
-        * and verify if it can do the correspondant
-        * get correctly.
-        * @param aPairsArray
-        * @return true if it is correct
-        */
-       private boolean checkPutAndGetPairs(String[][] aPairsArray) {
-               boolean retValue = true;
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               //putting values
-               for (int i = 0; i < aPairsArray.length; i++)
-                       methodSFS.putSingle(aPairsArray[i][0], 
aPairsArray[i][1]);
-               for (int i = 0; i < aPairsArray.length; i++)            
//getting values
-                       retValue &= 
methodSFS.get(aPairsArray[i][0]).equals(aPairsArray[i][1]);
-               retValue &= checkSimpleFieldSetSize(methodSFS, 
aPairsArray.length);
-               return retValue;
-       }
-       
-       /**
-        * Tests subset(String) method
-        * putting two levels keys and
-        * fetching it through subset() method
-        * on the first level and then get()
-        * on the second
-        */
-       public void testSimpleFieldSetSubset_String() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               String[][] methodPairsArray_MultiLevel = { 
-                               {"A","A","aa"},
-                               {"A","B","ab"},
-                               {"A","C","ac"},
-                               {"A","D","ad"},
-                               {"A","E","ae"},
-                               {"A","F","af"} };
-               //putting values
-               for (int i = 0; i < methodPairsArray_MultiLevel.length; i++)
-                       methodSFS.putSingle(methodPairsArray_MultiLevel[i][0] 
-                                           + SimpleFieldSet.MULTI_LEVEL_CHAR 
-                                           + 
methodPairsArray_MultiLevel[i][1], 
-                                           methodPairsArray_MultiLevel[i][2]);
-               //getting subsets and then values
-               for (int i = 0; i < methodPairsArray_MultiLevel.length; i++)
-                       assertEquals(
-                                       methodSFS.subset(
-                                                       
methodPairsArray_MultiLevel[i][0]).get(methodPairsArray_MultiLevel[i][1]),
-                                       methodPairsArray_MultiLevel[i][2]);
-               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray_MultiLevel.length));
-       }
-       
-       /**
-        * Tests putAllOverwrite(SimpleFieldSet) method
-        * trying to overwrite a whole SimpleFieldSet
-        * with another with same keys but different
-        * values
-        */
-       public void testPutAllOverwrite() {
-               String methodAppendedString = "buu";
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               SimpleFieldSet methodNewSFS = 
this.sfsFromStringPairs(methodAppendedString);
-               methodSFS.putAllOverwrite(methodNewSFS);
-               for (int i=0; i < SAMPLE_STRING_PAIRS.length; i++)
-                       assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]),
-                                       
SAMPLE_STRING_PAIRS[i][1]+methodAppendedString);
-       }
-       
-       /**
-        * Tests put(String,SimpleFieldSet) method
-        */
-       public void testPut_StringSimpleFieldSet() {
-               String methodKey = "prefix";
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               methodSFS.put(methodKey,sfsFromSampleStringPairs());
-               for (int i=0; i < SAMPLE_STRING_PAIRS.length; i++)
-                       assertEquals(
-                                       
methodSFS.get(methodKey+SimpleFieldSet.MULTI_LEVEL_CHAR+SAMPLE_STRING_PAIRS[i][0]),
-                                       SAMPLE_STRING_PAIRS[i][1]);
-       }
-       
-       /**
-        * Tests put(String,SimpleFieldSet) method
-        */
-       public void testTPut_StringSimpleFieldSet() {
-               String methodKey = "prefix";
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               methodSFS.tput(methodKey,sfsFromSampleStringPairs());
-               for (int i=0; i < SAMPLE_STRING_PAIRS.length; i++)
-                       
assertEquals(methodSFS.get(methodKey+SimpleFieldSet.MULTI_LEVEL_CHAR+SAMPLE_STRING_PAIRS[i][0]),
-                                       SAMPLE_STRING_PAIRS[i][1]);
-       }
-       
-       /**
-        * Tests put(String,SimpleFieldSet) and
-        * tput(String,SimpleFieldSet) trying to
-        * add empty data structures
-        */
-       public void testPutAndTPut_WithEmpty() {
-               SimpleFieldSet methodEmptySFS = new SimpleFieldSet(true);
-               SimpleFieldSet methodSampleSFS = sfsFromSampleStringPairs();
-               try {
-                       methodSampleSFS.put("sample",methodEmptySFS);
-                       fail("Expected Exception Error Not Thrown!"); } 
-        catch (IllegalArgumentException anException) {
-            assertNotNull(anException); }
-        try {
-               methodSampleSFS.tput("sample",methodSampleSFS); }
-        catch (IllegalArgumentException aException) {
-               fail("Not expected exception thrown : " + 
aException.getMessage()); }                   
-       }
-       
-       /**
-        * It creates a SFS from the SAMPLE_STRING_PAIRS
-        * and putting a suffix after every value
-        * @param aSuffix to put after every value
-        * @return the SimpleFieldSet created
-        */
-       private SimpleFieldSet sfsFromStringPairs(String aSuffix) {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               //creating new
-               for (int i = 0; i < SAMPLE_STRING_PAIRS.length; i++) 
-                       methodSFS.putSingle(SAMPLE_STRING_PAIRS[i][0],
-                                       SAMPLE_STRING_PAIRS[i][1]+aSuffix);
-               return methodSFS;
-       }
-       
-       /**
-        * Tests put(String,boolean) and getBoolean(String,boolean)
-        * methods consistency.
-        * The default value (returned if the key is not found) is set to 
"false"
-        * and the real value is always set to "true", so
-        * we are sure if it finds the right value or not
-        * (and does not use the default).
-        */
-       public void testPut_StringBoolean() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               int length = 15;
-               for(int i = 0; i < length; i++)
-                       methodSFS.put(Integer.toString(i),true);
-               for (int i = 0; i < length; i++)
-                       
assertEquals(methodSFS.getBoolean(Integer.toString(i),false),true);
-               assertTrue(checkSimpleFieldSetSize(methodSFS,length));
-       }
-       
-       
-       /**
-        * Checks if the provided SimpleFieldSet
-        * has the right size
-        * @param aSimpleFieldSet
-        * @param expectedSize
-        * @return true if the size is the expected
-        */
-       private boolean checkSimpleFieldSetSize(SimpleFieldSet aSimpleFieldSet, 
int expectedSize) {
-               int actualSize = 0;
-               Iterator methodKeyIterator = aSimpleFieldSet.keyIterator();
-               while (methodKeyIterator.hasNext()) {
-                       methodKeyIterator.next();
-                       actualSize++; }
-               return expectedSize == actualSize;
-       }
-       
-       /**
-        * Tests put(String,int) and 
-        * [getInt(String),getInt(String,int)]
-        * methods consistency.
-        * The default value (returned if the key is not found)
-        * is set to a not present int value, so we are sure 
-        * if it finds the right value or not 
-        * (and does not use the default).
-        */
-       public void testPut_StringInt() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               int[][] methodPairsArray =
-                       { {1,1},{2,2},{3,3},{4,4} };
-               for (int i = 0; i < methodPairsArray.length; i++)
-                       methodSFS.put(Integer.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
-               
-               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
-               
-               for (int i = 0; i < methodPairsArray.length; i++) {
-                       try {
-                               
assertEquals(methodSFS.getInt(Integer.toString(methodPairsArray[i][0])),
-                                               methodPairsArray[i][1]);
-                               
assertEquals(methodSFS.getInt(Integer.toString(methodPairsArray[i][0]),5),
-                                               methodPairsArray[i][1]);
-                       } catch (FSParseException aException) {
-                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
-               }
-       }
-       
-       /**
-        * Tests put(String,long) and 
-        * [getLong(String),getLong(String,long)]
-        * methods consistency.
-        * The default value (returned if the key is not found)
-        * is set to a not present long value, so we are sure 
-        * if it finds the right value or not 
-        * (and does not use the default).
-        */
-       public void testPut_StringLong() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               long[][] methodPairsArray =
-                       { {1,1},{2,2},{3,3},{4,4} };
-               for (int i = 0; i < methodPairsArray.length; i++)
-                       methodSFS.put(Long.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
-               
-               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
-               
-               for (int i = 0; i < methodPairsArray.length; i++) {
-                       try {
-                               
assertEquals(methodSFS.getLong(Long.toString(methodPairsArray[i][0])),
-                                               methodPairsArray[i][1]);
-                               
assertEquals(methodSFS.getLong(Long.toString(methodPairsArray[i][0]),5),
-                                               methodPairsArray[i][1]);
-                       } catch (FSParseException aException) {
-                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
-               }
-       }
-       
-       /**
-        * Tests put(String,char) and 
-        * [getChar(String),getChar(String,char)]
-        * methods consistency.
-        * The default value (returned if the key is not found)
-        * is set to a not present char value, so we are sure 
-        * if it finds the right value or not 
-        * (and does not use the default).
-        */
-       public void testPut_StringChar() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               char[][] methodPairsArray =
-                       { {'1','1'},{'2','2'},{'3','3'},{'4','4'} };
-               for (int i = 0; i < methodPairsArray.length; i++)
-                       methodSFS.put(String.valueOf(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
-               
-               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
-               
-               for (int i = 0; i < methodPairsArray.length; i++) {
-                       try {
-                               
assertEquals(methodSFS.getChar(String.valueOf(methodPairsArray[i][0])),
-                                               methodPairsArray[i][1]);
-                               
assertEquals(methodSFS.getChar(String.valueOf(methodPairsArray[i][0]),'5'),
-                                               methodPairsArray[i][1]);
-                       } catch (FSParseException aException) {
-                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
-               }
-       }
-       
-       /**
-        * Tests put(String,short) and 
-        * [getShort(String)|getShort(String,short)]
-        * methods consistency.
-        * The default value (returned if the key is not found)
-        * is set to a not present short value, so we are sure 
-        * if it finds the right value or not 
-        * (and does not use the default).
-        */
-       public void testPut_StringShort() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               short[][] methodPairsArray =
-                       { {1,1},{2,2},{3,3},{4,4} };
-               for (int i = 0; i < methodPairsArray.length; i++)
-                       methodSFS.put(Short.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
-               
-               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
-               
-               for (int i = 0; i < methodPairsArray.length; i++) {
-                       try {
-                               
assertEquals(methodSFS.getShort(Short.toString(methodPairsArray[i][0])),
-                                               methodPairsArray[i][1]);
-                               
assertEquals(methodSFS.getShort(Short.toString(methodPairsArray[i][0]),(short)5),
-                                               methodPairsArray[i][1]);
-                       } catch (FSParseException aException) {
-                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
-               }
-       }
-       
-       /**
-        * Tests put(String,double) and 
-        * [getDouble(String)|getDouble(String,double)]
-        * methods consistency.
-        * The default value (returned if the key is not found)
-        * is set to a not present double value, so we are sure 
-        * if it finds the right value or not 
-        * (and does not use the default).
-        */
-       public void testPut_StringDouble() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               double[][] methodPairsArray =
-                       { {1,1},{2,2},{3,3},{4,4} };
-               for (int i = 0; i < methodPairsArray.length; i++)
-                       methodSFS.put(Double.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
-               
-               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
-               
-               for (int i = 0; i < methodPairsArray.length; i++) {
-                       try {
-                               //there is no assertEquals(Double,Double) so we 
are obliged to do this way -_-
-                               assertEquals(
-                                               
Double.compare((methodSFS.getDouble(Double.toString(methodPairsArray[i][0]))),
-                                                                               
        methodPairsArray[i][1]),0);
-                               assertEquals(
-                                               
Double.compare(methodSFS.getDouble(Double.toString(methodPairsArray[i][0]),(double)5),
-                                                                               
        methodPairsArray[i][1]),0);
-                       } catch (FSParseException aException) {
-                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
-               }
-       }
-       
-       /**
-        * Generates a string for the SFS parser in the canonical form:
-        *  key=value
-        *  END
-        * @param aStringPairsArray
-        * @return a String ready to be read by a SFS parser
-        */
-       private String sfsReadyString(String[][] aStringPairsArray) {
-               
-               String methodStringToReturn = "";
-               for(int i = 0; i < aStringPairsArray.length; i++)
-                       methodStringToReturn += 
aStringPairsArray[i][0]+KEY_VALUE_SEPARATOR+aStringPairsArray[i][1]+'\n';
-               methodStringToReturn += SAMPLE_END_MARKER;
-               return methodStringToReturn;
-       }
-       
-       /**
-        * Tests SimpleFieldSet(String,boolean,boolean) constructor,
-        * with simple and border cases of the canonical form.
-        */
-       public void testSimpleFieldSet_StringBooleanBoolean() {
-               String[][] methodStringPairs = SAMPLE_STRING_PAIRS;
-               String methodStringToParse = sfsReadyString(methodStringPairs);
-               try {
-                       SimpleFieldSet methodSFS = new 
SimpleFieldSet(methodStringToParse,false,false);
-                       for (int i=0; i < methodStringPairs.length; i++)
-                               
assertEquals(methodSFS.get(methodStringPairs[i][0]),
-                                               methodStringPairs[i][1]);
-               } catch (IOException aException) {
-                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
-       }
-       
-       /**
-        * Tests SimpleFieldSet(BufferedReader,boolean,boolean) constructor,
-        * with simple and border cases of the canonical form.
-        */
-       public void testSimpleFieldSet_BufferedReaderBooleanBoolean() {
-               String[][] methodStringPairs = SAMPLE_STRING_PAIRS;
-        BufferedReader methodBufferedReader = 
-               new BufferedReader(new 
StringReader(sfsReadyString(methodStringPairs)));
-               try {
-                       SimpleFieldSet methodSFS = new 
SimpleFieldSet(methodBufferedReader,false,false);
-                       for (int i=0; i < methodStringPairs.length; i++)
-                               
assertEquals(methodSFS.get(methodStringPairs[i][0]),
-                                               methodStringPairs[i][1]);
-               } catch (IOException aException) {
-                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
-       }
-       
-       /**
-        * Generates a SimpleFieldSet using the 
-        * SAMPLE_STRING_PAIRS and sfs put method
-        * @return a SimpleFieldSet
-        */
-       private SimpleFieldSet sfsFromSampleStringPairs() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               for (int i = 0; i < SAMPLE_STRING_PAIRS.length; i++)
-                       methodSFS.putSingle(SAMPLE_STRING_PAIRS[i][0],
-                                       SAMPLE_STRING_PAIRS[i][1]);
-               assertTrue(checkSimpleFieldSetSize(methodSFS,
-                               SAMPLE_STRING_PAIRS.length));
-               return methodSFS;
-       }
-       
-       /**
-        * Tests SimpleFieldSet(SimpleFieldSet) constructor,
-        * with simple and border cases of the canonical form.
-        */
-       public void testSimpleFieldSet_SimpleFieldSet() {
-               SimpleFieldSet methodSFS = new 
SimpleFieldSet(sfsFromSampleStringPairs());
-               String[][] methodStringPairs = SAMPLE_STRING_PAIRS;
-               for (int i=0; i < methodStringPairs.length; i++)
-                       assertEquals(methodSFS.get(methodStringPairs[i][0]),
-                                       methodStringPairs[i][1]);
-       }
-       
-       /**
-        * Tests {get,set}EndMarker(String) methods
-        * using them after a String parsing
-        */
-       public void testEndMarker() {
-               String methodEndMarker = "ANOTHER-ENDING";
-               String methodStringToParse = 
sfsReadyString(SAMPLE_STRING_PAIRS);
-               try {
-                       SimpleFieldSet methodSFS = new 
SimpleFieldSet(methodStringToParse,false,false);
-                       
assertEquals(methodSFS.getEndMarker(),SAMPLE_END_MARKER);
-                       methodSFS.setEndMarker(methodEndMarker);
-                       assertEquals(methodSFS.getEndMarker(),methodEndMarker);
-               } catch (IOException aException) {
-                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
-       }
-       
-       /**
-        * Tests isEmpty() method.
-        */
-       public void testIsEmpty() {
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               assertFalse(methodSFS.isEmpty());
-               methodSFS = new SimpleFieldSet(true);
-               assertTrue(methodSFS.isEmpty());
-       }
-       
-       /**
-        * Tests directSubsetNameIterator() method.
-        * It uses SAMPLE_STRING_PAIRS and for this reason
-        * the expected subset is "foo".
-        */
-       public void testDirectSubsetNameIterator() {
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               String expectedSubset = SAMPLE_STRING_PAIRS[0][0];      //"foo"
-               Iterator methodIter = methodSFS.directSubsetNameIterator();
-               while (methodIter.hasNext())
-                       ((String)methodIter.next()).equals(expectedSubset);
-               methodSFS = new SimpleFieldSet(true);
-               methodIter = methodSFS.directSubsetNameIterator();
-               assertNull(methodIter);
-       }
-       
-       /**
-        * Tests nameOfDirectSubsets() method.
-        */
-       public void testNamesOfDirectSubsets() {
-               String[] expectedResult = {SAMPLE_STRING_PAIRS[0][0]};
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               
assertTrue(Arrays.equals(methodSFS.namesOfDirectSubsets(),expectedResult));
-
-               methodSFS = new SimpleFieldSet(true);
-               assertTrue(Arrays.equals(methodSFS.namesOfDirectSubsets(), new 
String[0]));
-       }
-       
-       /**
-        * Test the putOverwrite(String,String) method.
-        */
-       public void testPutOverwrite_String() {
-               String methodKey = "foo.bar";
-               String[] methodValues = {"boo","bar","zoo"};
-               String expectedResult = "zoo";
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               for (int i = 0 ; i < methodValues.length; i++)
-                       methodSFS.putOverwrite(methodKey,methodValues[i]);
-               assertEquals(methodSFS.get(methodKey),expectedResult);
-       }
-       
-       /**
-        * Test the putOverwrite(String,String[]) method.
-        */
-       public void testPutOverwrite_StringArray() {
-               String methodKey = "foo.bar";
-               String[] methodValues = {"boo","bar","zoo"};
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               methodSFS.putOverwrite(methodKey,methodValues);
-               
assertTrue(Arrays.equals(methodSFS.getAll(methodKey),methodValues));
-       }
-       
-       /**
-        * Test the putAppend(String,String) method.
-        */
-       public void testPutAppend() {
-               String methodKey = "foo.bar";
-               String[] methodValues = {"boo","bar","zoo"};
-               String expectedResult = "boo"+SimpleFieldSet.MULTI_VALUE_CHAR
-                                                               
+"bar"+SimpleFieldSet.MULTI_VALUE_CHAR
-                                                               +"zoo";
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               for (int i = 0 ; i < methodValues.length; i++)
-                       methodSFS.putAppend(methodKey,methodValues[i]);
-               assertEquals(methodSFS.get(methodKey),expectedResult);
-       }
-       
-       /**
-        * Tests the getAll(String) method.
-        */
-       public void testGetAll() {
-               String methodKey = "foo.bar";
-               String[] methodValues = {"boo","bar","zoo"};
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               for (int i = 0 ; i < methodValues.length; i++)
-                       methodSFS.putAppend(methodKey,methodValues[i]);
-               
assertTrue(Arrays.equals(methodSFS.getAll(methodKey),methodValues));
-       }
-       
-       /**
-        * Tests the getIntArray(String) method
-        */
-       public void testGetIntArray() {
-               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
-               String keyPrefix = "foo";
-               for (int i = 0; i<15; i++)
-                       methodSFS.putAppend(keyPrefix,String.valueOf(i));
-               int[] result = methodSFS.getIntArray(keyPrefix);
-               for (int i = 0; i<15; i++)
-                       assertTrue(result[i]==i);
-               
-       }
-       
-       /**
-        * Tests removeValue(String) method
-        */
-       public void testRemoveValue() {
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               methodSFS.removeValue("foo");
-               assertNull(methodSFS.get(SAMPLE_STRING_PAIRS[0][0]));
-               for(int i=1;i<SAMPLE_STRING_PAIRS.length;i++)
-                       assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]),
-                                       SAMPLE_STRING_PAIRS[i][1]);
-       }
-       
-       /**
-        * Tests removeSubset(String) method
-        */
-       public void testRemoveSubset() {
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               methodSFS.removeSubset("foo");
-               for(int i = 1; i< 3; i++)
-                       assertNull(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]));
-               assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[0][0]),
-                               SAMPLE_STRING_PAIRS[0][1]);
-               for(int i = 3; i< 5; i++)
-                       assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]),
-                                       SAMPLE_STRING_PAIRS[i][1]);
-       }
-
-       /**
-        * Searches for a key in a given String[][] array.
-        * We consider that keys are stored in String[x][0] 
-        * @param aStringPairsArray
-        * @param aPrefix that could be put before found key
-        * @param aKey to be searched
-        * @return true if there is the key
-        */
-       private boolean isAKey(String[][] aStringPairsArray, String aPrefix, 
String aKey) {
-               for (int i=0; i<aStringPairsArray.length; i++)
-                       if (aKey.equals(aPrefix+aStringPairsArray[i][0])) 
-                               return true;
-               return false;
-       }
-       
-       /**
-        * Verifies if all keys in a String[][]
-        * (We consider that keys are stored in String[x][0])
-        * are the same that the Iterator provides.
-        * In this way both hasNext() and next() methods
-        * are tested.
-        * @param aStringPairsArray
-        * @param aPrefix that could be put before found key
-        * @param aIterator
-        * @return true if they have the same key set
-        */
-       private boolean areAllContainedKeys(String[][] aStringPairsArray, 
String aPrefix, Iterator aIterator) {
-               boolean retValue = true;
-               int actualLength = 0;
-               while (aIterator.hasNext()) {
-                       actualLength++;
-                       retValue &= 
isAKey(aStringPairsArray,aPrefix,(String)aIterator.next());
-               }
-               retValue &= (actualLength==aStringPairsArray.length);
-               return retValue;
-       }
-       
-       /**
-        * Tests the Iterator given for the
-        * SimpleFieldSet class.
-        * It tests hasNext() and next() methods.
-        */
-       public void testKeyIterator() {
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               Iterator itr = methodSFS.keyIterator();
-               assertTrue(areAllContainedKeys(SAMPLE_STRING_PAIRS,"",itr));
-       }
-       
-       /**
-        * Tests the Iterator created using prefix 
-        * given for the SimpleFieldSet class
-        */
-       public void testKeyIterator_String() {
-               String methodPrefix = "bob";
-               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
-               Iterator itr = methodSFS.keyIterator(methodPrefix);
-               
assertTrue(areAllContainedKeys(SAMPLE_STRING_PAIRS,methodPrefix,itr));  
-       }
-}

Copied: branches/freenet-jfk/test/freenet/support/SimpleFieldSetTest.java (from 
rev 14796, trunk/freenet/test/freenet/support/SimpleFieldSetTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/SimpleFieldSetTest.java           
                (rev 0)
+++ branches/freenet-jfk/test/freenet/support/SimpleFieldSetTest.java   
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,701 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package freenet.support;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import freenet.node.FSParseException;
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.SimpleFieldSet} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class SimpleFieldSetTest extends TestCase {
+
+       private static final char KEY_VALUE_SEPARATOR = '=';
+       
+       /* A double string array used across all tests 
+        * it must not be changed in order to perform tests
+        * correctly */
+       private static final String[][] SAMPLE_STRING_PAIRS = {  
+               //directSubset
+               {"foo","bar"},                  
+               {"foo.bar","foobar"},   
+               {"foo.bar.boo.far","foobar"},
+               {"foo2","foobar.fooboo.foofar.foofoo"},
+               {"foo3",KEY_VALUE_SEPARATOR+"bar"} };
+       
+       private static final String SAMPLE_END_MARKER = "END";
+       
+       /**
+        * Tests putSingle(String,String) method
+        * trying to store a key with two paired
+        * multi_level_chars (i.e. "..").
+        */
+       public void 
testSimpleFieldSetPutSingle_StringString_WithTwoPairedMultiLevelChars() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               String methodKey = "foo..bar.";
+               String methodValue = "foobar";
+               methodSFS.putSingle(methodKey,methodValue);
+               
assertEquals(methodSFS.subset("foo").subset("").subset("bar").get(""),methodValue);
+               assertEquals(methodSFS.get(methodKey),methodValue);
+       }
+       
+       /**
+        * Tests putAppend(String,String) method
+        * trying to store a key with two paired
+        * multi_level_chars (i.e. "..").
+        */
+       public void 
testSimpleFieldSetPutAppend_StringString_WithTwoPairedMultiLevelChars() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               String methodKey = "foo..bar";
+               String methodValue = "foobar";
+               methodSFS.putAppend(methodKey,methodValue);
+               assertEquals(methodSFS.get(methodKey),methodValue);
+       }
+       
+       /**
+        * Tests put() and get() methods
+        * using a normal Map behaviour
+        * and without MULTI_LEVEL_CHARs
+        */
+       public void testSimpleFieldSetPutAndGet_NoMultiLevel(){
+               String[][] methodPairsArray = { 
+                               
{"A","a"},{"B","b"},{"C","c"},{"D","d"},{"E","e"},{"F","f"} };
+               assertTrue(checkPutAndGetPairs(methodPairsArray));
+       }
+       
+       /**
+        * Tests put() and get() methods
+        * using a normal Map behaviour
+        * and with MULTI_LEVEL_CHARs
+        */
+       public void testSimpleFieldSetPutAndGet_MultiLevel(){
+               String[][] methodPairsArray_DoubleLevel = { 
+                               {"A.A","aa"},
+                               {"A.B","ab"},
+                               {"A.C","ac"},
+                               {"A.D","ad"},
+                               {"A.E","ae"},
+                               {"A.F","af"} };
+               String[][] methodPairsArray_MultiLevel = { 
+                               {"A.A.A.A","aa"},
+                               {"A.B.A","ab"},
+                               {"A.C.Cc","ac"},
+                               {"A.D.F","ad"},
+                               {"A.E.G","ae"},
+                               {"A.F.J.II.UI.BOO","af"} };
+               assertTrue(checkPutAndGetPairs(methodPairsArray_DoubleLevel));
+               assertTrue(checkPutAndGetPairs(methodPairsArray_MultiLevel));
+       }
+       
+       
+       /**
+        * It puts key-value pairs in a SimpleFieldSet
+        * and verify if it can do the correspondant
+        * get correctly.
+        * @param aPairsArray
+        * @return true if it is correct
+        */
+       private boolean checkPutAndGetPairs(String[][] aPairsArray) {
+               boolean retValue = true;
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               //putting values
+               for (int i = 0; i < aPairsArray.length; i++)
+                       methodSFS.putSingle(aPairsArray[i][0], 
aPairsArray[i][1]);
+               for (int i = 0; i < aPairsArray.length; i++)            
//getting values
+                       retValue &= 
methodSFS.get(aPairsArray[i][0]).equals(aPairsArray[i][1]);
+               retValue &= checkSimpleFieldSetSize(methodSFS, 
aPairsArray.length);
+               return retValue;
+       }
+       
+       /**
+        * Tests subset(String) method
+        * putting two levels keys and
+        * fetching it through subset() method
+        * on the first level and then get()
+        * on the second
+        */
+       public void testSimpleFieldSetSubset_String() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               String[][] methodPairsArray_MultiLevel = { 
+                               {"A","A","aa"},
+                               {"A","B","ab"},
+                               {"A","C","ac"},
+                               {"A","D","ad"},
+                               {"A","E","ae"},
+                               {"A","F","af"} };
+               //putting values
+               for (int i = 0; i < methodPairsArray_MultiLevel.length; i++)
+                       methodSFS.putSingle(methodPairsArray_MultiLevel[i][0] 
+                                           + SimpleFieldSet.MULTI_LEVEL_CHAR 
+                                           + 
methodPairsArray_MultiLevel[i][1], 
+                                           methodPairsArray_MultiLevel[i][2]);
+               //getting subsets and then values
+               for (int i = 0; i < methodPairsArray_MultiLevel.length; i++)
+                       assertEquals(
+                                       methodSFS.subset(
+                                                       
methodPairsArray_MultiLevel[i][0]).get(methodPairsArray_MultiLevel[i][1]),
+                                       methodPairsArray_MultiLevel[i][2]);
+               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray_MultiLevel.length));
+       }
+       
+       /**
+        * Tests putAllOverwrite(SimpleFieldSet) method
+        * trying to overwrite a whole SimpleFieldSet
+        * with another with same keys but different
+        * values
+        */
+       public void testPutAllOverwrite() {
+               String methodAppendedString = "buu";
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               SimpleFieldSet methodNewSFS = 
this.sfsFromStringPairs(methodAppendedString);
+               methodSFS.putAllOverwrite(methodNewSFS);
+               for (int i=0; i < SAMPLE_STRING_PAIRS.length; i++)
+                       assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]),
+                                       
SAMPLE_STRING_PAIRS[i][1]+methodAppendedString);
+       }
+       
+       /**
+        * Tests put(String,SimpleFieldSet) method
+        */
+       public void testPut_StringSimpleFieldSet() {
+               String methodKey = "prefix";
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               methodSFS.put(methodKey,sfsFromSampleStringPairs());
+               for (int i=0; i < SAMPLE_STRING_PAIRS.length; i++)
+                       assertEquals(
+                                       
methodSFS.get(methodKey+SimpleFieldSet.MULTI_LEVEL_CHAR+SAMPLE_STRING_PAIRS[i][0]),
+                                       SAMPLE_STRING_PAIRS[i][1]);
+       }
+       
+       /**
+        * Tests put(String,SimpleFieldSet) method
+        */
+       public void testTPut_StringSimpleFieldSet() {
+               String methodKey = "prefix";
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               methodSFS.tput(methodKey,sfsFromSampleStringPairs());
+               for (int i=0; i < SAMPLE_STRING_PAIRS.length; i++)
+                       
assertEquals(methodSFS.get(methodKey+SimpleFieldSet.MULTI_LEVEL_CHAR+SAMPLE_STRING_PAIRS[i][0]),
+                                       SAMPLE_STRING_PAIRS[i][1]);
+       }
+       
+       /**
+        * Tests put(String,SimpleFieldSet) and
+        * tput(String,SimpleFieldSet) trying to
+        * add empty data structures
+        */
+       public void testPutAndTPut_WithEmpty() {
+               SimpleFieldSet methodEmptySFS = new SimpleFieldSet(true);
+               SimpleFieldSet methodSampleSFS = sfsFromSampleStringPairs();
+               try {
+                       methodSampleSFS.put("sample",methodEmptySFS);
+                       fail("Expected Exception Error Not Thrown!"); } 
+        catch (IllegalArgumentException anException) {
+            assertNotNull(anException); }
+        try {
+               methodSampleSFS.tput("sample",methodSampleSFS); }
+        catch (IllegalArgumentException aException) {
+               fail("Not expected exception thrown : " + 
aException.getMessage()); }                   
+       }
+       
+       /**
+        * It creates a SFS from the SAMPLE_STRING_PAIRS
+        * and putting a suffix after every value
+        * @param aSuffix to put after every value
+        * @return the SimpleFieldSet created
+        */
+       private SimpleFieldSet sfsFromStringPairs(String aSuffix) {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               //creating new
+               for (int i = 0; i < SAMPLE_STRING_PAIRS.length; i++) 
+                       methodSFS.putSingle(SAMPLE_STRING_PAIRS[i][0],
+                                       SAMPLE_STRING_PAIRS[i][1]+aSuffix);
+               return methodSFS;
+       }
+       
+       /**
+        * Tests put(String,boolean) and getBoolean(String,boolean)
+        * methods consistency.
+        * The default value (returned if the key is not found) is set to 
"false"
+        * and the real value is always set to "true", so
+        * we are sure if it finds the right value or not
+        * (and does not use the default).
+        */
+       public void testPut_StringBoolean() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               int length = 15;
+               for(int i = 0; i < length; i++)
+                       methodSFS.put(Integer.toString(i),true);
+               for (int i = 0; i < length; i++)
+                       
assertEquals(methodSFS.getBoolean(Integer.toString(i),false),true);
+               assertTrue(checkSimpleFieldSetSize(methodSFS,length));
+       }
+       
+       
+       /**
+        * Checks if the provided SimpleFieldSet
+        * has the right size
+        * @param aSimpleFieldSet
+        * @param expectedSize
+        * @return true if the size is the expected
+        */
+       private boolean checkSimpleFieldSetSize(SimpleFieldSet aSimpleFieldSet, 
int expectedSize) {
+               int actualSize = 0;
+               Iterator methodKeyIterator = aSimpleFieldSet.keyIterator();
+               while (methodKeyIterator.hasNext()) {
+                       methodKeyIterator.next();
+                       actualSize++; }
+               return expectedSize == actualSize;
+       }
+       
+       /**
+        * Tests put(String,int) and 
+        * [getInt(String),getInt(String,int)]
+        * methods consistency.
+        * The default value (returned if the key is not found)
+        * is set to a not present int value, so we are sure 
+        * if it finds the right value or not 
+        * (and does not use the default).
+        */
+       public void testPut_StringInt() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               int[][] methodPairsArray =
+                       { {1,1},{2,2},{3,3},{4,4} };
+               for (int i = 0; i < methodPairsArray.length; i++)
+                       methodSFS.put(Integer.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
+               
+               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
+               
+               for (int i = 0; i < methodPairsArray.length; i++) {
+                       try {
+                               
assertEquals(methodSFS.getInt(Integer.toString(methodPairsArray[i][0])),
+                                               methodPairsArray[i][1]);
+                               
assertEquals(methodSFS.getInt(Integer.toString(methodPairsArray[i][0]),5),
+                                               methodPairsArray[i][1]);
+                       } catch (FSParseException aException) {
+                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
+               }
+       }
+       
+       /**
+        * Tests put(String,long) and 
+        * [getLong(String),getLong(String,long)]
+        * methods consistency.
+        * The default value (returned if the key is not found)
+        * is set to a not present long value, so we are sure 
+        * if it finds the right value or not 
+        * (and does not use the default).
+        */
+       public void testPut_StringLong() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               long[][] methodPairsArray =
+                       { {1,1},{2,2},{3,3},{4,4} };
+               for (int i = 0; i < methodPairsArray.length; i++)
+                       methodSFS.put(Long.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
+               
+               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
+               
+               for (int i = 0; i < methodPairsArray.length; i++) {
+                       try {
+                               
assertEquals(methodSFS.getLong(Long.toString(methodPairsArray[i][0])),
+                                               methodPairsArray[i][1]);
+                               
assertEquals(methodSFS.getLong(Long.toString(methodPairsArray[i][0]),5),
+                                               methodPairsArray[i][1]);
+                       } catch (FSParseException aException) {
+                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
+               }
+       }
+       
+       /**
+        * Tests put(String,char) and 
+        * [getChar(String),getChar(String,char)]
+        * methods consistency.
+        * The default value (returned if the key is not found)
+        * is set to a not present char value, so we are sure 
+        * if it finds the right value or not 
+        * (and does not use the default).
+        */
+       public void testPut_StringChar() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               char[][] methodPairsArray =
+                       { {'1','1'},{'2','2'},{'3','3'},{'4','4'} };
+               for (int i = 0; i < methodPairsArray.length; i++)
+                       methodSFS.put(String.valueOf(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
+               
+               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
+               
+               for (int i = 0; i < methodPairsArray.length; i++) {
+                       try {
+                               
assertEquals(methodSFS.getChar(String.valueOf(methodPairsArray[i][0])),
+                                               methodPairsArray[i][1]);
+                               
assertEquals(methodSFS.getChar(String.valueOf(methodPairsArray[i][0]),'5'),
+                                               methodPairsArray[i][1]);
+                       } catch (FSParseException aException) {
+                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
+               }
+       }
+       
+       /**
+        * Tests put(String,short) and 
+        * [getShort(String)|getShort(String,short)]
+        * methods consistency.
+        * The default value (returned if the key is not found)
+        * is set to a not present short value, so we are sure 
+        * if it finds the right value or not 
+        * (and does not use the default).
+        */
+       public void testPut_StringShort() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               short[][] methodPairsArray =
+                       { {1,1},{2,2},{3,3},{4,4} };
+               for (int i = 0; i < methodPairsArray.length; i++)
+                       methodSFS.put(Short.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
+               
+               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
+               
+               for (int i = 0; i < methodPairsArray.length; i++) {
+                       try {
+                               
assertEquals(methodSFS.getShort(Short.toString(methodPairsArray[i][0])),
+                                               methodPairsArray[i][1]);
+                               
assertEquals(methodSFS.getShort(Short.toString(methodPairsArray[i][0]),(short)5),
+                                               methodPairsArray[i][1]);
+                       } catch (FSParseException aException) {
+                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
+               }
+       }
+       
+       /**
+        * Tests put(String,double) and 
+        * [getDouble(String)|getDouble(String,double)]
+        * methods consistency.
+        * The default value (returned if the key is not found)
+        * is set to a not present double value, so we are sure 
+        * if it finds the right value or not 
+        * (and does not use the default).
+        */
+       public void testPut_StringDouble() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               double[][] methodPairsArray =
+                       { {1,1},{2,2},{3,3},{4,4} };
+               for (int i = 0; i < methodPairsArray.length; i++)
+                       methodSFS.put(Double.toString(methodPairsArray[i][0]), 
methodPairsArray[i][1]);
+               
+               
assertTrue(checkSimpleFieldSetSize(methodSFS,methodPairsArray.length));
+               
+               for (int i = 0; i < methodPairsArray.length; i++) {
+                       try {
+                               //there is no assertEquals(Double,Double) so we 
are obliged to do this way -_-
+                               assertEquals(
+                                               
Double.compare((methodSFS.getDouble(Double.toString(methodPairsArray[i][0]))),
+                                                                               
        methodPairsArray[i][1]),0);
+                               assertEquals(
+                                               
Double.compare(methodSFS.getDouble(Double.toString(methodPairsArray[i][0]),(double)5),
+                                                                               
        methodPairsArray[i][1]),0);
+                       } catch (FSParseException aException) {
+                               fail("Not expected exception thrown : " + 
aException.getMessage()); }
+               }
+       }
+       
+       /**
+        * Generates a string for the SFS parser in the canonical form:
+        *  key=value
+        *  END
+        * @param aStringPairsArray
+        * @return a String ready to be read by a SFS parser
+        */
+       private String sfsReadyString(String[][] aStringPairsArray) {
+               
+               String methodStringToReturn = "";
+               for(int i = 0; i < aStringPairsArray.length; i++)
+                       methodStringToReturn += 
aStringPairsArray[i][0]+KEY_VALUE_SEPARATOR+aStringPairsArray[i][1]+'\n';
+               methodStringToReturn += SAMPLE_END_MARKER;
+               return methodStringToReturn;
+       }
+       
+       /**
+        * Tests SimpleFieldSet(String,boolean,boolean) constructor,
+        * with simple and border cases of the canonical form.
+        */
+       public void testSimpleFieldSet_StringBooleanBoolean() {
+               String[][] methodStringPairs = SAMPLE_STRING_PAIRS;
+               String methodStringToParse = sfsReadyString(methodStringPairs);
+               try {
+                       SimpleFieldSet methodSFS = new 
SimpleFieldSet(methodStringToParse,false,false);
+                       for (int i=0; i < methodStringPairs.length; i++)
+                               
assertEquals(methodSFS.get(methodStringPairs[i][0]),
+                                               methodStringPairs[i][1]);
+               } catch (IOException aException) {
+                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
+       }
+       
+       /**
+        * Tests SimpleFieldSet(BufferedReader,boolean,boolean) constructor,
+        * with simple and border cases of the canonical form.
+        */
+       public void testSimpleFieldSet_BufferedReaderBooleanBoolean() {
+               String[][] methodStringPairs = SAMPLE_STRING_PAIRS;
+        BufferedReader methodBufferedReader = 
+               new BufferedReader(new 
StringReader(sfsReadyString(methodStringPairs)));
+               try {
+                       SimpleFieldSet methodSFS = new 
SimpleFieldSet(methodBufferedReader,false,false);
+                       for (int i=0; i < methodStringPairs.length; i++)
+                               
assertEquals(methodSFS.get(methodStringPairs[i][0]),
+                                               methodStringPairs[i][1]);
+               } catch (IOException aException) {
+                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
+       }
+       
+       /**
+        * Generates a SimpleFieldSet using the 
+        * SAMPLE_STRING_PAIRS and sfs put method
+        * @return a SimpleFieldSet
+        */
+       private SimpleFieldSet sfsFromSampleStringPairs() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               for (int i = 0; i < SAMPLE_STRING_PAIRS.length; i++)
+                       methodSFS.putSingle(SAMPLE_STRING_PAIRS[i][0],
+                                       SAMPLE_STRING_PAIRS[i][1]);
+               assertTrue(checkSimpleFieldSetSize(methodSFS,
+                               SAMPLE_STRING_PAIRS.length));
+               return methodSFS;
+       }
+       
+       /**
+        * Tests SimpleFieldSet(SimpleFieldSet) constructor,
+        * with simple and border cases of the canonical form.
+        */
+       public void testSimpleFieldSet_SimpleFieldSet() {
+               SimpleFieldSet methodSFS = new 
SimpleFieldSet(sfsFromSampleStringPairs());
+               String[][] methodStringPairs = SAMPLE_STRING_PAIRS;
+               for (int i=0; i < methodStringPairs.length; i++)
+                       assertEquals(methodSFS.get(methodStringPairs[i][0]),
+                                       methodStringPairs[i][1]);
+       }
+       
+       /**
+        * Tests {get,set}EndMarker(String) methods
+        * using them after a String parsing
+        */
+       public void testEndMarker() {
+               String methodEndMarker = "ANOTHER-ENDING";
+               String methodStringToParse = 
sfsReadyString(SAMPLE_STRING_PAIRS);
+               try {
+                       SimpleFieldSet methodSFS = new 
SimpleFieldSet(methodStringToParse,false,false);
+                       
assertEquals(methodSFS.getEndMarker(),SAMPLE_END_MARKER);
+                       methodSFS.setEndMarker(methodEndMarker);
+                       assertEquals(methodSFS.getEndMarker(),methodEndMarker);
+               } catch (IOException aException) {
+                       fail("Not expected exception thrown : " + 
aException.getMessage()); }
+       }
+       
+       /**
+        * Tests isEmpty() method.
+        */
+       public void testIsEmpty() {
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               assertFalse(methodSFS.isEmpty());
+               methodSFS = new SimpleFieldSet(true);
+               assertTrue(methodSFS.isEmpty());
+       }
+       
+       /**
+        * Tests directSubsetNameIterator() method.
+        * It uses SAMPLE_STRING_PAIRS and for this reason
+        * the expected subset is "foo".
+        */
+       public void testDirectSubsetNameIterator() {
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               String expectedSubset = SAMPLE_STRING_PAIRS[0][0];      //"foo"
+               Iterator methodIter = methodSFS.directSubsetNameIterator();
+               while (methodIter.hasNext())
+                       ((String)methodIter.next()).equals(expectedSubset);
+               methodSFS = new SimpleFieldSet(true);
+               methodIter = methodSFS.directSubsetNameIterator();
+               assertNull(methodIter);
+       }
+       
+       /**
+        * Tests nameOfDirectSubsets() method.
+        */
+       public void testNamesOfDirectSubsets() {
+               String[] expectedResult = {SAMPLE_STRING_PAIRS[0][0]};
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               
assertTrue(Arrays.equals(methodSFS.namesOfDirectSubsets(),expectedResult));
+
+               methodSFS = new SimpleFieldSet(true);
+               assertTrue(Arrays.equals(methodSFS.namesOfDirectSubsets(), new 
String[0]));
+       }
+       
+       /**
+        * Test the putOverwrite(String,String) method.
+        */
+       public void testPutOverwrite_String() {
+               String methodKey = "foo.bar";
+               String[] methodValues = {"boo","bar","zoo"};
+               String expectedResult = "zoo";
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               for (int i = 0 ; i < methodValues.length; i++)
+                       methodSFS.putOverwrite(methodKey,methodValues[i]);
+               assertEquals(methodSFS.get(methodKey),expectedResult);
+       }
+       
+       /**
+        * Test the putOverwrite(String,String[]) method.
+        */
+       public void testPutOverwrite_StringArray() {
+               String methodKey = "foo.bar";
+               String[] methodValues = {"boo","bar","zoo"};
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               methodSFS.putOverwrite(methodKey,methodValues);
+               
assertTrue(Arrays.equals(methodSFS.getAll(methodKey),methodValues));
+       }
+       
+       /**
+        * Test the putAppend(String,String) method.
+        */
+       public void testPutAppend() {
+               String methodKey = "foo.bar";
+               String[] methodValues = {"boo","bar","zoo"};
+               String expectedResult = "boo"+SimpleFieldSet.MULTI_VALUE_CHAR
+                                                               
+"bar"+SimpleFieldSet.MULTI_VALUE_CHAR
+                                                               +"zoo";
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               for (int i = 0 ; i < methodValues.length; i++)
+                       methodSFS.putAppend(methodKey,methodValues[i]);
+               assertEquals(methodSFS.get(methodKey),expectedResult);
+       }
+       
+       /**
+        * Tests the getAll(String) method.
+        */
+       public void testGetAll() {
+               String methodKey = "foo.bar";
+               String[] methodValues = {"boo","bar","zoo"};
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               for (int i = 0 ; i < methodValues.length; i++)
+                       methodSFS.putAppend(methodKey,methodValues[i]);
+               
assertTrue(Arrays.equals(methodSFS.getAll(methodKey),methodValues));
+       }
+       
+       /**
+        * Tests the getIntArray(String) method
+        */
+       public void testGetIntArray() {
+               SimpleFieldSet methodSFS = new SimpleFieldSet(true);
+               String keyPrefix = "foo";
+               for (int i = 0; i<15; i++)
+                       methodSFS.putAppend(keyPrefix,String.valueOf(i));
+               int[] result = methodSFS.getIntArray(keyPrefix);
+               for (int i = 0; i<15; i++)
+                       assertTrue(result[i]==i);
+               
+       }
+       
+       /**
+        * Tests removeValue(String) method
+        */
+       public void testRemoveValue() {
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               methodSFS.removeValue("foo");
+               assertNull(methodSFS.get(SAMPLE_STRING_PAIRS[0][0]));
+               for(int i=1;i<SAMPLE_STRING_PAIRS.length;i++)
+                       assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]),
+                                       SAMPLE_STRING_PAIRS[i][1]);
+       }
+       
+       /**
+        * Tests removeSubset(String) method
+        */
+       public void testRemoveSubset() {
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               methodSFS.removeSubset("foo");
+               for(int i = 1; i< 3; i++)
+                       assertNull(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]));
+               assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[0][0]),
+                               SAMPLE_STRING_PAIRS[0][1]);
+               for(int i = 3; i< 5; i++)
+                       assertEquals(methodSFS.get(SAMPLE_STRING_PAIRS[i][0]),
+                                       SAMPLE_STRING_PAIRS[i][1]);
+       }
+
+       /**
+        * Searches for a key in a given String[][] array.
+        * We consider that keys are stored in String[x][0] 
+        * @param aStringPairsArray
+        * @param aPrefix that could be put before found key
+        * @param aKey to be searched
+        * @return true if there is the key
+        */
+       private boolean isAKey(String[][] aStringPairsArray, String aPrefix, 
String aKey) {
+               for (int i=0; i<aStringPairsArray.length; i++)
+                       if (aKey.equals(aPrefix+aStringPairsArray[i][0])) 
+                               return true;
+               return false;
+       }
+       
+       /**
+        * Verifies if all keys in a String[][]
+        * (We consider that keys are stored in String[x][0])
+        * are the same that the Iterator provides.
+        * In this way both hasNext() and next() methods
+        * are tested.
+        * @param aStringPairsArray
+        * @param aPrefix that could be put before found key
+        * @param aIterator
+        * @return true if they have the same key set
+        */
+       private boolean areAllContainedKeys(String[][] aStringPairsArray, 
String aPrefix, Iterator aIterator) {
+               boolean retValue = true;
+               int actualLength = 0;
+               while (aIterator.hasNext()) {
+                       actualLength++;
+                       retValue &= 
isAKey(aStringPairsArray,aPrefix,(String)aIterator.next());
+               }
+               retValue &= (actualLength==aStringPairsArray.length);
+               return retValue;
+       }
+       
+       /**
+        * Tests the Iterator given for the
+        * SimpleFieldSet class.
+        * It tests hasNext() and next() methods.
+        */
+       public void testKeyIterator() {
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               Iterator itr = methodSFS.keyIterator();
+               assertTrue(areAllContainedKeys(SAMPLE_STRING_PAIRS,"",itr));
+       }
+       
+       /**
+        * Tests the Iterator created using prefix 
+        * given for the SimpleFieldSet class
+        */
+       public void testKeyIterator_String() {
+               String methodPrefix = "bob";
+               SimpleFieldSet methodSFS = sfsFromSampleStringPairs();
+               Iterator itr = methodSFS.keyIterator(methodPrefix);
+               
assertTrue(areAllContainedKeys(SAMPLE_STRING_PAIRS,methodPrefix,itr));  
+       }
+}

Deleted: branches/freenet-jfk/test/freenet/support/SizeUtilTest.java
===================================================================
--- trunk/freenet/test/freenet/support/SizeUtilTest.java        2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/SizeUtilTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,77 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.SizeUtil} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class SizeUtilTest extends TestCase {
-       
-       String[][] valAndExpected = {
-                       //one byte
-                       {"1","B"},
-                       //one kilobyte
-                       {"1024","KiB"},
-                       //one megabyte
-                       {"1048576","MiB"},                                      
                
-                       //one gigabyte
-                       {"1073741824","GiB"},                                   
        
-                       //one terabyte
-                       {"1099511627776","TiB"},                                
        
-                       //one petabyte
-                       //{"1125899906842624","1.0 PiB"},                       
-                       //one exabyte
-                       //{"1152921504606846976", "1.0 EiB"},           
-                       //one zettabyte
-                       //{"1180591620717411303424", "1.0 ZiB"},        
-                       //one yottabyte
-                       //{"1208925819614629174706176","1.0 YiB"},      
-       };
-       
-       public void testFormatSizeLong() {
-               Long methodLong;
-               methodLong = Long.valueOf(valAndExpected[0][0]);
-               assertEquals(SizeUtil.formatSize(methodLong.longValue()),
-                               "1 "+valAndExpected[0][1]);
-               
-               for(int i = 1; i < valAndExpected.length; i++) {
-                       methodLong = Long.valueOf(valAndExpected[i][0]);
-                       
assertEquals(SizeUtil.formatSize(methodLong.longValue()),
-                                       "1.0 "+valAndExpected[i][1]); }
-       }
-
-       /**
-        * Tests if formatSize(long) method
-        * works correctly with intermediate values
-        * (i.e. 1/4,1/2,3/4)
-        */
-       public void testFormatSizeLong_WithIntermediateValues() {
-               Long methodLong;
-               String[] actualValue = {"1.0","1.25","1.5","1.75"};
-               
-               for(int i = 1; i < valAndExpected.length; i++) {
-                       methodLong = Long.valueOf(valAndExpected[i][0]);
-                       for(int j = 0; j < 4; j++)
-                               
assertEquals(SizeUtil.formatSize(methodLong.longValue()+(methodLong.longValue()*j/4)),
-                                               actualValue[j]+" 
"+valAndExpected[i][1]);
-                       }
-       }
-
-}

Copied: branches/freenet-jfk/test/freenet/support/SizeUtilTest.java (from rev 
14796, trunk/freenet/test/freenet/support/SizeUtilTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/SizeUtilTest.java                 
        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/SizeUtilTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,77 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.SizeUtil} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class SizeUtilTest extends TestCase {
+       
+       String[][] valAndExpected = {
+                       //one byte
+                       {"1","B"},
+                       //one kilobyte
+                       {"1024","KiB"},
+                       //one megabyte
+                       {"1048576","MiB"},                                      
                
+                       //one gigabyte
+                       {"1073741824","GiB"},                                   
        
+                       //one terabyte
+                       {"1099511627776","TiB"},                                
        
+                       //one petabyte
+                       //{"1125899906842624","1.0 PiB"},                       
+                       //one exabyte
+                       //{"1152921504606846976", "1.0 EiB"},           
+                       //one zettabyte
+                       //{"1180591620717411303424", "1.0 ZiB"},        
+                       //one yottabyte
+                       //{"1208925819614629174706176","1.0 YiB"},      
+       };
+       
+       public void testFormatSizeLong() {
+               Long methodLong;
+               methodLong = Long.valueOf(valAndExpected[0][0]);
+               assertEquals(SizeUtil.formatSize(methodLong.longValue()),
+                               "1 "+valAndExpected[0][1]);
+               
+               for(int i = 1; i < valAndExpected.length; i++) {
+                       methodLong = Long.valueOf(valAndExpected[i][0]);
+                       
assertEquals(SizeUtil.formatSize(methodLong.longValue()),
+                                       "1.0 "+valAndExpected[i][1]); }
+       }
+
+       /**
+        * Tests if formatSize(long) method
+        * works correctly with intermediate values
+        * (i.e. 1/4,1/2,3/4)
+        */
+       public void testFormatSizeLong_WithIntermediateValues() {
+               Long methodLong;
+               String[] actualValue = {"1.0","1.25","1.5","1.75"};
+               
+               for(int i = 1; i < valAndExpected.length; i++) {
+                       methodLong = Long.valueOf(valAndExpected[i][0]);
+                       for(int j = 0; j < 4; j++)
+                               
assertEquals(SizeUtil.formatSize(methodLong.longValue()+(methodLong.longValue()*j/4)),
+                                               actualValue[j]+" 
"+valAndExpected[i][1]);
+                       }
+       }
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/TimeUtilTest.java
===================================================================
--- trunk/freenet/test/freenet/support/TimeUtilTest.java        2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/TimeUtilTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,147 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import java.util.Locale;
-
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.TimeUtil} class.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class TimeUtilTest extends TestCase {
-
-       //1w+1d+1h+1m+1s+1ms
-       private long oneForTermLong = 694861001;
-       
-       protected void setUp() throws Exception {
-               Locale.setDefault(Locale.US);
-       }
-       
-       /**
-        * Tests formatTime(long,int,boolean) method
-        * trying the biggest long value
-        */
-       public void testFormatTime_LongIntBoolean_MaxValue() {
-               String expectedForMaxLongValue = "15250284452w3d7h12m55.807s";
-               assertEquals(TimeUtil.formatTime(Long.MAX_VALUE,6,true),
-                               expectedForMaxLongValue);
-       }
-
-       /**
-        * Tests formatTime(long,int) method
-        * trying the biggest long value
-        */
-       public void testFormatTime_LongInt() {
-               String expectedForMaxLongValue = "15250284452w3d7h12m55s";
-               assertEquals(TimeUtil.formatTime(Long.MAX_VALUE,6),
-                               expectedForMaxLongValue);
-       }
-       
-       /**
-        * Tests formatTime(long) method
-        * trying the biggest long value
-        */
-       public void testFormatTime_Long() {
-               //it uses two terms by default
-               String expectedForMaxLongValue = "15250284452w3d";
-               assertEquals(TimeUtil.formatTime(Long.MAX_VALUE),
-                               expectedForMaxLongValue);
-       }
-       
-       /**
-        * Tests formatTime(long) method
-        * using known values.
-        * They could be checked using Google Calculator
-        * http://www.google.com/intl/en/help/features.html#calculator
-        */
-       public void testFormatTime_KnownValues() {
-               Long methodLong;
-               String[][] valAndExpected = {
-                               //one week
-                               {"604800000","1w"},     
-                               //one day
-                               {"86400000","1d"},      
-                               //one hour
-                               {"3600000","1h"},       
-                               //one minute
-                               {"60000","1m"},         
-                               //one second
-                               {"1000","1s"}           
-               };
-               for(int i = 0; i < valAndExpected.length; i++) {
-                       methodLong = Long.valueOf(valAndExpected[i][0]);
-                       
assertEquals(TimeUtil.formatTime(methodLong.longValue()),
-                                       valAndExpected[i][1]); }        
-       }
-       
-       /**
-        * Tests formatTime(long,int) method
-        * using a long value that generate every possible
-        * term kind. It tests if the maxTerms arguments
-        * works correctly
-        */
-       public void testFormatTime_LongIntBoolean_maxTerms() {
-               String[] valAndExpected = {
-                               //0 terms
-                               "",                                     
-                               //1 term
-                               "1w",                           
-                               //2 terms
-                               "1w1d",                         
-                               //3 terms
-                               "1w1d1h",                       
-                               //4 terms
-                               "1w1d1h1m",                     
-                               //5 terms
-                               "1w1d1h1m1s",           
-                               //6 terms
-                               "1w1d1h1m1.001s"        
-               };
-               for(int i = 0; i < valAndExpected.length; i++)
-                       assertEquals(TimeUtil.formatTime(oneForTermLong,i,true),
-                                       valAndExpected[i]);
-       }
-       
-       /**
-        * Tests formatTime(long,int) method
-        * using one millisecond time interval. 
-        * It tests if the withSecondFractions argument
-        * works correctly
-        */
-       public void testFormatTime_LongIntBoolean_milliseconds() {
-               long methodValue = 1;   //1ms
-               assertEquals(TimeUtil.formatTime(methodValue,6,false),"");
-               assertEquals(TimeUtil.formatTime(methodValue,6,true),"0.001s");
-       }
-       
-       /**
-        * Tests formatTime(long,int) method
-        * using a long value that generate every possible
-        * term kind. It tests if the maxTerms arguments
-        * works correctly
-        */
-       public void testFormatTime_LongIntBoolean_tooManyTerms() {      
-               try {
-                       TimeUtil.formatTime(oneForTermLong,7);
-                       fail("Expected IllegalArgumentException not thrown"); }
-               catch (IllegalArgumentException anException) {
-                       assertNotNull(anException); }
-       }
-
-}

Copied: branches/freenet-jfk/test/freenet/support/TimeUtilTest.java (from rev 
14796, trunk/freenet/test/freenet/support/TimeUtilTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/TimeUtilTest.java                 
        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/TimeUtilTest.java 2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,147 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.TimeUtil} class.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class TimeUtilTest extends TestCase {
+
+       //1w+1d+1h+1m+1s+1ms
+       private long oneForTermLong = 694861001;
+       
+       protected void setUp() throws Exception {
+               Locale.setDefault(Locale.US);
+       }
+       
+       /**
+        * Tests formatTime(long,int,boolean) method
+        * trying the biggest long value
+        */
+       public void testFormatTime_LongIntBoolean_MaxValue() {
+               String expectedForMaxLongValue = "15250284452w3d7h12m55.807s";
+               assertEquals(TimeUtil.formatTime(Long.MAX_VALUE,6,true),
+                               expectedForMaxLongValue);
+       }
+
+       /**
+        * Tests formatTime(long,int) method
+        * trying the biggest long value
+        */
+       public void testFormatTime_LongInt() {
+               String expectedForMaxLongValue = "15250284452w3d7h12m55s";
+               assertEquals(TimeUtil.formatTime(Long.MAX_VALUE,6),
+                               expectedForMaxLongValue);
+       }
+       
+       /**
+        * Tests formatTime(long) method
+        * trying the biggest long value
+        */
+       public void testFormatTime_Long() {
+               //it uses two terms by default
+               String expectedForMaxLongValue = "15250284452w3d";
+               assertEquals(TimeUtil.formatTime(Long.MAX_VALUE),
+                               expectedForMaxLongValue);
+       }
+       
+       /**
+        * Tests formatTime(long) method
+        * using known values.
+        * They could be checked using Google Calculator
+        * http://www.google.com/intl/en/help/features.html#calculator
+        */
+       public void testFormatTime_KnownValues() {
+               Long methodLong;
+               String[][] valAndExpected = {
+                               //one week
+                               {"604800000","1w"},     
+                               //one day
+                               {"86400000","1d"},      
+                               //one hour
+                               {"3600000","1h"},       
+                               //one minute
+                               {"60000","1m"},         
+                               //one second
+                               {"1000","1s"}           
+               };
+               for(int i = 0; i < valAndExpected.length; i++) {
+                       methodLong = Long.valueOf(valAndExpected[i][0]);
+                       
assertEquals(TimeUtil.formatTime(methodLong.longValue()),
+                                       valAndExpected[i][1]); }        
+       }
+       
+       /**
+        * Tests formatTime(long,int) method
+        * using a long value that generate every possible
+        * term kind. It tests if the maxTerms arguments
+        * works correctly
+        */
+       public void testFormatTime_LongIntBoolean_maxTerms() {
+               String[] valAndExpected = {
+                               //0 terms
+                               "",                                     
+                               //1 term
+                               "1w",                           
+                               //2 terms
+                               "1w1d",                         
+                               //3 terms
+                               "1w1d1h",                       
+                               //4 terms
+                               "1w1d1h1m",                     
+                               //5 terms
+                               "1w1d1h1m1s",           
+                               //6 terms
+                               "1w1d1h1m1.001s"        
+               };
+               for(int i = 0; i < valAndExpected.length; i++)
+                       assertEquals(TimeUtil.formatTime(oneForTermLong,i,true),
+                                       valAndExpected[i]);
+       }
+       
+       /**
+        * Tests formatTime(long,int) method
+        * using one millisecond time interval. 
+        * It tests if the withSecondFractions argument
+        * works correctly
+        */
+       public void testFormatTime_LongIntBoolean_milliseconds() {
+               long methodValue = 1;   //1ms
+               assertEquals(TimeUtil.formatTime(methodValue,6,false),"");
+               assertEquals(TimeUtil.formatTime(methodValue,6,true),"0.001s");
+       }
+       
+       /**
+        * Tests formatTime(long,int) method
+        * using a long value that generate every possible
+        * term kind. It tests if the maxTerms arguments
+        * works correctly
+        */
+       public void testFormatTime_LongIntBoolean_tooManyTerms() {      
+               try {
+                       TimeUtil.formatTime(oneForTermLong,7);
+                       fail("Expected IllegalArgumentException not thrown"); }
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/URIPreEncoderTest.java
===================================================================
--- trunk/freenet/test/freenet/support/URIPreEncoderTest.java   2007-08-18 
19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/URIPreEncoderTest.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,69 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import freenet.utils.UTFUtil;
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.URIPreEncoder} class
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class URIPreEncoderTest extends TestCase {
-
-       private String prtblAscii = new String(UTFUtil.PRINTABLE_ASCII);
-       private String stressedUTF_8Chars = new String(UTFUtil.STRESSED_UTF);
-       
-       private boolean containsOnlyValidChars(String aString) {
-               char eachChar;
-               for (int i = 0; i < aString.length(); i++) {
-                       eachChar = aString.charAt(i);
-                       if (URIPreEncoder.allowedChars.indexOf(eachChar) < 0)
-                               return false;
-               };
-               return true;
-       }
-       
-       /**
-        * Tests encode(String) method
-        * to verify if it converts all
-        * not safe chars into safe chars.
-        */
-       public void testEncode() {
-               String toEncode = prtblAscii+stressedUTF_8Chars;
-               String encoded = URIPreEncoder.encode(toEncode);
-               assertTrue(containsOnlyValidChars(encoded));
-       }
-
-       /**
-        * Tests encodeURI(String) method
-        * to verify if it converts all
-        * not safe chars into safe chars.
-        */
-       public void testEncodeURI() {
-               String toEncode = prtblAscii+stressedUTF_8Chars;
-               URI encoded;
-               //try {
-               //      encoded = URIPreEncoder.encodeURI(toEncode);            
this method will throw a not expected exception because '%' is included as a 
valid char
-               //      assertTrue(containsOnlyValidChars(encoded.toString()));
-               //} catch (URISyntaxException anException) {
-               //      fail("Not expected exception thrown : " + 
anException.getMessage()); }
-       }
-
-}

Copied: branches/freenet-jfk/test/freenet/support/URIPreEncoderTest.java (from 
rev 14796, trunk/freenet/test/freenet/support/URIPreEncoderTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/URIPreEncoderTest.java            
                (rev 0)
+++ branches/freenet-jfk/test/freenet/support/URIPreEncoderTest.java    
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,69 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import freenet.utils.UTFUtil;
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.URIPreEncoder} class
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class URIPreEncoderTest extends TestCase {
+
+       private String prtblAscii = new String(UTFUtil.PRINTABLE_ASCII);
+       private String stressedUTF_8Chars = new String(UTFUtil.STRESSED_UTF);
+       
+       private boolean containsOnlyValidChars(String aString) {
+               char eachChar;
+               for (int i = 0; i < aString.length(); i++) {
+                       eachChar = aString.charAt(i);
+                       if (URIPreEncoder.allowedChars.indexOf(eachChar) < 0)
+                               return false;
+               };
+               return true;
+       }
+       
+       /**
+        * Tests encode(String) method
+        * to verify if it converts all
+        * not safe chars into safe chars.
+        */
+       public void testEncode() {
+               String toEncode = prtblAscii+stressedUTF_8Chars;
+               String encoded = URIPreEncoder.encode(toEncode);
+               assertTrue(containsOnlyValidChars(encoded));
+       }
+
+       /**
+        * Tests encodeURI(String) method
+        * to verify if it converts all
+        * not safe chars into safe chars.
+        */
+       public void testEncodeURI() {
+               String toEncode = prtblAscii+stressedUTF_8Chars;
+               URI encoded;
+               //try {
+               //      encoded = URIPreEncoder.encodeURI(toEncode);            
this method will throw a not expected exception because '%' is included as a 
valid char
+               //      assertTrue(containsOnlyValidChars(encoded.toString()));
+               //} catch (URISyntaxException anException) {
+               //      fail("Not expected exception thrown : " + 
anException.getMessage()); }
+       }
+
+}

Deleted: branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java
===================================================================
--- trunk/freenet/test/freenet/support/URLEncoderDecoderTest.java       
2007-08-18 19:36:17 UTC (rev 14796)
+++ branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -1,167 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.support;
-
-import java.io.UnsupportedEncodingException;
-import freenet.utils.*;
-import junit.framework.TestCase;
-
-/**
- * Test case for {@link freenet.support.URLEncoder} and 
- * {@link freenet.support.URLDecoder} classes.
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public class URLEncoderDecoderTest extends TestCase {
-
-       private String prtblAscii = new String(UTFUtil.PRINTABLE_ASCII);
-       private String stressedUTF_8Chars = new String(UTFUtil.STRESSED_UTF);
-                       
-       /**
-        * Tests if URLEncode.encode(String) and
-        * URLDecode.decode(String,boolean) methods
-        * work correctly together, both with safe
-        * characters and not safe "base" (i.e. ASCII) chars .
-        */
-       public void testEncodeDecodeString_notSafeBaseChars() {
-               String[] toEncode = {
-                               //safe chars
-                               URLEncoder.safeURLCharacters,
-                               prtblAscii,
-                               //triple % char, if badly encoded it will 
generate an exception
-                               "%%%",
-                               //no chars
-                               ""};
-               try {
-                       assertTrue(areCorrectlyEncodedDecoded(toEncode));
-               } catch (URLEncodedFormatException anException) {
-                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
-       }
-       
-       /**
-        * Tests if URLEncode.encode(String) and
-        * URLDecode.decode(String,boolean) methods
-        * work correctly together, both with safe
-        * characters and not safe "advanced" (i.e. not ASCII) chars .
-        */
-       public void testEncodeDecodeString_notSafeAdvChars() {
-               String[] toEncode = {stressedUTF_8Chars};
-               try {
-                       assertTrue(areCorrectlyEncodedDecoded(toEncode));
-               } catch (URLEncodedFormatException anException) {
-                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
-       }
-
-       /**
-        * Verifies if a string is the same after
-        * being processed by encoding and 
-        * decoding methods
-        * @param toEncode String to Encode
-        * @return true means to be tolerant of bogus escapes
-        * @throws URLEncodedFormatException
-        */
-       private boolean areCorrectlyEncodedDecoded(String[] toEncode) throws 
URLEncodedFormatException {
-               boolean retValue = true;
-               String[] encoded = new String[toEncode.length];
-               //encoding
-               for (int i = 0; i < encoded.length; i++)
-                       encoded[i] = URLEncoder.encode(toEncode[i]);
-               //decoding
-               for (int i = 0; i < encoded.length; i++)
-                       retValue &= 
(URLDecoder.decode(encoded[i],false)).equals(toEncode[i]);
-               return retValue;
-       }
-       
-       /**
-        * Tests encode(String,String,boolean) method
-        * to verify if the force parameter is
-        * well-managed for each safeURLCharacter,
-        * with both true and false ascii-flag.
-        */
-       public void testEncodeForced() {
-               String toEncode,expectedResult;
-               char eachChar;
-               for(int i=0; i<URLEncoder.safeURLCharacters.length(); i++) {
-                       eachChar = URLEncoder.safeURLCharacters.charAt(i);
-                       toEncode = String.valueOf(eachChar);
-                       try {
-                               expectedResult = "%"+ HexUtil.bytesToHex(
-                                               //since safe chars are only 
US-ASCII
-                                               toEncode.getBytes("US-ASCII")); 
-                               
assertEquals(URLEncoder.encode(toEncode,toEncode,false),
-                                               expectedResult);
-                               
assertEquals(URLEncoder.encode(toEncode,toEncode,true),
-                                               expectedResult);
-                       } catch (UnsupportedEncodingException anException) {
-                               fail("Not expected exception thrown : " + 
anException.getMessage()); }
-               }
-       }
-       
-       
-       /**
-        * Verifies if a URLEncodedFormatException is raised
-        * when decoding the provided String
-        * @param toDecode the String to decode
-        * @param tolerant whether the decoding should be tolerant with 
-        * @return
-        */
-       private boolean isDecodeRaisingEncodedException(String toDecode, 
boolean tolerant) {
-               boolean retValue = false;
-               try {
-                       System.out.println(URLDecoder.decode(toDecode,false));
-               } catch (URLEncodedFormatException anException) {
-                       retValue = true; }
-               return retValue;
-       }
-       
-       /**
-        * Tests decode(String,boolean) method
-        * using not valid encoded String to
-        * verifies if it raises an exception
-        */
-       public void testDecodeWrongString() {
-               String toDecode = "%00";
-               assertTrue(isDecodeRaisingEncodedException(toDecode,false));
-       }
-       
-       /**
-        * Tests decode(String,boolean) method
-        * using not valid hex values String to
-        * verifies if it raises an exception
-        */
-       public void testDecodeWrongHex() {
-               String toDecode = 
"123456789abcde"+prtblAscii+stressedUTF_8Chars;
-               
-               for (int i = 0; i<toDecode.length(); i++)
-                       assertTrue(
-                                       
isDecodeRaisingEncodedException("%"+toDecode.substring(i,i+1),false));
-       }
-       
-       /**
-        * Tests decode(String,boolean) method
-        * trying the boolean argument, to verify
-        * if it work correctly as a hack to allow users 
-        * to paste in URLs containing %'s.
-        */
-       public void testTolerantDecoding() {
-               String toDecode = "%%%";
-               
-               try {
-                       assertEquals(URLDecoder.decode(toDecode,true),toDecode);
-               } catch (URLEncodedFormatException anException) {
-                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
-       }
-}
\ No newline at end of file

Copied: branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java 
(from rev 14796, trunk/freenet/test/freenet/support/URLEncoderDecoderTest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java        
                        (rev 0)
+++ branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java        
2007-08-21 20:26:59 UTC (rev 14828)
@@ -0,0 +1,167 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.support;
+
+import java.io.UnsupportedEncodingException;
+import freenet.utils.*;
+import junit.framework.TestCase;
+
+/**
+ * Test case for {@link freenet.support.URLEncoder} and 
+ * {@link freenet.support.URLDecoder} classes.
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class URLEncoderDecoderTest extends TestCase {
+
+       private String prtblAscii = new String(UTFUtil.PRINTABLE_ASCII);
+       private String stressedUTF_8Chars = new String(UTFUtil.STRESSED_UTF);
+                       
+       /**
+        * Tests if URLEncode.encode(String) and
+        * URLDecode.decode(String,boolean) methods
+        * work correctly together, both with safe
+        * characters and not safe "base" (i.e. ASCII) chars .
+        */
+       public void testEncodeDecodeString_notSafeBaseChars() {
+               String[] toEncode = {
+                               //safe chars
+                               URLEncoder.safeURLCharacters,
+                               prtblAscii,
+                               //triple % char, if badly encoded it will 
generate an exception
+                               "%%%",
+                               //no chars
+                               ""};
+               try {
+                       assertTrue(areCorrectlyEncodedDecoded(toEncode));
+               } catch (URLEncodedFormatException anException) {
+                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
+       }
+       
+       /**
+        * Tests if URLEncode.encode(String) and
+        * URLDecode.decode(String,boolean) methods
+        * work correctly together, both with safe
+        * characters and not safe "advanced" (i.e. not ASCII) chars .
+        */
+       public void testEncodeDecodeString_notSafeAdvChars() {
+               String[] toEncode = {stressedUTF_8Chars};
+               try {
+                       assertTrue(areCorrectlyEncodedDecoded(toEncode));
+               } catch (URLEncodedFormatException anException) {
+                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
+       }
+
+       /**
+        * Verifies if a string is the same after
+        * being processed by encoding and 
+        * decoding methods
+        * @param toEncode String to Encode
+        * @return true means to be tolerant of bogus escapes
+        * @throws URLEncodedFormatException
+        */
+       private boolean areCorrectlyEncodedDecoded(String[] toEncode) throws 
URLEncodedFormatException {
+               boolean retValue = true;
+               String[] encoded = new String[toEncode.length];
+               //encoding
+               for (int i = 0; i < encoded.length; i++)
+                       encoded[i] = URLEncoder.encode(toEncode[i]);
+               //decoding
+               for (int i = 0; i < encoded.length; i++)
+                       retValue &= 
(URLDecoder.decode(encoded[i],false)).equals(toEncode[i]);
+               return retValue;
+       }
+       
+       /**
+        * Tests encode(String,String,boolean) method
+        * to verify if the force parameter is
+        * well-managed for each safeURLCharacter,
+        * with both true and false ascii-flag.
+        */
+       public void testEncodeForced() {
+               String toEncode,expectedResult;
+               char eachChar;
+               for(int i=0; i<URLEncoder.safeURLCharacters.length(); i++) {
+                       eachChar = URLEncoder.safeURLCharacters.charAt(i);
+                       toEncode = String.valueOf(eachChar);
+                       try {
+                               expectedResult = "%"+ HexUtil.bytesToHex(
+                                               //since safe chars are only 
US-ASCII
+                                               toEncode.getBytes("US-ASCII")); 
+                               
assertEquals(URLEncoder.encode(toEncode,toEncode,false),
+                                               expectedResult);
+                               
assertEquals(URLEncoder.encode(toEncode,toEncode,true),
+                                               expectedResult);
+                       } catch (UnsupportedEncodingException anException) {
+                               fail("Not expected exception thrown : " + 
anException.getMessage()); }
+               }
+       }
+       
+       
+       /**
+        * Verifies if a URLEncodedFormatException is raised
+        * when decoding the provided String
+        * @param toDecode the String to decode
+        * @param tolerant whether the decoding should be tolerant with 
+        * @return
+        */
+       private boolean isDecodeRaisingEncodedException(String toDecode, 
boolean tolerant) {
+               boolean retValue = false;
+               try {
+                       System.out.println(URLDecoder.decode(toDecode,false));
+               } catch (URLEncodedFormatException anException) {
+                       retValue = true; }
+               return retValue;
+       }
+       
+       /**
+        * Tests decode(String,boolean) method
+        * using not valid encoded String to
+        * verifies if it raises an exception
+        */
+       public void testDecodeWrongString() {
+               String toDecode = "%00";
+               assertTrue(isDecodeRaisingEncodedException(toDecode,false));
+       }
+       
+       /**
+        * Tests decode(String,boolean) method
+        * using not valid hex values String to
+        * verifies if it raises an exception
+        */
+       public void testDecodeWrongHex() {
+               String toDecode = 
"123456789abcde"+prtblAscii+stressedUTF_8Chars;
+               
+               for (int i = 0; i<toDecode.length(); i++)
+                       assertTrue(
+                                       
isDecodeRaisingEncodedException("%"+toDecode.substring(i,i+1),false));
+       }
+       
+       /**
+        * Tests decode(String,boolean) method
+        * trying the boolean argument, to verify
+        * if it work correctly as a hack to allow users 
+        * to paste in URLs containing %'s.
+        */
+       public void testTolerantDecoding() {
+               String toDecode = "%%%";
+               
+               try {
+                       assertEquals(URLDecoder.decode(toDecode,true),toDecode);
+               } catch (URLEncodedFormatException anException) {
+                       fail("Not expected exception thrown : " + 
anException.getMessage()); }
+       }
+}
\ No newline at end of file

Copied: branches/freenet-jfk/test/freenet/utils (from rev 14796, 
trunk/freenet/test/freenet/utils)

Deleted: branches/freenet-jfk/test/freenet/utils/UTFUtil.java
===================================================================
--- trunk/freenet/test/freenet/utils/UTFUtil.java       2007-08-18 19:36:17 UTC 
(rev 14796)
+++ branches/freenet-jfk/test/freenet/utils/UTFUtil.java        2007-08-21 
20:26:59 UTC (rev 14828)
@@ -1,147 +0,0 @@
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-package freenet.utils;
-
-import junit.framework.TestCase;
-
-/**
- * Utility class used throught test cases classes
- * 
- * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
- */
-public final class UTFUtil extends TestCase {
-
-       public void testFake() {
-               
-       }
-       
-       //printable ascii symbols
-       public static final char PRINTABLE_ASCII[] = {
-               ' 
','!','@','#','$','%','^','&','(',')','+','=','{','}','[',']',':',';','\\','\"','\'',
-               ',','<','>','.','?','~','`'};
-
-       //stressed UTF chars values
-       public static final char STRESSED_UTF[] = { 
-               
//???????????????????????????????????????????????????????????????
-               
'\u00c9','\u00e2','\u00fb','\u0114','\u012d','\u0146','\u015f','\u00ca','\u00e3','\u00fc',
-               
'\u0115','\u012e','\u0147','\u0160','\u00cb','\u00e4','\u00fd','\u0116','\u012f','\u0148',
-               
'\u0161','\u00cc','\u00e5','\u00fe','\u0117','\u0130','\u0149','\u0162','\u00cd','\u00e6',
-               
'\u00ff','\u0118','\u0131','\u014a','\u0163','\u00ce','\u00e7','\u0100','\u0119','\u0132',
-               
'\u014b','\u0164','\u00cf','\u00e8','\u0101','\u011a','\u0133','\u014c','\u0165','\u00d0',
-               
'\u00e9','\u0102','\u011b','\u0134','\u014d','\u0166','\u00d1','\u00ea','\u0103','\u011c',
-               '\u0135','\u014e','\u0167',
-               
//???????????????????????????????????????????????????????????????
-               
'\u00d2','\u00eb','\u0104','\u011d','\u0136','\u014f','\u0168','\u00d3','\u00ec','\u0105',
-               
'\u011e','\u0137','\u0150','\u0169','\u00d4','\u00ed','\u0106','\u011f','\u0138','\u0151',
-               
'\u016a','\u00d5','\u00ee','\u0107','\u0120','\u0139','\u0152','\u016b','\u00d6','\u00ef',
-               
'\u0108','\u0121','\u013a','\u0153','\u016c','\u00d7','\u00f0','\u0109','\u0122','\u013b',
-               
'\u0154','\u016d','\u00d8','\u00f1','\u010a','\u0123','\u013c','\u0155','\u016e','\u00d9',
-               
'\u00f2','\u010b','\u0124','\u013d','\u0156','\u016f','\u00da','\u00f3','\u010c','\u0125',
-               '\u013e','\u0157','\u0170',
-               //?????????????????????????????????????????????????
-               
'\u00db','\u00f4','\u010d','\u0126','\u013f','\u0158','\u0171','\u00dc','\u00f5','\u010e',
-               
'\u0127','\u0140','\u0159','\u0172','\u00dd','\u00f6','\u010f','\u0128','\u0141','\u015a',
-               
'\u0173','\u00de','\u00f7','\u0110','\u0129','\u0142','\u015b','\u0174','\u00df','\u00f8',
-               
'\u0111','\u012a','\u0143','\u015c','\u0175','\u00e0','\u00f9','\u0112','\u012b','\u0144',
-               
'\u015d','\u0176','\u00e1','\u00fa','\u0113','\u012c','\u0145','\u015e','\u0177'};
-       
-       /*
-        * HTML entities ISO-88591
-        * see for reference 
http://www.w3.org/TR/html4/sgml/entities.html#iso-88591
-        */
-       public static final String HTML_ENTITIES_UTF[][] = {
-               //ISO 8859-1 Symbol Entities
-               
{"\u00a1","&iexcl;"},{"\u00a2","&cent;"},{"\u00a3","&pound;"},{"\u00a4","&curren;"},
-               
{"\u00a5","&yen;"},{"\u00a6","&brvbar;"},{"\u00a7","&sect;"},{"\u00a8","&uml;"},
-               
{"\u00a9","&copy;"},{"\u00aa","&ordf;"},{"\u00ab","&laquo;"},{"\u00ac","&not;"},
-               {"\u00ad","&shy;"},{"\u00ae","&reg;"},{"\u00af","&macr;"},
-               
{"\u00b0","&deg;"},{"\u00b1","&plusmn;"},{"\u00b2","&sup2;"},{"\u00b3","&sup3;"},
-               
{"\u00b4","&acute;"},{"\u00b5","&micro;"},{"\u00b6","&para;"},{"\u00b7","&middot;"},
-               
{"\u00b8","&cedil;"},{"\u00b9","&sup1;"},{"\u00ba","&ordm;"},{"\u00bb","&raquo;"},
-               
{"\u00bc","&frac14;"},{"\u00bd","&frac12;"},{"\u00be","&frac34;"},{"\u00bf","&iquest;"},
-               //ISO 8859-1 Character Entities
-               
{"\u00c0","&Agrave;"},{"\u00c1","&Aacute;"},{"\u00c2","&Acirc;"},{"\u00c3","&Atilde;"},
-               
{"\u00c4","&Auml;"},{"\u00c5","&Aring;"},{"\u00c6","&AElig;"},{"\u00c7","&Ccedil;"},
-               
{"\u00c8","&Egrave;"},{"\u00c9","&Eacute;"},{"\u00ca","&Ecirc;"},{"\u00cb","&Euml;"},
-               
{"\u00cc","&Igrave;"},{"\u00cd","&Iacute;"},{"\u00ce","&Icirc;"},{"\u00cf","&Iuml;"},
-               
{"\u00d0","&ETH;"},{"\u00d1","&Ntilde;"},{"\u00d2","&Ograve;"},{"\u00d3","&Oacute;"},
-               
{"\u00d4","&Ocirc;"},{"\u00d5","&Otilde;"},{"\u00d6","&Ouml;"},{"\u00d7","&times;"},
-               
{"\u00d8","&Oslash;"},{"\u00d9","&Ugrave;"},{"\u00da","&Uacute;"},{"\u00db","&Ucirc;"},
-               
{"\u00dc","&Uuml;"},{"\u00dd","&Yacute;"},{"\u00de","&THORN;"},{"\u00df","&szlig;"},
-               
{"\u00e0","&agrave;"},{"\u00e1","&aacute;"},{"\u00e2","&acirc;"},{"\u00e3","&atilde;"},
-               
{"\u00e4","&auml;"},{"\u00e5","&aring;"},{"\u00e6","&aelig;"},{"\u00e7","&ccedil;"},
-               
{"\u00e8","&egrave;"},{"\u00e9","&eacute;"},{"\u00ea","&ecirc;"},{"\u00eb","&euml;"},
-               
{"\u00ec","&igrave;"},{"\u00ed","&iacute;"},{"\u00ee","&icirc;"},{"\u00ef","&iuml;"},
-               {"\u00f0","&eth;"},{"\u00f1","&ntilde;"},
-               
{"\u00f2","&ograve;"},{"\u00f3","&oacute;"},{"\u00f4","&ocirc;"},{"\u00f5","&otilde;"},
-               {"\u00f6","&ouml;"},{"\u00f7","&divide;"},{"\u00f8","&oslash;"},
-               
{"\u00f9","&ugrave;"},{"\u00fa","&uacute;"},{"\u00fb","&ucirc;"},{"\u00fc","&uuml;"},
-               {"\u00fd","&yacute;"},{"\u00fe","&thorn;"},{"\u00ff","&yuml;"},
-               //Greek
-               
{"\u0391","&Alpha;"},{"\u0392","&Beta;"},{"\u0393","&Gamma;"},{"\u0394","&Delta;"},
-               
{"\u0395","&Epsilon;"},{"\u0396","&Zeta;"},{"\u0397","&Eta;"},{"\u0398","&Theta;"},
-               
{"\u0399","&Iota;"},{"\u039a","&Kappa;"},{"\u039b","&Lambda;"},{"\u039c","&Mu;"},
-               
{"\u039d","&Nu;"},{"\u039e","&Xi;"},{"\u039f","&Omicron;"},{"\u03a0","&Pi;"},
-               
{"\u03a1","&Rho;"},{"\u03a3","&Sigma;"},{"\u03a4","&Tau;"},{"\u03a5","&Upsilon;"},
-               
{"\u03a6","&Phi;"},{"\u03a7","&Chi;"},{"\u03a8","&Psi;"},{"\u03a9","&Omega;"},
-               
{"\u03b1","&alpha;"},{"\u03b2","&beta;"},{"\u03b3","&gamma;"},{"\u03b4","&delta;"},
-               
{"\u03b5","&epsilon;"},{"\u03b6","&zeta;"},{"\u03b7","&eta;"},{"\u03b8","&theta;"},
-               
{"\u03b9","&iota;"},{"\u03ba","&kappa;"},{"\u03bb","&lambda;"},{"\u03bc","&mu;"},
-               
{"\u03bd","&nu;"},{"\u03be","&xi;"},{"\u03bf","&omicron;"},{"\u03c0","&pi;"},
-               
{"\u03c1","&rho;"},{"\u03c2","&sigmaf;"},{"\u03c3","&sigma;"},{"\u03c4","&tau;"},
-               
{"\u03c5","&upsilon;"},{"\u03c6","&phi;"},{"\u03c7","&chi;"},{"\u03c8","&psi;"},
-               
{"\u03c9","&omega;"},{"\u03d1","&thetasym;"},{"\u03d2","&upsih;"},{"\u03d6","&piv;"},
-               //General Punctuation
-               
{"\u2022","&bull;"},{"\u2026","&hellip;"},{"\u2032","&prime;"},{"\u2033","&Prime;"},
-               {"\u203e","&oline;"},{"\u2044","&frasl;"},
-               //Letterlike Symbols
-               
{"\u2118","&weierp;"},{"\u2111","&image;"},{"\u211c","&real;"},{"\u2122","&trade;"},
-               {"\u2135","&alefsym;"},
-               //Arrows
-               
{"\u2190","&larr;"},{"\u2191","&uarr;"},{"\u2192","&rarr;"},{"\u2193","&darr;"},
-               
{"\u2194","&harr;"},{"\u21b5","&crarr;"},{"\u21d0","&lArr;"},{"\u21d1","&uArr;"},
-               {"\u21d2","&rArr;"},{"\u21d3","&dArr;"},{"\u21d4","&hArr;"},
-               //Mathematical Operators
-               
{"\u2200","&forall;"},{"\u2202","&part;"},{"\u2203","&exist;"},{"\u2205","&empty;"},
-               
{"\u2207","&nabla;"},{"\u2208","&isin;"},{"\u2209","&notin;"},{"\u220b","&ni;"},
-               
{"\u220f","&prod;"},{"\u2211","&sum;"},{"\u2212","&minus;"},{"\u2217","&lowast;"},
-               
{"\u221a","&radic;"},{"\u221d","&prop;"},{"\u221e","&infin;"},{"\u2220","&ang;"},
-               
{"\u2227","&and;"},{"\u2228","&or;"},{"\u2229","&cap;"},{"\u222a","&cup;"},
-               
{"\u222b","&int;"},{"\u2234","&there4;"},{"\u223c","&sim;"},{"\u2245","&cong;"},
-               
{"\u2248","&asymp;"},{"\u2260","&ne;"},{"\u2261","&equiv;"},{"\u2264","&le;"},
-               
{"\u2265","&ge;"},{"\u2282","&sub;"},{"\u2283","&sup;"},{"\u2284","&nsub;"},
-               
{"\u2286","&sube;"},{"\u2287","&supe;"},{"\u2295","&oplus;"},{"\u2297","&otimes;"},
-               {"\u22a5","&perp;"},{"\u22c5","&sdot;"},
-               //Miscellaneous Technical
-               
{"\u2308","&lceil;"},{"\u2309","&rceil;"},{"\u230a","&lfloor;"},{"\u230b","&rfloor;"},
-               {"\u2329","&lang;"},{"\u232a","&rang;"},
-               //Geometric Shapes
-               
{"\u25ca","&loz;"},{"\u2660","&spades;"},{"\u2663","&clubs;"},{"\u2665","&hearts;"},
-               {"\u2666","&diams;"},
-               //Latin Extended-A
-               
{"\u0152","&OElig;"},{"\u0153","&oelig;"},{"\u0160","&Scaron;"},{"\u0161","&scaron;"},
-               {"\u0178","&Yuml;"},
-               //Spacing Modifier Letters
-               {"\u02c6","&circ;"},{"\u02dc","&tilde;"},
-               //General Punctuation
-               
{"\u2002","&ensp;"},{"\u2003","&emsp;"},{"\u2009","&thinsp;"},{"\u200c","&zwnj;"},
-               
{"\u200d","&zwj;"},{"\u200e","&lrm;"},{"\u200f","&rlm;"},{"\u2013","&ndash;"},
-               
{"\u2014","&mdash;"},{"\u2018","&lsquo;"},{"\u2019","&rsquo;"},{"\u201a","&sbquo;"},
-               
{"\u201c","&ldquo;"},{"\u201d","&rdquo;"},{"\u201e","&bdquo;"},{"\u2020","&dagger;"},
-               
{"\u2021","&Dagger;"},{"\u2030","&permil;"},{"\u2039","&lsaquo;"},{"\u203a","&rsaquo;"},
-               {"\u20ac","&euro;"}
-       };
-}

Copied: branches/freenet-jfk/test/freenet/utils/UTFUtil.java (from rev 14796, 
trunk/freenet/test/freenet/utils/UTFUtil.java)
===================================================================
--- branches/freenet-jfk/test/freenet/utils/UTFUtil.java                        
        (rev 0)
+++ branches/freenet-jfk/test/freenet/utils/UTFUtil.java        2007-08-21 
20:26:59 UTC (rev 14828)
@@ -0,0 +1,147 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+package freenet.utils;
+
+import junit.framework.TestCase;
+
+/**
+ * Utility class used throught test cases classes
+ * 
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public final class UTFUtil extends TestCase {
+
+       public void testFake() {
+               
+       }
+       
+       //printable ascii symbols
+       public static final char PRINTABLE_ASCII[] = {
+               ' 
','!','@','#','$','%','^','&','(',')','+','=','{','}','[',']',':',';','\\','\"','\'',
+               ',','<','>','.','?','~','`'};
+
+       //stressed UTF chars values
+       public static final char STRESSED_UTF[] = { 
+               
//???????????????????????????????????????????????????????????????
+               
'\u00c9','\u00e2','\u00fb','\u0114','\u012d','\u0146','\u015f','\u00ca','\u00e3','\u00fc',
+               
'\u0115','\u012e','\u0147','\u0160','\u00cb','\u00e4','\u00fd','\u0116','\u012f','\u0148',
+               
'\u0161','\u00cc','\u00e5','\u00fe','\u0117','\u0130','\u0149','\u0162','\u00cd','\u00e6',
+               
'\u00ff','\u0118','\u0131','\u014a','\u0163','\u00ce','\u00e7','\u0100','\u0119','\u0132',
+               
'\u014b','\u0164','\u00cf','\u00e8','\u0101','\u011a','\u0133','\u014c','\u0165','\u00d0',
+               
'\u00e9','\u0102','\u011b','\u0134','\u014d','\u0166','\u00d1','\u00ea','\u0103','\u011c',
+               '\u0135','\u014e','\u0167',
+               
//???????????????????????????????????????????????????????????????
+               
'\u00d2','\u00eb','\u0104','\u011d','\u0136','\u014f','\u0168','\u00d3','\u00ec','\u0105',
+               
'\u011e','\u0137','\u0150','\u0169','\u00d4','\u00ed','\u0106','\u011f','\u0138','\u0151',
+               
'\u016a','\u00d5','\u00ee','\u0107','\u0120','\u0139','\u0152','\u016b','\u00d6','\u00ef',
+               
'\u0108','\u0121','\u013a','\u0153','\u016c','\u00d7','\u00f0','\u0109','\u0122','\u013b',
+               
'\u0154','\u016d','\u00d8','\u00f1','\u010a','\u0123','\u013c','\u0155','\u016e','\u00d9',
+               
'\u00f2','\u010b','\u0124','\u013d','\u0156','\u016f','\u00da','\u00f3','\u010c','\u0125',
+               '\u013e','\u0157','\u0170',
+               //?????????????????????????????????????????????????
+               
'\u00db','\u00f4','\u010d','\u0126','\u013f','\u0158','\u0171','\u00dc','\u00f5','\u010e',
+               
'\u0127','\u0140','\u0159','\u0172','\u00dd','\u00f6','\u010f','\u0128','\u0141','\u015a',
+               
'\u0173','\u00de','\u00f7','\u0110','\u0129','\u0142','\u015b','\u0174','\u00df','\u00f8',
+               
'\u0111','\u012a','\u0143','\u015c','\u0175','\u00e0','\u00f9','\u0112','\u012b','\u0144',
+               
'\u015d','\u0176','\u00e1','\u00fa','\u0113','\u012c','\u0145','\u015e','\u0177'};
+       
+       /*
+        * HTML entities ISO-88591
+        * see for reference 
http://www.w3.org/TR/html4/sgml/entities.html#iso-88591
+        */
+       public static final String HTML_ENTITIES_UTF[][] = {
+               //ISO 8859-1 Symbol Entities
+               
{"\u00a1","&iexcl;"},{"\u00a2","&cent;"},{"\u00a3","&pound;"},{"\u00a4","&curren;"},
+               
{"\u00a5","&yen;"},{"\u00a6","&brvbar;"},{"\u00a7","&sect;"},{"\u00a8","&uml;"},
+               
{"\u00a9","&copy;"},{"\u00aa","&ordf;"},{"\u00ab","&laquo;"},{"\u00ac","&not;"},
+               {"\u00ad","&shy;"},{"\u00ae","&reg;"},{"\u00af","&macr;"},
+               
{"\u00b0","&deg;"},{"\u00b1","&plusmn;"},{"\u00b2","&sup2;"},{"\u00b3","&sup3;"},
+               
{"\u00b4","&acute;"},{"\u00b5","&micro;"},{"\u00b6","&para;"},{"\u00b7","&middot;"},
+               
{"\u00b8","&cedil;"},{"\u00b9","&sup1;"},{"\u00ba","&ordm;"},{"\u00bb","&raquo;"},
+               
{"\u00bc","&frac14;"},{"\u00bd","&frac12;"},{"\u00be","&frac34;"},{"\u00bf","&iquest;"},
+               //ISO 8859-1 Character Entities
+               
{"\u00c0","&Agrave;"},{"\u00c1","&Aacute;"},{"\u00c2","&Acirc;"},{"\u00c3","&Atilde;"},
+               
{"\u00c4","&Auml;"},{"\u00c5","&Aring;"},{"\u00c6","&AElig;"},{"\u00c7","&Ccedil;"},
+               
{"\u00c8","&Egrave;"},{"\u00c9","&Eacute;"},{"\u00ca","&Ecirc;"},{"\u00cb","&Euml;"},
+               
{"\u00cc","&Igrave;"},{"\u00cd","&Iacute;"},{"\u00ce","&Icirc;"},{"\u00cf","&Iuml;"},
+               
{"\u00d0","&ETH;"},{"\u00d1","&Ntilde;"},{"\u00d2","&Ograve;"},{"\u00d3","&Oacute;"},
+               
{"\u00d4","&Ocirc;"},{"\u00d5","&Otilde;"},{"\u00d6","&Ouml;"},{"\u00d7","&times;"},
+               
{"\u00d8","&Oslash;"},{"\u00d9","&Ugrave;"},{"\u00da","&Uacute;"},{"\u00db","&Ucirc;"},
+               
{"\u00dc","&Uuml;"},{"\u00dd","&Yacute;"},{"\u00de","&THORN;"},{"\u00df","&szlig;"},
+               
{"\u00e0","&agrave;"},{"\u00e1","&aacute;"},{"\u00e2","&acirc;"},{"\u00e3","&atilde;"},
+               
{"\u00e4","&auml;"},{"\u00e5","&aring;"},{"\u00e6","&aelig;"},{"\u00e7","&ccedil;"},
+               
{"\u00e8","&egrave;"},{"\u00e9","&eacute;"},{"\u00ea","&ecirc;"},{"\u00eb","&euml;"},
+               
{"\u00ec","&igrave;"},{"\u00ed","&iacute;"},{"\u00ee","&icirc;"},{"\u00ef","&iuml;"},
+               {"\u00f0","&eth;"},{"\u00f1","&ntilde;"},
+               
{"\u00f2","&ograve;"},{"\u00f3","&oacute;"},{"\u00f4","&ocirc;"},{"\u00f5","&otilde;"},
+               {"\u00f6","&ouml;"},{"\u00f7","&divide;"},{"\u00f8","&oslash;"},
+               
{"\u00f9","&ugrave;"},{"\u00fa","&uacute;"},{"\u00fb","&ucirc;"},{"\u00fc","&uuml;"},
+               {"\u00fd","&yacute;"},{"\u00fe","&thorn;"},{"\u00ff","&yuml;"},
+               //Greek
+               
{"\u0391","&Alpha;"},{"\u0392","&Beta;"},{"\u0393","&Gamma;"},{"\u0394","&Delta;"},
+               
{"\u0395","&Epsilon;"},{"\u0396","&Zeta;"},{"\u0397","&Eta;"},{"\u0398","&Theta;"},
+               
{"\u0399","&Iota;"},{"\u039a","&Kappa;"},{"\u039b","&Lambda;"},{"\u039c","&Mu;"},
+               
{"\u039d","&Nu;"},{"\u039e","&Xi;"},{"\u039f","&Omicron;"},{"\u03a0","&Pi;"},
+               
{"\u03a1","&Rho;"},{"\u03a3","&Sigma;"},{"\u03a4","&Tau;"},{"\u03a5","&Upsilon;"},
+               
{"\u03a6","&Phi;"},{"\u03a7","&Chi;"},{"\u03a8","&Psi;"},{"\u03a9","&Omega;"},
+               
{"\u03b1","&alpha;"},{"\u03b2","&beta;"},{"\u03b3","&gamma;"},{"\u03b4","&delta;"},
+               
{"\u03b5","&epsilon;"},{"\u03b6","&zeta;"},{"\u03b7","&eta;"},{"\u03b8","&theta;"},
+               
{"\u03b9","&iota;"},{"\u03ba","&kappa;"},{"\u03bb","&lambda;"},{"\u03bc","&mu;"},
+               
{"\u03bd","&nu;"},{"\u03be","&xi;"},{"\u03bf","&omicron;"},{"\u03c0","&pi;"},
+               
{"\u03c1","&rho;"},{"\u03c2","&sigmaf;"},{"\u03c3","&sigma;"},{"\u03c4","&tau;"},
+               
{"\u03c5","&upsilon;"},{"\u03c6","&phi;"},{"\u03c7","&chi;"},{"\u03c8","&psi;"},
+               
{"\u03c9","&omega;"},{"\u03d1","&thetasym;"},{"\u03d2","&upsih;"},{"\u03d6","&piv;"},
+               //General Punctuation
+               
{"\u2022","&bull;"},{"\u2026","&hellip;"},{"\u2032","&prime;"},{"\u2033","&Prime;"},
+               {"\u203e","&oline;"},{"\u2044","&frasl;"},
+               //Letterlike Symbols
+               
{"\u2118","&weierp;"},{"\u2111","&image;"},{"\u211c","&real;"},{"\u2122","&trade;"},
+               {"\u2135","&alefsym;"},
+               //Arrows
+               
{"\u2190","&larr;"},{"\u2191","&uarr;"},{"\u2192","&rarr;"},{"\u2193","&darr;"},
+               
{"\u2194","&harr;"},{"\u21b5","&crarr;"},{"\u21d0","&lArr;"},{"\u21d1","&uArr;"},
+               {"\u21d2","&rArr;"},{"\u21d3","&dArr;"},{"\u21d4","&hArr;"},
+               //Mathematical Operators
+               
{"\u2200","&forall;"},{"\u2202","&part;"},{"\u2203","&exist;"},{"\u2205","&empty;"},
+               
{"\u2207","&nabla;"},{"\u2208","&isin;"},{"\u2209","&notin;"},{"\u220b","&ni;"},
+               
{"\u220f","&prod;"},{"\u2211","&sum;"},{"\u2212","&minus;"},{"\u2217","&lowast;"},
+               
{"\u221a","&radic;"},{"\u221d","&prop;"},{"\u221e","&infin;"},{"\u2220","&ang;"},
+               
{"\u2227","&and;"},{"\u2228","&or;"},{"\u2229","&cap;"},{"\u222a","&cup;"},
+               
{"\u222b","&int;"},{"\u2234","&there4;"},{"\u223c","&sim;"},{"\u2245","&cong;"},
+               
{"\u2248","&asymp;"},{"\u2260","&ne;"},{"\u2261","&equiv;"},{"\u2264","&le;"},
+               
{"\u2265","&ge;"},{"\u2282","&sub;"},{"\u2283","&sup;"},{"\u2284","&nsub;"},
+               
{"\u2286","&sube;"},{"\u2287","&supe;"},{"\u2295","&oplus;"},{"\u2297","&otimes;"},
+               {"\u22a5","&perp;"},{"\u22c5","&sdot;"},
+               //Miscellaneous Technical
+               
{"\u2308","&lceil;"},{"\u2309","&rceil;"},{"\u230a","&lfloor;"},{"\u230b","&rfloor;"},
+               {"\u2329","&lang;"},{"\u232a","&rang;"},
+               //Geometric Shapes
+               
{"\u25ca","&loz;"},{"\u2660","&spades;"},{"\u2663","&clubs;"},{"\u2665","&hearts;"},
+               {"\u2666","&diams;"},
+               //Latin Extended-A
+               
{"\u0152","&OElig;"},{"\u0153","&oelig;"},{"\u0160","&Scaron;"},{"\u0161","&scaron;"},
+               {"\u0178","&Yuml;"},
+               //Spacing Modifier Letters
+               {"\u02c6","&circ;"},{"\u02dc","&tilde;"},
+               //General Punctuation
+               
{"\u2002","&ensp;"},{"\u2003","&emsp;"},{"\u2009","&thinsp;"},{"\u200c","&zwnj;"},
+               
{"\u200d","&zwj;"},{"\u200e","&lrm;"},{"\u200f","&rlm;"},{"\u2013","&ndash;"},
+               
{"\u2014","&mdash;"},{"\u2018","&lsquo;"},{"\u2019","&rsquo;"},{"\u201a","&sbquo;"},
+               
{"\u201c","&ldquo;"},{"\u201d","&rdquo;"},{"\u201e","&bdquo;"},{"\u2020","&dagger;"},
+               
{"\u2021","&Dagger;"},{"\u2030","&permil;"},{"\u2039","&lsaquo;"},{"\u203a","&rsaquo;"},
+               {"\u20ac","&euro;"}
+       };
+}


Reply via email to