Author: nextgens
Date: 2007-09-23 09:58:34 +0000 (Sun, 23 Sep 2007)
New Revision: 15282

Added:
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.es.properties
   branches/freenet-jfk/src/freenet/pluginmanager/ForwardPort.java
   branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortCallback.java
   branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortStatus.java
   branches/freenet-jfk/src/freenet/pluginmanager/FredPluginPortForward.java
   branches/freenet-jfk/src/freenet/support/OSThread.java
   branches/freenet-jfk/src/freenet/support/io/NullBucketFactory.java
   
branches/freenet-jfk/src/freenet/support/transport/ip/HostnameSyntaxException.java
   branches/freenet-jfk/src/freenet/support/transport/ip/HostnameUtil.java
   branches/freenet-jfk/test/freenet/crypt/DSATest.java
Modified:
   branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs
   branches/freenet-jfk/build.xml
   branches/freenet-jfk/src/freenet/client/FECCodec.java
   branches/freenet-jfk/src/freenet/client/MetadataParseException.java
   branches/freenet-jfk/src/freenet/client/async/BackgroundBlockEncoder.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/SingleFileFetcher.java
   branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java
   branches/freenet-jfk/src/freenet/client/async/USKFetcher.java
   branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.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/NinjaSpider.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/Spider.java
   branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/ToadletContext.java
   branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java
   branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java
   branches/freenet-jfk/src/freenet/clients/http/bookmark/Bookmark.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/BookmarkManager.java
   branches/freenet-jfk/src/freenet/clients/http/filter/CSSTokenizerFilter.java
   branches/freenet-jfk/src/freenet/clients/http/filter/ContentFilter.java
   branches/freenet-jfk/src/freenet/clients/http/filter/HTMLFilter.java
   branches/freenet-jfk/src/freenet/config/FilePersistentConfig.java
   branches/freenet-jfk/src/freenet/config/StringArrOption.java
   branches/freenet-jfk/src/freenet/crypt/KeyAgreementSchemeContext.java
   branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael.java
   branches/freenet-jfk/src/freenet/io/AddressIdentifier.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/MessageCore.java
   branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java
   branches/freenet-jfk/src/freenet/io/comm/Peer.java
   branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java
   branches/freenet-jfk/src/freenet/keys/BaseClientKey.java
   branches/freenet-jfk/src/freenet/keys/CHKBlock.java
   branches/freenet-jfk/src/freenet/keys/ClientCHK.java
   branches/freenet-jfk/src/freenet/keys/ClientCHKBlock.java
   branches/freenet-jfk/src/freenet/keys/ClientSSK.java
   branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java
   branches/freenet-jfk/src/freenet/keys/InsertableClientSSK.java
   branches/freenet-jfk/src/freenet/keys/Key.java
   branches/freenet-jfk/src/freenet/l10n/L10n.java
   branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties
   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/node/CHKInsertSender.java
   branches/freenet-jfk/src/freenet/node/DNSRequester.java
   branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java
   branches/freenet-jfk/src/freenet/node/FNPPacketMangler.java
   branches/freenet-jfk/src/freenet/node/GlobalProbe.java
   branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java
   branches/freenet-jfk/src/freenet/node/InsertHandler.java
   branches/freenet-jfk/src/freenet/node/KeyTracker.java
   branches/freenet-jfk/src/freenet/node/LocationManager.java
   branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java
   branches/freenet-jfk/src/freenet/node/MemoryChecker.java
   branches/freenet-jfk/src/freenet/node/Node.java
   branches/freenet-jfk/src/freenet/node/NodeClientCore.java
   branches/freenet-jfk/src/freenet/node/NodeCrypto.java
   branches/freenet-jfk/src/freenet/node/NodeDispatcher.java
   branches/freenet-jfk/src/freenet/node/NodeIPDetector.java
   branches/freenet-jfk/src/freenet/node/NodePinger.java
   branches/freenet-jfk/src/freenet/node/NodeStarter.java
   branches/freenet-jfk/src/freenet/node/NodeStats.java
   branches/freenet-jfk/src/freenet/node/OpennetManager.java
   branches/freenet-jfk/src/freenet/node/PacketSender.java
   branches/freenet-jfk/src/freenet/node/PeerManager.java
   branches/freenet-jfk/src/freenet/node/PeerNode.java
   branches/freenet-jfk/src/freenet/node/PeerNodeStatus.java
   branches/freenet-jfk/src/freenet/node/Persister.java
   branches/freenet-jfk/src/freenet/node/RequestHandler.java
   branches/freenet-jfk/src/freenet/node/RequestSender.java
   branches/freenet-jfk/src/freenet/node/RequestStarter.java
   branches/freenet-jfk/src/freenet/node/SSKInsertHandler.java
   branches/freenet-jfk/src/freenet/node/SSKInsertSender.java
   branches/freenet-jfk/src/freenet/node/TestnetHandler.java
   branches/freenet-jfk/src/freenet/node/TestnetStatusUploader.java
   branches/freenet-jfk/src/freenet/node/TextModeClientInterface.java
   branches/freenet-jfk/src/freenet/node/TextModeClientInterfaceServer.java
   branches/freenet-jfk/src/freenet/node/Version.java
   branches/freenet-jfk/src/freenet/node/fcp/AllDataMessage.java
   branches/freenet-jfk/src/freenet/node/fcp/ClientGet.java
   branches/freenet-jfk/src/freenet/node/fcp/ClientPut.java
   branches/freenet-jfk/src/freenet/node/fcp/ClientPutBase.java
   branches/freenet-jfk/src/freenet/node/fcp/ClientPutDir.java
   branches/freenet-jfk/src/freenet/node/fcp/ClientRequest.java
   branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionHandler.java
   branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionInputHandler.java
   branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionOutputHandler.java
   branches/freenet-jfk/src/freenet/node/fcp/FCPServer.java
   branches/freenet-jfk/src/freenet/node/fcp/PutSuccessfulMessage.java
   branches/freenet-jfk/src/freenet/node/simulator/RealNodePingTest.java
   
branches/freenet-jfk/src/freenet/node/simulator/RealNodeRequestInsertTest.java
   branches/freenet-jfk/src/freenet/node/simulator/RealNodeRoutingTest.java
   branches/freenet-jfk/src/freenet/node/updater/NodeUpdateManager.java
   branches/freenet-jfk/src/freenet/node/updater/NodeUpdater.java
   branches/freenet-jfk/src/freenet/node/updater/UpdateOverMandatoryManager.java
   branches/freenet-jfk/src/freenet/node/useralerts/IPUndetectedUserAlert.java
   
branches/freenet-jfk/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
   branches/freenet-jfk/src/freenet/node/useralerts/PeerManagerUserAlert.java
   
branches/freenet-jfk/src/freenet/node/useralerts/UpdatedVersionAvailableUserAlert.java
   branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java
   branches/freenet-jfk/src/freenet/pluginmanager/PluginInfoWrapper.java
   branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java
   branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java
   branches/freenet-jfk/src/freenet/support/CPUInformation/CPUID.java
   branches/freenet-jfk/src/freenet/support/HTMLNode.java
   branches/freenet-jfk/src/freenet/support/LRUQueue.java
   branches/freenet-jfk/src/freenet/support/Logger.java
   branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArray.java
   
branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArrayWithObject.java
   branches/freenet-jfk/src/freenet/support/TimeSortedHashtable.java
   branches/freenet-jfk/src/freenet/support/URLEncoder.java
   branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java
   branches/freenet-jfk/src/freenet/support/io/FileUtil.java
   branches/freenet-jfk/src/freenet/support/io/LineReadingInputStream.java
   
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
   
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
   branches/freenet-jfk/src/freenet/support/transport/ip/IPAddressDetector.java
   branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java
   branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java
   branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java
Log:
freenet-jfk: merge changes back from trunk to the branch (up to r15281)

Modified: branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs
===================================================================
--- branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/.settings/org.eclipse.core.resources.prefs     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -1,10 +1,3 @@
-#Fri Jul 27 18:21:13 BST 2007
+#Tue Sep 04 15:20:10 CEST 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
-encoding//src/freenet/l10n/freenet.l10n.no.properties=UTF-8
-encoding//src/freenet/l10n/freenet.l10n.pl.properties=UTF-8
-encoding//src/freenet/l10n/freenet.l10n.se.properties=UTF-8
-encoding//src/freenet/l10n/freenet.l10n.unlisted.properties=UTF-8
+encoding/<project>=UTF-8

Modified: branches/freenet-jfk/build.xml
===================================================================
--- branches/freenet-jfk/build.xml      2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/build.xml      2007-09-23 09:58:34 UTC (rev 15282)
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!-- ant build file for Freenet -->

 <project name="Freenet" default="dist" basedir=".">
@@ -126,11 +126,12 @@

        <!-- ================================================== -->

-       <target name="unit-build" depends="compile">
+       <target name="unit-build" depends="compile" if="junit.present">
                <javac srcdir="${test}" destdir="${build-test}" debug="on" 
optimize="on" source="1.4">
                        <classpath>
                                <pathelement path="${build}"/>
                                <pathelement 
location="${freenet-ext.location}"/>
+                               <pathelement location="${junit.location}"/>
                        </classpath>
                        <include name="**/*.java"/>
                        <exclude name="*.java"/>
@@ -160,6 +161,7 @@

        <target name="clean" description="Delete class files and docs dir.">
                <delete dir="${build}"/>
+               <delete dir="${build-test}"/>
        </target>
        <target name="distclean" description="Delete class files, lib dir and 
docs dir.">
                <delete file="${CSSTokenizerFilter.java}"/>

Modified: branches/freenet-jfk/src/freenet/client/FECCodec.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/FECCodec.java       2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/FECCodec.java       2007-09-23 
09:58:34 UTC (rev 15282)
@@ -373,6 +373,7 @@
        private static class FECRunner implements Runnable {

                public void run(){
+                   freenet.support.Logger.OSThread.logPID(this);
                        try {
                        while(true){
                                FECJob job = null;

Modified: branches/freenet-jfk/src/freenet/client/MetadataParseException.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/MetadataParseException.java 
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/MetadataParseException.java 
2007-09-23 09:58:34 UTC (rev 15282)
@@ -3,8 +3,6 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.client;

-import java.io.IOException;
-
 /** Thrown when Metadata parse fails. */
 public class MetadataParseException extends Exception {


Modified: 
branches/freenet-jfk/src/freenet/client/async/BackgroundBlockEncoder.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/BackgroundBlockEncoder.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/BackgroundBlockEncoder.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -45,6 +45,7 @@
        }

        public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
                while(true) {
                        SingleBlockInserter sbi = null;
                        synchronized(this) {

Modified: branches/freenet-jfk/src/freenet/client/async/BinaryBlobInserter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/BinaryBlobInserter.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/BinaryBlobInserter.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -47,6 +47,8 @@

                BinaryBlob.readBinaryBlob(dis, blocks, tolerant);

+               dis.close();
+               
                Vector myInserters = new Vector();
                Iterator i = blocks.keys().iterator();


Modified: branches/freenet-jfk/src/freenet/client/async/ClientGetter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/ClientGetter.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/ClientGetter.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -115,6 +115,8 @@
        }

        public void onSuccess(FetchResult result, ClientGetState state) {
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, "Succeeded from "+state);
                if(!closeBinaryBlobStream()) return;
                synchronized(this) {
                        finished = true;
@@ -147,11 +149,13 @@
                        public void run() {
                                client.onSuccess(res, ClientGetter.this);
                        }
-               }, "ClientGetter onSuccess callback");
+               }, "ClientGetter onSuccess callback for "+this);

        }

        public void onFailure(FetchException e, ClientGetState state) {
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, "Failed from "+state+" : "+e, e);
                closeBinaryBlobStream();
                while(true) {
                        if(e.mode == FetchException.ARCHIVE_RESTART) {

Modified: 
branches/freenet-jfk/src/freenet/client/async/ClientRequestScheduler.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/ClientRequestScheduler.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/ClientRequestScheduler.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -193,7 +193,7 @@
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR) Logger.minor(this, "Registering "+req, new 
Exception("debug"));
                if(isInsertScheduler != (req instanceof SendableInsert))
-                       throw new IllegalArgumentException("Expected a 
SendableGet: "+req);
+                       throw new IllegalArgumentException("Expected a 
SendableInsert: "+req);
                if(req instanceof SendableGet) {
                        SendableGet getter = (SendableGet)req;
                        if(!getter.ignoreStore()) {
@@ -215,6 +215,9 @@
                                                                block = 
node.fetchKey(key, getter.dontCache());
                                                        if(block == null) {
                                                                
addPendingKey(key, getter);
+                                                       } else {
+                                                               if(logMINOR)
+                                                                       
Logger.minor(this, "Got "+block);
                                                        }
                                                }
                                        } catch (KeyVerifyException e) {
@@ -223,7 +226,7 @@
                                                if(logMINOR)
                                                        Logger.minor(this, 
"Decode failed: "+e, e);
                                                getter.onFailure(new 
LowLevelGetException(LowLevelGetException.DECODE_FAILED), tok);
-                                               return;
+                                               continue; // other keys might 
be valid
                                        }
                                        if(block != null) {
                                                if(logMINOR) Logger.minor(this, 
"Can fulfill "+req+" ("+tok+") immediately from store");
@@ -232,7 +235,11 @@
                                                anyValid = true;
                                        }
                                }
-                               if(!anyValid) return;
+                               if(!anyValid) {
+                                       if(logMINOR)
+                                               Logger.minor(this, "No valid 
keys, returning without registering");
+                                       return;
+                               }
                        }
                }
                innerRegister(req);
@@ -242,6 +249,8 @@
        }

        private void addPendingKey(ClientKey key, SendableGet getter) {
+               if(logMINOR)
+                       Logger.minor(this, "Adding pending key "+key+" for 
"+getter);
                Key nodeKey = key.getNodeKey();
                synchronized(pendingKeys) {
                        Object o = pendingKeys.get(nodeKey);
@@ -298,12 +307,14 @@
                if(clientGrabber == null) {
                        clientGrabber = new 
SectoredRandomGrabArrayWithInt(random, rc);
                        prio.add(clientGrabber);
-                       if(logMINOR) Logger.minor(this, "Registering retry 
count "+rc+" with prioclass "+priorityClass);
+                       if(logMINOR) Logger.minor(this, "Registering retry 
count "+rc+" with prioclass "+priorityClass+" on "+clientGrabber+" for "+prio);
                }
                // Request
                SectoredRandomGrabArrayWithObject requestGrabber = 
(SectoredRandomGrabArrayWithObject) clientGrabber.getGrabber(client);
                if(requestGrabber == null) {
                        requestGrabber = new 
SectoredRandomGrabArrayWithObject(client, random);
+                       if(logMINOR)
+                               Logger.minor(this, "Creating new grabber: 
"+requestGrabber+" for "+client+" from "+clientGrabber+" : "+prio+" : 
prio="+priorityClass+", rc="+rc);
                        clientGrabber.addGrabber(client, requestGrabber);
                }
                requestGrabber.add(cr, req);
@@ -372,7 +383,7 @@
                                        Logger.minor(this, "Got retry count 
tracker "+rga);
                                SendableRequest req = (SendableRequest) 
rga.removeRandom();
                                if(rga.isEmpty()) {
-                                       if(logMINOR) Logger.minor(this, 
"Removing retrycount "+rga.getNumber());
+                                       if(logMINOR) Logger.minor(this, 
"Removing retrycount "+rga.getNumber()+" : "+rga);
                                        s.remove(rga.getNumber());
                                        if(s.isEmpty()) {
                                                if(logMINOR) Logger.minor(this, 
"Should remove priority ");

Modified: 
branches/freenet-jfk/src/freenet/client/async/SimpleManifestPutter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SimpleManifestPutter.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/SimpleManifestPutter.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -239,32 +239,34 @@

        public void start() throws InsertException {
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
-               if(logMINOR) Logger.minor(this, "Starting "+this);
+               if (logMINOR)
+                       Logger.minor(this, "Starting " + this);
                PutHandler[] running;

-               boolean cancel = false;
-               
-               synchronized(this) {
-                       cancel = cancelled;
-                       if(!cancelled) {
-                               running = (PutHandler[]) 
runningPutHandlers.toArray(new PutHandler[runningPutHandlers.size()]);
-                               try {
-                                       for(int i=0;i<running.length;i++) {
-                                               running[i].start();
-                                       }
-                                       if(logMINOR) Logger.minor(this, 
"Started "+running.length+" PutHandler's for "+this);
-                                       if(cancelled) cancel();
-                                       if(running.length == 0) {
-                                               insertedAllFiles = true;
-                                               gotAllMetadata();
-                                       }
-                               } catch (InsertException e) {
-                                       cancelAndFinish();
-                                       throw e;
+               synchronized (this) {
+                       running = (PutHandler[]) runningPutHandlers.toArray(new 
PutHandler[runningPutHandlers.size()]);
+               }
+               try {
+                       for (int i = 0; i < running.length; i++) {
+                               running[i].start();
+                               if (logMINOR)
+                                       Logger.minor(this, "Started " + i + " 
of " + running.length);
+                               if (isFinished()) {
+                                       if (logMINOR)
+                                               Logger.minor(this, "Already 
finished, killing start() on " + this);
+                                       return;
                                }
                        }
+                       if (logMINOR)
+                               Logger.minor(this, "Started " + running.length 
+ " PutHandler's for " + this);
+                       if (running.length == 0) {
+                               insertedAllFiles = true;
+                               gotAllMetadata();
+                       }
+               } catch (InsertException e) {
+                       cancelAndFinish();
+                       throw e;
                }
-               if(cancel) cancel();
        }

        private void makePutHandlers(HashMap manifestElements, HashMap 
putHandlersByName) throws InsertException {
@@ -530,7 +532,10 @@

                cb.onFailure(e, this);
        }
-
+       
+       /**
+        * Cancel all running inserters and set finished to true.
+        */
        private void cancelAndFinish() {
                PutHandler[] running;
                synchronized(this) {
@@ -545,6 +550,7 @@
        }

        public void cancel() {
+               super.cancel();
                fail(new InsertException(InsertException.CANCELLED));
        }


Modified: branches/freenet-jfk/src/freenet/client/async/SingleFileFetcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SingleFileFetcher.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/SingleFileFetcher.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -129,6 +129,8 @@
                }
                Bucket data = extract(block);
                if(data == null) {
+                       if(logMINOR)
+                               Logger.minor(this, "No data");
                        // Already failed: if extract returns null it will call 
onFailure first.
                        return;
                }
@@ -435,7 +437,7 @@
                                        public void run() {
                                                f.schedule();
                                        }
-                               }, "Schedule");
+                               }, "Schedule "+this);
                                // All done! No longer our problem!
                                return;
                        } else if(metadata.isSplitfile()) {

Modified: branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/SingleFileInserter.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -114,6 +114,7 @@

        private class OffThreadCompressor implements Runnable {
                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        try {
                                tryCompress();
                        } catch (InsertException e) {

Modified: branches/freenet-jfk/src/freenet/client/async/USKFetcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/client/async/USKFetcher.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/client/async/USKFetcher.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -24,7 +24,7 @@
  * we are talking about. If this is fast enough then people will use the "-" 
form.
  * 
  * FProxy should cause USKs with negative edition numbers to be redirected to 
USKs
- * with negative edition numbers.
+ * with positive edition numbers.
  * 
  * If the number specified is up to date, we just do the fetch. If a more 
recent
  * USK can be found, then we fail with an exception with the new version. The
@@ -165,9 +165,10 @@
                        if(backgroundPoll) {
                                if(minFailures == origMinFailures && 
minFailures != maxMinFailures) {
                                        // Either just started, or just 
advanced, either way boost the priority.
-                                       return 
RequestStarter.IMMEDIATE_SPLITFILE_PRIORITY_CLASS;
+                                       return 
RequestStarter.UPDATE_PRIORITY_CLASS;
+                               } else {
+                                       return 
RequestStarter.PREFETCH_PRIORITY_CLASS;
                                }
-                               return RequestStarter.UPDATE_PRIORITY_CLASS;
                        } else
                                return parent.getPriorityClass();
                }
@@ -183,8 +184,8 @@
        long minFailures;
        final long origMinFailures;

-       static final long origSleepTime = 1000;
-       static final long maxSleepTime = 60 * 60 * 1000;
+       static final long origSleepTime = 5 * 60 * 1000;
+       static final long maxSleepTime = 24 * 60 * 60 * 1000;
        long sleepTime = origSleepTime;

        /** Maximum number of editions to probe ahead. */
@@ -256,7 +257,7 @@
                                        minFailures = origMinFailures;
                                        sleepTime = origSleepTime;
                                } else {
-                                       // Not exponential; it is more likely 
that it is close to the known edition than not.
+                                       // Increase exponentially but 
relatively slowly
                                        long newMinFailures = 
Math.max(((int)(minFailures * 1.25)), minFailures+1);
                                        if(newMinFailures > maxMinFailures)
                                                newMinFailures = maxMinFailures;
@@ -303,7 +304,7 @@
        void onSuccess(USKAttempt att, boolean dontUpdate, ClientSSKBlock 
block) {
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                LinkedList l = null;
-               long lastEd = uskManager.lookup(origUSK);
+               final long lastEd = uskManager.lookup(origUSK);
                synchronized(this) {
                        runningAttempts.remove(att);
                        long curLatest = att.number;
@@ -340,21 +341,27 @@
                        cancelBefore(curLatest);
                }
                if(l == null) return;
+               final LinkedList toSched = l;
                // If we schedule them here, we don't get icky recursion 
problems.
-               else if(!cancelled) {
-                       for(Iterator i=l.iterator();i.hasNext();) {
-                               // We may be called recursively through 
onSuccess().
-                               // So don't start obsolete requests.
-                               USKAttempt a = (USKAttempt) i.next();
-                               lastEd = uskManager.lookup(origUSK);
-                               if((lastEd <= a.number) && !a.cancelled)
-                                       a.schedule();
-                               else {
-                                       synchronized(this) {
-                                               runningAttempts.remove(a);
+               if(!cancelled) {
+                       ctx.executor.execute(new Runnable() {
+                               public void run() {
+                                       long last = lastEd;
+                                       for(Iterator 
i=toSched.iterator();i.hasNext();) {
+                                               // We may be called recursively 
through onSuccess().
+                                               // So don't start obsolete 
requests.
+                                               USKAttempt a = (USKAttempt) 
i.next();
+                                               last = 
uskManager.lookup(origUSK);
+                                               if((last <= a.number) && 
!a.cancelled)
+                                                       a.schedule();
+                                               else {
+                                                       synchronized(this) {
+                                                               
runningAttempts.remove(a);
+                                                       }
+                                               }
                                        }
                                }
-                       }
+                       }, "USK scheduler"); // Run on separate thread because 
otherwise can get loooong recursions
                }
        }


Modified: 
branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/BookmarkEditorToadlet.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -56,7 +56,7 @@

                for(int i = 0; i < items.size(); i++) {

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

                        HTMLNode actions = new HTMLNode("span", "class", 
"actions");
@@ -80,7 +80,7 @@
                BookmarkCategories cats = cat.getSubCategories();
                for(int i = 0; i < cats.size(); i++) {

-                       String catPath = URLEncoder.encode(path + 
cats.get(i).getName() + "/");
+                       String catPath = URLEncoder.encode(path + 
cats.get(i).getName() + "/", false);

                        HTMLNode subCat = list.addChild("li", "class", "cat", 
cats.get(i).getName());


Modified: branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/ConnectionsToadlet.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -173,6 +173,7 @@
                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 numberOfDisconnecting = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISCONNECTING);

                int numberOfSimpleConnected = numberOfConnected + 
numberOfRoutingBackedOff;
                int numberOfNotConnected = numberOfTooNew + numberOfTooOld + 
numberOfDisconnected + numberOfNeverConnected + numberOfDisabled + 
numberOfBursting + numberOfListening + numberOfListenOnly + 
numberOfClockProblem + numberOfConnError;
@@ -310,9 +311,14 @@
                        }
                        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", 
new String[] { "class", "title", "style" }, new String[] { "peer_conn_error", 
l10n("connError"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("connErrorShort"));
                                peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfConnError);
                        }
+                       if (numberOfDisconnecting > 0) {
+                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { 
"peer_disconnecting", l10n("disconnecting"), "border-bottom: 1px dotted; 
cursor: help;" }, l10n("disconnectingShort"));
+                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfDisconnecting);
+                       }

                        // Peer routing backoff reason box
                        if(advancedModeEnabled) {
@@ -676,10 +682,11 @@
                peerAdditionForm.addChild("#", (l10n("fileReference") + ' '));
                peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "reffile", "file", "reffile" });
                peerAdditionForm.addChild("br");
-               if(!isOpennet())
+               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[] { "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") });
        }


Modified: branches/freenet-jfk/src/freenet/clients/http/FProxyToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/FProxyToadlet.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/FProxyToadlet.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -359,7 +359,7 @@
                        return;
                }
                String requestedMimeType = httprequest.getParam("type", null);
-               String override = (requestedMimeType == null) ? "" : 
"?type="+URLEncoder.encode(requestedMimeType);
+               String override = (requestedMimeType == null) ? "" : 
"?type="+URLEncoder.encode(requestedMimeType,true);
                try {
                        if(Logger.shouldLog(Logger.MINOR, this))
                                Logger.minor(this, "FProxy fetching "+key+" 
("+maxSize+ ')');
@@ -512,7 +512,7 @@
                sb.append(uri.toACIIString());
                char c = '?';
                if(requestedMimeType != null) {
-                       
sb.append(c).append("type=").append(URLEncoder.encode(requestedMimeType)); c = 
'&';
+                       
sb.append(c).append("type=").append(URLEncoder.encode(requestedMimeType,false));
 c = '&';
                }
                if(maxSize > 0) {
                        sb.append(c).append("max-size=").append(maxSize); c = 
'&';

Modified: 
branches/freenet-jfk/src/freenet/clients/http/FirstTimeWizardToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/FirstTimeWizardToadlet.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/FirstTimeWizardToadlet.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -251,11 +251,13 @@
                        super.writeTemporaryRedirect(ctx, "step1", 
TOADLET_URL+"?step=2");
                        return;
                } else if(request.isPartSet("nnameF")) {
-                       String selectedNName = request.getPartAsString("nname", 
255);
+                       String selectedNName = request.getPartAsString("nname", 
128);

                        try {
                                config.get("node").set("name", selectedNName);
-                               Logger.normal(this, "The node name has been set 
to "+ selectedNName);
+                               // We call the callback once again to ensure 
MeaningfulNodeNameUserAlert
+                               // has been unregistered ... see #1595
+                               Logger.normal(this, "The node name has been set 
to "+ config.get("node.name"));
                        } catch (InvalidConfigValueException e) {
                                Logger.error(this, "Should not happen, please 
report!" + e);
                        }

Modified: branches/freenet-jfk/src/freenet/clients/http/HTTPRequestImpl.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/HTTPRequestImpl.java  
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/HTTPRequestImpl.java  
2007-09-23 09:58:34 UTC (rev 15282)
@@ -533,6 +533,9 @@
         * @see freenet.clients.http.HTTPRequest#isPartSet(java.lang.String)
         */
        public boolean isPartSet(String name) {
+               if(parts == null)
+                       return false;
+
                return this.parts.containsKey(name);
        }


Modified: 
branches/freenet-jfk/src/freenet/clients/http/LocalFileInsertToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/LocalFileInsertToadlet.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/LocalFileInsertToadlet.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -44,7 +44,7 @@
                        if (currentPath == null) {
                                currentPath = new 
File(System.getProperty("user.home"));
                        }
-                       writePermanentRedirect(toadletContext, "Found", 
"?path=" + URLEncoder.encode(currentPath.getAbsolutePath()));
+                       writePermanentRedirect(toadletContext, "Found", 
"?path=" + URLEncoder.encode(currentPath.getAbsolutePath(),true));
                        return;
                }

@@ -94,7 +94,7 @@
                                HTMLNode rootRow = listingTable.addChild("tr");
                                rootRow.addChild("td");
                                HTMLNode rootLinkCellNode = 
rootRow.addChild("td");
-                               rootLinkCellNode.addChild("a", "href", "?path=" 
+ URLEncoder.encode(currentRoot.getCanonicalPath()), 
currentRoot.getCanonicalPath());
+                               rootLinkCellNode.addChild("a", "href", "?path=" 
+ URLEncoder.encode(currentRoot.getCanonicalPath(),false), 
currentRoot.getCanonicalPath());
                                rootRow.addChild("td");
                        }
                        /* add back link */
@@ -102,7 +102,7 @@
                                HTMLNode backlinkRow = 
listingTable.addChild("tr");
                                backlinkRow.addChild("td");
                                HTMLNode backlinkCellNode = 
backlinkRow.addChild("td");
-                               backlinkCellNode.addChild("a", "href", "?path=" 
+ URLEncoder.encode(currentPath.getParent()), "..");
+                               backlinkCellNode.addChild("a", "href", "?path=" 
+ URLEncoder.encode(currentPath.getParent(),false), "..");
                                backlinkRow.addChild("td");
                        }
                        for (int fileIndex = 0, fileCount = files.length; 
fileIndex < fileCount; fileIndex++) {
@@ -112,7 +112,7 @@
                                        fileRow.addChild("td");
                                        if (currentFile.canRead()) {
                                                HTMLNode directoryCellNode = 
fileRow.addChild("td");
-                                               directoryCellNode.addChild("a", 
"href", "?path=" + URLEncoder.encode(currentFile.getAbsolutePath()), 
currentFile.getName());
+                                               directoryCellNode.addChild("a", 
"href", "?path=" + URLEncoder.encode(currentFile.getAbsolutePath(),false), 
currentFile.getName());
                                        } else {
                                                fileRow.addChild("td", "class", 
"unreadable-file", currentFile.getName());
                                        }

Modified: branches/freenet-jfk/src/freenet/clients/http/NinjaSpider.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/NinjaSpider.java      
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/NinjaSpider.java      
2007-09-23 09:58:34 UTC (rev 15282)
@@ -53,6 +53,7 @@
 import freenet.support.MultiValueTable;
 import freenet.support.api.Bucket;
 import freenet.support.api.HTTPRequest;
+import freenet.support.io.NullBucketFactory;

 /**
  * FIXME move to a proper plugin.
@@ -192,7 +193,7 @@
                mimeOfURIs.put(uri.toString(), mimeType);

                try {
-                       ContentFilter.filter(data, ctx.bucketFactory, mimeType, 
new URI("http://127.0.0.1:8888/"; + uri.toString()), this);
+                       ContentFilter.filter(data, new NullBucketFactory(), 
mimeType, new URI("http://127.0.0.1:8888/"; + uri.toString()), this);
                } catch (UnsafeContentTypeException e) {
                        return; // Ignore
                } catch (IOException e) {

Modified: branches/freenet-jfk/src/freenet/clients/http/PproxyToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/PproxyToadlet.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/PproxyToadlet.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -1,7 +1,12 @@
 package freenet.clients.http;

+import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
 import java.net.URI;
+import java.net.URL;
 import java.util.Date;
 import java.util.Iterator;

@@ -20,9 +25,12 @@
 import freenet.support.Logger;
 import freenet.support.MultiValueTable;
 import freenet.support.api.HTTPRequest;
+import freenet.support.io.FileUtil;

 public class PproxyToadlet extends Toadlet {
        private static final int MAX_PLUGIN_NAME_LENGTH = 1024;
+       /** Maximum time to wait for a threaded plugin to exit */
+       private static final int MAX_THREADED_UNLOAD_WAIT_TIME = 60*1000;
        private final Node node;
        private final NodeClientCore core;

@@ -37,7 +45,7 @@
        }

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

                MultiValueTable headers = new MultiValueTable();

@@ -53,7 +61,7 @@
                        super.sendErrorPage(ctx, 403, "Unauthorized", 
l10n("unauthorized"));
                        return;
                }
-               
+
                String path=request.getPath();

                // remove leading / and plugins/ from path
@@ -63,7 +71,7 @@
                if(Logger.shouldLog(Logger.MINOR, this)) Logger.minor(this, 
"Pproxy received POST on "+path);

                PluginManager pm = node.pluginManager;
-               
+
                if(path.length()>0)
                {
                        try
@@ -112,10 +120,85 @@
                {

                        if (request.isPartSet("load")) {
-                               if(Logger.shouldLog(Logger.MINOR, this)) 
Logger.minor(this, "Loading "+request.getPartAsString("load", 
MAX_PLUGIN_NAME_LENGTH));
-                               pm.startPlugin(request.getPartAsString("load", 
MAX_PLUGIN_NAME_LENGTH), true);
-                               //writeReply(ctx, 200, "text/html", "OK", 
mkForwardPage("Loading plugin", "Loading plugin...", ".", 5));
+                               String filename = 
request.getPartAsString("load", MAX_PLUGIN_NAME_LENGTH);
+                               final boolean logMINOR = 
Logger.shouldLog(Logger.MINOR, this);
+                               boolean downloaded = false;

+                               if(logMINOR) Logger.minor(this, "Loading 
"+filename);
+                               if (filename.endsWith("#")) {
+                                       for (int tries = 0; (tries <= 5) && 
(downloaded == false); tries++) {
+                                               if (filename.indexOf('@') > -1) 
{
+                                                       Logger
+                                                       .error(this,
+                                                       "We don't allow 
downloads from anywhere else but our server");
+                                                       return;
+                                               }
+                                               String pluginname = 
filename.substring(0,
+                                                               
filename.length() - 1);
+                                               filename = null;
+
+                                               URL url;
+                                               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();
+
+                                                       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;
+                                                       }
+
+                                                       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);
+                                                       downloaded = true;
+                                               } catch (MalformedURLException 
mue) {
+                                                       Logger.error(this,
+                                                                       
"MalformedURLException has occured : " + mue,
+                                                                       mue);
+                                                       return;
+                                               } catch (FileNotFoundException 
e) {
+                                                       Logger.error(this,
+                                                                       
"FileNotFoundException has occured : " + e, e);
+                                                       return;
+                                               } catch (IOException ioe) {
+                                                       
System.out.println("Caught :" + ioe.getMessage());
+                                                       ioe.printStackTrace();
+                                                       return;
+                                               } finally {
+                                                       try {
+                                                               if (is != null)
+                                                                       
is.close();
+                                                       } catch (IOException 
ioe) {
+                                                       }
+                                               }
+                                       }
+                                       if (filename == null)
+                                               return;
+                                       else if(!downloaded) {
+                                               Logger.error(this, "Can't load 
the given plugin; giving up");
+                                               return;
+                                       }
+                               }
+
+                               pm.startPlugin(filename, true);
                                headers.put("Location", ".");
                                ctx.sendReplyHeaders(302, "Found", headers, 
null, 0);
                                return;
@@ -124,7 +207,7 @@
                                ctx.sendReplyHeaders(302, "Found", headers, 
null, 0);
                                return;
                        }if (request.getPartAsString("unloadconfirm", 
MAX_PLUGIN_NAME_LENGTH).length() > 0) {
-                               
pm.killPlugin(request.getPartAsString("unloadconfirm", MAX_PLUGIN_NAME_LENGTH));
+                               
pm.killPlugin(request.getPartAsString("unloadconfirm", MAX_PLUGIN_NAME_LENGTH), 
MAX_THREADED_UNLOAD_WAIT_TIME);
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("plugins"), ctx);
                                HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
                                HTMLNode infobox = contentNode.addChild("div", 
"class", "infobox infobox-success");
@@ -164,7 +247,7 @@
                                        sendErrorPage(ctx, 404, 
l10n("pluginNotFoundReloadTitle"), 
                                                        
L10n.getString("PluginToadlet.pluginNotFoundReload"));
                                } else {
-                                       
pm.killPlugin(request.getPartAsString("reload", MAX_PLUGIN_NAME_LENGTH));
+                                       
pm.killPlugin(request.getPartAsString("reload", MAX_PLUGIN_NAME_LENGTH), 
MAX_THREADED_UNLOAD_WAIT_TIME);
                                        pm.startPlugin(fn, true);

                                        headers.put("Location", ".");
@@ -190,7 +273,7 @@
        }

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

                //String basepath = "/plugins/";
                String path = request.getPath();
@@ -200,7 +283,7 @@
                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 {
@@ -239,6 +322,7 @@
                } catch(PluginHTTPException e) {
                        sendErrorPage(ctx, PluginHTTPException.code, e.message, 
e.location);
                } catch (Throwable t) {
+                       ctx.forceDisconnect();
                        writeInternalError(t, ctx);
                }
        }
@@ -275,17 +359,21 @@
                                        pluginRow.addChild("td", 
pi.getThreadName());
                                        pluginRow.addChild("td", new 
Date(pi.getStarted()).toString());
                                        HTMLNode actionCell = 
pluginRow.addChild("td");
-                                       if (pi.isPproxyPlugin()) {
-                                               HTMLNode visitForm = 
actionCell.addChild("form", new String[] { "method", "action", "target" }, new 
String[] { "get", pi.getPluginClassName(), "_new" });
-                                               visitForm.addChild("input", new 
String[] { "type", "name", "value" }, new String[] { "hidden", "formPassword", 
core.formPassword });
-                                               visitForm.addChild("input", new 
String[] { "type", "value" }, new String[] { "submit", 
L10n.getString("PluginToadlet.visit") });
+                                       if(pi.isStopping()) {
+                                               actionCell.addChild("#", 
l10n("pluginStopping"));
+                                       } else {
+                                               if (pi.isPproxyPlugin()) {
+                                                       HTMLNode visitForm = 
actionCell.addChild("form", new String[] { "method", "action", "target" }, new 
String[] { "get", pi.getPluginClassName(), "_new" });
+                                                       
visitForm.addChild("input", new String[] { "type", "name", "value" }, new 
String[] { "hidden", "formPassword", core.formPassword });
+                                                       
visitForm.addChild("input", new String[] { "type", "value" }, new String[] { 
"submit", L10n.getString("PluginToadlet.visit") });
+                                               }
+                                               HTMLNode unloadForm = 
ctx.addFormChild(actionCell, ".", "unloadPluginForm");
+                                               unloadForm.addChild("input", 
new String[] { "type", "name", "value" }, new String[] { "hidden", "unload", 
pi.getThreadName() });
+                                               unloadForm.addChild("input", 
new String[] { "type", "value" }, new String[] { "submit", l10n("unload") });
+                                               HTMLNode reloadForm = 
ctx.addFormChild(actionCell, ".", "reloadPluginForm");
+                                               reloadForm.addChild("input", 
new String[] { "type", "name", "value" }, new String[] { "hidden", "reload", 
pi.getThreadName() });
+                                               reloadForm.addChild("input", 
new String[] { "type", "value" }, new String[] { "submit", l10n("reload") });
                                        }
-                                       HTMLNode unloadForm = 
ctx.addFormChild(actionCell, ".", "unloadPluginForm");
-                                       unloadForm.addChild("input", new 
String[] { "type", "name", "value" }, new String[] { "hidden", "unload", 
pi.getThreadName() });
-                                       unloadForm.addChild("input", new 
String[] { "type", "value" }, new String[] { "submit", l10n("unload") });
-                                       HTMLNode reloadForm = 
ctx.addFormChild(actionCell, ".", "reloadPluginForm");
-                                       reloadForm.addChild("input", new 
String[] { "type", "name", "value" }, new String[] { "hidden", "reload", 
pi.getThreadName() });
-                                       reloadForm.addChild("input", new 
String[] { "type", "value" }, new String[] { "submit", l10n("reload") });
                                }
                        }


Modified: branches/freenet-jfk/src/freenet/clients/http/QueueToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/QueueToadlet.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/QueueToadlet.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -117,14 +117,7 @@
                                for (int requestIndex = 0, requestCount = 
clientRequests.length; requestIndex < requestCount; requestIndex++) {
                                        ClientRequest clientRequest = 
clientRequests[requestIndex];
                                        if 
(clientRequest.getIdentifier().equals(identifier)) {
-                                               if(!clientRequest.restart()) {
-                                                       sendErrorPage(ctx, 200, 
-                                                                       
L10n.getString("QueueToadlet.failedToRestartRequest"),
-                                                                       
L10n.getString("QueueToadlet.failedToRestart", 
-                                                                               
        new String[]{ "id" },
-                                                                               
        new String[] { identifier}
-                                                       ));
-                                               }
+                                               clientRequest.restartAsync();
                                        }
                                }
                                fcp.forceStorePersistentRequests();

Modified: branches/freenet-jfk/src/freenet/clients/http/SimpleToadletServer.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/SimpleToadletServer.java      
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/SimpleToadletServer.java      
2007-09-23 09:58:34 UTC (rev 15282)
@@ -425,6 +425,7 @@
                }

                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                        if(logMINOR) Logger.minor(this, "Handling connection");
                        try {

Modified: branches/freenet-jfk/src/freenet/clients/http/Spider.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/Spider.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/Spider.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -43,6 +43,7 @@
 import freenet.support.MultiValueTable;
 import freenet.support.api.Bucket;
 import freenet.support.api.HTTPRequest;
+import freenet.support.io.NullBucketFactory;

 /**
  * Spider. Produces an index.
@@ -136,7 +137,7 @@
                Bucket data = result.asBucket();
                String mimeType = cm.getMIMEType();
                try {
-                       ContentFilter.filter(data, ctx.bucketFactory, mimeType, 
uri.toURI("http://127.0.0.1:8888/";), this);
+                       ContentFilter.filter(data, new NullBucketFactory(), 
mimeType, uri.toURI("http://127.0.0.1:8888/";), this);
                } catch (UnsafeContentTypeException e) {
                        return; // Ignore
                } catch (IOException e) {

Modified: branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/StatisticsToadlet.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -457,6 +457,8 @@
                int swapsRejectedLoop = node.getSwapsRejectedLoop();
                int swapsRejectedRecognizedID = 
node.getSwapsRejectedRecognizedID();
                double locChangeSession = node.getLocationChangeSession();
+               int averageSwapTime = node.getAverageOutgoingSwapTime();
+               int sendSwapInterval = node.getSendSwapInterval();

                HTMLNode locationSwapInfoboxContent = 
locationSwapInfobox.addChild("div", "class", "infobox-content");
                HTMLNode locationSwapList = 
locationSwapInfoboxContent.addChild("ul");
@@ -499,8 +501,9 @@
                }
                if (swapsRejectedRecognizedID > 0) {
                        locationSwapList.addChild("li", 
"swapsRejectedRecognizedID:\u00a0" + swapsRejectedRecognizedID);
-               }                       
-               
+               }
+               locationSwapList.addChild("li", "averageSwapTime:\u00a0" + 
TimeUtil.formatTime(averageSwapTime, 2, true));
+               locationSwapList.addChild("li", "sendSwapInterval:\u00a0" + 
TimeUtil.formatTime(sendSwapInterval, 2, true));
        }

        private void drawPeerStatsBox(HTMLNode peerStatsInfobox, boolean 
advancedModeEnabled, int numberOfConnected, 
@@ -878,7 +881,7 @@
                        } else {
                                histogramDisconnected[histogramIndex]++;
                        }
-                       peerCircleInfoboxContent.addChild("span", new String[] 
{ "style", "class" }, new String[] { 
generatePeerCircleStyleString(peerLocation, false, (1.0 - 
peerNodeStatus.getPReject())), 
((peerNodeStatus.isConnected())?"connected":"disconnected") }, "x");
+                       peerCircleInfoboxContent.addChild("span", new String[] 
{ "style", "class" }, new String[] { 
generatePeerCircleStyleString(peerLocation, false, (1.0 - 
peerNodeStatus.getPReject())), 
((peerNodeStatus.isConnected())?"connected":"disconnected") }, 
((peerNodeStatus.isOpennet())?"o":"x"));
                }
                peerCircleInfoboxContent.addChild("span", new String[] { 
"style", "class" }, new String[] { generatePeerCircleStyleString(myLocation, 
true, 1.0), "me" }, "x");
                //

Modified: branches/freenet-jfk/src/freenet/clients/http/ToadletContext.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/ToadletContext.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/ToadletContext.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -29,6 +29,13 @@
        void writeData(byte[] data, int offset, int length) throws 
ToadletContextClosedException, IOException;

        /**
+        * Force a disconnection after handling this request. Used only when a 
throwable was thrown and we don't know
+        * what the state of the connection is. FIXME we could handle this 
better by remembering whether headers have
+        * been sent, how long the attached data should be, how much data has 
been sent etc.
+        */
+       void forceDisconnect();
+       
+       /**
         * Convenience method that simply calls {@link #writeData(byte[], int, 
int)}.
         * 
         * @param data

Modified: branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/ToadletContextImpl.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -270,10 +270,10 @@
                                        headers.put(before, after);
                                }

-                               boolean shouldDisconnect = 
shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers);
+                               boolean disconnect = 
shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers);

                                ToadletContextImpl ctx = new 
ToadletContextImpl(sock, headers, container.getCSSName(), bf, pageMaker, 
container);
-                               ctx.shouldDisconnect = shouldDisconnect;
+                               ctx.shouldDisconnect = disconnect;

                                /*
                                 * if we're handling a POST, copy the data into 
a bucket now,
@@ -313,7 +313,7 @@
                                        Toadlet t = container.findToadlet(uri);

                                        if(t == null) {
-                                               
ctx.sendNoToadletError(shouldDisconnect);
+                                               
ctx.sendNoToadletError(ctx.shouldDisconnect);
                                                break;
                                        }

@@ -346,11 +346,11 @@
                                                }

                                        } else {
-                                               
ctx.sendMethodNotAllowed(method, shouldDisconnect);
+                                               
ctx.sendMethodNotAllowed(method, ctx.shouldDisconnect);
                                                ctx.close();
                                        }
                                }
-                               if(shouldDisconnect) {
+                               if(ctx.shouldDisconnect) {
                                        sock.close();
                                        return;
                                }
@@ -438,4 +438,8 @@
        public boolean doRobots() {
                return container.doRobots();
        }
+
+       public void forceDisconnect() {
+               this.shouldDisconnect = true;
+       }
 }

Modified: branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/WelcomeToadlet.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -8,8 +8,6 @@
 import java.net.URISyntaxException;

 import java.io.File;
-import java.io.FileReader;
-import java.io.StringWriter;

 import org.tanukisoftware.wrapper.WrapperManager;

@@ -34,6 +32,7 @@
 import freenet.support.MultiValueTable;
 import freenet.support.api.Bucket;
 import freenet.support.api.HTTPRequest;
+import freenet.support.io.FileUtil;


 import freenet.frost.message.*;
@@ -404,16 +403,9 @@
                if(ctx.isAllowedFullAccess()) {

                        if(request.isParameterSet("latestlog")) {
-
-                               FileReader reader = new 
FileReader(node.config.get("logger").getString("dirname") + File.separator + 
"freenet-latest.log");
-
-                               StringWriter sw = new StringWriter();
-                               char[] buffer = new char[1024];
-                               int read;
-                               while((read = reader.read(buffer)) != -1)
-                                       sw.write(buffer, 0, read);
-
-                               this.writeHTMLReply(ctx, 200, "OK", 
sw.toString());
+                               final File logs = new 
File(node.config.get("logger").getString("dirname") + File.separator + 
"freenet-latest.log");
+                               
+                               this.writeHTMLReply(ctx, 200, "OK", 
FileUtil.readUTF(logs));
                                return;
                        } else if (request.isParameterSet("terminated")) {
                                if((!request.isParameterSet("formPassword")) || 
!request.getParam("formPassword").equals(core.formPassword)) {
@@ -442,19 +434,19 @@
                                writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());
                                Logger.normal(this, "Node is restarting");
                                return;
-                        } else if (request.getParam("newbookmark").length() > 
0) {
+                       } else if (request.getParam("newbookmark").length() > 
0) {
                                HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("confirmAddBookmarkTitle"), ctx);
                                HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
                                HTMLNode infobox = 
contentNode.addChild(ctx.getPageMaker().getInfobox(l10n("confirmAddBookmarkSubTitle")));
                                HTMLNode addForm = 
ctx.addFormChild(ctx.getPageMaker().getContentNode(infobox), 
"/bookmarkEditor/", "editBookmarkForm");
                                addForm.addChild("#", 
l10n("confirmAddBookmarkWithKey", "key", request.getParam("newbookmark")));
                                addForm.addChild("br");
-                                String key  = request.getParam("newbookmark");
-                                if(key.startsWith("freenet:"))
-                                    key = key.substring(8);
+                               String key  = request.getParam("newbookmark");
+                               if(key.startsWith("freenet:"))
+                                       key = key.substring(8);
                                addForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "hidden", "key", key});
                                addForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "text", "name", 
request.getParam("desc") });
-                                addForm.addChild("input", new String[] 
{"type", "name", "value"}, new String[] {"hidden", "bookmark", "/"});
+                               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.writeHTMLReply(ctx, 200, "OK", 
pageNode.generate());

Modified: branches/freenet-jfk/src/freenet/clients/http/bookmark/Bookmark.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/bookmark/Bookmark.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/bookmark/Bookmark.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -28,4 +28,16 @@
        public void setDesc(String s) {
                desc = s;
        }
+       
+       public boolean equals(Object o) {
+               if(o == this) return true;
+               if(o instanceof Bookmark) {
+                       Bookmark b = (Bookmark) o;
+                       if(!b.name.equals(name)) return false;
+                       // Compensate for nulls
+                       
if(!String.valueOf(b.desc).equals(String.valueOf(desc))) return false;
+                       if(b.privateBookmark != privateBookmark) return false;
+                       return true;
+               } else return false;
+       }
 }

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategory.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategory.java    
    2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkCategory.java    
    2007-09-23 09:58:34 UTC (rev 15282)
@@ -17,12 +17,14 @@
                setDesc(desc);
        }

-       protected Bookmark addBookmark(Bookmark b) {
+       protected synchronized Bookmark addBookmark(Bookmark b) {
+               int x = bookmarks.indexOf(b);
+               if(x >= 0) return (Bookmark) bookmarks.get(x);
                bookmarks.add(b);
                return b;
        }

-       protected void removeBookmark(Bookmark b) {
+       protected synchronized void removeBookmark(Bookmark b) {
                bookmarks.remove(b);
        }

@@ -123,4 +125,6 @@
                for (int i = 0; i < size(); i++)
                        subCategories.get(i).setPrivate(bool);
        }
+       
+       // Don't override equals(), two categories are equal if they have the 
same name and description.
 }

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItem.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItem.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkItem.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -153,4 +153,20 @@
        public USK getUSK() throws MalformedURLException {
                return USK.create(key);
        }
+       
+       public boolean equals(Object o) {
+               if(o == this) return true;
+               if(o instanceof BookmarkItem) {
+                       BookmarkItem b = (BookmarkItem) o;
+                       if(!super.equals(o)) return false;
+                       if(!b.key.equals(key)) {
+                               if(b.key.getKeyType().equals("USK")) {
+                                       
if(!b.key.setSuggestedEdition(key.getSuggestedEdition()).equals(key))
+                                               return false;
+                               } else return false;
+                       }
+                       if(b.alerts != alerts) return false; // Belongs to a 
different node???
+                       return true;
+               } else return false;
+       }
 }

Modified: 
branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkManager.java 
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/bookmark/BookmarkManager.java 
2007-09-23 09:58:34 UTC (rev 15282)
@@ -34,30 +34,42 @@
                        BookmarkCategory indexes = (BookmarkCategory) 
defaultRoot.addBookmark(new BookmarkCategory("Indexes"));
                        indexes.addBookmark(new BookmarkItem(
                                        new FreenetURI(
-                                                       "USK at 
7H66rhYmxIFgMyw5Dl11JazXGHPhp7dSN7WMa1pbtEo,jQHUQUPTkeRcjmjgrc7t5cDRdDkK3uKkrSzuw5CO9uk,AQACAAE/ENTRY.POINT/25/"),
+                                                       "USK at 
7H66rhYmxIFgMyw5Dl11JazXGHPhp7dSN7WMa1pbtEo,jQHUQUPTkeRcjmjgrc7t5cDRdDkK3uKkrSzuw5CO9uk,AQACAAE/ENTRY.POINT/36/"),
                                                        "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/"),
+                                                       "USK at 
zQyF2O1o8B4y40w7Twz8y2I9haW3d2DTlxjTHPu7zc8,h2mhQNNE9aQvF~2yKAmKV1uorr7141-QOroBf5hrlbw,AQACAAE/AnotherIndex/23/"),
                                                        "Another Index 
(freesites with categories but no descriptions)",
                                                        node.alerts));

+                       indexes.addBookmark(new BookmarkItem(
+                                       new FreenetURI(
+                                                       "USK at 
RJnh1EnvOSPwOWVRS2nyhC4eIQkKoNE5hcTv7~yY-sM,pOloLxnKWM~AL24iDMHOAvTvCqMlB-p2BO9zK96TOZA,AQACAAE/index_fr/11/"),
+                                                       "Index des sites 
Fran?ais (french freesites with descriptions but no categories)",
+                                                       node.alerts));
+
+                       indexes.addBookmark(new BookmarkItem(
+                                       new FreenetURI(
+                                                       "USK at 
cvZEZFWynx~4hmakaimts4Ruusl9mEUpU6mSvNvZ9p8,K2Xopc6GWPkKrs27EDuqzTcca2bE5H2YAXw0qKnkON4,AQACAAE/TSOF/1/"),
+                                                       "The Start Of Freenet 
(another human-maintained index, so far relatively small)",
+                                                       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/"),
+                                                       "USK at 
yGvITGZzrY1vUZK-4AaYLgcjZ7ysRqNTMfdcO8gS-LY,-ab5bJVD3Lp-LXEQqBAhJpMKrKJ19RnNaZMIkusU79s,AQACAAE/toad/7/"),
                                                        "Toad", node.alerts));
                        flog.addBookmark(new BookmarkItem(
                                        new FreenetURI(
-                                                       "USK at 
hM9XRwjXIzU8xTSBXNZvTn2KuvTSRFnVn4EER9FQnpM,gsth24O7ud4gL4NwNuYJDUqfaWASOG2zxZY~ChtgPxc,AQACAAE/Flog/4/"),
+                                                       "USK at 
hM9XRwjXIzU8xTSBXNZvTn2KuvTSRFnVn4EER9FQnpM,gsth24O7ud4gL4NwNuYJDUqfaWASOG2zxZY~ChtgPxc,AQACAAE/Flog/7/"),
                                                        "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"),
+                                                       "USK at 
QRZAI1nSm~dAY2hTdzVWXmEhkaI~dso0OadnppBR7kE,wq5rHGBI7kpChBe4yRmgBChIGDug7Xa5SG9vYGXdxR0,AQACAAE/frost/4"),
                                                        "Frost", node.alerts));

                        sc.register("bookmarks", defaultRoot.toStrings(), 0, 
true, false,"BookmarkManager.list", "BookmarkManager.listLong", configCB);
@@ -80,6 +92,7 @@

                public void set(String[] newVals) throws 
InvalidConfigValueException {
                        FreenetURI key;
+                       clear();
                        for (int i = 0; i < newVals.length; i++) {
                                try {
                                        Matcher matcher = 
pattern.matcher(newVals[i]);
@@ -225,8 +238,7 @@
                        }
                }

-               getCategoryByPath(parentPath(path)).removeBookmark(
-                               getBookmarkByPath(path));
+               getCategoryByPath(parentPath(path)).removeBookmark(bookmark);
                bookmarks.remove(path);

                if (store)

Modified: 
branches/freenet-jfk/src/freenet/clients/http/filter/CSSTokenizerFilter.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/clients/http/filter/CSSTokenizerFilter.java    
    2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/clients/http/filter/CSSTokenizerFilter.java    
    2007-09-23 09:58:34 UTC (rev 15282)
@@ -1,11 +1,10 @@
-/* The following code was generated by JFlex 1.4.1 on 10/05/07 18:37 */
+/* The following code was generated by JFlex 1.4.1 on 13-aug-07 8:18:51  */

 /* This code is part of Freenet. It is distributed under the GNU General
  * Public License, version 2 (or at your option any later version). See
  * http://www.gnu.org/ for further details of the GPL. */ 
 package freenet.clients.http.filter;
 import java.io.*;
-import java.util.*;
 import freenet.l10n.L10n;
 /* This class tokenizes a CSS2 Reader stream, writes it out to the output 
Writer, and filters any URLs found */
 // WARNING: this is not as thorough as the HTML parser - new versions of the 
standard could lead to anonymity risks. See comments in SaferFilter.java
@@ -17,7 +16,7 @@
 /**
  * This class is a scanner generated by 
  * <a href="http://www.jflex.de/";>JFlex</a> 1.4.1
- * on 10/05/07 18:37 from the specification file
+ * on 13-aug-07 8:18:51  from the specification file
  * <tt>freenet/clients/http/filter/CSSTokenizerFilter.jflex</tt>
  */
 class CSSTokenizerFilter {
@@ -36,14 +35,14 @@
    */
   private static final String ZZ_CMAP_PACKED = 
     "\11\0\1\3\1\3\1\0\1\3\1\3\22\0\1\5\1\41\1\11"+
-    "\1\17\1\4\1\64\1\4\1\12\1\23\1\15\1\37\1\56\1\24"+
-    "\1\13\1\14\1\36\12\1\1\46\1\33\1\40\1\44\1\42\1\16"+
-    "\1\25\1\47\1\6\1\55\1\52\1\51\1\53\1\50\1\61\1\26"+
-    "\1\7\1\63\1\22\1\27\1\54\1\31\1\30\1\7\1\21\1\60"+
-    "\1\32\1\20\2\7\1\57\1\7\1\62\1\4\1\2\2\4\1\10"+
-    "\1\4\1\47\1\6\1\55\1\52\1\51\1\53\1\50\1\61\1\26"+
-    "\1\7\1\63\1\22\1\27\1\54\1\31\1\30\1\7\1\21\1\60"+
-    "\1\32\1\20\2\7\1\57\1\7\1\62\1\34\1\45\1\35\1\43"+
+    "\1\17\1\4\1\63\1\4\1\12\1\23\1\15\1\37\1\55\1\24"+
+    "\1\13\1\14\1\36\12\1\1\64\1\33\1\40\1\44\1\42\1\16"+
+    "\1\25\1\46\1\6\1\54\1\51\1\50\1\52\1\47\1\60\1\26"+
+    "\1\7\1\62\1\22\1\27\1\53\1\31\1\30\1\7\1\21\1\57"+
+    "\1\32\1\20\2\7\1\56\1\7\1\61\1\4\1\2\2\4\1\10"+
+    "\1\4\1\46\1\6\1\54\1\51\1\50\1\52\1\47\1\60\1\26"+
+    "\1\7\1\62\1\22\1\27\1\53\1\31\1\30\1\7\1\21\1\57"+
+    "\1\32\1\20\2\7\1\56\1\7\1\61\1\34\1\45\1\35\1\43"+
     "\uff81\0";

   /** 
@@ -60,27 +59,28 @@
     "\1\0\1\1\1\2\1\1\1\3\1\2\2\1\2\4"+
     "\1\5\1\1\1\2\1\4\1\1\1\6\1\7\1\10"+
     "\1\4\4\1\3\0\1\11\1\0\1\12\1\13\1\2"+
-    "\2\0\1\14\2\0\1\15\2\0\1\16\1\17\1\0"+
-    "\1\17\1\2\1\0\1\20\1\0\5\20\4\0\1\21"+
-    "\1\22\2\0\1\2\2\14\2\0\1\15\1\23\12\0"+
-    "\2\17\1\2\2\24\6\20\4\0\1\2\1\0\1\15"+
-    "\2\0\1\25\1\17\1\11\1\24\1\0\1\24\6\20"+
-    "\1\26\1\27\2\0\1\2\1\0\1\15\2\17\5\0"+
-    "\1\30\2\24\1\31\3\20\1\32\2\20\2\0\1\2"+
-    "\1\0\1\15\2\17\1\30\3\0\2\24\1\31\5\20"+
-    "\2\0\1\2\1\0\1\15\1\25\1\17\2\0\2\24"+
-    "\1\31\2\20\1\33\2\0\2\20\6\0\1\34\1\15"+
-    "\1\17\2\30\2\24\1\31\1\20\1\0\1\20\4\0"+
-    "\1\20\1\35\2\33\2\20\2\0\1\34\3\0\1\31"+
-    "\1\0\2\20\1\35\4\0\1\20\1\33\1\20\6\0"+
-    "\1\31\1\20\5\0\1\35\1\0\1\20\1\33\1\36"+
-    "\3\0\1\37\2\34\1\20\4\0\1\35\5\0\1\35"+
-    "\1\0\1\33\1\0\1\40\1\0\1\20\15\0\1\33"+
-    "\2\40\1\20\11\0\1\33\1\20\6\0\1\35\23\0"+
-    "\1\35\3\0\1\35\32\0";
+    "\2\0\1\14\2\0\1\15\1\0\1\16\1\0\1\15"+
+    "\1\17\1\0\1\17\1\2\1\0\1\20\1\0\5\20"+
+    "\4\0\1\21\1\22\2\0\1\2\2\14\1\0\10\16"+
+    "\1\13\2\16\1\0\1\16\1\23\12\0\2\17\1\2"+
+    "\2\24\6\20\4\0\1\2\2\16\1\0\1\16\2\0"+
+    "\1\25\1\17\1\11\1\24\1\0\1\24\6\20\1\26"+
+    "\1\27\2\0\1\2\1\0\1\16\2\17\5\0\1\30"+
+    "\2\24\1\31\3\20\1\32\2\20\2\0\1\2\1\0"+
+    "\1\16\2\17\1\30\3\0\2\24\1\31\5\20\2\0"+
+    "\1\2\1\0\1\16\1\25\1\17\2\0\2\24\1\31"+
+    "\2\20\1\33\2\0\2\20\6\0\1\34\1\16\1\17"+
+    "\2\30\2\24\1\31\1\20\1\0\1\20\4\0\1\20"+
+    "\1\35\2\33\2\20\2\0\1\34\3\0\1\31\1\0"+
+    "\2\20\1\35\4\0\1\20\1\33\1\20\6\0\1\31"+
+    "\1\20\5\0\1\35\1\0\1\20\1\33\1\36\3\0"+
+    "\1\37\2\34\1\20\4\0\1\35\5\0\1\35\1\0"+
+    "\1\33\1\0\1\40\1\0\1\20\15\0\1\33\2\40"+
+    "\1\20\11\0\1\33\1\20\6\0\1\35\23\0\1\35"+
+    "\3\0\1\35\32\0";

   private static int [] zzUnpackAction() {
-    int [] result = new int[343];
+    int [] result = new int[357];
     int offset = 0;
     offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
     return result;
@@ -111,46 +111,48 @@
     "\0\u0424\0\u01dd\0\65\0\u0459\0\65\0\65\0\u048e\0\u013e"+
     "\0\u04c3\0\65\0\u0173\0\u04f8\0\u052d\0\u0562\0\u0597\0\u05cc"+
     "\0\u0601\0\u0636\0\u066b\0\u06a0\0\u06d5\0\u070a\0\u073f\0\u0774"+
-    "\0\u07a9\0\u07de\0\u0813\0\u0848\0\u087d\0\u08b2\0\u0385\0\u08e7"+
-    "\0\65\0\65\0\u091c\0\u0951\0\u0986\0\u013e\0\u0173\0\u09bb"+
-    "\0\u09f0\0\u0a25\0\65\0\u0a5a\0\u0a8f\0\u0ac4\0\u0af9\0\u0b2e"+
-    "\0\u0b63\0\u0b98\0\u0bcd\0\u0c02\0\u0c37\0\u0c6c\0\u0ca1\0\u0cd6"+
-    "\0\u0d0b\0\u0d40\0\u0d75\0\u0daa\0\u0ddf\0\u0e14\0\u0e49\0\u0e7e"+
-    "\0\u0eb3\0\u0ee8\0\u0f1d\0\u0f52\0\u0f87\0\u0fbc\0\u0ff1\0\u1026"+
-    "\0\u105b\0\u1090\0\u10c5\0\u10fa\0\u112f\0\u1164\0\u1199\0\u11ce"+
-    "\0\u1203\0\u1238\0\u126d\0\u12a2\0\u12d7\0\65\0\65\0\u130c"+
-    "\0\u1341\0\u1376\0\u13ab\0\u13e0\0\u1415\0\u144a\0\u147f\0\u14b4"+
-    "\0\u10fa\0\u14e9\0\u151e\0\65\0\u1553\0\u1588\0\u15bd\0\u15f2"+
-    "\0\u1627\0\u165c\0\u070a\0\u1691\0\u16c6\0\u16fb\0\u1730\0\u1765"+
-    "\0\u179a\0\u17cf\0\u1804\0\u1839\0\u147f\0\u186e\0\u18a3\0\u18d8"+
-    "\0\u190d\0\u1942\0\u1977\0\u19ac\0\u19e1\0\u1a16\0\u1a4b\0\u1a80"+
-    "\0\u1ab5\0\u1aea\0\u1b1f\0\u1b54\0\u1b89\0\u066b\0\u1bbe\0\u1bf3"+
+    "\0\u07a9\0\u07de\0\u0813\0\u0848\0\u087d\0\u08b2\0\u08e7\0\u0385"+
+    "\0\u091c\0\65\0\65\0\u0951\0\u0986\0\u09bb\0\u013e\0\u0173"+
+    "\0\u09f0\0\u0a25\0\u0a5a\0\u0a8f\0\u0ac4\0\u0af9\0\u0b2e\0\u0b63"+
+    "\0\u0b98\0\u0597\0\u0bcd\0\u0c02\0\u0c37\0\u0c6c\0\65\0\u0ca1"+
+    "\0\u0cd6\0\u0d0b\0\u0d40\0\u0d75\0\u0daa\0\u0ddf\0\u0e14\0\u0e49"+
+    "\0\u0e7e\0\u0eb3\0\u0ee8\0\u0f1d\0\u0f52\0\u0f87\0\u0fbc\0\u0ff1"+
+    "\0\u1026\0\u105b\0\u1090\0\u10c5\0\u10fa\0\u112f\0\u1164\0\u1199"+
+    "\0\u11ce\0\u1203\0\u1238\0\u126d\0\u12a2\0\u12d7\0\u130c\0\u1341"+
+    "\0\u1376\0\u13ab\0\u13e0\0\u1415\0\u144a\0\u147f\0\u14b4\0\u14e9"+
+    "\0\u151e\0\u1553\0\u1588\0\65\0\65\0\u15bd\0\u15f2\0\u1627"+
+    "\0\u165c\0\u1691\0\u16c6\0\u16fb\0\u1730\0\u1765\0\u13ab\0\u179a"+
+    "\0\u17cf\0\65\0\u1804\0\u1839\0\u186e\0\u18a3\0\u18d8\0\u190d"+
+    "\0\u073f\0\u1942\0\u1977\0\u19ac\0\u19e1\0\u1a16\0\u1a4b\0\u1a80"+
+    "\0\u1ab5\0\u1aea\0\u1730\0\u1b1f\0\u1b54\0\u1b89\0\u1bbe\0\u1bf3"+
     "\0\u1c28\0\u1c5d\0\u1c92\0\u1cc7\0\u1cfc\0\u1d31\0\u1d66\0\u1d9b"+
-    "\0\u1dd0\0\u1e05\0\u1e3a\0\u1e6f\0\u1ea4\0\u1ed9\0\u1f0e\0\u1f43"+
-    "\0\u1f78\0\65\0\u1fad\0\u1fe2\0\u14e9\0\u151e\0\u2017\0\65"+
-    "\0\u204c\0\u2081\0\u20b6\0\u20eb\0\u2120\0\u2155\0\u218a\0\u21bf"+
-    "\0\u21f4\0\u20b6\0\u2229\0\u225e\0\u2293\0\u22c8\0\u22fd\0\u2332"+
-    "\0\u1ed9\0\u2367\0\u239c\0\u23d1\0\u2406\0\u243b\0\u2470\0\u24a5"+
-    "\0\u20eb\0\u24da\0\u250f\0\u2544\0\u2579\0\u25ae\0\u25e3\0\u2618"+
-    "\0\u264d\0\u2682\0\u26b7\0\u26ec\0\u2721\0\u2756\0\65\0\u278b"+
-    "\0\u27c0\0\u27f5\0\u282a\0\u285f\0\u2894\0\65\0\u28c9\0\u28fe"+
-    "\0\u2933\0\u070a\0\u2968\0\u299d\0\u29d2\0\65\0\u1f43\0\u1f78"+
-    "\0\u2a07\0\u2a3c\0\u2a71\0\u2aa6\0\u2adb\0\u218a\0\u2b10\0\u2b45"+
-    "\0\u2b7a\0\u2baf\0\u2be4\0\u21bf\0\u2c19\0\u2c4e\0\u2c83\0\65"+
-    "\0\u2cb8\0\u2ced\0\u2d22\0\u2d57\0\u2d8c\0\u2dc1\0\u2df6\0\u2e2b"+
-    "\0\u2e60\0\u2e95\0\u2eca\0\u2eff\0\u2f34\0\u2f69\0\u2f9e\0\u2fd3"+
-    "\0\u2682\0\u26b7\0\u3008\0\u303d\0\u3072\0\u30a7\0\u30dc\0\u3111"+
-    "\0\u3146\0\u317b\0\u31b0\0\u31e5\0\u321a\0\u324f\0\u3284\0\u32b9"+
-    "\0\u32ee\0\u3323\0\u3358\0\u338d\0\u2eff\0\u33c2\0\u33f7\0\u342c"+
+    "\0\u1dd0\0\u1e05\0\u1e3a\0\u06a0\0\u1e6f\0\u1ea4\0\u1ed9\0\u1f0e"+
+    "\0\u1f43\0\u1f78\0\u1fad\0\u1fe2\0\u2017\0\u204c\0\u2081\0\u20b6"+
+    "\0\u20eb\0\u2120\0\u2155\0\u218a\0\u21bf\0\u21f4\0\u2229\0\65"+
+    "\0\u225e\0\u2293\0\u179a\0\u17cf\0\u22c8\0\65\0\u22fd\0\u2332"+
+    "\0\u2367\0\u239c\0\u23d1\0\u2406\0\u243b\0\u2470\0\u24a5\0\u2367"+
+    "\0\u24da\0\u250f\0\u2544\0\u2579\0\u25ae\0\u25e3\0\u218a\0\u2618"+
+    "\0\u264d\0\u2682\0\u26b7\0\u26ec\0\u2721\0\u2756\0\u239c\0\u278b"+
+    "\0\u27c0\0\u27f5\0\u282a\0\u285f\0\u2894\0\u28c9\0\u28fe\0\u2933"+
+    "\0\u2968\0\u299d\0\u29d2\0\u2a07\0\65\0\u2a3c\0\u2a71\0\u2aa6"+
+    "\0\u2adb\0\u2b10\0\u2b45\0\65\0\u2b7a\0\u2baf\0\u2be4\0\u073f"+
+    "\0\u2c19\0\u2c4e\0\u2c83\0\65\0\u21f4\0\u2229\0\u2cb8\0\u2ced"+
+    "\0\u2d22\0\u2d57\0\u2d8c\0\u243b\0\u2dc1\0\u2df6\0\u2e2b\0\u2e60"+
+    "\0\u2e95\0\u2470\0\u2eca\0\u2eff\0\u2f34\0\65\0\u2f69\0\u2f9e"+
+    "\0\u2fd3\0\u3008\0\u303d\0\u3072\0\u30a7\0\u30dc\0\u3111\0\u3146"+
+    "\0\u317b\0\u31b0\0\u31e5\0\u321a\0\u324f\0\u3284\0\u2933\0\u2968"+
+    "\0\u32b9\0\u32ee\0\u3323\0\u3358\0\u338d\0\u33c2\0\u33f7\0\u342c"+
     "\0\u3461\0\u3496\0\u34cb\0\u3500\0\u3535\0\u356a\0\u359f\0\u35d4"+
-    "\0\u3609\0\u363e\0\u3673\0\u36a8\0\u36dd\0\u3712\0\u3747\0\u377c"+
-    "\0\u2f69\0\u37b1\0\u37e6\0\u381b\0\u2f9e\0\u3850\0\u3885\0\u38ba"+
-    "\0\u38ef\0\u3924\0\u3959\0\u398e\0\u39c3\0\u39f8\0\u3a2d\0\u3a62"+
-    "\0\u3a97\0\u3acc\0\u3b01\0\u3b36\0\u3b6b\0\u3ba0\0\u3bd5\0\u3c0a"+
-    "\0\u3c3f\0\u3c74\0\u3ca9\0\u3cde\0\u3d13\0\u3d48\0\u3d7d";
+    "\0\u3609\0\u363e\0\u31b0\0\u3673\0\u36a8\0\u36dd\0\u3712\0\u3747"+
+    "\0\u377c\0\u37b1\0\u37e6\0\u381b\0\u3850\0\u3885\0\u38ba\0\u38ef"+
+    "\0\u3924\0\u3959\0\u398e\0\u39c3\0\u39f8\0\u3a2d\0\u321a\0\u3a62"+
+    "\0\u3a97\0\u3acc\0\u324f\0\u3b01\0\u3b36\0\u3b6b\0\u3ba0\0\u3bd5"+
+    "\0\u3c0a\0\u3c3f\0\u3c74\0\u3ca9\0\u3cde\0\u3d13\0\u3d48\0\u3d7d"+
+    "\0\u3db2\0\u3de7\0\u3e1c\0\u3e51\0\u3e86\0\u3ebb\0\u3ef0\0\u3f25"+
+    "\0\u3f5a\0\u3f8f\0\u3fc4\0\u3ff9\0\u402e";

   private static int [] zzUnpackRowMap() {
-    int [] result = new int[343];
+    int [] result = new int[357];
     int offset = 0;
     offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
     return result;
@@ -177,568 +179,605 @@
     "\1\7\1\10\1\11\1\12\1\13\1\2\1\14\1\15"+
     "\2\6\1\2\1\16\1\17\5\6\1\20\1\21\1\22"+
     "\1\23\1\16\1\24\1\25\1\16\1\26\1\2\1\27"+
-    "\1\16\7\6\1\16\5\6\1\2\66\0\1\3\1\30"+
+    "\7\6\1\16\5\6\1\2\1\16\66\0\1\3\1\30"+
     "\1\31\1\0\1\31\3\6\2\0\1\6\1\32\3\0"+
-    "\3\6\1\33\1\34\1\0\5\6\1\35\13\0\7\6"+
-    "\1\0\5\6\1\36\1\0\1\37\1\6\1\0\2\6"+
-    "\1\37\40\6\1\37\1\6\3\37\1\6\1\37\7\6"+
+    "\3\6\1\33\1\34\1\0\5\6\1\35\12\0\7\6"+
+    "\1\0\5\6\1\36\2\0\1\37\1\6\1\0\2\6"+
+    "\1\37\37\6\1\37\1\6\3\37\1\6\1\37\10\6"+
     "\3\0\1\5\1\0\1\5\26\0\1\21\1\22\30\0"+
     "\1\6\1\30\1\31\1\0\1\31\3\6\2\0\1\6"+
-    "\4\0\3\6\1\33\1\34\1\0\5\6\1\35\13\0"+
-    "\7\6\1\0\5\6\1\0\2\40\1\41\6\40\1\42"+
+    "\4\0\3\6\1\33\1\34\1\0\5\6\1\35\12\0"+
+    "\7\6\1\0\5\6\2\0\2\40\1\41\6\40\1\42"+
     "\53\40\2\43\1\44\7\43\1\42\52\43\1\0\1\45"+
-    "\1\46\3\0\2\45\3\0\1\47\4\0\3\45\3\0"+
-    "\5\45\14\0\7\45\1\0\5\45\2\0\1\50\64\0"+
-    "\1\51\1\52\3\0\1\51\2\53\2\0\1\53\4\0"+
-    "\3\53\3\0\5\53\14\0\1\51\1\53\3\51\1\53"+
-    "\1\51\1\0\5\53\2\0\1\6\1\30\1\31\1\0"+
-    "\1\31\3\6\2\0\1\6\4\0\1\6\1\54\1\6"+
-    "\1\33\1\34\1\0\5\6\1\35\13\0\7\6\1\55"+
-    "\5\6\2\0\1\56\1\57\3\0\2\56\10\0\3\56"+
-    "\3\0\1\60\1\61\1\62\2\56\14\0\4\56\1\63"+
-    "\1\56\1\64\1\0\5\56\4\0\1\21\1\0\1\21"+
-    "\62\0\1\22\1\0\1\22\116\0\1\65\66\0\1\66"+
-    "\26\0\1\67\1\0\1\67\20\0\1\70\102\0\1\71"+
-    "\64\0\1\72\23\0\1\31\1\0\1\31\16\0\1\34"+
-    "\6\0\1\35\32\0\1\73\1\74\1\34\1\0\1\34"+
-    "\2\73\10\0\3\73\3\0\5\73\14\0\7\73\1\0"+
-    "\5\73\2\0\1\75\1\30\1\6\1\0\1\6\1\75"+
-    "\2\6\2\0\1\6\4\0\3\6\1\33\1\34\1\0"+
-    "\5\6\1\35\13\0\1\75\1\6\3\75\1\6\1\75"+
-    "\1\0\5\6\1\0\2\40\1\41\6\40\1\76\53\40"+
-    "\2\43\1\44\7\43\1\77\52\43\1\0\1\45\1\46"+
-    "\1\100\1\0\1\100\3\45\2\0\1\45\4\0\3\45"+
-    "\3\0\5\45\13\0\1\101\7\45\1\0\5\45\2\0"+
-    "\1\102\1\45\1\0\2\45\1\102\40\45\1\102\1\45"+
-    "\3\102\1\45\1\102\7\45\42\0\1\103\23\0\1\50"+
-    "\17\0\1\104\4\0\1\105\1\106\1\107\17\0\1\110"+
-    "\1\111\1\112\2\0\1\113\2\0\1\36\1\114\1\0"+
-    "\1\115\1\36\1\0\1\116\1\52\3\0\1\116\2\53"+
-    "\2\0\1\53\4\0\3\53\3\0\5\53\14\0\1\116"+
-    "\1\53\3\116\1\53\1\116\1\0\5\53\2\0\1\117"+
-    "\1\53\1\0\2\53\1\117\40\53\1\117\1\53\3\117"+
-    "\1\53\1\117\7\53\1\0\1\53\1\52\3\0\3\53"+
-    "\2\0\1\53\4\0\3\53\3\0\5\53\14\0\7\53"+
-    "\1\0\5\53\2\0\1\6\1\30\1\31\1\0\1\31"+
-    "\3\6\2\0\1\6\4\0\2\6\1\120\1\33\1\34"+
-    "\1\0\5\6\1\35\13\0\7\6\1\0\5\6\2\0"+
-    "\1\121\4\0\1\121\7\0\1\122\30\0\1\121\1\0"+
-    "\3\121\1\0\1\121\10\0\1\56\1\57\3\0\3\56"+
-    "\2\0\1\56\4\0\3\56\3\0\5\56\14\0\7\56"+
-    "\1\0\5\56\2\0\1\123\1\56\1\0\2\56\1\123"+
-    "\40\56\1\123\1\56\3\123\1\56\1\123\7\56\1\0"+
-    "\1\56\1\57\3\0\3\56\2\0\1\56\4\0\3\56"+
-    "\3\0\1\56\1\124\3\56\14\0\7\56\1\0\5\56"+
-    "\2\0\1\56\1\57\3\0\3\56\2\0\1\56\4\0"+
-    "\3\56\3\0\5\56\14\0\2\56\1\125\4\56\1\0"+
-    "\5\56\2\0\1\56\1\57\3\0\3\56\2\0\1\56"+
-    "\4\0\3\56\3\0\5\56\14\0\1\126\6\56\1\0"+
-    "\5\56\2\0\1\56\1\57\3\0\3\56\2\0\1\56"+
-    "\4\0\3\56\3\0\3\56\1\127\1\56\14\0\7\56"+
-    "\1\0\5\56\2\0\1\56\1\57\3\0\3\56\2\0"+
-    "\1\56\4\0\3\56\3\0\5\56\14\0\7\56\1\0"+
-    "\2\56\1\130\2\56\1\0\37\65\1\131\25\65\13\0"+
-    "\1\132\100\0\1\133\36\0\1\73\1\74\1\31\1\0"+
-    "\1\31\3\73\2\0\1\73\4\0\3\73\1\0\1\34"+
-    "\1\0\5\73\1\35\13\0\7\73\1\0\5\73\2\0"+
-    "\1\134\1\73\1\0\2\73\1\134\40\73\1\134\1\73"+
-    "\3\134\1\73\1\134\7\73\1\0\1\135\1\30\1\6"+
-    "\1\0\1\6\1\135\2\6\2\0\1\6\4\0\3\6"+
-    "\1\33\1\34\1\0\5\6\1\35\13\0\1\135\1\6"+
-    "\3\135\1\6\1\135\1\0\5\6\4\0\1\100\1\0"+
-    "\1\100\40\0\1\101\21\0\1\101\1\0\1\101\12\0"+
-    "\1\136\45\0\1\137\1\46\1\45\1\0\1\45\1\137"+
-    "\2\45\2\0\1\45\4\0\3\45\3\0\5\45\13\0"+
-    "\1\101\1\137\1\45\3\137\1\45\1\137\1\0\5\45"+
-    "\50\0\1\140\71\0\1\36\37\0\1\36\30\0\1\36"+
-    "\61\0\1\36\1\0\1\36\26\0\1\104\72\0\1\36"+
-    "\27\0\1\36\56\0\1\141\42\0\1\36\117\0\1\36"+
-    "\63\0\1\114\4\0\1\142\1\52\3\0\1\142\2\53"+
-    "\2\0\1\53\4\0\3\53\3\0\5\53\14\0\1\142"+
-    "\1\53\3\142\1\53\1\142\1\0\5\53\2\0\1\143"+
-    "\1\52\1\53\1\0\1\53\1\143\2\53\2\0\1\53"+
-    "\4\0\3\53\3\0\5\53\14\0\1\143\1\53\3\143"+
-    "\1\53\1\143\1\0\5\53\2\0\1\6\1\30\1\31"+
-    "\1\0\1\31\3\6\2\0\1\6\4\0\3\6\1\144"+
-    "\1\34\1\0\5\6\1\35\13\0\7\6\1\0\5\6"+
-    "\2\0\1\145\4\0\1\145\4\0\1\146\2\0\1\147"+
-    "\30\0\1\145\1\0\3\145\1\0\1\145\25\0\1\147"+
-    "\47\0\1\150\1\57\1\56\1\0\1\56\1\150\2\56"+
-    "\2\0\1\56\4\0\3\56\3\0\5\56\14\0\1\150"+
-    "\1\56\3\150\1\56\1\150\1\0\5\56\2\0\1\56"+
-    "\1\57\3\0\3\56\2\0\1\56\4\0\3\56\3\0"+
-    "\2\56\1\151\2\56\14\0\7\56\1\0\5\56\2\0"+
-    "\1\56\1\57\3\0\3\56\2\0\1\56\4\0\3\56"+
-    "\3\0\5\56\14\0\3\56\1\152\3\56\1\0\5\56"+
-    "\2\0\1\56\1\57\3\0\3\56\2\0\1\56\4\0"+
-    "\3\56\3\0\5\56\14\0\1\56\1\153\5\56\1\0"+
-    "\5\56\2\0\1\56\1\57\3\0\3\56\2\0\1\56"+
-    "\4\0\3\56\3\0\5\56\14\0\5\56\1\154\1\56"+
-    "\1\0\5\56\2\0\1\56\1\57\3\0\3\56\2\0"+
-    "\1\56\4\0\3\56\3\0\5\56\14\0\1\155\6\56"+
-    "\1\0\5\56\1\0\36\65\1\156\1\131\25\65\13\0"+
-    "\1\157\101\0\1\160\35\0\1\161\1\74\1\73\1\0"+
-    "\1\73\1\161\2\73\2\0\1\73\4\0\3\73\1\0"+
-    "\1\34\1\0\5\73\1\35\13\0\1\161\1\73\3\161"+
-    "\1\73\1\161\1\0\5\73\2\0\1\162\1\30\1\6"+
-    "\1\0\1\6\1\162\2\6\2\0\1\6\4\0\3\6"+
-    "\1\33\1\34\1\0\5\6\1\35\13\0\1\162\1\6"+
-    "\3\162\1\6\1\162\1\0\5\6\22\0\1\163\44\0"+
-    "\1\164\1\46\1\45\1\0\1\45\1\164\2\45\2\0"+
-    "\1\45\4\0\3\45\3\0\5\45\13\0\1\101\1\164"+
-    "\1\45\3\164\1\45\1\164\1\0\5\45\53\0\1\36"+
-    "\62\0\1\36\15\0\1\165\1\52\3\0\1\165\2\53"+
-    "\2\0\1\53\4\0\3\53\3\0\5\53\14\0\1\165"+
-    "\1\53\3\165\1\53\1\165\1\0\5\53\2\0\1\166"+
-    "\1\52\1\53\1\0\1\53\1\166\2\53\2\0\1\53"+
-    "\4\0\3\53\3\0\5\53\14\0\1\166\1\53\3\166"+
-    "\1\53\1\166\1\0\5\53\1\0\2\167\1\170\1\171"+
-    "\1\167\1\171\3\167\1\172\1\173\2\167\1\174\5\167"+
-    "\1\0\41\167\1\0\1\175\4\0\1\175\4\0\1\146"+
-    "\2\0\1\176\30\0\1\175\1\0\3\175\1\0\1\175"+
-    "\10\0\1\177\4\0\1\177\40\0\1\177\1\0\3\177"+
-    "\1\0\1\177\25\0\1\176\47\0\1\200\1\57\1\56"+
-    "\1\0\1\56\1\200\2\56\2\0\1\56\4\0\3\56"+
-    "\3\0\5\56\14\0\1\200\1\56\3\200\1\56\1\200"+
-    "\1\0\5\56\2\0\1\56\1\57\3\0\3\56\2\0"+
-    "\1\56\4\0\3\56\3\0\3\56\1\201\1\56\14\0"+
-    "\7\56\1\0\5\56\2\0\1\56\1\57\3\0\3\56"+
-    "\2\0\1\56\4\0\3\56\3\0\1\202\4\56\14\0"+
-    "\7\56\1\0\5\56\2\0\1\56\1\57\3\0\3\56"+
-    "\2\0\1\56\4\0\3\56\3\0\5\56\14\0\2\56"+
-    "\1\203\4\56\1\0\5\56\2\0\1\56\1\57\3\0"+
-    "\3\56\2\0\1\56\4\0\3\56\3\0\4\56\1\204"+
-    "\14\0\7\56\1\0\5\56\2\0\1\56\1\57\3\0"+
-    "\3\56\2\0\1\56\4\0\1\56\1\205\1\56\3\0"+
-    "\5\56\14\0\7\56\1\0\5\56\32\0\1\206\34\0"+
-    "\1\207\1\74\1\73\1\0\1\73\1\207\2\73\2\0"+
-    "\1\73\4\0\3\73\1\0\1\34\1\0\5\73\1\35"+
-    "\13\0\1\207\1\73\3\207\1\73\1\207\1\0\5\73"+
-    "\2\0\1\210\1\30\1\6\1\0\1\6\1\210\2\6"+
+    "\1\46\3\0\2\47\3\0\1\50\1\32\3\0\3\47"+
+    "\3\0\5\47\13\0\7\47\1\0\5\47\3\0\1\51"+
+    "\64\0\1\52\1\53\3\0\1\52\2\54\2\0\1\54"+
+    "\4\0\3\54\3\0\5\54\13\0\1\52\1\54\3\52"+
+    "\1\54\1\52\1\0\5\54\3\0\1\6\1\30\1\31"+
+    "\1\0\1\31\3\6\2\0\1\6\4\0\1\6\1\55"+
+    "\1\6\1\33\1\34\1\0\5\6\1\35\12\0\7\6"+
+    "\1\56\5\6\3\0\1\57\1\60\3\0\2\57\10\0"+
+    "\3\57\3\0\1\61\1\62\1\63\2\57\13\0\4\57"+
+    "\1\64\1\57\1\65\1\0\5\57\5\0\1\21\1\0"+
+    "\1\21\62\0\1\22\1\0\1\22\116\0\1\66\66\0"+
+    "\1\67\26\0\1\70\1\0\1\70\20\0\1\71\102\0"+
+    "\1\72\64\0\1\73\23\0\1\31\1\0\1\31\16\0"+
+    "\1\34\6\0\1\35\32\0\1\74\1\75\1\34\1\0"+
+    "\1\34\2\74\10\0\3\74\3\0\5\74\13\0\7\74"+
+    "\1\0\5\74\3\0\1\76\1\30\1\6\1\0\1\6"+
+    "\1\76\2\6\2\0\1\6\4\0\3\6\1\33\1\34"+
+    "\1\0\5\6\1\35\12\0\1\76\1\6\3\76\1\6"+
+    "\1\76\1\0\5\6\2\0\2\40\1\41\6\40\1\77"+
+    "\53\40\2\43\1\44\7\43\1\100\52\43\1\0\1\45"+
+    "\1\46\1\101\1\0\1\101\3\47\2\0\1\47\1\32"+
+    "\3\0\1\47\1\102\1\47\3\0\1\103\1\104\1\105"+
+    "\2\47\13\0\1\47\1\106\1\107\1\110\2\47\1\111"+
+    "\1\0\1\47\1\112\1\113\1\47\1\114\1\36\1\115"+
+    "\1\0\1\116\1\47\1\0\2\47\1\116\37\47\1\116"+
+    "\1\47\3\116\1\47\1\116\10\47\1\0\1\47\1\46"+
+    "\1\101\1\0\1\101\3\47\2\0\1\47\4\0\3\47"+
+    "\3\0\5\47\13\0\7\47\1\0\5\47\1\0\1\115"+
+    "\42\0\1\117\23\0\1\51\17\0\1\120\4\0\1\121"+
+    "\1\122\1\123\16\0\1\124\1\125\1\126\2\0\1\127"+
+    "\2\0\1\36\1\130\1\0\1\131\1\36\2\0\1\132"+
+    "\1\53\3\0\1\132\2\54\2\0\1\54\4\0\3\54"+
+    "\3\0\5\54\13\0\1\132\1\54\3\132\1\54\1\132"+
+    "\1\0\5\54\3\0\1\133\1\54\1\0\2\54\1\133"+
+    "\37\54\1\133\1\54\3\133\1\54\1\133\10\54\1\0"+
+    "\1\54\1\53\3\0\3\54\2\0\1\54\4\0\3\54"+
+    "\3\0\5\54\13\0\7\54\1\0\5\54\3\0\1\6"+
+    "\1\30\1\31\1\0\1\31\3\6\2\0\1\6\4\0"+
+    "\2\6\1\134\1\33\1\34\1\0\5\6\1\35\12\0"+
+    "\7\6\1\0\5\6\3\0\1\135\4\0\1\135\7\0"+
+    "\1\136\27\0\1\135\1\0\3\135\1\0\1\135\11\0"+
+    "\1\57\1\60\3\0\3\57\2\0\1\57\4\0\3\57"+
+    "\3\0\5\57\13\0\7\57\1\0\5\57\3\0\1\137"+
+    "\1\57\1\0\2\57\1\137\37\57\1\137\1\57\3\137"+
+    "\1\57\1\137\10\57\1\0\1\57\1\60\3\0\3\57"+
+    "\2\0\1\57\4\0\3\57\3\0\1\57\1\140\3\57"+
+    "\13\0\7\57\1\0\5\57\3\0\1\57\1\60\3\0"+
+    "\3\57\2\0\1\57\4\0\3\57\3\0\5\57\13\0"+
+    "\2\57\1\141\4\57\1\0\5\57\3\0\1\57\1\60"+
+    "\3\0\3\57\2\0\1\57\4\0\3\57\3\0\5\57"+
+    "\13\0\1\142\6\57\1\0\5\57\3\0\1\57\1\60"+
+    "\3\0\3\57\2\0\1\57\4\0\3\57\3\0\3\57"+
+    "\1\143\1\57\13\0\7\57\1\0\5\57\3\0\1\57"+
+    "\1\60\3\0\3\57\2\0\1\57\4\0\3\57\3\0"+
+    "\5\57\13\0\7\57\1\0\2\57\1\144\2\57\2\0"+
+    "\37\66\1\145\25\66\13\0\1\146\100\0\1\147\36\0"+
+    "\1\74\1\75\1\31\1\0\1\31\3\74\2\0\1\74"+
+    "\4\0\3\74\1\0\1\34\1\0\5\74\1\35\12\0"+
+    "\7\74\1\0\5\74\3\0\1\150\1\74\1\0\2\74"+
+    "\1\150\37\74\1\150\1\74\3\150\1\74\1\150\10\74"+
+    "\1\0\1\151\1\30\1\6\1\0\1\6\1\151\2\6"+
     "\2\0\1\6\4\0\3\6\1\33\1\34\1\0\5\6"+
-    "\1\35\13\0\1\210\1\6\3\210\1\6\1\210\1\0"+
-    "\5\6\23\0\1\211\43\0\1\212\1\46\1\45\1\0"+
-    "\1\45\1\212\2\45\2\0\1\45\4\0\3\45\3\0"+
-    "\5\45\13\0\1\101\1\212\1\45\3\212\1\45\1\212"+
-    "\1\0\5\45\2\0\1\213\1\52\3\0\1\213\2\53"+
-    "\2\0\1\53\4\0\3\53\3\0\5\53\14\0\1\213"+
-    "\1\53\3\213\1\53\1\213\1\0\5\53\2\0\1\214"+
-    "\1\52\1\53\1\0\1\53\1\214\2\53\2\0\1\53"+
-    "\4\0\3\53\3\0\5\53\14\0\1\214\1\53\3\214"+
-    "\1\53\1\214\1\0\5\53\1\0\2\167\1\170\6\167"+
-    "\2\0\2\167\1\174\5\167\1\0\43\167\1\170\12\167"+
-    "\1\215\47\167\2\172\1\216\6\172\1\217\53\172\2\173"+
-    "\1\220\7\173\1\217\52\173\1\0\1\221\4\0\1\221"+
-    "\4\0\1\146\2\0\1\222\30\0\1\221\1\0\3\221"+
-    "\1\0\1\221\25\0\1\222\47\0\1\223\4\0\1\223"+
-    "\40\0\1\223\1\0\3\223\1\0\1\223\10\0\1\224"+
-    "\1\57\1\56\1\0\1\56\1\224\2\56\2\0\1\56"+
-    "\4\0\3\56\3\0\5\56\14\0\1\224\1\56\3\224"+
-    "\1\56\1\224\1\0\5\56\2\0\1\56\1\57\3\0"+
-    "\3\56\2\0\1\56\4\0\1\56\1\225\1\56\3\0"+
-    "\5\56\14\0\7\56\1\0\5\56\2\0\1\56\1\57"+
-    "\3\0\3\56\2\0\1\56\4\0\3\56\3\0\5\56"+
-    "\14\0\1\226\6\56\1\0\5\56\2\0\1\56\1\57"+
-    "\3\0\3\56\2\0\1\227\4\0\3\56\3\0\5\56"+
-    "\14\0\7\56\1\0\5\56\2\0\1\56\1\57\3\0"+
-    "\3\56\2\0\1\56\4\0\3\56\3\0\5\56\14\0"+
-    "\7\56\1\0\1\56\1\230\3\56\22\0\1\231\44\0"+
-    "\1\232\1\74\1\73\1\0\1\73\1\232\2\73\2\0"+
-    "\1\73\4\0\3\73\1\0\1\34\1\0\5\73\1\35"+
-    "\13\0\1\232\1\73\3\232\1\73\1\232\1\0\5\73"+
-    "\2\0\1\233\1\30\1\6\1\0\1\6\1\233\2\6"+
-    "\2\0\1\6\4\0\3\6\1\33\1\34\1\0\5\6"+
-    "\1\35\13\0\1\233\1\6\3\233\1\6\1\233\1\0"+
-    "\5\6\24\0\1\234\42\0\1\235\1\46\1\45\1\0"+
-    "\1\45\1\235\2\45\2\0\1\45\4\0\3\45\3\0"+
-    "\5\45\13\0\1\101\1\235\1\45\3\235\1\45\1\235"+
-    "\1\0\5\45\2\0\1\236\1\52\3\0\1\236\2\53"+
-    "\2\0\1\53\4\0\3\53\3\0\5\53\14\0\1\236"+
-    "\1\53\3\236\1\53\1\236\1\0\5\53\2\0\1\237"+
-    "\1\52\1\53\1\0\1\53\1\237\2\53\2\0\1\53"+
-    "\4\0\3\53\3\0\5\53\14\0\1\237\1\53\3\237"+
-    "\1\53\1\237\1\0\5\53\1\0\2\172\1\216\6\172"+
-    "\1\240\53\172\3\0\1\217\1\0\1\217\7\0\1\174"+
-    "\47\0\2\173\1\220\7\173\1\241\52\173\1\0\1\242"+
-    "\4\0\1\242\4\0\1\146\2\0\1\243\30\0\1\242"+
-    "\1\0\3\242\1\0\1\242\25\0\1\243\47\0\1\244"+
-    "\4\0\1\244\40\0\1\244\1\0\3\244\1\0\1\244"+
-    "\10\0\1\245\1\57\1\56\1\0\1\56\1\245\2\56"+
-    "\2\0\1\56\4\0\3\56\3\0\5\56\14\0\1\245"+
-    "\1\56\3\245\1\56\1\245\1\0\5\56\2\0\1\56"+
-    "\1\57\3\0\3\56\2\0\1\56\4\0\3\56\3\0"+
-    "\4\56\1\246\14\0\7\56\1\0\5\56\2\0\1\247"+
-    "\1\250\1\251\1\0\1\251\2\247\1\56\2\0\1\56"+
-    "\4\0\3\247\3\0\5\247\14\0\7\247\1\0\5\247"+
-    "\2\0\1\56\1\57\3\0\3\56\2\0\1\56\4\0"+
-    "\3\56\3\0\5\56\14\0\4\56\1\252\2\56\1\0"+
-    "\5\56\2\0\1\56\1\57\3\0\3\56\2\0\1\56"+
-    "\4\0\3\56\3\0\5\56\14\0\2\56\1\253\4\56"+
-    "\1\0\5\56\33\0\1\254\33\0\1\255\1\74\1\73"+
-    "\1\0\1\73\1\255\2\73\2\0\1\73\4\0\3\73"+
-    "\1\0\1\34\1\0\5\73\1\35\13\0\1\255\1\73"+
-    "\3\255\1\73\1\255\1\0\5\73\2\0\1\6\1\30"+
-    "\1\6\1\0\4\6\2\0\1\6\4\0\3\6\1\33"+
-    "\1\34\1\0\5\6\1\35\13\0\7\6\1\0\5\6"+
-    "\1\0\2\256\1\257\1\234\1\256\1\234\3\256\1\260"+
-    "\1\261\2\256\1\262\5\256\1\0\41\256\1\0\1\263"+
-    "\1\46\1\45\1\0\1\45\1\263\2\45\2\0\1\45"+
-    "\4\0\3\45\3\0\5\45\13\0\1\101\1\263\1\45"+
-    "\3\263\1\45\1\263\1\0\5\45\2\0\1\264\1\52"+
-    "\1\53\1\0\1\53\1\264\2\53\2\0\1\53\4\0"+
-    "\3\53\3\0\5\53\14\0\1\264\1\53\3\264\1\53"+
-    "\1\264\1\0\5\53\1\0\2\172\1\216\1\240\1\172"+
-    "\1\240\3\172\1\217\3\172\1\265\47\172\2\173\1\220"+
-    "\1\241\1\173\1\241\4\173\1\217\2\173\1\266\47\173"+
-    "\1\0\1\267\4\0\1\267\4\0\1\146\2\0\1\270"+
-    "\30\0\1\267\1\0\3\267\1\0\1\267\25\0\1\270"+
-    "\47\0\1\271\4\0\1\271\40\0\1\271\1\0\3\271"+
-    "\1\0\1\271\10\0\1\272\1\57\1\56\1\0\1\56"+
-    "\1\272\2\56\2\0\1\56\4\0\3\56\3\0\5\56"+
-    "\14\0\1\272\1\56\3\272\1\56\1\272\1\0\5\56"+
-    "\1\0\1\273\1\274\1\275\1\276\1\273\1\276\3\274"+
-    "\1\277\1\300\1\274\1\273\1\0\2\273\1\301\2\274"+
-    "\1\0\2\273\5\274\1\302\13\273\7\274\1\273\5\274"+
-    "\1\273\1\0\1\247\1\250\1\303\1\0\1\303\3\247"+
-    "\2\0\1\247\4\0\3\247\1\0\1\251\1\0\5\247"+
-    "\14\0\7\247\1\0\5\247\2\0\1\304\1\247\1\0"+
-    "\2\247\1\304\40\247\1\304\1\247\3\304\1\247\1\304"+
-    "\7\247\1\0\1\247\1\250\1\251\1\0\1\251\2\247"+
-    "\10\0\3\247\3\0\5\247\14\0\7\247\1\0\5\247"+
-    "\2\0\1\56\1\57\3\0\3\56\2\0\1\56\4\0"+
-    "\3\56\3\0\5\56\14\0\1\305\6\56\1\0\5\56"+
-    "\2\0\1\56\1\57\3\0\3\56\2\0\1\56\4\0"+
-    "\3\56\3\0\4\56\1\306\14\0\7\56\1\0\5\56"+
-    "\50\0\1\307\16\0\1\310\1\74\1\73\1\0\1\73"+
-    "\1\310\2\73\2\0\1\73\4\0\3\73\1\0\1\34"+
-    "\1\0\5\73\1\35\13\0\1\310\1\73\3\310\1\73"+
-    "\1\310\1\0\5\73\1\0\2\256\1\257\6\256\2\0"+
-    "\2\256\1\262\5\256\1\0\43\256\1\257\12\256\1\311"+
-    "\47\256\2\260\1\312\6\260\1\313\53\260\2\261\1\314"+
-    "\7\261\1\313\52\261\1\0\1\45\1\46\1\45\1\0"+
-    "\4\45\2\0\1\45\4\0\3\45\3\0\5\45\13\0"+
-    "\1\101\7\45\1\0\5\45\2\0\1\53\1\52\1\53"+
-    "\1\0\4\53\2\0\1\53\4\0\3\53\3\0\5\53"+
-    "\14\0\7\53\1\0\5\53\14\0\1\146\52\0\1\315"+
-    "\4\0\1\315\40\0\1\315\1\0\3\315\1\0\1\315"+
-    "\10\0\1\56\1\57\1\56\1\0\4\56\2\0\1\56"+
-    "\4\0\3\56\3\0\5\56\14\0\7\56\1\0\5\56"+
-    "\1\0\2\273\1\316\6\273\2\0\2\273\1\0\5\273"+
-    "\1\0\7\273\1\302\32\273\1\274\1\275\3\273\3\274"+
-    "\2\0\1\274\1\273\1\0\2\273\3\274\1\0\2\273"+
-    "\5\274\1\302\13\273\7\274\1\273\5\274\2\273\1\317"+
-    "\1\320\1\273\2\274\1\317\24\274\1\321\13\274\1\317"+
-    "\1\274\3\317\1\274\1\317\7\274\2\273\1\316\1\276"+
-    "\1\273\1\276\3\273\1\277\1\300\2\273\1\0\2\273"+
-    "\1\322\2\273\1\0\7\273\1\302\31\273\2\277\1\323"+
-    "\6\277\1\324\53\277\2\300\1\325\7\300\1\324\52\300"+
-    "\1\273\1\274\1\275\3\273\3\274\2\0\1\274\1\273"+
-    "\1\0\2\273\1\274\1\326\1\274\1\0\2\273\5\274"+
-    "\1\302\13\273\7\274\1\273\5\274\1\273\3\0\1\303"+
-    "\1\0\1\303\16\0\1\251\41\0\1\327\1\250\1\247"+
-    "\1\0\1\247\1\327\2\247\2\0\1\247\4\0\3\247"+
-    "\1\0\1\251\1\0\5\247\14\0\1\327\1\247\3\327"+
-    "\1\247\1\327\1\0\5\247\2\0\1\56\1\57\3\0"+
-    "\3\56\2\0\1\56\4\0\3\56\3\0\5\56\14\0"+
-    "\6\56\1\330\1\0\5\56\2\0\1\56\1\57\1\331"+
-    "\1\0\1\331\3\56\1\332\1\333\1\56\4\0\3\56"+
-    "\3\0\5\56\14\0\7\56\1\0\5\56\55\0\1\334"+
-    "\11\0\1\73\1\74\1\73\1\0\4\73\2\0\1\73"+
-    "\4\0\3\73\1\0\1\34\1\0\5\73\1\35\13\0"+
-    "\7\73\1\0\5\73\1\0\2\260\1\312\6\260\1\335"+
-    "\53\260\3\0\1\313\1\0\1\313\7\0\1\262\47\0"+
-    "\2\261\1\314\7\261\1\336\52\261\1\0\1\337\4\0"+
-    "\1\337\40\0\1\337\1\0\3\337\1\0\1\337\7\0"+
-    "\2\273\1\316\30\273\1\302\32\273\1\340\1\275\1\274"+
-    "\1\273\1\274\1\340\2\274\2\0\1\274\1\273\1\0"+
-    "\2\273\3\274\1\0\2\273\5\274\1\302\13\273\1\340"+
-    "\1\274\3\340\1\274\1\340\1\273\5\274\2\273\1\274"+
-    "\1\275\3\273\3\274\2\273\1\274\4\273\3\274\3\273"+
-    "\5\274\1\302\13\273\7\274\1\273\5\274\3\273\1\316"+
-    "\6\273\2\0\2\273\1\0\3\273\1\341\1\273\1\0"+
-    "\7\273\1\302\31\273\2\277\1\323\6\277\1\342\53\277"+
-    "\1\0\1\343\1\344\1\345\1\0\1\345\2\343\10\0"+
-    "\3\343\3\0\5\343\1\346\13\0\7\343\1\0\5\343"+
-    "\1\0\2\300\1\325\7\300\1\347\52\300\1\273\1\274"+
-    "\1\275\3\273\3\274\2\0\1\274\1\273\1\0\2\273"+
-    "\2\274\1\350\1\0\2\273\5\274\1\302\13\273\7\274"+
-    "\1\273\5\274\1\273\1\0\1\351\1\250\1\247\1\0"+
-    "\1\247\1\351\2\247\2\0\1\247\4\0\3\247\1\0"+
-    "\1\251\1\0\5\247\14\0\1\351\1\247\3\351\1\247"+
-    "\1\351\1\0\5\247\2\0\1\56\1\57\3\0\3\56"+
-    "\2\0\1\56\4\0\3\56\3\0\5\56\14\0\2\56"+
-    "\1\352\4\56\1\0\5\56\4\0\1\331\1\0\1\331"+
-    "\3\0\1\332\1\333\52\0\2\332\1\353\6\332\1\354"+
-    "\53\332\2\333\1\355\7\333\1\354\52\333\32\0\1\356"+
-    "\32\0\2\260\1\312\1\335\1\260\1\335\3\260\1\313"+
-    "\3\260\1\357\47\260\2\261\1\314\1\336\1\261\1\336"+
-    "\4\261\1\313\2\261\1\360\47\261\1\273\1\361\1\275"+
-    "\1\274\1\273\1\274\1\361\2\274\2\0\1\274\1\273"+
-    "\1\0\2\273\3\274\1\0\2\273\5\274\1\302\13\273"+
-    "\1\361\1\274\3\361\1\274\1\361\1\273\5\274\3\273"+
-    "\1\316\6\273\2\0\2\273\1\0\4\273\1\362\1\0"+
-    "\7\273\1\302\31\273\1\277\1\363\1\364\1\365\1\277"+
-    "\1\365\2\363\1\277\1\324\6\277\3\363\3\277\5\363"+
-    "\1\366\13\277\7\363\1\277\5\363\1\277\1\0\1\343"+
-    "\1\344\1\367\1\0\1\367\3\343\2\0\1\343\4\0"+
-    "\3\343\1\0\1\345\1\0\5\343\1\346\13\0\7\343"+
-    "\1\0\5\343\2\0\1\370\1\343\1\0\2\343\1\370"+
-    "\40\343\1\370\1\343\3\370\1\343\1\370\7\343\1\0"+
-    "\1\343\1\344\1\345\1\0\1\345\2\343\10\0\3\343"+
-    "\3\0\5\343\14\0\7\343\1\0\5\343\1\0\1\300"+
-    "\1\371\1\372\1\373\1\300\1\373\2\371\2\300\1\324"+
-    "\5\300\3\371\3\300\5\371\1\374\13\300\7\371\1\300"+
-    "\5\371\1\300\1\273\1\274\1\275\3\273\3\274\2\0"+
-    "\1\274\1\273\1\0\2\273\3\274\1\375\2\273\5\274"+
-    "\1\302\13\273\7\274\1\273\5\274\1\273\1\0\1\376"+
-    "\1\250\1\247\1\0\1\247\1\376\2\247\2\0\1\247"+
-    "\4\0\3\247\1\0\1\251\1\0\5\247\14\0\1\376"+
-    "\1\247\3\376\1\247\1\376\1\0\5\247\1\0\2\332"+
-    "\1\353\6\332\1\377\53\332\3\0\1\354\1\0\1\354"+
-    "\25\0\1\u0100\31\0\2\333\1\355\7\333\1\u0101\52\333"+
-    "\1\273\1\u0102\1\275\1\274\1\273\1\274\1\u0102\2\274"+
-    "\2\0\1\274\1\273\1\0\2\273\3\274\1\0\2\273"+
-    "\5\274\1\302\13\273\1\u0102\1\274\3\u0102\1\274\1\u0102"+
-    "\1\273\5\274\3\273\1\316\6\273\2\0\2\273\1\0"+
-    "\5\273\1\375\7\273\1\302\31\273\1\277\1\363\1\364"+
-    "\1\u0103\1\277\1\u0103\3\363\1\324\1\277\1\363\4\277"+
-    "\3\363\1\277\1\365\1\277\5\363\1\366\13\277\7\363"+
-    "\1\277\5\363\2\277\1\u0104\1\u0105\1\277\2\363\1\u0104"+
-    "\2\363\1\u0106\35\363\1\u0104\1\363\3\u0104\1\363\1\u0104"+
-    "\7\363\1\277\1\363\1\364\1\365\1\277\1\365\2\363"+
-    "\1\277\1\324\6\277\3\363\3\277\5\363\14\277\7\363"+
-    "\1\277\5\363\1\277\3\0\1\367\1\0\1\367\16\0"+
-    "\1\345\6\0\1\346\32\0\1\u0107\1\344\1\343\1\0"+
-    "\1\343\1\u0107\2\343\2\0\1\343\4\0\3\343\1\0"+
-    "\1\345\1\0\5\343\1\346\13\0\1\u0107\1\343\3\u0107"+
-    "\1\343\1\u0107\1\0\5\343\1\0\1\300\1\371\1\372"+
-    "\1\u0108\1\300\1\u0108\3\371\1\300\1\324\1\371\4\300"+
-    "\3\371\1\300\1\373\1\300\5\371\1\374\13\300\7\371"+
-    "\1\300\5\371\2\300\1\u0109\1\u010a\1\300\2\371\1\u0109"+
-    "\3\371\1\u010b\34\371\1\u0109\1\371\3\u0109\1\371\1\u0109"+
-    "\7\371\1\300\1\371\1\372\1\373\1\300\1\373\2\371"+
-    "\2\300\1\324\5\300\3\371\3\300\5\371\14\300\7\371"+
-    "\1\300\5\371\1\300\2\u010c\1\u010d\1\375\1\u010c\1\375"+
-    "\3\u010c\1\u010e\1\u010f\2\u010c\1\324\5\u010c\1\0\41\u010c"+
-    "\1\0\1\u0110\1\250\1\247\1\0\1\247\1\u0110\2\247"+
-    "\2\0\1\247\4\0\3\247\1\0\1\251\1\0\5\247"+
-    "\14\0\1\u0110\1\247\3\u0110\1\247\1\u0110\1\0\5\247"+
-    "\1\0\2\332\1\353\1\377\1\332\1\377\3\332\1\354"+
-    "\21\332\1\u0111\31\332\2\333\1\355\1\u0101\1\333\1\u0101"+
-    "\4\333\1\354\20\333\1\u0112\31\333\1\273\1\u0113\1\275"+
-    "\1\274\1\273\1\274\1\u0113\2\274\2\0\1\274\1\273"+
-    "\1\0\2\273\3\274\1\0\2\273\5\274\1\302\13\273"+
-    "\1\u0113\1\274\3\u0113\1\274\1\u0113\1\273\5\274\1\273"+
-    "\2\277\1\323\1\u0103\1\277\1\u0103\3\277\1\324\12\277"+
-    "\1\365\6\277\1\366\32\277\1\u0114\1\364\1\363\1\277"+
-    "\1\363\1\u0114\2\363\1\324\1\277\1\363\4\277\3\363"+
-    "\1\277\1\365\1\277\5\363\1\366\13\277\1\u0114\1\363"+
-    "\3\u0114\1\363\1\u0114\1\277\5\363\2\277\1\363\1\364"+
-    "\1\u0103\1\277\1\u0103\3\363\1\342\1\277\1\363\4\277"+
-    "\3\363\1\277\1\365\1\277\5\363\1\366\13\277\7\363"+
-    "\1\277\5\363\2\277\1\363\1\364\1\u0115\1\277\1\u0115"+
-    "\3\363\1\324\1\277\1\363\4\277\3\363\1\277\1\365"+
-    "\1\277\5\363\1\366\13\277\7\363\1\277\5\363\1\277"+
-    "\1\0\1\u0116\1\344\1\343\1\0\1\343\1\u0116\2\343"+
-    "\2\0\1\343\4\0\3\343\1\0\1\345\1\0\5\343"+
-    "\1\346\13\0\1\u0116\1\343\3\u0116\1\343\1\u0116\1\0"+
-    "\5\343\1\0\2\300\1\325\1\u0108\1\300\1\u0108\4\300"+
-    "\1\324\11\300\1\373\6\300\1\374\32\300\1\u0117\1\372"+
-    "\1\371\1\300\1\371\1\u0117\2\371\1\300\1\324\1\371"+
-    "\4\300\3\371\1\300\1\373\1\300\5\371\1\374\13\300"+
-    "\1\u0117\1\371\3\u0117\1\371\1\u0117\1\300\5\371\2\300"+
-    "\1\371\1\372\1\u0108\1\300\1\u0108\3\371\1\300\1\347"+
-    "\1\371\4\300\3\371\1\300\1\373\1\300\5\371\1\374"+
-    "\13\300\7\371\1\300\5\371\2\300\1\371\1\372\1\u0118"+
-    "\1\300\1\u0118\3\371\1\300\1\324\1\371\4\300\3\371"+
-    "\1\300\1\373\1\300\5\371\1\374\13\300\7\371\1\300"+
-    "\5\371\1\300\2\u010c\1\u010d\6\u010c\2\0\2\u010c\1\324"+
-    "\5\u010c\1\0\43\u010c\1\u010d\12\u010c\1\u0119\47\u010c\2\u010e"+
-    "\1\u011a\6\u010e\1\u011b\53\u010e\2\u010f\1\u011c\7\u010f\1\u011b"+
-    "\52\u010f\1\0\1\u011d\1\250\1\247\1\0\1\247\1\u011d"+
-    "\2\247\2\0\1\247\4\0\3\247\1\0\1\251\1\0"+
-    "\5\247\14\0\1\u011d\1\247\3\u011d\1\247\1\u011d\1\0"+
-    "\5\247\1\0\1\273\1\u011e\1\275\1\274\1\273\1\274"+
-    "\1\u011e\2\274\2\0\1\274\1\273\1\0\2\273\3\274"+
-    "\1\0\2\273\5\274\1\302\13\273\1\u011e\1\274\3\u011e"+
-    "\1\274\1\u011e\1\273\5\274\1\273\1\277\1\u011f\1\364"+
-    "\1\363\1\277\1\363\1\u011f\2\363\1\324\1\277\1\363"+
-    "\4\277\3\363\1\277\1\365\1\277\5\363\1\366\13\277"+
-    "\1\u011f\1\363\3\u011f\1\363\1\u011f\1\277\5\363\2\277"+
-    "\1\363\1\364\1\u0115\1\277\1\u0115\2\363\1\277\1\324"+
-    "\6\277\3\363\1\277\1\365\1\277\5\363\1\366\13\277"+
-    "\7\363\1\277\5\363\1\277\1\0\1\u0120\1\344\1\343"+
-    "\1\0\1\343\1\u0120\2\343\2\0\1\343\4\0\3\343"+
-    "\1\0\1\345\1\0\5\343\1\346\13\0\1\u0120\1\343"+
-    "\3\u0120\1\343\1\u0120\1\0\5\343\1\0\1\300\1\u0121"+
-    "\1\372\1\371\1\300\1\371\1\u0121\2\371\1\300\1\324"+
-    "\1\371\4\300\3\371\1\300\1\373\1\300\5\371\1\374"+
-    "\13\300\1\u0121\1\371\3\u0121\1\371\1\u0121\1\300\5\371"+
-    "\2\300\1\371\1\372\1\u0118\1\300\1\u0118\2\371\2\300"+
-    "\1\324\5\300\3\371\1\300\1\373\1\300\5\371\1\374"+
-    "\13\300\7\371\1\300\5\371\1\300\1\u010c\1\u0122\1\u0123"+
-    "\1\u0124\1\u010c\1\u0124\2\u0122\1\u010c\2\0\2\u010c\1\324"+
-    "\2\u010c\3\u0122\1\0\2\u010c\5\u0122\1\u0125\13\u010c\7\u0122"+
-    "\1\u010c\5\u0122\1\u010c\2\u010e\1\u011a\6\u010e\1\u0126\53\u010e"+
-    "\3\0\1\u011b\1\0\1\u011b\7\0\1\324\47\0\2\u010f"+
-    "\1\u011c\7\u010f\1\u0127\52\u010f\1\0\1\247\1\250\1\247"+
-    "\1\0\4\247\2\0\1\247\4\0\3\247\1\0\1\251"+
-    "\1\0\5\247\14\0\7\247\1\0\5\247\1\0\1\273"+
-    "\1\274\1\275\1\274\1\273\4\274\2\0\1\274\1\273"+
-    "\1\0\2\273\3\274\1\0\2\273\5\274\1\302\13\273"+
-    "\7\274\1\273\5\274\1\273\1\277\1\u0128\1\364\1\363"+
-    "\1\277\1\363\1\u0128\2\363\1\324\1\277\1\363\4\277"+
-    "\3\363\1\277\1\365\1\277\5\363\1\366\13\277\1\u0128"+
-    "\1\363\3\u0128\1\363\1\u0128\1\277\5\363\1\277\1\0"+
-    "\1\u0129\1\344\1\343\1\0\1\343\1\u0129\2\343\2\0"+
-    "\1\343\4\0\3\343\1\0\1\345\1\0\5\343\1\346"+
-    "\13\0\1\u0129\1\343\3\u0129\1\343\1\u0129\1\0\5\343"+
-    "\1\0\1\300\1\u012a\1\372\1\371\1\300\1\371\1\u012a"+
-    "\2\371\1\300\1\324\1\371\4\300\3\371\1\300\1\373"+
-    "\1\300\5\371\1\374\13\300\1\u012a\1\371\3\u012a\1\371"+
-    "\1\u012a\1\300\5\371\1\300\1\u010c\1\u0122\1\u0123\1\u012b"+
-    "\1\u010c\1\u012b\3\u0122\2\0\1\u0122\1\u010c\1\324\2\u010c"+
-    "\3\u0122\1\0\1\u0124\1\u010c\5\u0122\1\u0125\13\u010c\7\u0122"+
-    "\1\u010c\5\u0122\2\u010c\1\u012c\1\u012d\1\u010c\2\u0122\1\u012c"+
-    "\6\u0122\1\u012e\31\u0122\1\u012c\1\u0122\3\u012c\1\u0122\1\u012c"+
-    "\7\u0122\1\u010c\1\u0122\1\u0123\1\u0124\1\u010c\1\u0124\2\u0122"+
-    "\1\u010c\2\0\2\u010c\1\324\2\u010c\3\u0122\1\0\2\u010c"+
-    "\5\u0122\14\u010c\7\u0122\1\u010c\5\u0122\1\u010c\2\u010e\1\u011a"+
-    "\1\u0126\1\u010e\1\u0126\3\u010e\1\u011b\3\u010e\1\u012f\47\u010e"+
-    "\2\u010f\1\u011c\1\u0127\1\u010f\1\u0127\4\u010f\1\u011b\2\u010f"+
-    "\1\u0130\47\u010f\1\277\1\u0131\1\364\1\363\1\277\1\363"+
-    "\1\u0131\2\363\1\324\1\277\1\363\4\277\3\363\1\277"+
-    "\1\365\1\277\5\363\1\366\13\277\1\u0131\1\363\3\u0131"+
-    "\1\363\1\u0131\1\277\5\363\1\277\1\0\1\u0132\1\344"+
-    "\1\343\1\0\1\343\1\u0132\2\343\2\0\1\343\4\0"+
-    "\3\343\1\0\1\345\1\0\5\343\1\346\13\0\1\u0132"+
-    "\1\343\3\u0132\1\343\1\u0132\1\0\5\343\1\0\1\300"+
-    "\1\u0133\1\372\1\371\1\300\1\371\1\u0133\2\371\1\300"+
-    "\1\324\1\371\4\300\3\371\1\300\1\373\1\300\5\371"+
-    "\1\374\13\300\1\u0133\1\371\3\u0133\1\371\1\u0133\1\300"+
-    "\5\371\1\300\2\u010c\1\u010d\1\u012b\1\u010c\1\u012b\3\u010c"+
-    "\2\0\2\u010c\1\324\5\u010c\1\0\1\u0124\6\u010c\1\u0125"+
-    "\32\u010c\1\u0134\1\u0123\1\u0122\1\u010c\1\u0122\1\u0134\2\u0122"+
-    "\2\0\1\u0122\1\u010c\1\324\2\u010c\3\u0122\1\0\1\u0124"+
-    "\1\u010c\5\u0122\1\u0125\13\u010c\1\u0134\1\u0122\3\u0134\1\u0122"+
-    "\1\u0134\1\u010c\5\u0122\2\u010c\1\u0122\1\u0123\1\u012b\1\u010c"+
-    "\1\u012b\3\u0122\2\u010c\1\u0122\1\u010c\1\u0119\2\u010c\3\u0122"+
-    "\1\u010c\1\u0124\1\u010c\5\u0122\1\u0125\13\u010c\7\u0122\1\u010c"+
-    "\5\u0122\2\u010c\1\u0122\1\u0123\1\u0135\1\u010c\1\u0135\3\u0122"+
-    "\2\0\1\u0122\1\u010c\1\324\2\u010c\3\u0122\1\0\1\u0124"+
-    "\1\u010c\5\u0122\1\u0125\13\u010c\7\u0122\1\u010c\5\u0122\1\u010c"+
-    "\1\u010e\1\u0136\1\u0137\1\u0138\1\u010e\1\u0138\2\u0136\1\u010e"+
-    "\1\u011b\6\u010e\3\u0136\3\u010e\5\u0136\1\u0139\13\u010e\7\u0136"+
-    "\1\u010e\5\u0136\1\u010e\1\u010f\1\u013a\1\u013b\1\u013c\1\u010f"+
-    "\1\u013c\2\u013a\2\u010f\1\u011b\5\u010f\3\u013a\3\u010f\5\u013a"+
-    "\1\u013d\13\u010f\7\u013a\1\u010f\5\u013a\1\u010f\1\277\1\u013e"+
-    "\1\364\1\363\1\277\1\363\1\u013e\2\363\1\324\1\277"+
-    "\1\363\4\277\3\363\1\277\1\365\1\277\5\363\1\366"+
-    "\13\277\1\u013e\1\363\3\u013e\1\363\1\u013e\1\277\5\363"+
-    "\1\277\1\0\1\343\1\344\1\343\1\0\4\343\2\0"+
-    "\1\343\4\0\3\343\1\0\1\345\1\0\5\343\1\346"+
-    "\13\0\7\343\1\0\5\343\1\0\1\300\1\u013f\1\372"+
-    "\1\371\1\300\1\371\1\u013f\2\371\1\300\1\324\1\371"+
-    "\4\300\3\371\1\300\1\373\1\300\5\371\1\374\13\300"+
-    "\1\u013f\1\371\3\u013f\1\371\1\u013f\1\300\5\371\1\300"+
-    "\1\u010c\1\u0140\1\u0123\1\u0122\1\u010c\1\u0122\1\u0140\2\u0122"+
-    "\2\0\1\u0122\1\u010c\1\324\2\u010c\3\u0122\1\0\1\u0124"+
-    "\1\u010c\5\u0122\1\u0125\13\u010c\1\u0140\1\u0122\3\u0140\1\u0122"+
-    "\1\u0140\1\u010c\5\u0122\2\u010c\1\u0122\1\u0123\1\u0135\1\u010c"+
-    "\1\u0135\2\u0122\1\u010c\2\0\2\u010c\1\324\2\u010c\3\u0122"+
-    "\1\0\1\u0124\1\u010c\5\u0122\1\u0125\13\u010c\7\u0122\1\u010c"+
-    "\5\u0122\1\u010c\1\u010e\1\u0136\1\u0137\1\u0141\1\u010e\1\u0141"+
-    "\3\u0136\1\u011b\1\u010e\1\u0136\4\u010e\3\u0136\1\u010e\1\u0138"+
-    "\1\u010e\5\u0136\1\u0139\13\u010e\7\u0136\1\u010e\5\u0136\2\u010e"+
-    "\1\u0142\1\u0143\1\u010e\2\u0136\1\u0142\2\u0136\1\u0144\35\u0136"+
-    "\1\u0142\1\u0136\3\u0142\1\u0136\1\u0142\7\u0136\1\u010e\1\u0136"+
-    "\1\u0137\1\u0138\1\u010e\1\u0138\2\u0136\1\u010e\1\u011b\6\u010e"+
-    "\3\u0136\3\u010e\5\u0136\14\u010e\7\u0136\1\u010e\5\u0136\1\u010e"+
-    "\1\u010f\1\u013a\1\u013b\1\u0145\1\u010f\1\u0145\3\u013a\1\u010f"+
-    "\1\u011b\1\u013a\4\u010f\3\u013a\1\u010f\1\u013c\1\u010f\5\u013a"+
-    "\1\u013d\13\u010f\7\u013a\1\u010f\5\u013a\2\u010f\1\u0146\1\u0147"+
-    "\1\u010f\2\u013a\1\u0146\3\u013a\1\u0148\34\u013a\1\u0146\1\u013a"+
-    "\3\u0146\1\u013a\1\u0146\7\u013a\1\u010f\1\u013a\1\u013b\1\u013c"+
-    "\1\u010f\1\u013c\2\u013a\2\u010f\1\u011b\5\u010f\3\u013a\3\u010f"+
-    "\5\u013a\14\u010f\7\u013a\1\u010f\5\u013a\1\u010f\1\277\1\363"+
-    "\1\364\1\363\1\277\4\363\1\324\1\277\1\363\4\277"+
-    "\3\363\1\277\1\365\1\277\5\363\1\366\13\277\7\363"+
-    "\1\277\5\363\1\277\1\300\1\371\1\372\1\371\1\300"+
-    "\4\371\1\300\1\324\1\371\4\300\3\371\1\300\1\373"+
-    "\1\300\5\371\1\374\13\300\7\371\1\300\5\371\1\300"+
-    "\1\u010c\1\u0149\1\u0123\1\u0122\1\u010c\1\u0122\1\u0149\2\u0122"+
-    "\2\0\1\u0122\1\u010c\1\324\2\u010c\3\u0122\1\0\1\u0124"+
-    "\1\u010c\5\u0122\1\u0125\13\u010c\1\u0149\1\u0122\3\u0149\1\u0122"+
-    "\1\u0149\1\u010c\5\u0122\1\u010c\2\u010e\1\u011a\1\u0141\1\u010e"+
-    "\1\u0141\3\u010e\1\u011b\12\u010e\1\u0138\6\u010e\1\u0139\32\u010e"+
-    "\1\u014a\1\u0137\1\u0136\1\u010e\1\u0136\1\u014a\2\u0136\1\u011b"+
-    "\1\u010e\1\u0136\4\u010e\3\u0136\1\u010e\1\u0138\1\u010e\5\u0136"+
-    "\1\u0139\13\u010e\1\u014a\1\u0136\3\u014a\1\u0136\1\u014a\1\u010e"+
-    "\5\u0136\2\u010e\1\u0136\1\u0137\1\u0141\1\u010e\1\u0141\3\u0136"+
-    "\1\u0126\1\u010e\1\u0136\4\u010e\3\u0136\1\u010e\1\u0138\1\u010e"+
-    "\5\u0136\1\u0139\13\u010e\7\u0136\1\u010e\5\u0136\2\u010e\1\u0136"+
-    "\1\u0137\1\u014b\1\u010e\1\u014b\3\u0136\1\u011b\1\u010e\1\u0136"+
-    "\1\u010e\1\u012f\2\u010e\3\u0136\1\u010e\1\u0138\1\u010e\5\u0136"+
-    "\1\u0139\13\u010e\7\u0136\1\u010e\5\u0136\1\u010e\2\u010f\1\u011c"+
-    "\1\u0145\1\u010f\1\u0145\4\u010f\1\u011b\11\u010f\1\u013c\6\u010f"+
-    "\1\u013d\32\u010f\1\u014c\1\u013b\1\u013a\1\u010f\1\u013a\1\u014c"+
-    "\2\u013a\1\u010f\1\u011b\1\u013a\4\u010f\3\u013a\1\u010f\1\u013c"+
-    "\1\u010f\5\u013a\1\u013d\13\u010f\1\u014c\1\u013a\3\u014c\1\u013a"+
-    "\1\u014c\1\u010f\5\u013a\2\u010f\1\u013a\1\u013b\1\u0145\1\u010f"+
-    "\1\u0145\3\u013a\1\u010f\1\u0127\1\u013a\4\u010f\3\u013a\1\u010f"+
-    "\1\u013c\1\u010f\5\u013a\1\u013d\13\u010f\7\u013a\1\u010f\5\u013a"+
-    "\2\u010f\1\u013a\1\u013b\1\u014d\1\u010f\1\u014d\3\u013a\1\u010f"+
-    "\1\u011b\1\u013a\1\u010f\1\u0130\2\u010f\3\u013a\1\u010f\1\u013c"+
-    "\1\u010f\5\u013a\1\u013d\13\u010f\7\u013a\1\u010f\5\u013a\1\u010f"+
-    "\1\u010c\1\u014e\1\u0123\1\u0122\1\u010c\1\u0122\1\u014e\2\u0122"+
-    "\2\0\1\u0122\1\u010c\1\324\2\u010c\3\u0122\1\0\1\u0124"+
-    "\1\u010c\5\u0122\1\u0125\13\u010c\1\u014e\1\u0122\3\u014e\1\u0122"+
-    "\1\u014e\1\u010c\5\u0122\1\u010c\1\u010e\1\u014f\1\u0137\1\u0136"+
-    "\1\u010e\1\u0136\1\u014f\2\u0136\1\u011b\1\u010e\1\u0136\4\u010e"+
-    "\3\u0136\1\u010e\1\u0138\1\u010e\5\u0136\1\u0139\13\u010e\1\u014f"+
-    "\1\u0136\3\u014f\1\u0136\1\u014f\1\u010e\5\u0136\3\u010e\1\u011a"+
-    "\1\u014b\1\u010e\1\u014b\3\u010e\1\u011b\3\u010e\1\u012f\6\u010e"+
-    "\1\u0138\6\u010e\1\u0139\31\u010e\1\u010f\1\u0150\1\u013b\1\u013a"+
-    "\1\u010f\1\u013a\1\u0150\2\u013a\1\u010f\1\u011b\1\u013a\4\u010f"+
-    "\3\u013a\1\u010f\1\u013c\1\u010f\5\u013a\1\u013d\13\u010f\1\u0150"+
-    "\1\u013a\3\u0150\1\u013a\1\u0150\1\u010f\5\u013a\3\u010f\1\u011c"+
-    "\1\u014d\1\u010f\1\u014d\4\u010f\1\u011b\2\u010f\1\u0130\6\u010f"+
-    "\1\u013c\6\u010f\1\u013d\31\u010f\1\u010c\1\u0151\1\u0123\1\u0122"+
-    "\1\u010c\1\u0122\1\u0151\2\u0122\2\0\1\u0122\1\u010c\1\324"+
-    "\2\u010c\3\u0122\1\0\1\u0124\1\u010c\5\u0122\1\u0125\13\u010c"+
-    "\1\u0151\1\u0122\3\u0151\1\u0122\1\u0151\1\u010c\5\u0122\1\u010c"+
-    "\1\u010e\1\u0152\1\u0137\1\u0136\1\u010e\1\u0136\1\u0152\2\u0136"+
-    "\1\u011b\1\u010e\1\u0136\4\u010e\3\u0136\1\u010e\1\u0138\1\u010e"+
-    "\5\u0136\1\u0139\13\u010e\1\u0152\1\u0136\3\u0152\1\u0136\1\u0152"+
-    "\1\u010e\5\u0136\1\u010e\1\u010f\1\u0153\1\u013b\1\u013a\1\u010f"+
-    "\1\u013a\1\u0153\2\u013a\1\u010f\1\u011b\1\u013a\4\u010f\3\u013a"+
-    "\1\u010f\1\u013c\1\u010f\5\u013a\1\u013d\13\u010f\1\u0153\1\u013a"+
-    "\3\u0153\1\u013a\1\u0153\1\u010f\5\u013a\1\u010f\1\u010c\1\u0122"+
-    "\1\u0123\1\u0122\1\u010c\4\u0122\2\0\1\u0122\1\u010c\1\324"+
-    "\2\u010c\3\u0122\1\0\1\u0124\1\u010c\5\u0122\1\u0125\13\u010c"+
-    "\7\u0122\1\u010c\5\u0122\1\u010c\1\u010e\1\u0154\1\u0137\1\u0136"+
-    "\1\u010e\1\u0136\1\u0154\2\u0136\1\u011b\1\u010e\1\u0136\4\u010e"+
-    "\3\u0136\1\u010e\1\u0138\1\u010e\5\u0136\1\u0139\13\u010e\1\u0154"+
-    "\1\u0136\3\u0154\1\u0136\1\u0154\1\u010e\5\u0136\1\u010e\1\u010f"+
-    "\1\u0155\1\u013b\1\u013a\1\u010f\1\u013a\1\u0155\2\u013a\1\u010f"+
-    "\1\u011b\1\u013a\4\u010f\3\u013a\1\u010f\1\u013c\1\u010f\5\u013a"+
-    "\1\u013d\13\u010f\1\u0155\1\u013a\3\u0155\1\u013a\1\u0155\1\u010f"+
-    "\5\u013a\1\u010f\1\u010e\1\u0156\1\u0137\1\u0136\1\u010e\1\u0136"+
-    "\1\u0156\2\u0136\1\u011b\1\u010e\1\u0136\4\u010e\3\u0136\1\u010e"+
-    "\1\u0138\1\u010e\5\u0136\1\u0139\13\u010e\1\u0156\1\u0136\3\u0156"+
-    "\1\u0136\1\u0156\1\u010e\5\u0136\1\u010e\1\u010f\1\u0157\1\u013b"+
-    "\1\u013a\1\u010f\1\u013a\1\u0157\2\u013a\1\u010f\1\u011b\1\u013a"+
-    "\4\u010f\3\u013a\1\u010f\1\u013c\1\u010f\5\u013a\1\u013d\13\u010f"+
-    "\1\u0157\1\u013a\3\u0157\1\u013a\1\u0157\1\u010f\5\u013a\1\u010f"+
-    "\1\u010e\1\u0136\1\u0137\1\u0136\1\u010e\4\u0136\1\u011b\1\u010e"+
-    "\1\u0136\4\u010e\3\u0136\1\u010e\1\u0138\1\u010e\5\u0136\1\u0139"+
-    "\13\u010e\7\u0136\1\u010e\5\u0136\1\u010e\1\u010f\1\u013a\1\u013b"+
-    "\1\u013a\1\u010f\4\u013a\1\u010f\1\u011b\1\u013a\4\u010f\3\u013a"+
-    "\1\u010f\1\u013c\1\u010f\5\u013a\1\u013d\13\u010f\7\u013a\1\u010f"+
-    "\5\u013a\1\u010f";
+    "\1\35\12\0\1\151\1\6\3\151\1\6\1\151\1\0"+
+    "\5\6\5\0\1\101\1\0\1\101\56\0\1\115\1\0"+
+    "\1\47\1\46\1\101\1\0\1\101\3\47\2\0\1\47"+
+    "\4\0\3\47\3\0\5\47\13\0\1\152\6\47\1\0"+
+    "\5\47\1\0\1\115\1\0\1\47\1\46\1\101\1\0"+
+    "\1\101\3\47\2\0\1\47\4\0\3\47\3\0\5\47"+
+    "\13\0\5\47\1\112\1\47\1\0\5\47\1\0\1\115"+
+    "\1\0\1\47\1\46\1\101\1\0\1\101\3\47\2\0"+
+    "\1\47\4\0\3\47\3\0\1\47\1\112\3\47\13\0"+
+    "\7\47\1\0\1\47\1\112\3\47\1\0\1\115\1\0"+
+    "\1\47\1\46\1\101\1\0\1\101\3\47\2\0\1\47"+
+    "\4\0\3\47\3\0\5\47\13\0\6\47\1\112\1\0"+
+    "\1\112\4\47\1\0\1\115\1\0\1\47\1\46\1\101"+
+    "\1\0\1\101\3\47\2\0\1\47\4\0\1\47\1\102"+
+    "\1\47\3\0\5\47\13\0\7\47\1\0\5\47\1\0"+
+    "\1\115\1\0\1\47\1\46\1\101\1\0\1\101\3\47"+
+    "\2\0\1\47\4\0\3\47\3\0\1\47\1\112\3\47"+
+    "\13\0\7\47\1\0\1\112\4\47\1\0\1\115\1\0"+
+    "\1\47\1\46\1\101\1\0\1\101\3\47\2\0\1\47"+
+    "\4\0\3\47\3\0\5\47\13\0\2\47\1\153\4\47"+
+    "\1\0\5\47\1\0\1\115\1\0\1\47\1\46\1\101"+
+    "\1\0\1\101\3\47\2\0\1\47\4\0\3\47\3\0"+
+    "\1\47\1\112\3\47\13\0\7\47\1\0\5\47\1\0"+
+    "\1\115\1\0\1\47\1\46\1\101\1\0\1\101\3\47"+
+    "\2\0\1\47\4\0\3\47\3\0\5\47\13\0\7\47"+
+    "\1\0\3\47\1\112\1\47\1\0\1\115\1\0\1\47"+
+    "\1\46\1\101\1\0\1\101\3\47\2\0\1\47\4\0"+
+    "\3\47\3\0\5\47\13\0\7\47\1\0\2\47\1\113"+
+    "\2\47\1\0\1\115\3\0\1\115\1\0\1\115\12\0"+
+    "\1\154\45\0\1\155\1\46\1\47\1\0\1\47\1\155"+
+    "\2\47\2\0\1\47\4\0\3\47\3\0\5\47\13\0"+
+    "\1\155\1\47\3\155\1\47\1\155\1\0\5\47\1\0"+
+    "\1\115\46\0\1\156\71\0\1\36\40\0\1\36\27\0"+
+    "\1\36\61\0\1\36\1\0\1\36\27\0\1\120\72\0"+
+    "\1\36\26\0\1\36\56\0\1\157\43\0\1\36\116\0"+
+    "\1\36\63\0\1\130\5\0\1\160\1\53\3\0\1\160"+
+    "\2\54\2\0\1\54\4\0\3\54\3\0\5\54\13\0"+
+    "\1\160\1\54\3\160\1\54\1\160\1\0\5\54\3\0"+
+    "\1\161\1\53\1\54\1\0\1\54\1\161\2\54\2\0"+
+    "\1\54\4\0\3\54\3\0\5\54\13\0\1\161\1\54"+
+    "\3\161\1\54\1\161\1\0\5\54\3\0\1\6\1\30"+
+    "\1\31\1\0\1\31\3\6\2\0\1\6\4\0\3\6"+
+    "\1\162\1\34\1\0\5\6\1\35\12\0\7\6\1\0"+
+    "\5\6\3\0\1\163\4\0\1\163\4\0\1\164\2\0"+
+    "\1\165\27\0\1\163\1\0\3\163\1\0\1\163\26\0"+
+    "\1\165\47\0\1\166\1\60\1\57\1\0\1\57\1\166"+
+    "\2\57\2\0\1\57\4\0\3\57\3\0\5\57\13\0"+
+    "\1\166\1\57\3\166\1\57\1\166\1\0\5\57\3\0"+
+    "\1\57\1\60\3\0\3\57\2\0\1\57\4\0\3\57"+
+    "\3\0\2\57\1\167\2\57\13\0\7\57\1\0\5\57"+
+    "\3\0\1\57\1\60\3\0\3\57\2\0\1\57\4\0"+
+    "\3\57\3\0\5\57\13\0\3\57\1\170\3\57\1\0"+
+    "\5\57\3\0\1\57\1\60\3\0\3\57\2\0\1\57"+
+    "\4\0\3\57\3\0\5\57\13\0\1\57\1\171\5\57"+
+    "\1\0\5\57\3\0\1\57\1\60\3\0\3\57\2\0"+
+    "\1\57\4\0\3\57\3\0\5\57\13\0\5\57\1\172"+
+    "\1\57\1\0\5\57\3\0\1\57\1\60\3\0\3\57"+
+    "\2\0\1\57\4\0\3\57\3\0\5\57\13\0\1\173"+
+    "\6\57\1\0\5\57\2\0\36\66\1\174\1\145\25\66"+
+    "\13\0\1\175\101\0\1\176\35\0\1\177\1\75\1\74"+
+    "\1\0\1\74\1\177\2\74\2\0\1\74\4\0\3\74"+
+    "\1\0\1\34\1\0\5\74\1\35\12\0\1\177\1\74"+
+    "\3\177\1\74\1\177\1\0\5\74\3\0\1\200\1\30"+
+    "\1\6\1\0\1\6\1\200\2\6\2\0\1\6\4\0"+
+    "\3\6\1\33\1\34\1\0\5\6\1\35\12\0\1\200"+
+    "\1\6\3\200\1\6\1\200\1\0\5\6\3\0\1\47"+
+    "\1\46\1\101\1\0\1\101\3\47\2\0\1\47\4\0"+
+    "\3\47\3\0\5\47\13\0\3\47\1\112\3\47\1\0"+
+    "\5\47\1\0\1\115\1\0\1\47\1\46\1\101\1\0"+
+    "\1\101\3\47\2\0\1\47\4\0\3\47\3\0\5\47"+
+    "\13\0\1\47\1\112\5\47\1\0\5\47\1\0\1\115"+
+    "\21\0\1\201\44\0\1\202\1\46\1\47\1\0\1\47"+
+    "\1\202\2\47\2\0\1\47\4\0\3\47\3\0\5\47"+
+    "\13\0\1\202\1\47\3\202\1\47\1\202\1\0\5\47"+
+    "\1\0\1\115\51\0\1\36\62\0\1\36\16\0\1\203"+
+    "\1\53\3\0\1\203\2\54\2\0\1\54\4\0\3\54"+
+    "\3\0\5\54\13\0\1\203\1\54\3\203\1\54\1\203"+
+    "\1\0\5\54\3\0\1\204\1\53\1\54\1\0\1\54"+
+    "\1\204\2\54\2\0\1\54\4\0\3\54\3\0\5\54"+
+    "\13\0\1\204\1\54\3\204\1\54\1\204\1\0\5\54"+
+    "\2\0\2\205\1\206\1\207\1\205\1\207\3\205\1\210"+
+    "\1\211\2\205\1\212\5\205\1\0\41\205\1\0\1\213"+
+    "\4\0\1\213\4\0\1\164\2\0\1\214\27\0\1\213"+
+    "\1\0\3\213\1\0\1\213\11\0\1\215\4\0\1\215"+
+    "\37\0\1\215\1\0\3\215\1\0\1\215\26\0\1\214"+
+    "\47\0\1\216\1\60\1\57\1\0\1\57\1\216\2\57"+
+    "\2\0\1\57\4\0\3\57\3\0\5\57\13\0\1\216"+
+    "\1\57\3\216\1\57\1\216\1\0\5\57\3\0\1\57"+
+    "\1\60\3\0\3\57\2\0\1\57\4\0\3\57\3\0"+
+    "\3\57\1\217\1\57\13\0\7\57\1\0\5\57\3\0"+
+    "\1\57\1\60\3\0\3\57\2\0\1\57\4\0\3\57"+
+    "\3\0\1\220\4\57\13\0\7\57\1\0\5\57\3\0"+
+    "\1\57\1\60\3\0\3\57\2\0\1\57\4\0\3\57"+
+    "\3\0\5\57\13\0\2\57\1\221\4\57\1\0\5\57"+
+    "\3\0\1\57\1\60\3\0\3\57\2\0\1\57\4\0"+
+    "\3\57\3\0\4\57\1\222\13\0\7\57\1\0\5\57"+
+    "\3\0\1\57\1\60\3\0\3\57\2\0\1\57\4\0"+
+    "\1\57\1\223\1\57\3\0\5\57\13\0\7\57\1\0"+
+    "\5\57\33\0\1\224\34\0\1\225\1\75\1\74\1\0"+
+    "\1\74\1\225\2\74\2\0\1\74\4\0\3\74\1\0"+
+    "\1\34\1\0\5\74\1\35\12\0\1\225\1\74\3\225"+
+    "\1\74\1\225\1\0\5\74\3\0\1\226\1\30\1\6"+
+    "\1\0\1\6\1\226\2\6\2\0\1\6\4\0\3\6"+
+    "\1\33\1\34\1\0\5\6\1\35\12\0\1\226\1\6"+
+    "\3\226\1\6\1\226\1\0\5\6\24\0\1\227\43\0"+
+    "\1\230\1\46\1\47\1\0\1\47\1\230\2\47\2\0"+
+    "\1\47\4\0\3\47\3\0\5\47\13\0\1\230\1\47"+
+    "\3\230\1\47\1\230\1\0\5\47\1\0\1\115\1\0"+
+    "\1\231\1\53\3\0\1\231\2\54\2\0\1\54\4\0"+
+    "\3\54\3\0\5\54\13\0\1\231\1\54\3\231\1\54"+
+    "\1\231\1\0\5\54\3\0\1\232\1\53\1\54\1\0"+
+    "\1\54\1\232\2\54\2\0\1\54\4\0\3\54\3\0"+
+    "\5\54\13\0\1\232\1\54\3\232\1\54\1\232\1\0"+
+    "\5\54\2\0\2\205\1\206\6\205\2\0\2\205\1\212"+
+    "\5\205\1\0\43\205\1\206\12\205\1\233\47\205\2\210"+
+    "\1\234\6\210\1\235\53\210\2\211\1\236\7\211\1\235"+
+    "\52\211\1\0\1\237\4\0\1\237\4\0\1\164\2\0"+
+    "\1\240\27\0\1\237\1\0\3\237\1\0\1\237\26\0"+
+    "\1\240\47\0\1\241\4\0\1\241\37\0\1\241\1\0"+
+    "\3\241\1\0\1\241\11\0\1\242\1\60\1\57\1\0"+
+    "\1\57\1\242\2\57\2\0\1\57\4\0\3\57\3\0"+
+    "\5\57\13\0\1\242\1\57\3\242\1\57\1\242\1\0"+
+    "\5\57\3\0\1\57\1\60\3\0\3\57\2\0\1\57"+
+    "\4\0\1\57\1\243\1\57\3\0\5\57\13\0\7\57"+
+    "\1\0\5\57\3\0\1\57\1\60\3\0\3\57\2\0"+
+    "\1\57\4\0\3\57\3\0\5\57\13\0\1\244\6\57"+
+    "\1\0\5\57\3\0\1\57\1\60\3\0\3\57\2\0"+
+    "\1\245\4\0\3\57\3\0\5\57\13\0\7\57\1\0"+
+    "\5\57\3\0\1\57\1\60\3\0\3\57\2\0\1\57"+
+    "\4\0\3\57\3\0\5\57\13\0\7\57\1\0\1\57"+
+    "\1\246\3\57\23\0\1\247\44\0\1\250\1\75\1\74"+
+    "\1\0\1\74\1\250\2\74\2\0\1\74\4\0\3\74"+
+    "\1\0\1\34\1\0\5\74\1\35\12\0\1\250\1\74"+
+    "\3\250\1\74\1\250\1\0\5\74\3\0\1\251\1\30"+
+    "\1\6\1\0\1\6\1\251\2\6\2\0\1\6\4\0"+
+    "\3\6\1\33\1\34\1\0\5\6\1\35\12\0\1\251"+
+    "\1\6\3\251\1\6\1\251\1\0\5\6\25\0\1\252"+
+    "\42\0\1\253\1\46\1\47\1\0\1\47\1\253\2\47"+
+    "\2\0\1\47\4\0\3\47\3\0\5\47\13\0\1\253"+
+    "\1\47\3\253\1\47\1\253\1\0\5\47\1\0\1\115"+
+    "\1\0\1\254\1\53\3\0\1\254\2\54\2\0\1\54"+
+    "\4\0\3\54\3\0\5\54\13\0\1\254\1\54\3\254"+
+    "\1\54\1\254\1\0\5\54\3\0\1\255\1\53\1\54"+
+    "\1\0\1\54\1\255\2\54\2\0\1\54\4\0\3\54"+
+    "\3\0\5\54\13\0\1\255\1\54\3\255\1\54\1\255"+
+    "\1\0\5\54\2\0\2\210\1\234\6\210\1\256\53\210"+
+    "\3\0\1\235\1\0\1\235\7\0\1\212\47\0\2\211"+
+    "\1\236\7\211\1\257\52\211\1\0\1\260\4\0\1\260"+
+    "\4\0\1\164\2\0\1\261\27\0\1\260\1\0\3\260"+
+    "\1\0\1\260\26\0\1\261\47\0\1\262\4\0\1\262"+
+    "\37\0\1\262\1\0\3\262\1\0\1\262\11\0\1\263"+
+    "\1\60\1\57\1\0\1\57\1\263\2\57\2\0\1\57"+
+    "\4\0\3\57\3\0\5\57\13\0\1\263\1\57\3\263"+
+    "\1\57\1\263\1\0\5\57\3\0\1\57\1\60\3\0"+
+    "\3\57\2\0\1\57\4\0\3\57\3\0\4\57\1\264"+
+    "\13\0\7\57\1\0\5\57\3\0\1\265\1\266\1\267"+
+    "\1\0\1\267\2\265\1\57\2\0\1\57\4\0\3\265"+
+    "\3\0\5\265\13\0\7\265\1\0\5\265\3\0\1\57"+
+    "\1\60\3\0\3\57\2\0\1\57\4\0\3\57\3\0"+
+    "\5\57\13\0\4\57\1\270\2\57\1\0\5\57\3\0"+
+    "\1\57\1\60\3\0\3\57\2\0\1\57\4\0\3\57"+
+    "\3\0\5\57\13\0\2\57\1\271\4\57\1\0\5\57"+
+    "\34\0\1\272\33\0\1\273\1\75\1\74\1\0\1\74"+
+    "\1\273\2\74\2\0\1\74\4\0\3\74\1\0\1\34"+
+    "\1\0\5\74\1\35\12\0\1\273\1\74\3\273\1\74"+
+    "\1\273\1\0\5\74\3\0\1\6\1\30\1\6\1\0"+
+    "\4\6\2\0\1\6\4\0\3\6\1\33\1\34\1\0"+
+    "\5\6\1\35\12\0\7\6\1\0\5\6\2\0\2\274"+
+    "\1\275\1\252\1\274\1\252\3\274\1\276\1\277\2\274"+
+    "\1\300\5\274\1\0\41\274\1\0\1\301\1\46\1\47"+
+    "\1\0\1\47\1\301\2\47\2\0\1\47\4\0\3\47"+
+    "\3\0\5\47\13\0\1\301\1\47\3\301\1\47\1\301"+
+    "\1\0\5\47\1\0\1\115\1\0\1\302\1\53\1\54"+
+    "\1\0\1\54\1\302\2\54\2\0\1\54\4\0\3\54"+
+    "\3\0\5\54\13\0\1\302\1\54\3\302\1\54\1\302"+
+    "\1\0\5\54\2\0\2\210\1\234\1\256\1\210\1\256"+
+    "\3\210\1\235\3\210\1\303\47\210\2\211\1\236\1\257"+
+    "\1\211\1\257\4\211\1\235\2\211\1\304\47\211\1\0"+
+    "\1\305\4\0\1\305\4\0\1\164\2\0\1\306\27\0"+
+    "\1\305\1\0\3\305\1\0\1\305\26\0\1\306\47\0"+
+    "\1\307\4\0\1\307\37\0\1\307\1\0\3\307\1\0"+
+    "\1\307\11\0\1\310\1\60\1\57\1\0\1\57\1\310"+
+    "\2\57\2\0\1\57\4\0\3\57\3\0\5\57\13\0"+
+    "\1\310\1\57\3\310\1\57\1\310\1\0\5\57\2\0"+
+    "\1\311\1\312\1\313\1\314\1\311\1\314\3\312\1\315"+
+    "\1\316\1\312\1\311\1\0\2\311\1\317\2\312\1\0"+
+    "\2\311\5\312\1\320\12\311\7\312\1\311\5\312\2\311"+
+    "\1\0\1\265\1\266\1\321\1\0\1\321\3\265\2\0"+
+    "\1\265\4\0\3\265\1\0\1\267\1\0\5\265\13\0"+
+    "\7\265\1\0\5\265\3\0\1\322\1\265\1\0\2\265"+
+    "\1\322\37\265\1\322\1\265\3\322\1\265\1\322\10\265"+
+    "\1\0\1\265\1\266\1\267\1\0\1\267\2\265\10\0"+
+    "\3\265\3\0\5\265\13\0\7\265\1\0\5\265\3\0"+
+    "\1\57\1\60\3\0\3\57\2\0\1\57\4\0\3\57"+
+    "\3\0\5\57\13\0\1\323\6\57\1\0\5\57\3\0"+
+    "\1\57\1\60\3\0\3\57\2\0\1\57\4\0\3\57"+
+    "\3\0\4\57\1\324\13\0\7\57\1\0\5\57\50\0"+
+    "\1\325\17\0\1\326\1\75\1\74\1\0\1\74\1\326"+
+    "\2\74\2\0\1\74\4\0\3\74\1\0\1\34\1\0"+
+    "\5\74\1\35\12\0\1\326\1\74\3\326\1\74\1\326"+
+    "\1\0\5\74\2\0\2\274\1\275\6\274\2\0\2\274"+
+    "\1\300\5\274\1\0\43\274\1\275\12\274\1\327\47\274"+
+    "\2\276\1\330\6\276\1\331\53\276\2\277\1\332\7\277"+
+    "\1\331\52\277\1\0\1\47\1\46\1\47\1\0\4\47"+
+    "\2\0\1\47\4\0\3\47\3\0\5\47\13\0\7\47"+
+    "\1\0\5\47\1\0\1\115\1\0\1\54\1\53\1\54"+
+    "\1\0\4\54\2\0\1\54\4\0\3\54\3\0\5\54"+
+    "\13\0\7\54\1\0\5\54\15\0\1\164\52\0\1\333"+
+    "\4\0\1\333\37\0\1\333\1\0\3\333\1\0\1\333"+
+    "\11\0\1\57\1\60\1\57\1\0\4\57\2\0\1\57"+
+    "\4\0\3\57\3\0\5\57\13\0\7\57\1\0\5\57"+
+    "\2\0\2\311\1\334\6\311\2\0\2\311\1\0\5\311"+
+    "\1\0\7\311\1\320\32\311\1\312\1\313\3\311\3\312"+
+    "\2\0\1\312\1\311\1\0\2\311\3\312\1\0\2\311"+
+    "\5\312\1\320\12\311\7\312\1\311\5\312\3\311\1\335"+
+    "\1\336\1\311\2\312\1\335\24\312\1\337\12\312\1\335"+
+    "\1\312\3\335\1\312\1\335\10\312\2\311\1\334\1\314"+
+    "\1\311\1\314\3\311\1\315\1\316\2\311\1\0\2\311"+
+    "\1\340\2\311\1\0\7\311\1\320\31\311\2\315\1\341"+
+    "\6\315\1\342\53\315\2\316\1\343\7\316\1\342\52\316"+
+    "\1\311\1\312\1\313\3\311\3\312\2\0\1\312\1\311"+
+    "\1\0\2\311\1\312\1\344\1\312\1\0\2\311\5\312"+
+    "\1\320\12\311\7\312\1\311\5\312\2\311\3\0\1\321"+
+    "\1\0\1\321\16\0\1\267\41\0\1\345\1\266\1\265"+
+    "\1\0\1\265\1\345\2\265\2\0\1\265\4\0\3\265"+
+    "\1\0\1\267\1\0\5\265\13\0\1\345\1\265\3\345"+
+    "\1\265\1\345\1\0\5\265\3\0\1\57\1\60\3\0"+
+    "\3\57\2\0\1\57\4\0\3\57\3\0\5\57\13\0"+
+    "\6\57\1\346\1\0\5\57\3\0\1\57\1\60\1\347"+
+    "\1\0\1\347\3\57\1\350\1\351\1\57\4\0\3\57"+
+    "\3\0\5\57\13\0\7\57\1\0\5\57\55\0\1\352"+
+    "\12\0\1\74\1\75\1\74\1\0\4\74\2\0\1\74"+
+    "\4\0\3\74\1\0\1\34\1\0\5\74\1\35\12\0"+
+    "\7\74\1\0\5\74\2\0\2\276\1\330\6\276\1\353"+
+    "\53\276\3\0\1\331\1\0\1\331\7\0\1\300\47\0"+
+    "\2\277\1\332\7\277\1\354\52\277\1\0\1\355\4\0"+
+    "\1\355\37\0\1\355\1\0\3\355\1\0\1\355\10\0"+
+    "\2\311\1\334\30\311\1\320\32\311\1\356\1\313\1\312"+
+    "\1\311\1\312\1\356\2\312\2\0\1\312\1\311\1\0"+
+    "\2\311\3\312\1\0\2\311\5\312\1\320\12\311\1\356"+
+    "\1\312\3\356\1\312\1\356\1\311\5\312\3\311\1\312"+
+    "\1\313\3\311\3\312\2\311\1\312\4\311\3\312\3\311"+
+    "\5\312\1\320\12\311\7\312\1\311\5\312\4\311\1\334"+
+    "\6\311\2\0\2\311\1\0\3\311\1\357\1\311\1\0"+
+    "\7\311\1\320\31\311\2\315\1\341\6\315\1\360\53\315"+
+    "\1\0\1\361\1\362\1\363\1\0\1\363\2\361\10\0"+
+    "\3\361\3\0\5\361\1\364\12\0\7\361\1\0\5\361"+
+    "\2\0\2\316\1\343\7\316\1\365\52\316\1\311\1\312"+
+    "\1\313\3\311\3\312\2\0\1\312\1\311\1\0\2\311"+
+    "\2\312\1\366\1\0\2\311\5\312\1\320\12\311\7\312"+
+    "\1\311\5\312\2\311\1\0\1\367\1\266\1\265\1\0"+
+    "\1\265\1\367\2\265\2\0\1\265\4\0\3\265\1\0"+
+    "\1\267\1\0\5\265\13\0\1\367\1\265\3\367\1\265"+
+    "\1\367\1\0\5\265\3\0\1\57\1\60\3\0\3\57"+
+    "\2\0\1\57\4\0\3\57\3\0\5\57\13\0\2\57"+
+    "\1\370\4\57\1\0\5\57\5\0\1\347\1\0\1\347"+
+    "\3\0\1\350\1\351\52\0\2\350\1\371\6\350\1\372"+
+    "\53\350\2\351\1\373\7\351\1\372\52\351\32\0\1\374"+
+    "\32\0\2\276\1\330\1\353\1\276\1\353\3\276\1\331"+
+    "\3\276\1\375\47\276\2\277\1\332\1\354\1\277\1\354"+
+    "\4\277\1\331\2\277\1\376\47\277\1\311\1\377\1\313"+
+    "\1\312\1\311\1\312\1\377\2\312\2\0\1\312\1\311"+
+    "\1\0\2\311\3\312\1\0\2\311\5\312\1\320\12\311"+
+    "\1\377\1\312\3\377\1\312\1\377\1\311\5\312\4\311"+
+    "\1\334\6\311\2\0\2\311\1\0\4\311\1\u0100\1\0"+
+    "\7\311\1\320\31\311\1\315\1\u0101\1\u0102\1\u0103\1\315"+
+    "\1\u0103\2\u0101\1\315\1\342\6\315\3\u0101\3\315\5\u0101"+
+    "\1\u0104\12\315\7\u0101\1\315\5\u0101\2\315\1\0\1\361"+
+    "\1\362\1\u0105\1\0\1\u0105\3\361\2\0\1\361\4\0"+
+    "\3\361\1\0\1\363\1\0\5\361\1\364\12\0\7\361"+
+    "\1\0\5\361\3\0\1\u0106\1\361\1\0\2\361\1\u0106"+
+    "\37\361\1\u0106\1\361\3\u0106\1\361\1\u0106\10\361\1\0"+
+    "\1\361\1\362\1\363\1\0\1\363\2\361\10\0\3\361"+
+    "\3\0\5\361\13\0\7\361\1\0\5\361\2\0\1\316"+
+    "\1\u0107\1\u0108\1\u0109\1\316\1\u0109\2\u0107\2\316\1\342"+
+    "\5\316\3\u0107\3\316\5\u0107\1\u010a\12\316\7\u0107\1\316"+
+    "\5\u0107\2\316\1\311\1\312\1\313\3\311\3\312\2\0"+
+    "\1\312\1\311\1\0\2\311\3\312\1\u010b\2\311\5\312"+
+    "\1\320\12\311\7\312\1\311\5\312\2\311\1\0\1\u010c"+
+    "\1\266\1\265\1\0\1\265\1\u010c\2\265\2\0\1\265"+
+    "\4\0\3\265\1\0\1\267\1\0\5\265\13\0\1\u010c"+
+    "\1\265\3\u010c\1\265\1\u010c\1\0\5\265\2\0\2\350"+
+    "\1\371\6\350\1\u010d\53\350\3\0\1\372\1\0\1\372"+
+    "\25\0\1\u010e\31\0\2\351\1\373\7\351\1\u010f\52\351"+
+    "\1\311\1\u0110\1\313\1\312\1\311\1\312\1\u0110\2\312"+
+    "\2\0\1\312\1\311\1\0\2\311\3\312\1\0\2\311"+
+    "\5\312\1\320\12\311\1\u0110\1\312\3\u0110\1\312\1\u0110"+
+    "\1\311\5\312\4\311\1\334\6\311\2\0\2\311\1\0"+
+    "\5\311\1\u010b\7\311\1\320\31\311\1\315\1\u0101\1\u0102"+
+    "\1\u0111\1\315\1\u0111\3\u0101\1\342\1\315\1\u0101\4\315"+
+    "\3\u0101\1\315\1\u0103\1\315\5\u0101\1\u0104\12\315\7\u0101"+
+    "\1\315\5\u0101\3\315\1\u0112\1\u0113\1\315\2\u0101\1\u0112"+
+    "\2\u0101\1\u0114\34\u0101\1\u0112\1\u0101\3\u0112\1\u0101\1\u0112"+
+    "\10\u0101\1\315\1\u0101\1\u0102\1\u0103\1\315\1\u0103\2\u0101"+
+    "\1\315\1\342\6\315\3\u0101\3\315\5\u0101\13\315\7\u0101"+
+    "\1\315\5\u0101\2\315\3\0\1\u0105\1\0\1\u0105\16\0"+
+    "\1\363\6\0\1\364\32\0\1\u0115\1\362\1\361\1\0"+
+    "\1\361\1\u0115\2\361\2\0\1\361\4\0\3\361\1\0"+
+    "\1\363\1\0\5\361\1\364\12\0\1\u0115\1\361\3\u0115"+
+    "\1\361\1\u0115\1\0\5\361\2\0\1\316\1\u0107\1\u0108"+
+    "\1\u0116\1\316\1\u0116\3\u0107\1\316\1\342\1\u0107\4\316"+
+    "\3\u0107\1\316\1\u0109\1\316\5\u0107\1\u010a\12\316\7\u0107"+
+    "\1\316\5\u0107\3\316\1\u0117\1\u0118\1\316\2\u0107\1\u0117"+
+    "\3\u0107\1\u0119\33\u0107\1\u0117\1\u0107\3\u0117\1\u0107\1\u0117"+
+    "\10\u0107\1\316\1\u0107\1\u0108\1\u0109\1\316\1\u0109\2\u0107"+
+    "\2\316\1\342\5\316\3\u0107\3\316\5\u0107\13\316\7\u0107"+
+    "\1\316\5\u0107\2\316\2\u011a\1\u011b\1\u010b\1\u011a\1\u010b"+
+    "\3\u011a\1\u011c\1\u011d\2\u011a\1\342\5\u011a\1\0\41\u011a"+
+    "\1\0\1\u011e\1\266\1\265\1\0\1\265\1\u011e\2\265"+
+    "\2\0\1\265\4\0\3\265\1\0\1\267\1\0\5\265"+
+    "\13\0\1\u011e\1\265\3\u011e\1\265\1\u011e\1\0\5\265"+
+    "\2\0\2\350\1\371\1\u010d\1\350\1\u010d\3\350\1\372"+
+    "\21\350\1\u011f\31\350\2\351\1\373\1\u010f\1\351\1\u010f"+
+    "\4\351\1\372\20\351\1\u0120\31\351\1\311\1\u0121\1\313"+
+    "\1\312\1\311\1\312\1\u0121\2\312\2\0\1\312\1\311"+
+    "\1\0\2\311\3\312\1\0\2\311\5\312\1\320\12\311"+
+    "\1\u0121\1\312\3\u0121\1\312\1\u0121\1\311\5\312\2\311"+
+    "\2\315\1\341\1\u0111\1\315\1\u0111\3\315\1\342\12\315"+
+    "\1\u0103\6\315\1\u0104\32\315\1\u0122\1\u0102\1\u0101\1\315"+
+    "\1\u0101\1\u0122\2\u0101\1\342\1\315\1\u0101\4\315\3\u0101"+
+    "\1\315\1\u0103\1\315\5\u0101\1\u0104\12\315\1\u0122\1\u0101"+
+    "\3\u0122\1\u0101\1\u0122\1\315\5\u0101\3\315\1\u0101\1\u0102"+
+    "\1\u0111\1\315\1\u0111\3\u0101\1\360\1\315\1\u0101\4\315"+
+    "\3\u0101\1\315\1\u0103\1\315\5\u0101\1\u0104\12\315\7\u0101"+
+    "\1\315\5\u0101\3\315\1\u0101\1\u0102\1\u0123\1\315\1\u0123"+
+    "\3\u0101\1\342\1\315\1\u0101\4\315\3\u0101\1\315\1\u0103"+
+    "\1\315\5\u0101\1\u0104\12\315\7\u0101\1\315\5\u0101\2\315"+
+    "\1\0\1\u0124\1\362\1\361\1\0\1\361\1\u0124\2\361"+
+    "\2\0\1\361\4\0\3\361\1\0\1\363\1\0\5\361"+
+    "\1\364\12\0\1\u0124\1\361\3\u0124\1\361\1\u0124\1\0"+
+    "\5\361\2\0\2\316\1\343\1\u0116\1\316\1\u0116\4\316"+
+    "\1\342\11\316\1\u0109\6\316\1\u010a\32\316\1\u0125\1\u0108"+
+    "\1\u0107\1\316\1\u0107\1\u0125\2\u0107\1\316\1\342\1\u0107"+
+    "\4\316\3\u0107\1\316\1\u0109\1\316\5\u0107\1\u010a\12\316"+
+    "\1\u0125\1\u0107\3\u0125\1\u0107\1\u0125\1\316\5\u0107\3\316"+
+    "\1\u0107\1\u0108\1\u0116\1\316\1\u0116\3\u0107\1\316\1\365"+
+    "\1\u0107\4\316\3\u0107\1\316\1\u0109\1\316\5\u0107\1\u010a"+
+    "\12\316\7\u0107\1\316\5\u0107\3\316\1\u0107\1\u0108\1\u0126"+
+    "\1\316\1\u0126\3\u0107\1\316\1\342\1\u0107\4\316\3\u0107"+
+    "\1\316\1\u0109\1\316\5\u0107\1\u010a\12\316\7\u0107\1\316"+
+    "\5\u0107\2\316\2\u011a\1\u011b\6\u011a\2\0\2\u011a\1\342"+
+    "\5\u011a\1\0\43\u011a\1\u011b\12\u011a\1\u0127\47\u011a\2\u011c"+
+    "\1\u0128\6\u011c\1\u0129\53\u011c\2\u011d\1\u012a\7\u011d\1\u0129"+
+    "\52\u011d\1\0\1\u012b\1\266\1\265\1\0\1\265\1\u012b"+
+    "\2\265\2\0\1\265\4\0\3\265\1\0\1\267\1\0"+
+    "\5\265\13\0\1\u012b\1\265\3\u012b\1\265\1\u012b\1\0"+
+    "\5\265\2\0\1\311\1\u012c\1\313\1\312\1\311\1\312"+
+    "\1\u012c\2\312\2\0\1\312\1\311\1\0\2\311\3\312"+
+    "\1\0\2\311\5\312\1\320\12\311\1\u012c\1\312\3\u012c"+
+    "\1\312\1\u012c\1\311\5\312\2\311\1\315\1\u012d\1\u0102"+
+    "\1\u0101\1\315\1\u0101\1\u012d\2\u0101\1\342\1\315\1\u0101"+
+    "\4\315\3\u0101\1\315\1\u0103\1\315\5\u0101\1\u0104\12\315"+
+    "\1\u012d\1\u0101\3\u012d\1\u0101\1\u012d\1\315\5\u0101\3\315"+
+    "\1\u0101\1\u0102\1\u0123\1\315\1\u0123\2\u0101\1\315\1\342"+
+    "\6\315\3\u0101\1\315\1\u0103\1\315\5\u0101\1\u0104\12\315"+
+    "\7\u0101\1\315\5\u0101\2\315\1\0\1\u012e\1\362\1\361"+
+    "\1\0\1\361\1\u012e\2\361\2\0\1\361\4\0\3\361"+
+    "\1\0\1\363\1\0\5\361\1\364\12\0\1\u012e\1\361"+
+    "\3\u012e\1\361\1\u012e\1\0\5\361\2\0\1\316\1\u012f"+
+    "\1\u0108\1\u0107\1\316\1\u0107\1\u012f\2\u0107\1\316\1\342"+
+    "\1\u0107\4\316\3\u0107\1\316\1\u0109\1\316\5\u0107\1\u010a"+
+    "\12\316\1\u012f\1\u0107\3\u012f\1\u0107\1\u012f\1\316\5\u0107"+
+    "\3\316\1\u0107\1\u0108\1\u0126\1\316\1\u0126\2\u0107\2\316"+
+    "\1\342\5\316\3\u0107\1\316\1\u0109\1\316\5\u0107\1\u010a"+
+    "\12\316\7\u0107\1\316\5\u0107\2\316\1\u011a\1\u0130\1\u0131"+
+    "\1\u0132\1\u011a\1\u0132\2\u0130\1\u011a\2\0\2\u011a\1\342"+
+    "\2\u011a\3\u0130\1\0\2\u011a\5\u0130\1\u0133\12\u011a\7\u0130"+
+    "\1\u011a\5\u0130\2\u011a\2\u011c\1\u0128\6\u011c\1\u0134\53\u011c"+
+    "\3\0\1\u0129\1\0\1\u0129\7\0\1\342\47\0\2\u011d"+
+    "\1\u012a\7\u011d\1\u0135\52\u011d\1\0\1\265\1\266\1\265"+
+    "\1\0\4\265\2\0\1\265\4\0\3\265\1\0\1\267"+
+    "\1\0\5\265\13\0\7\265\1\0\5\265\2\0\1\311"+
+    "\1\312\1\313\1\312\1\311\4\312\2\0\1\312\1\311"+
+    "\1\0\2\311\3\312\1\0\2\311\5\312\1\320\12\311"+
+    "\7\312\1\311\5\312\2\311\1\315\1\u0136\1\u0102\1\u0101"+
+    "\1\315\1\u0101\1\u0136\2\u0101\1\342\1\315\1\u0101\4\315"+
+    "\3\u0101\1\315\1\u0103\1\315\5\u0101\1\u0104\12\315\1\u0136"+
+    "\1\u0101\3\u0136\1\u0101\1\u0136\1\315\5\u0101\2\315\1\0"+
+    "\1\u0137\1\362\1\361\1\0\1\361\1\u0137\2\361\2\0"+
+    "\1\361\4\0\3\361\1\0\1\363\1\0\5\361\1\364"+
+    "\12\0\1\u0137\1\361\3\u0137\1\361\1\u0137\1\0\5\361"+
+    "\2\0\1\316\1\u0138\1\u0108\1\u0107\1\316\1\u0107\1\u0138"+
+    "\2\u0107\1\316\1\342\1\u0107\4\316\3\u0107\1\316\1\u0109"+
+    "\1\316\5\u0107\1\u010a\12\316\1\u0138\1\u0107\3\u0138\1\u0107"+
+    "\1\u0138\1\316\5\u0107\2\316\1\u011a\1\u0130\1\u0131\1\u0139"+
+    "\1\u011a\1\u0139\3\u0130\2\0\1\u0130\1\u011a\1\342\2\u011a"+
+    "\3\u0130\1\0\1\u0132\1\u011a\5\u0130\1\u0133\12\u011a\7\u0130"+
+    "\1\u011a\5\u0130\3\u011a\1\u013a\1\u013b\1\u011a\2\u0130\1\u013a"+
+    "\6\u0130\1\u013c\30\u0130\1\u013a\1\u0130\3\u013a\1\u0130\1\u013a"+
+    "\10\u0130\1\u011a\1\u0130\1\u0131\1\u0132\1\u011a\1\u0132\2\u0130"+
+    "\1\u011a\2\0\2\u011a\1\342\2\u011a\3\u0130\1\0\2\u011a"+
+    "\5\u0130\13\u011a\7\u0130\1\u011a\5\u0130\2\u011a\2\u011c\1\u0128"+
+    "\1\u0134\1\u011c\1\u0134\3\u011c\1\u0129\3\u011c\1\u013d\47\u011c"+
+    "\2\u011d\1\u012a\1\u0135\1\u011d\1\u0135\4\u011d\1\u0129\2\u011d"+
+    "\1\u013e\47\u011d\1\315\1\u013f\1\u0102\1\u0101\1\315\1\u0101"+
+    "\1\u013f\2\u0101\1\342\1\315\1\u0101\4\315\3\u0101\1\315"+
+    "\1\u0103\1\315\5\u0101\1\u0104\12\315\1\u013f\1\u0101\3\u013f"+
+    "\1\u0101\1\u013f\1\315\5\u0101\2\315\1\0\1\u0140\1\362"+
+    "\1\361\1\0\1\361\1\u0140\2\361\2\0\1\361\4\0"+
+    "\3\361\1\0\1\363\1\0\5\361\1\364\12\0\1\u0140"+
+    "\1\361\3\u0140\1\361\1\u0140\1\0\5\361\2\0\1\316"+
+    "\1\u0141\1\u0108\1\u0107\1\316\1\u0107\1\u0141\2\u0107\1\316"+
+    "\1\342\1\u0107\4\316\3\u0107\1\316\1\u0109\1\316\5\u0107"+
+    "\1\u010a\12\316\1\u0141\1\u0107\3\u0141\1\u0107\1\u0141\1\316"+
+    "\5\u0107\2\316\2\u011a\1\u011b\1\u0139\1\u011a\1\u0139\3\u011a"+
+    "\2\0\2\u011a\1\342\5\u011a\1\0\1\u0132\6\u011a\1\u0133"+
+    "\32\u011a\1\u0142\1\u0131\1\u0130\1\u011a\1\u0130\1\u0142\2\u0130"+
+    "\2\0\1\u0130\1\u011a\1\342\2\u011a\3\u0130\1\0\1\u0132"+
+    "\1\u011a\5\u0130\1\u0133\12\u011a\1\u0142\1\u0130\3\u0142\1\u0130"+
+    "\1\u0142\1\u011a\5\u0130\3\u011a\1\u0130\1\u0131\1\u0139\1\u011a"+
+    "\1\u0139\3\u0130\2\u011a\1\u0130\1\u011a\1\u0127\2\u011a\3\u0130"+
+    "\1\u011a\1\u0132\1\u011a\5\u0130\1\u0133\12\u011a\7\u0130\1\u011a"+
+    "\5\u0130\3\u011a\1\u0130\1\u0131\1\u0143\1\u011a\1\u0143\3\u0130"+
+    "\2\0\1\u0130\1\u011a\1\342\2\u011a\3\u0130\1\0\1\u0132"+
+    "\1\u011a\5\u0130\1\u0133\12\u011a\7\u0130\1\u011a\5\u0130\2\u011a"+
+    "\1\u011c\1\u0144\1\u0145\1\u0146\1\u011c\1\u0146\2\u0144\1\u011c"+
+    "\1\u0129\6\u011c\3\u0144\3\u011c\5\u0144\1\u0147\12\u011c\7\u0144"+
+    "\1\u011c\5\u0144\2\u011c\1\u011d\1\u0148\1\u0149\1\u014a\1\u011d"+
+    "\1\u014a\2\u0148\2\u011d\1\u0129\5\u011d\3\u0148\3\u011d\5\u0148"+
+    "\1\u014b\12\u011d\7\u0148\1\u011d\5\u0148\2\u011d\1\315\1\u014c"+
+    "\1\u0102\1\u0101\1\315\1\u0101\1\u014c\2\u0101\1\342\1\315"+
+    "\1\u0101\4\315\3\u0101\1\315\1\u0103\1\315\5\u0101\1\u0104"+
+    "\12\315\1\u014c\1\u0101\3\u014c\1\u0101\1\u014c\1\315\5\u0101"+
+    "\2\315\1\0\1\361\1\362\1\361\1\0\4\361\2\0"+
+    "\1\361\4\0\3\361\1\0\1\363\1\0\5\361\1\364"+
+    "\12\0\7\361\1\0\5\361\2\0\1\316\1\u014d\1\u0108"+
+    "\1\u0107\1\316\1\u0107\1\u014d\2\u0107\1\316\1\342\1\u0107"+
+    "\4\316\3\u0107\1\316\1\u0109\1\316\5\u0107\1\u010a\12\316"+
+    "\1\u014d\1\u0107\3\u014d\1\u0107\1\u014d\1\316\5\u0107\2\316"+
+    "\1\u011a\1\u014e\1\u0131\1\u0130\1\u011a\1\u0130\1\u014e\2\u0130"+
+    "\2\0\1\u0130\1\u011a\1\342\2\u011a\3\u0130\1\0\1\u0132"+
+    "\1\u011a\5\u0130\1\u0133\12\u011a\1\u014e\1\u0130\3\u014e\1\u0130"+
+    "\1\u014e\1\u011a\5\u0130\3\u011a\1\u0130\1\u0131\1\u0143\1\u011a"+
+    "\1\u0143\2\u0130\1\u011a\2\0\2\u011a\1\342\2\u011a\3\u0130"+
+    "\1\0\1\u0132\1\u011a\5\u0130\1\u0133\12\u011a\7\u0130\1\u011a"+
+    "\5\u0130\2\u011a\1\u011c\1\u0144\1\u0145\1\u014f\1\u011c\1\u014f"+
+    "\3\u0144\1\u0129\1\u011c\1\u0144\4\u011c\3\u0144\1\u011c\1\u0146"+
+    "\1\u011c\5\u0144\1\u0147\12\u011c\7\u0144\1\u011c\5\u0144\3\u011c"+
+    "\1\u0150\1\u0151\1\u011c\2\u0144\1\u0150\2\u0144\1\u0152\34\u0144"+
+    "\1\u0150\1\u0144\3\u0150\1\u0144\1\u0150\10\u0144\1\u011c\1\u0144"+
+    "\1\u0145\1\u0146\1\u011c\1\u0146\2\u0144\1\u011c\1\u0129\6\u011c"+
+    "\3\u0144\3\u011c\5\u0144\13\u011c\7\u0144\1\u011c\5\u0144\2\u011c"+
+    "\1\u011d\1\u0148\1\u0149\1\u0153\1\u011d\1\u0153\3\u0148\1\u011d"+
+    "\1\u0129\1\u0148\4\u011d\3\u0148\1\u011d\1\u014a\1\u011d\5\u0148"+
+    "\1\u014b\12\u011d\7\u0148\1\u011d\5\u0148\3\u011d\1\u0154\1\u0155"+
+    "\1\u011d\2\u0148\1\u0154\3\u0148\1\u0156\33\u0148\1\u0154\1\u0148"+
+    "\3\u0154\1\u0148\1\u0154\10\u0148\1\u011d\1\u0148\1\u0149\1\u014a"+
+    "\1\u011d\1\u014a\2\u0148\2\u011d\1\u0129\5\u011d\3\u0148\3\u011d"+
+    "\5\u0148\13\u011d\7\u0148\1\u011d\5\u0148\2\u011d\1\315\1\u0101"+
+    "\1\u0102\1\u0101\1\315\4\u0101\1\342\1\315\1\u0101\4\315"+
+    "\3\u0101\1\315\1\u0103\1\315\5\u0101\1\u0104\12\315\7\u0101"+
+    "\1\315\5\u0101\2\315\1\316\1\u0107\1\u0108\1\u0107\1\316"+
+    "\4\u0107\1\316\1\342\1\u0107\4\316\3\u0107\1\316\1\u0109"+
+    "\1\316\5\u0107\1\u010a\12\316\7\u0107\1\316\5\u0107\2\316"+
+    "\1\u011a\1\u0157\1\u0131\1\u0130\1\u011a\1\u0130\1\u0157\2\u0130"+
+    "\2\0\1\u0130\1\u011a\1\342\2\u011a\3\u0130\1\0\1\u0132"+
+    "\1\u011a\5\u0130\1\u0133\12\u011a\1\u0157\1\u0130\3\u0157\1\u0130"+
+    "\1\u0157\1\u011a\5\u0130\2\u011a\2\u011c\1\u0128\1\u014f\1\u011c"+
+    "\1\u014f\3\u011c\1\u0129\12\u011c\1\u0146\6\u011c\1\u0147\32\u011c"+
+    "\1\u0158\1\u0145\1\u0144\1\u011c\1\u0144\1\u0158\2\u0144\1\u0129"+
+    "\1\u011c\1\u0144\4\u011c\3\u0144\1\u011c\1\u0146\1\u011c\5\u0144"+
+    "\1\u0147\12\u011c\1\u0158\1\u0144\3\u0158\1\u0144\1\u0158\1\u011c"+
+    "\5\u0144\3\u011c\1\u0144\1\u0145\1\u014f\1\u011c\1\u014f\3\u0144"+
+    "\1\u0134\1\u011c\1\u0144\4\u011c\3\u0144\1\u011c\1\u0146\1\u011c"+
+    "\5\u0144\1\u0147\12\u011c\7\u0144\1\u011c\5\u0144\3\u011c\1\u0144"+
+    "\1\u0145\1\u0159\1\u011c\1\u0159\3\u0144\1\u0129\1\u011c\1\u0144"+
+    "\1\u011c\1\u013d\2\u011c\3\u0144\1\u011c\1\u0146\1\u011c\5\u0144"+
+    "\1\u0147\12\u011c\7\u0144\1\u011c\5\u0144\2\u011c\2\u011d\1\u012a"+
+    "\1\u0153\1\u011d\1\u0153\4\u011d\1\u0129\11\u011d\1\u014a\6\u011d"+
+    "\1\u014b\32\u011d\1\u015a\1\u0149\1\u0148\1\u011d\1\u0148\1\u015a"+
+    "\2\u0148\1\u011d\1\u0129\1\u0148\4\u011d\3\u0148\1\u011d\1\u014a"+
+    "\1\u011d\5\u0148\1\u014b\12\u011d\1\u015a\1\u0148\3\u015a\1\u0148"+
+    "\1\u015a\1\u011d\5\u0148\3\u011d\1\u0148\1\u0149\1\u0153\1\u011d"+
+    "\1\u0153\3\u0148\1\u011d\1\u0135\1\u0148\4\u011d\3\u0148\1\u011d"+
+    "\1\u014a\1\u011d\5\u0148\1\u014b\12\u011d\7\u0148\1\u011d\5\u0148"+
+    "\3\u011d\1\u0148\1\u0149\1\u015b\1\u011d\1\u015b\3\u0148\1\u011d"+
+    "\1\u0129\1\u0148\1\u011d\1\u013e\2\u011d\3\u0148\1\u011d\1\u014a"+
+    "\1\u011d\5\u0148\1\u014b\12\u011d\7\u0148\1\u011d\5\u0148\2\u011d"+
+    "\1\u011a\1\u015c\1\u0131\1\u0130\1\u011a\1\u0130\1\u015c\2\u0130"+
+    "\2\0\1\u0130\1\u011a\1\342\2\u011a\3\u0130\1\0\1\u0132"+
+    "\1\u011a\5\u0130\1\u0133\12\u011a\1\u015c\1\u0130\3\u015c\1\u0130"+
+    "\1\u015c\1\u011a\5\u0130\2\u011a\1\u011c\1\u015d\1\u0145\1\u0144"+
+    "\1\u011c\1\u0144\1\u015d\2\u0144\1\u0129\1\u011c\1\u0144\4\u011c"+
+    "\3\u0144\1\u011c\1\u0146\1\u011c\5\u0144\1\u0147\12\u011c\1\u015d"+
+    "\1\u0144\3\u015d\1\u0144\1\u015d\1\u011c\5\u0144\4\u011c\1\u0128"+
+    "\1\u0159\1\u011c\1\u0159\3\u011c\1\u0129\3\u011c\1\u013d\6\u011c"+
+    "\1\u0146\6\u011c\1\u0147\31\u011c\1\u011d\1\u015e\1\u0149\1\u0148"+
+    "\1\u011d\1\u0148\1\u015e\2\u0148\1\u011d\1\u0129\1\u0148\4\u011d"+
+    "\3\u0148\1\u011d\1\u014a\1\u011d\5\u0148\1\u014b\12\u011d\1\u015e"+
+    "\1\u0148\3\u015e\1\u0148\1\u015e\1\u011d\5\u0148\4\u011d\1\u012a"+
+    "\1\u015b\1\u011d\1\u015b\4\u011d\1\u0129\2\u011d\1\u013e\6\u011d"+
+    "\1\u014a\6\u011d\1\u014b\31\u011d\1\u011a\1\u015f\1\u0131\1\u0130"+
+    "\1\u011a\1\u0130\1\u015f\2\u0130\2\0\1\u0130\1\u011a\1\342"+
+    "\2\u011a\3\u0130\1\0\1\u0132\1\u011a\5\u0130\1\u0133\12\u011a"+
+    "\1\u015f\1\u0130\3\u015f\1\u0130\1\u015f\1\u011a\5\u0130\2\u011a"+
+    "\1\u011c\1\u0160\1\u0145\1\u0144\1\u011c\1\u0144\1\u0160\2\u0144"+
+    "\1\u0129\1\u011c\1\u0144\4\u011c\3\u0144\1\u011c\1\u0146\1\u011c"+
+    "\5\u0144\1\u0147\12\u011c\1\u0160\1\u0144\3\u0160\1\u0144\1\u0160"+
+    "\1\u011c\5\u0144\2\u011c\1\u011d\1\u0161\1\u0149\1\u0148\1\u011d"+
+    "\1\u0148\1\u0161\2\u0148\1\u011d\1\u0129\1\u0148\4\u011d\3\u0148"+
+    "\1\u011d\1\u014a\1\u011d\5\u0148\1\u014b\12\u011d\1\u0161\1\u0148"+
+    "\3\u0161\1\u0148\1\u0161\1\u011d\5\u0148\2\u011d\1\u011a\1\u0130"+
+    "\1\u0131\1\u0130\1\u011a\4\u0130\2\0\1\u0130\1\u011a\1\342"+
+    "\2\u011a\3\u0130\1\0\1\u0132\1\u011a\5\u0130\1\u0133\12\u011a"+
+    "\7\u0130\1\u011a\5\u0130\2\u011a\1\u011c\1\u0162\1\u0145\1\u0144"+
+    "\1\u011c\1\u0144\1\u0162\2\u0144\1\u0129\1\u011c\1\u0144\4\u011c"+
+    "\3\u0144\1\u011c\1\u0146\1\u011c\5\u0144\1\u0147\12\u011c\1\u0162"+
+    "\1\u0144\3\u0162\1\u0144\1\u0162\1\u011c\5\u0144\2\u011c\1\u011d"+
+    "\1\u0163\1\u0149\1\u0148\1\u011d\1\u0148\1\u0163\2\u0148\1\u011d"+
+    "\1\u0129\1\u0148\4\u011d\3\u0148\1\u011d\1\u014a\1\u011d\5\u0148"+
+    "\1\u014b\12\u011d\1\u0163\1\u0148\3\u0163\1\u0148\1\u0163\1\u011d"+
+    "\5\u0148\2\u011d\1\u011c\1\u0164\1\u0145\1\u0144\1\u011c\1\u0144"+
+    "\1\u0164\2\u0144\1\u0129\1\u011c\1\u0144\4\u011c\3\u0144\1\u011c"+
+    "\1\u0146\1\u011c\5\u0144\1\u0147\12\u011c\1\u0164\1\u0144\3\u0164"+
+    "\1\u0144\1\u0164\1\u011c\5\u0144\2\u011c\1\u011d\1\u0165\1\u0149"+
+    "\1\u0148\1\u011d\1\u0148\1\u0165\2\u0148\1\u011d\1\u0129\1\u0148"+
+    "\4\u011d\3\u0148\1\u011d\1\u014a\1\u011d\5\u0148\1\u014b\12\u011d"+
+    "\1\u0165\1\u0148\3\u0165\1\u0148\1\u0165\1\u011d\5\u0148\2\u011d"+
+    "\1\u011c\1\u0144\1\u0145\1\u0144\1\u011c\4\u0144\1\u0129\1\u011c"+
+    "\1\u0144\4\u011c\3\u0144\1\u011c\1\u0146\1\u011c\5\u0144\1\u0147"+
+    "\12\u011c\7\u0144\1\u011c\5\u0144\2\u011c\1\u011d\1\u0148\1\u0149"+
+    "\1\u0148\1\u011d\4\u0148\1\u011d\1\u0129\1\u0148\4\u011d\3\u0148"+
+    "\1\u011d\1\u014a\1\u011d\5\u0148\1\u014b\12\u011d\7\u0148\1\u011d"+
+    "\5\u0148\2\u011d";

   private static int [] zzUnpackTrans() {
-    int [] result = new int[15794];
+    int [] result = new int[16483];
     int offset = 0;
     offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
     return result;
@@ -778,22 +817,22 @@
   private static final String ZZ_ATTRIBUTE_PACKED_0 =
     "\1\0\1\11\10\1\1\11\2\1\1\11\1\1\1\11"+
     "\7\1\3\0\1\11\1\0\2\11\1\1\2\0\1\11"+
-    "\2\0\1\1\2\0\2\1\1\0\2\1\1\0\1\1"+
-    "\1\0\5\1\4\0\2\11\2\0\3\1\2\0\1\1"+
-    "\1\11\12\0\13\1\4\0\1\1\1\0\1\1\2\0"+
-    "\4\1\1\0\7\1\2\11\2\0\1\1\1\0\3\1"+
-    "\5\0\1\11\11\1\2\0\1\1\1\0\4\1\3\0"+
-    "\10\1\2\0\1\1\1\0\3\1\2\0\6\1\2\0"+
-    "\2\1\6\0\1\11\5\1\1\11\2\1\1\0\1\1"+
-    "\4\0\6\1\2\0\1\1\3\0\1\1\1\0\3\1"+
-    "\4\0\3\1\6\0\1\11\1\1\5\0\1\11\1\0"+
-    "\3\1\3\0\1\11\3\1\4\0\1\1\5\0\1\1"+
-    "\1\0\1\1\1\0\1\11\1\0\1\1\15\0\4\1"+
-    "\11\0\2\1\6\0\1\1\23\0\1\1\3\0\1\1"+
-    "\32\0";
+    "\2\0\1\1\1\0\1\1\1\0\2\1\1\0\2\1"+
+    "\1\0\1\1\1\0\5\1\4\0\2\11\2\0\3\1"+
+    "\1\0\13\1\1\0\1\1\1\11\12\0\13\1\4\0"+
+    "\3\1\1\0\1\1\2\0\4\1\1\0\7\1\2\11"+
+    "\2\0\1\1\1\0\3\1\5\0\1\11\11\1\2\0"+
+    "\1\1\1\0\4\1\3\0\10\1\2\0\1\1\1\0"+
+    "\3\1\2\0\6\1\2\0\2\1\6\0\1\11\5\1"+
+    "\1\11\2\1\1\0\1\1\4\0\6\1\2\0\1\1"+
+    "\3\0\1\1\1\0\3\1\4\0\3\1\6\0\1\11"+
+    "\1\1\5\0\1\11\1\0\3\1\3\0\1\11\3\1"+
+    "\4\0\1\1\5\0\1\1\1\0\1\1\1\0\1\11"+
+    "\1\0\1\1\15\0\4\1\11\0\2\1\6\0\1\1"+
+    "\23\0\1\1\3\0\1\1\32\0";

   private static int [] zzUnpackAttribute() {
-    int [] result = new int[343];
+    int [] result = new int[357];
     int offset = 0;
     offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
     return result;
@@ -840,23 +879,6 @@
       from input */
   private int zzEndRead;

-  /** number of newlines encountered up to the start of the matched text */
-  private int yyline;
-
-  /** the number of characters up to the start of the matched text */
-  private int yychar;
-
-  /**
-   * the number of characters from the last newline up to the start of the 
-   * matched text
-   */
-  private int yycolumn;
-
-  /** 
-   * zzAtBOL == true <=> the scanner is currently at the beginning of a line
-   */
-  private boolean zzAtBOL = true;
-
   /** zzAtEOF == true <=> the scanner is at the EOF */
   private boolean zzAtEOF;

@@ -1170,11 +1192,9 @@
    */
   public final void yyreset(java.io.Reader reader) {
     zzReader = reader;
-    zzAtBOL  = true;
     zzAtEOF  = false;
     zzEndRead = zzStartRead = 0;
     zzCurrentPos = zzMarkedPos = zzPushbackPos = 0;
-    yyline = yychar = yycolumn = 0;
     zzLexicalState = YYINITIAL;
   }

@@ -1349,37 +1369,9 @@
       zzMarkedPos = zzMarkedPosL;

       switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) {
-        case 24: 
-          { // This is horrible. However it seems that there is no other way 
to do it with either jflex or CUP, as {URL} cannot be an unambiguous token :(
-       String s = yytext();
-       if(debug) log("Recognized URL: "+s);
-       
-       DecodedStringThingy dst = new DecodedStringThingy(s);
-       
-       if(!dst.url) {
-               throw new IllegalStateException("parsing url().. isn't a 
url()");
-       }
-       if(dst.suffix.length() > 0) {
-               yypushback(dst.suffix.length());
-               dst.suffix = "";
-       }
-       
-       s = dst.data;
-       if(debug) log("URL now: "+s);
-       try {
-               s = processURL(s);
-               dst.data = s;
-               if(s == null || s.equals("")) {
-                       if(debug) log("URL invalid");
-                       w.write("url()");
-               } else {
-                       s = dst.toString();
-                       if(debug) log("Writing: "+s);
-                       w.write(s);
-               }
-       } catch (CommentException e) {
-               w.write("/* "+commentEncode(e.getMessage())+" */");
-       }
+        case 14: 
+          { if(debug) log("Deleted unofficial ident: "+yytext());
+       w.write("/* " + l10n("deletedUnofficialIdent") + " */");
           }
         case 33: break;
         case 20: 
@@ -1388,23 +1380,19 @@
        if(debug) log("Matched unicode: "+s);
           }
         case 34: break;
-        case 28: 
-          { if(debug) log("Deleted unofficial ident with url: "+yytext());
-       w.write("/* " + l10n("deletedUnofficialIdentWithURL") + " */");
+        case 2: 
+          { String s = yytext();
+       w.write(s);
+       if(debug) log("Matched ident: "+s);
           }
         case 35: break;
-        case 8: 
+        case 27: 
           { String s = yytext();
-       w.write(s);
-       if(debug) log("Matched close braces: "+s);
+       s = s.substring("@media".length()).trim();
+       w.write("@media "+s+" ");
+       if(debug) log("Matched @media: "+s);
           }
         case 36: break;
-        case 23: 
-          { String s = yytext();
-       w.write(s);
-       if(debug) log("Matched HTML comment: "+s);
-          }
-        case 37: break;
         case 12: 
           { String s = yytext();
        if(debug) log("Matched string: "+s);
@@ -1415,54 +1403,58 @@
                w.write(s);
        }
           }
+        case 37: break;
+        case 21: 
+          { String s = yytext();
+       if(debug) log("Got hexcolor: "+s);
+       w.write(s);
+          }
         case 38: break;
-        case 17: 
+        case 7: 
           { String s = yytext();
-       w.write(s); 
-       if(debug) log("Matched ~=: "+s);
+       w.write(s);
+       if(debug) log("Matched open braces: "+s);
           }
         case 39: break;
-        case 26: 
+        case 6: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched @page: "+s);
+       if(debug) log("Matched semicolon: "+s);
           }
         case 40: break;
-        case 14: 
+        case 13: 
           { String s = yytext();
        w.write(s);
        if(debug) log("Matched number: "+s);
           }
         case 41: break;
-        case 6: 
+        case 26: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched semicolon: "+s);
+       if(debug) log("Matched @page: "+s);
           }
         case 42: break;
-        case 21: 
+        case 17: 
           { String s = yytext();
-       if(debug) log("Got hexcolor: "+s);
-       w.write(s);
+       w.write(s); 
+       if(debug) log("Matched ~=: "+s);
           }
         case 43: break;
-        case 7: 
+        case 23: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched open braces: "+s);
+       if(debug) log("Matched HTML comment: "+s);
           }
         case 44: break;
-        case 27: 
-          { String s = yytext();
-       s = s.substring("@media".length()).trim();
-       w.write("@media "+s+" ");
-       if(debug) log("Matched @media: "+s);
+        case 28: 
+          { if(debug) log("Deleted unofficial ident with url: "+yytext());
+       w.write("/* " + l10n("deletedUnofficialIdentWithURL") + " */");
           }
         case 45: break;
-        case 2: 
+        case 8: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched ident: "+s);
+       if(debug) log("Matched close braces: "+s);
           }
         case 46: break;
         case 25: 
@@ -1471,21 +1463,49 @@
        if(debug) log("Matched unicode range: "+s);
           }
         case 47: break;
+        case 24: 
+          { // This is horrible. However it seems that there is no other way 
to do it with either jflex or CUP, as {URL} cannot be an unambiguous token :(
+       String s = yytext();
+       if(debug) log("Recognized URL: "+s);
+       
+       DecodedStringThingy dst = new DecodedStringThingy(s);
+       
+       if(!dst.url) {
+               throw new IllegalStateException("parsing url().. isn't a 
url()");
+       }
+       if(dst.suffix.length() > 0) {
+               yypushback(dst.suffix.length());
+               dst.suffix = "";
+       }
+       
+       s = dst.data;
+       if(debug) log("URL now: "+s);
+       try {
+               s = processURL(s);
+               dst.data = s;
+               if(s == null || s.equals("")) {
+                       if(debug) log("URL invalid");
+                       w.write("url()");
+               } else {
+                       s = dst.toString();
+                       if(debug) log("Writing: "+s);
+                       w.write(s);
+               }
+       } catch (CommentException e) {
+               w.write("/* "+commentEncode(e.getMessage())+" */");
+       }
+          }
+        case 48: break;
         case 30: 
           { String s = yytext();
        w.write(s);
        if(debug) log("Matched @font-face: "+s);
           }
-        case 48: break;
-        case 13: 
-          { if(debug) log("Deleted unofficial ident: "+yytext());
-       w.write("/* " + l10n("deletedUnofficialIdent") + " */");
-          }
         case 49: break;
-        case 19: 
+        case 5: 
           { String s = yytext();
-       w.write(s); 
-       if(debug) log("Matched HTML comment: "+s);
+       w.write(s);
+       if(debug) log("Matched function end: "+s);
           }
         case 50: break;
         case 29: 
@@ -1513,25 +1533,37 @@
        }
           }
         case 51: break;
-        case 3: 
+        case 4: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched whitespace: "+s);
+       if(debug) log("Matched single char: "+s);
           }
         case 52: break;
-        case 1: 
+        case 15: 
           { String s = yytext();
-       char c = s.charAt(0);
-       log("Matched anything: "+yytext()+" - ignoring");
-       w.write("/* "+l10n("deletedUnmatchedChar")+" "+c+" */"); // single char 
cannot break out of comment
+       w.write(s);
+       if(debug) log("Matched #name: "+s);
           }
         case 53: break;
-        case 31: 
+        case 10: 
+          { if(postBadImportFlag) {
+               // Ignore
+               postBadImportFlag = false;
+               if(debug) log("Ignoring mediums list because after bad import: 
"+
+                       yytext());
+       } else {
+               String s = yytext();
+               w.write(s);
+               if(debug) log("Matched and passing on mediums list: "+s);
+       }
+          }
+        case 54: break;
+        case 18: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched important: "+s);
+       if(debug) log("Matched |=: "+s);
           }
-        case 54: break;
+        case 55: break;
         case 22: 
           { String s = yytext();
        StringBuffer sb = new StringBuffer(s.length());
@@ -1556,7 +1588,7 @@
        w.write(sb.toString());
        if(debug) log("Matched comment: "+s+" -> "+sb.toString());
           }
-        case 55: break;
+        case 56: break;
         case 16: 
           { if(!deleteErrors) {
                throwError(l10n("unknownAtIdentifierLabel")+" "+yytext());
@@ -1566,38 +1598,25 @@
                // Ignore
        }
           }
-        case 56: break;
-        case 11: 
+        case 57: break;
+        case 31: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched measurement: "+s);
+       if(debug) log("Matched important: "+s);
           }
-        case 57: break;
-        case 18: 
+        case 58: break;
+        case 11: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched |=: "+s);
+       if(debug) log("Matched measurement: "+s);
           }
-        case 58: break;
+        case 59: break;
         case 32: 
           { String s = yytext();
        detectedCharset = s;
        if(debug) log("Matched and ignoring charset: "+s);
        // Ignore
           }
-        case 59: break;
-        case 10: 
-          { if(postBadImportFlag) {
-               // Ignore
-               postBadImportFlag = false;
-               if(debug) log("Ignoring mediums list because after bad import: 
"+
-                       yytext());
-       } else {
-               String s = yytext();
-               w.write(s);
-               if(debug) log("Matched and passing on mediums list: "+s);
-       }
-          }
         case 60: break;
         case 9: 
           { String s = yytext();
@@ -1606,22 +1625,23 @@
        if(debug) log("Matched function start: "+s);
           }
         case 61: break;
-        case 4: 
+        case 1: 
           { String s = yytext();
-       w.write(s);
-       if(debug) log("Matched single char: "+s);
+       char c = s.charAt(0);
+       log("Matched anything: "+yytext()+" - ignoring");
+       w.write("/* "+l10n("deletedUnmatchedChar")+" "+c+" */"); // single char 
cannot break out of comment
           }
         case 62: break;
-        case 15: 
+        case 3: 
           { String s = yytext();
        w.write(s);
-       if(debug) log("Matched #name: "+s);
+       if(debug) log("Matched whitespace: "+s);
           }
         case 63: break;
-        case 5: 
+        case 19: 
           { String s = yytext();
-       w.write(s);
-       if(debug) log("Matched function end: "+s);
+       w.write(s); 
+       if(debug) log("Matched HTML comment: "+s);
           }
         case 64: break;
         default: 

Modified: 
branches/freenet-jfk/src/freenet/clients/http/filter/ContentFilter.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/filter/ContentFilter.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/filter/ContentFilter.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -119,6 +119,7 @@

        /**
         * Filter some data.
+        * @param bf The bucket factory used to create the bucket to return the 
filtered data in.
         * @throws IOException If an internal error involving buckets occurred.
         */
        public static FilterOutput filter(Bucket data, BucketFactory bf, String 
typeName, URI baseURI, FoundURICallback cb) throws UnsafeContentTypeException, 
IOException {

Modified: branches/freenet-jfk/src/freenet/clients/http/filter/HTMLFilter.java
===================================================================
--- branches/freenet-jfk/src/freenet/clients/http/filter/HTMLFilter.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/clients/http/filter/HTMLFilter.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -58,7 +58,7 @@
                        strm.close();
                        throw UnknownCharsetException.create(e, charset);
                }
-               HTMLParseContext pc = new HTMLParseContext(r, w, charset, cb);
+               HTMLParseContext pc = new HTMLParseContext(r, w, charset, cb, 
false);
                pc.run(temp);
                r.close();
                w.close();
@@ -81,7 +81,7 @@
                Writer w = new NullWriter();
                Reader r;
                r = new BufferedReader(new InputStreamReader(bis, 
parseCharset), 4096);
-               HTMLParseContext pc = new HTMLParseContext(r, w, null, new 
NullFilterCallback());
+               HTMLParseContext pc = new HTMLParseContext(r, w, null, new 
NullFilterCallback(), true);
                try {
                        pc.run(null);
                } catch (Throwable t) {
@@ -98,12 +98,14 @@
                String charset;
                String detectedCharset;
                final FilterCallback cb;
+               final boolean noOutput;

-               HTMLParseContext(Reader r, Writer w, String charset, 
FilterCallback cb) {
+               HTMLParseContext(Reader r, Writer w, String charset, 
FilterCallback cb, boolean noOutput) {
                        this.r = r;
                        this.w = w;
                        this.charset = charset;
                        this.cb = cb;
+                       this.noOutput = noOutput;
                }

                Bucket run(Bucket temp) throws IOException, DataFilterException 
{
@@ -319,6 +321,8 @@

        void saveText(StringBuffer s, String tagName, Writer w, 
HTMLParseContext pc)
                throws IOException {
+               
+               if(pc.noOutput) return;

                if(logMINOR) Logger.minor(this, "Saving text: "+s.toString());
                if (pc.killText) {
@@ -366,6 +370,7 @@
                ParsedTag t = new ParsedTag(splitTag);
                if (!pc.killTag) {
                        t = t.sanitize(pc);
+                       if(pc.noOutput) return; // sanitize has done all the 
work we are interested in
                        if (t != null) {
                                if (pc.writeStyleScriptWithTag) {
                                        pc.writeStyleScriptWithTag = false;
@@ -391,6 +396,7 @@

        void saveComment(StringBuffer s, Writer w, HTMLParseContext pc)
                throws IOException {
+               if(pc.noOutput) return;
                if((s.length() > 3) && (s.charAt(0) == '!') && (s.charAt(1) == 
'-') && (s.charAt(2) == '-')) {
                        s.delete(0, 3);
                        if(s.charAt(s.length()-1) == '-')
@@ -1286,7 +1292,7 @@
                void processStyle(HTMLParseContext pc) {
                        try {
                                pc.currentStyleScriptChunk =
-                                       
sanitizeStyle(pc.currentStyleScriptChunk, pc.cb);
+                                       
sanitizeStyle(pc.currentStyleScriptChunk, pc.cb, pc);
                        } catch (DataFilterException e) {
                                Logger.error(this, "Error parsing style: "+e, 
e);
                                pc.currentStyleScriptChunk = "";
@@ -1363,7 +1369,7 @@
                        }
                        String style = getHashString(h, "style");
                        if (style != null) {
-                               style = sanitizeStyle(style, pc.cb);
+                               style = sanitizeStyle(style, pc.cb, pc);
                                if (style != null)
                                        style = escapeQuotes(style);
                                if (style != null)
@@ -1811,8 +1817,9 @@

        }

-       static String sanitizeStyle(String style, FilterCallback cb) throws 
DataFilterException {
+       static String sanitizeStyle(String style, FilterCallback cb, 
HTMLParseContext hpc) throws DataFilterException {
                if(style == null) return null;
+               if(hpc.noOutput) return null;
                Reader r = new StringReader(style);
                Writer w = new StringWriter();
                style = style.trim();

Modified: branches/freenet-jfk/src/freenet/config/FilePersistentConfig.java
===================================================================
--- branches/freenet-jfk/src/freenet/config/FilePersistentConfig.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/config/FilePersistentConfig.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -96,7 +96,7 @@
                try {
                        LineReadingInputStream lis = new 
LineReadingInputStream(bis);
                        // Config file is UTF-8 too!
-                       return new SimpleFieldSet(lis, 32768, 128, true, true, 
true, true); // FIXME? advanced users may edit the config file, hence true?
+                       return new SimpleFieldSet(lis, 1024*1024, 128, true, 
true, true, true); // FIXME? advanced users may edit the config file, hence 
true?
                } finally {
                        try {
                                fis.close();

Modified: branches/freenet-jfk/src/freenet/config/StringArrOption.java
===================================================================
--- branches/freenet-jfk/src/freenet/config/StringArrOption.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/config/StringArrOption.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -87,7 +87,7 @@
                        if(val.length() == 0)
                                sb.append(":").append(delimiter);
                        else
-                               
sb.append(URLEncoder.encode(arr[i])).append(delimiter);
+                               
sb.append(URLEncoder.encode(arr[i],false)).append(delimiter);
                }
                if(sb.length() > 0) sb.setLength(sb.length()-1); // drop 
surplus delimiter
                return sb.toString();

Modified: branches/freenet-jfk/src/freenet/crypt/KeyAgreementSchemeContext.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/KeyAgreementSchemeContext.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/crypt/KeyAgreementSchemeContext.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -27,7 +27,7 @@
         if(cipher != null) return cipher;
         getKey();
         try {
-            cipher = new Rijndael(256, 256, false);
+            cipher = new Rijndael(256, 256);
         } catch (UnsupportedCipherException e1) {
             throw new Error(e1);
         }

Modified: branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael.java
===================================================================
--- branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/crypt/ciphers/Rijndael.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -30,19 +30,16 @@
        }

        public Rijndael(int keysize) throws UnsupportedCipherException {
-               this(keysize, 128, false);
+               this(keysize, 128);
        }

        /**
         * Create a Rijndael instance.
         * @param keysize The key size.
         * @param blocksize The block size.
-        * @param fakeInsecure If true, only encrypt the first 128 bits of any 
block. This
-        * is insecure! It is used for backwards compatibility with old data 
encrypted with
-        * the old code.
         * @throws UnsupportedCipherException
         */
-       public Rijndael(int keysize, int blocksize, boolean fakeInsecure) 
throws UnsupportedCipherException {
+       public Rijndael(int keysize, int blocksize) throws 
UnsupportedCipherException {
                if (! ((keysize == 128) ||
                                (keysize == 192) ||
                                (keysize == 256)))
@@ -53,12 +50,7 @@
                        throw new UnsupportedCipherException("Invalid 
blocksize");
                this.keysize=keysize;
                this.blocksize=blocksize;
-               // FIXME This is deliberate insecurity! It is used for 
backwards compatibility *ONLY*!
-               // FIXME IT MUST BE REMOVED SOON!
-               if(fakeInsecure)
-                       this.cryptBlockSize = 128;
-               else
-                       this.cryptBlockSize = blocksize;
+               this.cryptBlockSize = blocksize;
        }

        public Rijndael() {

Modified: branches/freenet-jfk/src/freenet/io/AddressIdentifier.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/AddressIdentifier.java  2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/io/AddressIdentifier.java  2007-09-23 
09:58:34 UTC (rev 15282)
@@ -60,6 +60,21 @@
         *         otherwise
         */
        public static AddressType getAddressType(String address) {
+               return AddressIdentifier.getAddressType(address,false);
+       }
+
+       /**
+        * Tries to detemine the address type of the given address.
+        * 
+        * @param address
+        *            The address to determine the type of
+        * @param allowIPv6PercentScopeID
+        *            If true, match %<scope-id> suffixed IPv6 IP addresses
+        * @return {@link AddressType#OTHER} if <code>address</code> is a
+        *         hostname, {@link AddressType#IPv4} or {@link 
AddressType#IPv6}
+        *         otherwise
+        */
+       public static AddressType getAddressType(String address, boolean 
allowIPv6PercentScopeID) {
                String byteRegex = "([01]?[0-9]?[0-9]?|2[0-4][0-9]|25[0-5])";
                String ipv4AddressRegex = byteRegex + "\\.(" + byteRegex + 
"\\.)?(" + byteRegex + "\\.)?" + byteRegex;
                if (Pattern.matches(ipv4AddressRegex, address)) {
@@ -67,6 +82,9 @@
                }
                String wordRegex = "([0-9a-fA-F]{1,4})";
                String ipv6AddressRegex = wordRegex + "?:" + wordRegex + ':' + 
wordRegex + ':' + wordRegex + ':' + wordRegex + ':' + wordRegex + ':' + 
wordRegex + ':' + wordRegex;
+               if (allowIPv6PercentScopeID) {
+                       ipv6AddressRegex = ipv6AddressRegex + 
"(?:%[0-9]{1,3})?";
+               }
                if (Pattern.matches(ipv6AddressRegex, address)) {
                        return AddressType.IPv6;
                }

Modified: branches/freenet-jfk/src/freenet/io/NetworkInterface.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/NetworkInterface.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/io/NetworkInterface.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -277,6 +277,7 @@
                 * @see NetworkInterface#allowedHosts
                 */
                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        while (!closed) {
                                boolean logMINOR = 
Logger.shouldLog(Logger.MINOR, this);
                                try {

Modified: branches/freenet-jfk/src/freenet/io/comm/DMT.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/DMT.java   2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/io/comm/DMT.java   2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -116,6 +116,8 @@
        public static final String TIME_LEFT = "timeLeft";
        public static final String PREV_UID = "prevUID";
        public static final String OPENNET_NODEREF = "opennetNoderef";
+       public static final String REMOVE = "remove";
+       public static final String PURGE = "purge";

        //Diagnostic
        public static final MessageType ping = new MessageType("ping") {{
@@ -1052,6 +1054,29 @@
                return msg;
        }

+       public static final MessageType FNPDisconnect = new 
MessageType("FNPDisconnect") {{
+               // If true, remove from active routing table, likely to be down 
for a while.
+               // Otherwise just dump all current connection state and keep 
trying to connect.
+               addField(REMOVE, Boolean.class);
+               // If true, purge all references to this node. Otherwise, we 
can keep the node
+               // around in secondary tables etc in order to more easily 
reconnect later. 
+               // (Mostly used on opennet)
+               addField(PURGE, Boolean.class);
+               // Parting message, may be empty. A SimpleFieldSet in exactly 
the same format 
+               // as an N2NTM.
+               addField(NODE_TO_NODE_MESSAGE_TYPE, Integer.class);
+               addField(NODE_TO_NODE_MESSAGE_DATA, ShortBuffer.class);
+       }};
+       
+       public static final Message createFNPDisconnect(boolean remove, boolean 
purge, int messageType, ShortBuffer messageData) {
+               Message msg = new Message(FNPDisconnect);
+               msg.set(REMOVE, remove);
+               msg.set(PURGE, purge);
+               msg.set(NODE_TO_NODE_MESSAGE_TYPE, messageType);
+               msg.set(NODE_TO_NODE_MESSAGE_DATA, messageData);
+               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.

Modified: branches/freenet-jfk/src/freenet/io/comm/MessageCore.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/MessageCore.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/io/comm/MessageCore.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -102,7 +102,7 @@
                synchronized (_filters) {
                        for (ListIterator i = _filters.listIterator(); 
i.hasNext();) {
                                MessageFilter f = (MessageFilter) i.next();
-                               if (f.timedOut()) {
+                               if (f.timedOut(tStart)) {
                                        i.remove();
                                        _timedOutFilters.add(f);
                                } else { // Because _filters are in order of 
timeout, we

Modified: branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java 2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/io/comm/MessageFilter.java 2007-09-23 
09:58:34 UTC (rev 15282)
@@ -166,7 +166,6 @@
        }

        public boolean match(Message m) {
-               if(timedOut()) return false;
                if ((_or != null) && (_or.match(m))) {
                        _matched = true;
                        return true;
@@ -188,6 +187,7 @@
                                }
                        }
                }
+               if(timedOut(System.currentTimeMillis())) return false;
                _matched=true;
                return true;
        }
@@ -203,11 +203,11 @@
            return _droppedConnection;
        }

-       public boolean timedOut() {
+       public boolean timedOut(long time) {
                if(_matched) return false;
                if(_callback != null && _callback.shouldTimeout())
                        _timeout = -1; // timeout immediately
-               return _timeout < System.currentTimeMillis();
+               return _timeout < time;
        }

     public Message getMessage() {

Modified: branches/freenet-jfk/src/freenet/io/comm/Peer.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/Peer.java  2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/io/comm/Peer.java  2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -25,6 +25,8 @@
 import java.net.InetAddress;
 import java.net.UnknownHostException;

+import freenet.support.transport.ip.HostnameSyntaxException;
+import freenet.support.transport.ip.HostnameUtil;
 import freenet.support.transport.ip.IPUtil;

 /**
@@ -72,11 +74,45 @@
         * @param physical The string to be parsed, in the format [ ip or 
domain name ]:[ port number].
         * @param allowUnknown If true, allow construction of the Peer even if 
the domain name
         * lookup fails.
+        * @param checkHostname If true, validate the syntax of the given DNS 
hostname or IPv4
+        * IP address
+        * @throws HostSyntaxException If the string is not formatted as a 
proper DNS hostname
+        * or IPv4 IP address
         * @throws PeerParseException If the string is not valid e.g. if it 
doesn't contain a 
         * port.
         * @throws UnknownHostException If allowUnknown is not set, and a 
domain name which does
         * not exist was passed in.
         */
+    public Peer(String physical, boolean allowUnknown, boolean checkHostname) 
throws HostnameSyntaxException, PeerParseException, UnknownHostException {
+        int offset = physical.lastIndexOf(':'); // ipv6
+        if(offset < 0) throw new PeerParseException();
+        String host = physical.substring(0, offset);
+        if(checkHostname) {
+               if(!HostnameUtil.isValidHostname(host, true)) throw new 
HostnameSyntaxException();
+               }
+        addr = new FreenetInetAddress(host, allowUnknown);
+        String strport = physical.substring(offset+1);
+        try {
+            _port = Integer.parseInt(strport);
+        } catch (NumberFormatException e) {
+            throw new PeerParseException(e);
+        }
+    }
+
+
+       /**
+        * Create a Peer from a string. This may be an IP address or a domain 
name. If it
+        * is the latter, the name is primary rather than the IP address; 
+        * getHandshakeAddress() will do a new lookup on the name, and change 
the IP address
+        * if the domain name has changed.
+        * @param physical The string to be parsed, in the format [ ip or 
domain name ]:[ port number].
+        * @param allowUnknown If true, allow construction of the Peer even if 
the domain name
+        * lookup fails.
+        * @throws PeerParseException If the string is not valid e.g. if it 
doesn't contain a 
+        * port.
+        * @throws UnknownHostException If allowUnknown is not set, and a 
domain name which does
+        * not exist was passed in.
+        */
     public Peer(String physical, boolean allowUnknown) throws 
PeerParseException, UnknownHostException {
         int offset = physical.lastIndexOf(':'); // ipv6
         if(offset < 0) throw new PeerParseException();
@@ -88,7 +124,7 @@
         } catch (NumberFormatException e) {
             throw new PeerParseException(e);
         }
-    }
+       }

     public Peer(FreenetInetAddress addr, int port) {
        this.addr = addr;

Modified: branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java      
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/io/comm/UdpSocketHandler.java      
2007-09-23 09:58:34 UTC (rev 15282)
@@ -274,6 +274,7 @@

        public class USMChecker implements Runnable {
                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        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);

Modified: branches/freenet-jfk/src/freenet/keys/BaseClientKey.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/BaseClientKey.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/BaseClientKey.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -13,13 +13,13 @@
 public abstract class BaseClientKey {

        public static BaseClientKey getBaseKey(FreenetURI origURI) throws 
MalformedURLException {
-               if(origURI.getKeyType().equals("CHK"))
+               if("CHK".equals(origURI.getKeyType()))
                        return new ClientCHK(origURI);
-               if(origURI.getKeyType().equals("SSK"))
+               if("SSK".equals(origURI.getKeyType()))
                        return new ClientSSK(origURI);
-               if(origURI.getKeyType().equals("KSK"))
+               if("KSK".equals(origURI.getKeyType()))
                        return ClientKSK.create(origURI.getDocName());
-               if(origURI.getKeyType().equals("USK"))
+               if("USK".equals(origURI.getKeyType()))
                        return USK.create(origURI);
                throw new UnsupportedOperationException("Unknown keytype from 
"+origURI);
        }

Modified: branches/freenet-jfk/src/freenet/keys/CHKBlock.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/CHKBlock.java 2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/CHKBlock.java 2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -44,13 +44,8 @@
     }

     public static CHKBlock construct(byte[] data, byte[] header) throws 
CHKVerifyException {
-       try {
-               return new CHKBlock(data, header, null, true, 
Key.ALGO_AES_PCFB_256_SHA256);
-       } catch (CHKVerifyException e) {
-               // FIXME remove back compatibility code
-               return new CHKBlock(data, header, null, true, 
Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
-       }
-    }
+       return new CHKBlock(data, header, null, true, 
Key.ALGO_AES_PCFB_256_SHA256);
+     }

     public CHKBlock(byte[] data2, byte[] header2, NodeCHK key) throws 
CHKVerifyException {
        this(data2, header2, key, key.cryptoAlgorithm);

Modified: branches/freenet-jfk/src/freenet/keys/ClientCHK.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/ClientCHK.java        2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/ClientCHK.java        2007-09-23 
09:58:34 UTC (rev 15282)
@@ -71,8 +71,7 @@
             throw new MalformedURLException("No extra bytes in CHK - maybe a 
0.5 key?");
         // byte 0 is reserved, for now
         cryptoAlgorithm = extra[1];
-               if((!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
-                               (Key.ALLOW_INSECURE_CLIENT_CHKS && 
cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256))))
+               if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
                        throw new MalformedURLException("Invalid crypto 
algorithm");
         controlDocument = (extra[2] & 0x02) != 0;
         compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] & 
0xff));
@@ -88,8 +87,7 @@
                dis.readFully(extra);
                // byte 0 is reserved, for now
         cryptoAlgorithm = extra[1];
-               if((!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
-                               (Key.ALLOW_INSECURE_CLIENT_CHKS && 
cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256))))
+               if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
                        throw new MalformedURLException("Invalid crypto 
algorithm");
         compressionAlgorithm = (short)(((extra[3] & 0xff) << 8) + (extra[4] & 
0xff));
         controlDocument = (extra[2] & 0x02) != 0;

Modified: branches/freenet-jfk/src/freenet/keys/ClientCHKBlock.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/ClientCHKBlock.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/ClientCHKBlock.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -75,12 +75,11 @@
      */
     public Bucket decode(BucketFactory bf, int maxLength, boolean 
dontCompress) throws CHKDecodeException, IOException {
         // Overall hash already verified, so first job is to decrypt.
-               if((!(key.cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
-                               key.cryptoAlgorithm == 
Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
+               if(key.cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
             throw new UnsupportedOperationException();
         BlockCipher cipher;
         try {
-            cipher = new Rijndael(256, 256, key.cryptoAlgorithm == 
Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
+            cipher = new Rijndael(256, 256);
         } catch (UnsupportedCipherException e) {
             // FIXME - log this properly
             throw new Error(e);
@@ -180,7 +179,7 @@
         // Now encrypt the header, then the data, using the same PCFB instance
         BlockCipher cipher;
         try {
-            cipher = new Rijndael(256, 256, false);
+            cipher = new Rijndael(256, 256);
         } catch (UnsupportedCipherException e) {
             // FIXME - log this properly
             throw new Error(e);

Modified: branches/freenet-jfk/src/freenet/keys/ClientSSK.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/ClientSSK.java        2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/ClientSSK.java        2007-09-23 
09:58:34 UTC (rev 15282)
@@ -42,8 +42,7 @@
                if(extras.length < 5)
                        throw new MalformedURLException("Extra bytes too short: 
"+extras.length+" bytes");
                this.cryptoAlgorithm = extras[2];
-               if(!(cryptoAlgorithm == Key.ALGO_AES_PCFB_256_SHA256 ||
-                               (Key.ALLOW_INSECURE_CLIENT_SSKS && 
cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256)))
+               if(cryptoAlgorithm != Key.ALGO_AES_PCFB_256_SHA256)
                        throw new MalformedURLException("Unknown encryption 
algorithm "+cryptoAlgorithm);
                if(!Arrays.equals(extras, getExtraBytes()))
                        throw new MalformedURLException("Wrong extra bytes");
@@ -67,7 +66,7 @@
                }
                byte[] buf = md.digest();
                try {
-                       Rijndael aes = new Rijndael(256,256,cryptoAlgorithm == 
Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
+                       Rijndael aes = new Rijndael(256,256);
                        aes.initialize(cryptoKey);
                        aes.encipher(buf, buf);
                        ehDocname = buf;

Modified: branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/ClientSSKBlock.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -53,7 +53,7 @@
                Rijndael aes;
                try {
                        Logger.minor(this, 
"cryptoAlgorithm="+key.cryptoAlgorithm+" for "+getClientKey().getURI());
-                       aes = new 
Rijndael(256,256,key.cryptoAlgorithm==Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
+                       aes = new Rijndael(256,256);
                } catch (UnsupportedCipherException e) {
                        throw new Error(e);
                }

Modified: branches/freenet-jfk/src/freenet/keys/InsertableClientSSK.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/InsertableClientSSK.java      
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/InsertableClientSSK.java      
2007-09-23 09:58:34 UTC (rev 15282)
@@ -48,22 +48,19 @@

                byte[] extra = uri.getExtra();
                if(uri.getKeyType().equals("SSK")) {
-                       // FIXME: Remove once all SSKs migrated.
-                       if(extra == null) {
-                               keyType = Key.ALGO_INSECURE_AES_PCFB_256_SHA256;
-                       } else {
-                               // Formatted exactly as ,extra on fetching
-                               if(extra.length < 5)
-                                       throw new MalformedURLException("SSK 
private key ,extra too short");
-                               if(extra[1] != 1) {
-                                       throw new MalformedURLException("SSK 
not a private key");
-                               }
-                               keyType = extra[2];
-                               if(!(keyType == Key.ALGO_AES_PCFB_256_SHA256 ||
-                                               keyType == 
Key.ALGO_INSECURE_AES_PCFB_256_SHA256))
-                                       throw new 
MalformedURLException("Unrecognized crypto type in SSK private key");
+                       if(extra == null)
+                               throw new MalformedURLException("Inserting 
pre-1010 keys not supported");
+                       // Formatted exactly as ,extra on fetching
+                       if(extra.length < 5)
+                               throw new MalformedURLException("SSK private 
key ,extra too short");
+                       if(extra[1] != 1) {
+                               throw new MalformedURLException("SSK not a 
private key");
                        }
-               } else {
+                       keyType = extra[2];
+                       if(keyType != Key.ALGO_AES_PCFB_256_SHA256)
+                               throw new MalformedURLException("Unrecognized 
crypto type in SSK private key");
+               }
+               else {
                        throw new MalformedURLException("Not a valid SSK insert 
URI type: "+uri.getKeyType());
                }

@@ -113,7 +110,7 @@

         Rijndael aes;
         try {
-                       aes = new Rijndael(256, 256, cryptoAlgorithm == 
Key.ALGO_INSECURE_AES_PCFB_256_SHA256);
+                       aes = new Rijndael(256, 256);
                } catch (UnsupportedCipherException e) {
                        throw new Error("256/256 Rijndael not supported!");
                }
@@ -233,10 +230,5 @@
        public DSAGroup getCryptoGroup() {
                return Global.DSAgroupBigA;
        }
-
-       /** If true, this SSK is using the old, back compatible, insecure 
crypto algorithm. FIXME remove with support for old crypto. */
-       public boolean isInsecure() {
-               return cryptoAlgorithm == Key.ALGO_INSECURE_AES_PCFB_256_SHA256;
-       }

 }

Modified: branches/freenet-jfk/src/freenet/keys/Key.java
===================================================================
--- branches/freenet-jfk/src/freenet/keys/Key.java      2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/keys/Key.java      2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -38,12 +38,6 @@

     /** Code for 256-bit AES with PCFB and SHA-256 */
     static final byte ALGO_AES_PCFB_256_SHA256 = 2;
-    /** Code for old, insecure (only encrypts first 128 bits of block) 256-bit 
AES with PCFB and SHA-256.
-     * FIXME: REMOVE!! */
-       static final byte ALGO_INSECURE_AES_PCFB_256_SHA256 = 1;
-       
-       public static boolean ALLOW_INSECURE_CLIENT_CHKS;
-       public static boolean ALLOW_INSECURE_CLIENT_SSKS;

     protected Key(byte[] routingKey) {
        this.routingKey = routingKey;

Modified: branches/freenet-jfk/src/freenet/l10n/L10n.java
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/L10n.java     2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/l10n/L10n.java     2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -21,9 +21,7 @@
 *
 * @author Florent Daigni&egrave;re &lt;nextgens at freenetproject.org&gt;
 *
-* TODO: Maybe we ought to use the locale to set the default language.
 * TODO: Maybe base64 the override file ?
-* TODO: Add support for "custom", unknown languages ?
 *
 * comment(mario): for www interface we might detect locale from http requests?
 * for other access (telnet) using system locale would probably be good, but
@@ -37,7 +35,7 @@

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

        private static SimpleFieldSet currentTranslation = null;

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.de.properties    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -540,10 +540,6 @@
 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!
@@ -551,8 +547,6 @@
 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

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.en.properties    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.en.properties    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -114,6 +114,8 @@
 DarknetConnectionsToadlet.disabledShort=Disabled
 DarknetConnectionsToadlet.enterDescription=Enter description:
 DarknetConnectionsToadlet.connError=Connection failed (buggy node?)
+DarknetConnectionsToadlet.disconnectingShort=Disconnecting
+DarknetConnectionsToadlet.disconnecting=Disconnecting (we are currently 
removing the node, we need to tell it to go away and this can take a short time)
 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
@@ -147,7 +149,7 @@
 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.pasteReference=Paste the reference here (the node 
will usually automatically strip chat client line prefixes e.g. [14:56] 
<toad_>):
 DarknetConnectionsToadlet.privateNote=A private note concerning this peer
 DarknetConnectionsToadlet.privateNoteTitle=Private Note
 DarknetConnectionsToadlet.referenceCopyWarning=Node reference must be copied 
${bold}AS IS${/bold}. Modifying it will make it ${bold}useless${/bold}.
@@ -173,7 +175,7 @@
 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 (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.chooseNodeNameLong=Please enter a node name in the 
field below (we recommend a nickname possibly with an email address). This is 
so that your friends (trusted peers, which you have manually added) can easily 
tell your node from their other nodes. This is not visible to strangers 
(untrusted automatically added peers). Note that any friend or stranger may 
trivially identify you from your IP address, since you are connected to them, 
but they cannot easily tell what you are requesting.
 FirstTimeWizardToadlet.connectToStrangers=Connect to strangers?
 FirstTimeWizardToadlet.connectToStrangersLong=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?
@@ -386,14 +388,17 @@
 IPDetectorPluginManager.restricted=Your internet connection appears to be 
behind a "restricted cone" NAT (router). You should be able to connect to most 
other users.
 IPDetectorPluginManager.restrictedTitle=Restricted cone NAT detected
 IPDetectorPluginManager.suggestForwardPort= You may want to forward the port 
(UDP port number ${port}) manually. (See 
http://wiki.freenetproject.org/FirewallAndRouterIssues ).
+IPDetectorPluginManager.suggestForwardTwoPorts= You may want to forward the 
ports (UDP port numbers ${port1} and ${port2}) manually. (See 
http://wiki.freenetproject.org/FirewallAndRouterIssues ).
 IPDetectorPluginManager.suggestForwardPortWithLink= You may want to 
${link}forward the port${/link} (UDP port number ${port}) manually (or you may 
already have done so, Freenet cannot easily detect this).
 IPDetectorPluginManager.symmetric=Your internet connection appears to be 
behind a symmetric NAT or firewall. You will probably only be able to connect 
to users directly connected to the internet or behind restricted cone NATs.
 IPDetectorPluginManager.symmetricTitle=Symmetric firewall detected
 IPUndetectedUserAlert.detecting=Freenet is currently attempting to detect your 
external IP address. If this takes more than a few minutes there is something 
wrong...
-IPUndetectedUserAlert.detectingWithConfigLink=Freenet is currently attempting 
to detect your external IP address. If this takes more than a few minutes there 
is something wrong and you can use the Temporary IP Address Hint 
${link}configuration parameter${/link}. Also, it would be a good idea to 
forward the port ${port} on your router for UDP to make it easier to connect to 
your node.
-IPUndetectedUserAlert.unknownAddress=Freenet was unable to determine your 
external IP address (or the IP address of your NAT or Firewall). You can still 
exchange references with other people, however this will only work if the other 
user is not behind a NAT or Firewall. As soon as you have connected to one 
other user in this way, Freenet will be able to determine your external IP 
address. You can determine your current IP address and tell your node with the 
'Temporary IP address hint' ${link}configuration parameter${/link}. Also, it 
would be a good idea to forward the port ${port} on your router for UDP to make 
it easy to connect to your node.
+IPUndetectedUserAlert.detectingWithConfigLink=Freenet is currently attempting 
to detect your external IP address. If this takes more than a few minutes there 
is something wrong and you can use the Temporary IP Address Hint 
${link}configuration parameter${/link}.
+IPUndetectedUserAlert.unknownAddress=Freenet was unable to determine your 
external IP address (or the IP address of your NAT or Firewall). You can still 
exchange references with other people, however this will only work if the other 
user is not behind a NAT or Firewall. As soon as you have connected to one 
other user in this way, Freenet will be able to determine your external IP 
address. You can determine your current IP address and tell your node with the 
'Temporary IP address hint' ${link}configuration parameter${/link}.
 IPUndetectedUserAlert.unknownAddressTitle=Unknown external address
-IPUndetectedUserAlert.unknownAddressWithConfigLink=Freenet was unable to 
determine your external IP address (or the IP address of your NAT-device or 
firewall). You can still exchange references with other people, however this 
will only work if the other user is not behind a NAT-device or firewall. As 
soon as you have connected to one other user in this way, Freenet will be able 
to determine your external IP address. You can determine your current IP 
address and tell your node with the 'Temporary IP address hint' 
${link}configuration parameter${/link}. Also, it would be a good idea to 
forward the port ${port} on your router for UDP to make it easy to connect to 
your node.
+IPUndetectedUserAlert.unknownAddressWithConfigLink=Freenet was unable to 
determine your external IP address (or the IP address of your NAT-device or 
firewall). You can still exchange references with other people, however this 
will only work if the other user is not behind a NAT-device or firewall. As 
soon as you have connected to one other user in this way, Freenet will be able 
to determine your external IP address. You can determine your current IP 
address and tell your node with the 'Temporary IP address hint' 
${link}configuration parameter${/link}.
+IPUndetectedUserAlert.suggestForwardPort=Also, it would be a good idea to 
forward the port ${port} (UDP) on your router to make it easy to connect to 
your node.
+IPUndetectedUserAlert.suggestForwardTwoPorts=Also, it would be a good idea to 
forward the ports ${port1} and ${port2} (UDP) on your router to make it easy to 
connect to your node.
 InsertException.longError.10=Cancelled by user
 InsertException.longError.11=Meta string (most likely a '/') used in the URI
 InsertException.longError.12=Binary blob format error
@@ -476,7 +481,7 @@
 N2NTMToadlet.processingSend=Send Node to Node Text Message Processing
 N2NTMToadlet.queued=Queued: Peer not connected, so message queued for when it 
connects
 N2NTMToadlet.queuedTitle=Queued
-N2NTMToadlet.returnToFriends=Return to friends list
+N2NTMToadlet.returnToFriends=Return to Friends list
 N2NTMToadlet.sendMessage=Send Node to Node Text Message
 N2NTMToadlet.sendMessageShort=Send message
 N2NTMToadlet.sendStatus=N2NTM Send Status
@@ -541,10 +546,6 @@
 Node.storeSizeLong=Store size in bytes
 Node.swapRInterval=Swap request send interval (ms)
 Node.swapRIntervalLong=Interval between swap attempting to send swap requests 
in milliseconds. Leave this alone!
-NodeClientCore.allowInsecureCHK=Allow insecure CHKs?
-NodeClientCore.allowInsecureCHKLong=Before 1010, all CHKs were insecure (only 
half encrypted). Allow old CHKs?
-NodeClientCore.allowInsecureSSK=Allow insecure SSKs?
-NodeClientCore.allowInsecureSSKLong=Before 1010, all SSKs were insecure (only 
half encrypted). Allow old SSKs?
 NodeClientCore.couldNotFindOrCreateDir=Could not find or create directory
 NodeClientCore.downloadAllowedDirs=Directories downloading is allowed to
 NodeClientCore.downloadAllowedDirsLong=Semicolon separated list of directories 
to which downloads are allowed. "downloads" means downloadsDir, empty means no 
downloads to disk allowed, "all" means downloads allowed from anywhere. 
WARNING! If this is set to "all" any user can download any file to anywhere on 
your computer!
@@ -552,8 +553,6 @@
 NodeClientCore.downloadDirLong=The directory to save downloaded files into by 
default
 NodeClientCore.fileForClientStats=File to store client statistics in
 NodeClientCore.fileForClientStatsLong=File to store client throttling 
statistics in (used to decide how often to send requests)
-NodeClientCore.ignoreTooManyPathComponents=Ignore too many path components
-NodeClientCore.ignoreTooManyPathComponentsLong=If true, the node won't 
generate TOO_MANY_PATH_COMPONENTS errors when a URI is fed to it which has 
extra, meaningless subdirs (/blah/blah) on the end beyond what is needed to 
fetch the key (for example, old CHKs will often have filenames stuck on the end 
which weren't part of the original insert; this is obsolete because we can now 
include the filename, and it is confusing to be able to add arbitrary strings 
to a URI, and it makes them hard to compare). Only enable this option if you 
need it for compatibility with older apps; it will be removed soon.
 NodeClientCore.lazyResume=Complete loading of persistent requests after 
startup? (Uses more memory)
 NodeClientCore.lazyResumeLong=The node can load persistent queued requests 
during startup, or it can read the data into memory and then complete the 
request resuming process after the node has started up. Shorter start-up times, 
but uses more memory.
 NodeClientCore.maxUSKFetchers=Maximum number of allowed USK fetchers
@@ -609,7 +608,7 @@
 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)!
+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 oppressive 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
@@ -683,6 +682,7 @@
 PproxyToadlet.reload=Reload
 PproxyToadlet.returnToPluginPage=Return to plugin page
 PproxyToadlet.startedAtTitle=Started at
+PproxyToadlet.pluginStopping=Plugin Stopping
 PproxyToadlet.unload=Unload
 PproxyToadlet.unloadPluginTitle=Unload plugin?
 PproxyToadlet.unloadPluginWithName=Are you sure you wish to unload ${name}?
@@ -973,7 +973,7 @@
 WelcomeToadlet.shutdown=Shutdown
 WelcomeToadlet.shutdownConfirm=Are you sure you wish to shut down your Freenet 
node?
 WelcomeToadlet.shutdownConfirmTitle=Node Shutdown
-WelcomeToadlet.shutdownDone=The Freenet node has been successfully shut down.
+WelcomeToadlet.shutdownDone=The Freenet node is being shut down.
 WelcomeToadlet.shutdownNode=Shutdown the node
 WelcomeToadlet.splitfileErrorLabel=Splitfile-specific error:
 WelcomeToadlet.startIndexHeader=Index to start at

Copied: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.es.properties (from 
rev 15281, trunk/freenet/src/freenet/l10n/freenet.l10n.es.properties)
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.es.properties            
                (rev 0)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.es.properties    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -0,0 +1,511 @@
+BookmarkEditorToadlet.addBookmark=Agregar favorito
+BookmarkEditorToadlet.addCategory=Agregar Categoria
+BookmarkEditorToadlet.addNewBookmark=Agregar nuevo favorito
+BookmarkEditorToadlet.addNewCategory=Agregar nueva categoria
+BookmarkEditorToadlet.addedNewBookmark=El nuevo favorito ha sido agregado con 
exito.
+BookmarkEditorToadlet.addedNewBookmarkTitle=Nuevo favorito agregado
+BookmarkEditorToadlet.bookmarkDoesNotExist=El favorito "${bookmark}" no existe.
+BookmarkEditorToadlet.cancelCut=Cancelar cortar
+BookmarkEditorToadlet.changesSaved=Los cambios fueron guardados con exito.
+BookmarkEditorToadlet.changesSavedTitle=Modificaciones Guardadas
+BookmarkEditorToadlet.confirmDelete=Eliminar
+BookmarkEditorToadlet.cut=Cortar
+BookmarkEditorToadlet.delete=Eliminar
+BookmarkEditorToadlet.deleteBookmark=Borrar favorito
+BookmarkEditorToadlet.deleteCategory=Borrar Categoria
+BookmarkEditorToadlet.deleteCategoryConfirm=Seguro que quiere eliminar 
${bookmark}?
+BookmarkEditorToadlet.deleteSucceeded=El favorito se eliminio existosamente.
+BookmarkEditorToadlet.deleteSucceededTitle=Eliminacion exitosa
+BookmarkEditorToadlet.edit=Editar
+BookmarkEditorToadlet.editBookmarkTitle=Editar favorito
+BookmarkEditorToadlet.editCategoryTitle=Editar categoria
+BookmarkEditorToadlet.error=Error
+BookmarkEditorToadlet.invalidKey=La key es invalida
+BookmarkEditorToadlet.invalidKeyTitle=Key invalida
+BookmarkEditorToadlet.invalidKeyWithReason=Key invalida
+BookmarkEditorToadlet.keyLabel=Key:
+BookmarkEditorToadlet.moveDown=Abajo
+BookmarkEditorToadlet.moveUp=Arriba
+BookmarkEditorToadlet.myBookmarksTitle=Mis Favoritos
+BookmarkEditorToadlet.nameLabel=Nombre:
+BookmarkEditorToadlet.paste=Pegar
+BookmarkEditorToadlet.pasteTitle=Cortar/Pegar
+BookmarkEditorToadlet.save=Guardar
+BookmarkEditorToadlet.title=Editor de Favoritos
+BookmarkItem.bookmarkUpdated=El sitio ${name} de los favoritos se actualizo a 
la edici?n ${edition}.
+BookmarkItem.bookmarkUpdatedTitle=Notificaci?n actualizaci?n de favorito: 
${name}
+BookmarkItem.bookmarkUpdatedWithLink=El sitio ${link}${name}${/link} de los 
favoritos fue actualizado a la edici?n ${edition}.
+BookmarkItem.deleteBookmarkUpdateNotification=Ocultar notificaci?n
+BookmarkItem.unnamedBookmark=Favorito sin nombre
+BookmarkManager.list=Lista de Favoritos
+BookmarkManager.listLong=Lista de Freesites favoritos
+BookmarkManager.malformedBookmark=Bookmark Malformado
+CSSTokenizerFilter.invalidURLContents=Contenidos de url() inv?lidos
+CSSTokenizerFilter.supplementalCharsNotSupported=CARACTERES UCS-4 SOBRE 0xFFFF 
NO SOPORTADOS!
+ConfigToadlet.appliedFailureExceptions=Los cambios en la configuraci?n se 
aplicaron con las siguientes excepciones:
+ConfigToadlet.appliedFailureTitle=No se aplico la configuraci?n
+ConfigToadlet.appliedSuccess=Los cambios en la configuraci?n se aplicaron con 
?xito
+ConfigToadlet.appliedTitle=Configuraci?n Aplicada
+ConfigToadlet.apply=Aplicar
+ConfigToadlet.configNavTitle=Barra de Navegaci?n de la Configuraci?n
+ConfigToadlet.defaultIs=El default para esa opcion de la configuracion es: 
'${default}'.
+ConfigToadlet.fullTitle=Configuraci?n del nodo ${name}
+ConfigToadlet.reset=Resetear
+ConfigToadlet.returnToNodeConfig=Volver a la configuraci?n del nodo
+ConfigToadlet.shortTitle=Configuraci?n
+ConfigToadlet.title=Configuraci?n del Nodo de Freenet
+ContentDataFilter.warningUnknownCharsetTitle=Advertencia: Juego de caracteres 
(${charset}) desconocido
+ContentFilter.imageJpegReadAdvice=Imagen JPG - posiblemente no peligrosa
+DarknetConnectionsToadlet.activityTitle=Actividad
+DarknetConnectionsToadlet.add=Agregar
+DarknetConnectionsToadlet.addPeerTitle=Agregar otro contacto
+DarknetConnectionsToadlet.backedOffShort=Backed off
+DarknetConnectionsToadlet.cantFetchNoderefURL=No se pudo tomar la referencia 
de ${url}. Intente otra vez.
+DarknetConnectionsToadlet.confirmRemoveNode=Estas seguro que queres quitar el 
nodo "${name}" ? Si tiene menos de una semana abajo puede ser un nodo que 
quizas no este conectado las 24 horas.
+DarknetConnectionsToadlet.confirmRemoveNodeTitle=Confirmar
+DarknetConnectionsToadlet.confirmRemoveNodeWarningTitle=Quitar Nodo
+DarknetConnectionsToadlet.connError=Fallo en la coneccion (buggy node?)
+DarknetConnectionsToadlet.connErrorShort=Error de coneccion
+DarknetConnectionsToadlet.connected=Conectado: estamos conectados a los 
siguientes nodos
+DarknetConnectionsToadlet.connectedShort=Conectado
+DarknetConnectionsToadlet.disabledShort=Desactivado
+DarknetConnectionsToadlet.enterDescription=Ingresar descripcion:
+DarknetConnectionsToadlet.failedToAddNodeInternalErrorTitle=Error al agregar 
el nodo: Error interno
+DarknetConnectionsToadlet.failedToAddNodeTitle=Error al agregar contacto
+DarknetConnectionsToadlet.fileReference=Elegi el archivo que contiene la 
referencia desde aqui:
+DarknetConnectionsToadlet.fproxyDisabled=FProxy (esta interfaz web) esta 
desactivado
+DarknetConnectionsToadlet.fproxyPort=FProxy: ${port}/TCP (esta interface web)
+DarknetConnectionsToadlet.go=Ir
+DarknetConnectionsToadlet.idleTime=Cuanto paso desde que estamos conectados al 
nodo o desde la ultima vez que estuvimos conectados
+DarknetConnectionsToadlet.idleTimeTitle=Conectado / Inactivo
+DarknetConnectionsToadlet.invalidSignature=No se pudo verificar la firma de la 
referencia (${error}).
+DarknetConnectionsToadlet.ipAddressTitle=Direccion
+DarknetConnectionsToadlet.listenOnlyShort=Solo escuchar
+DarknetConnectionsToadlet.listeningShort=Escuchando
+DarknetConnectionsToadlet.myFriends=Mis amigos (contactos de confianza 
agregados por mi)
+DarknetConnectionsToadlet.nameClickToMessage=El nombre del nodo. Cliclea en el 
nombre del nodo para enviar un mensaje N2NTM (Mensaje de texto de Nodo a Nodo)
+DarknetConnectionsToadlet.nameTitle=Nombre
+DarknetConnectionsToadlet.neverConnected=Nunca conectado: El nodo nunca se 
conecto con estos contactos.
+DarknetConnectionsToadlet.neverConnectedShort=Nunca conectado
+DarknetConnectionsToadlet.nodePortsTitle=Puertos usados por mi nodo
+DarknetConnectionsToadlet.notConnectedShort=Desconectado
+DarknetConnectionsToadlet.pasteReference=Pegar la referencia aqui:
+DarknetConnectionsToadlet.privateNote=Nota privada acerca de este contacto
+DarknetConnectionsToadlet.privateNoteTitle=Notas personales
+DarknetConnectionsToadlet.referenceCopyWarning=La referencia del nodo debe ser 
copiada ${bold}COMO ESTA${/bold}. Modificarla la hace ${bold}in?til${/bold}.
+DarknetConnectionsToadlet.remove=Eliminar!
+DarknetConnectionsToadlet.removePeers=Eliminar contactos seleccionados
+DarknetConnectionsToadlet.selectAction=-- Seleccione accion --
+DarknetConnectionsToadlet.statusTitle=Estado
+DarknetConnectionsToadlet.tmciDisabled=El servicio TMCI esta desactivado 
(cliente de linea de comando a la cual se accede con telnet)
+DarknetConnectionsToadlet.tooNewShort=Muy nueva
+DarknetConnectionsToadlet.triedToAddSelf=No podes agregar tu propio nodo a la 
lista de tus contactos.
+DarknetConnectionsToadlet.unknownAddress=(direccion desconocida)
+DarknetConnectionsToadlet.updateChangedPrivnotes=Actualizar las notas privadas 
que hayan cambiado
+DarknetConnectionsToadlet.urlReference=Ingresa la URL con la referencia aqui:
+DarknetConnectionsToadlet.versionTitle=Versi?n
+ExtOldAgeUserAlert.extTooOld=El archivo freenet-ext.jar esta desactualizado: 
te recomendamos (MUCHO) actualizarlo, podes hacerlo desde 
http://downloads.freenetproject.org/alpha/freenet-ext.jar
+ExtOldAgeUserAlert.extTooOldTitle=Freenet-ext es muy viejo
+FProxyToadlet.abortToHomepage=Cancelar y volver a la pagina de inicio
+FProxyToadlet.cantBindPort=No se puede iniciar FProxy en ese puerto!
+FProxyToadlet.config=configurar tu nodo
+FProxyToadlet.configTitle=Configuraci?n
+FProxyToadlet.dangerousRSSSubtitle=RSS puede ser peligroso
+FProxyToadlet.downloadInBackgroundToDisk=Descargar en segundo plano y guardar 
en directorio de descargas
+FProxyToadlet.errorIsFatal=Este es un error fatal. Es poco probable que 
reintentado se solucione.
+FProxyToadlet.errorWithReason=Error: ${error}
+FProxyToadlet.expectedMimeType=Se esperaba MIME tipo: ${mime}
+FProxyToadlet.explanationTitle=Explicaci?n
+FProxyToadlet.fetchLargeFileAnywayAndDisplay=Descargar de todas formas y 
mostrar en el navegador
+FProxyToadlet.fileInformationTitle=Informaci?n del Archivo
+FProxyToadlet.filenameLabel=Nombre del Archivo:
+FProxyToadlet.friends=administrar conecciones f2f (amigo con amigo)
+FProxyToadlet.friendsTitle=Amigos
+FProxyToadlet.goBack=Volver
+FProxyToadlet.goBackToPrev=Volver a la pagina anterior
+FProxyToadlet.invalidKeyTitle=Key Invalida
+FProxyToadlet.largeFile=Archivo Grande
+FProxyToadlet.largeFileExplanationAndOptions=El archivo que solicitaste parece 
ser un archivo muy grande. Archivos de este tamano, generalmente, no suelen ser 
interpretados por el navegador y van a demorar en bajar. Puedes:
+FProxyToadlet.mayChange=(puede cambiar)
+FProxyToadlet.notFoundTitle=No encontrado
+FProxyToadlet.openRSSForce=${link}Clickear aqui${/link} para abrir el archivo 
como ${mime} (esto ${bold}podria ser peligroso ${/bold} en Internet Explorer 7 
o FF2).
+FProxyToadlet.opennetTitle=Desconocidos
+FProxyToadlet.options=Las opciones son:
+FProxyToadlet.pathNotFound=La ruta especificada no es valida
+FProxyToadlet.pathNotFoundTitle=Ruta No Encontrada
+FProxyToadlet.plugins=configurar y manejar plugins
+FProxyToadlet.queueTitle=Cola de Archivos
+FProxyToadlet.retryNow=Intentar nuevamente
+FProxyToadlet.sizeLabel=Tama?o:
+FProxyToadlet.sizeUnknown=Tama?o: desconocido
+FProxyToadlet.stats=ver estad?sticas
+FProxyToadlet.statsTitle=Estad?sticas
+FProxyToadlet.unableToRetrieve=Freenet no pudo descargar este archivo.
+FProxyToadlet.unknownMIMEType=Tipo MIME: desconocido
+FProxyToadlet.welcomeTitle=Inicio
+FcpServer.allowedHosts=Hosts permitidos (leer la advertencia!)
+FcpServer.allowedHostsFullAccess=Hosts con acceso total
+FcpServer.allowedHostsFullAccessLong=Lista de direcciones IPs de quienes 
tienen acceso total al servicio FCP. ADVERTENCIA: este tipo de acceso permite a 
quien lo usa reiniciar el nodo, reconfigurarlo, etc.
+FcpServer.allowedHostsLong=Direcciones IPs a las cuales se les permite el 
acceso al servicio FCP. ADVERTENCIA! Cualquiera que tenga acceso al servicio 
FCP puede subir archivos a los cuales el nodo tiene acceso, o bajar archivos al 
disco (aunque el nodo tratara de no sobreescribir archivos).
+FcpServer.bindToLong=Direcci?n IP en donde iniciar el servicio FCP
+FcpServer.couldNotChangeBindTo=No se pudo cambiar la direccion de inicio del 
FCP: ${error}.
+FcpServer.downloadsFileCanCreateCannotReadOrWrite=Se creo el archivo pero no 
se puede leerlo y escribirlo
+FcpServer.downloadsFileDoesNotExistCannotCreate=El archivo no existe y no se 
puede crear
+FcpServer.downloadsFileExistsCannotReadOrWrite=El archivo existe pero no se 
puede leer y escribirlo
+FcpServer.downloadsFileUnreadable=El archivo existe pero no se puede leer
+FcpServer.enablePersistentDownload=Activar descargas persistentes?
+FcpServer.filenameToStorePData=Archivo en donde guardar las descargas 
persistentes
+FcpServer.filenameToStorePDataLong=Archivo en donde guardar detalles de las 
descargas persistentes
+FcpServer.intervalBetweenWrites=Intervalo entre escritura de las descargas 
persistentes a disco
+FcpServer.intervalBetweenWritesLong=Intervalo entre la escritura de las 
descargas persistentes a disco
+FcpServer.isEnabled=Esta FCP activado?
+FcpServer.isEnabledLong=Desde aqui podemos activar o desactivar el servicio de 
 FCP
+FcpServer.portNumber=Puerto para FCP
+FcpServer.portNumberLong=En que numero de puerto va a abrirse FCP
+FetchException.longError.13=Datos no encontrados
+FetchException.longError.21=Muy grande
+FetchException.longError.25=Cancelado
+FetchException.longError.26=Archivo reiniciado
+FetchException.longError.27=Redireccion permanente: usar la nueva URI
+FetchException.shortError.12=Error en archivos temporales
+FetchException.shortError.13=Datos no encontrados
+FetchException.shortError.14=Ruta no encontrada
+FetchException.shortError.17=Error interno
+FetchException.shortError.18=Fallo en la transferencia
+FetchException.shortError.20=URI Invalida
+FetchException.shortError.21=Muy grande
+FetchException.shortError.22=Metadatos demasiado grandes
+FileOffer.acceptTransferButton=Aceptar Transferencia
+FileOffer.askUserTitle=Transferencia directa de archivo
+FileOffer.commentLabel=Comentario:
+FileOffer.failedReceiveTitle=Error al recibir el archivo
+FileOffer.fileLabel=Archivo:
+FileOffer.mimeLabel=Tipo MIME:
+FileOffer.offeredFileHeader=El nodo ${name} esta ofreciendo un archivo:
+FileOffer.rejectTransferButton=Rechazar Transferencia
+FileOffer.sizeLabel=Tama?o:
+FileOffer.succeededReceiveHeader=La transferencia del archivo ${filename} 
desde ${node} fue exitosa.
+FileOffer.succeededReceiveTitle=Archivo recibido con exito
+FirstTimeWizardToadlet.bandwidthLimit=Limites de ancho de banda
+FirstTimeWizardToadlet.chooseNodeName=Nombre del nodo requerido!
+FirstTimeWizardToadlet.congratz=Bienvenido a bordo!
+FirstTimeWizardToadlet.connectToStrangers=Conectarse a extranios?
+FirstTimeWizardToadlet.continue=Continuar
+FirstTimeWizardToadlet.datastoreSize=Tama?o del datastore
+FirstTimeWizardToadlet.enableOpennet=Conectarse automaticamente a nodos 
desconocidos?
+FirstTimeWizardToadlet.homepageTitle=Asistente de instalacion!
+FirstTimeWizardToadlet.iDoTrust=Confias en la gente conectada a la interface 
${interface} (${ip}) ?
+FirstTimeWizardToadlet.isNetworkTrusted=Es tu red local confiable/segura ?
+FirstTimeWizardToadlet.skipWizard=No soy un novato, saltear el asistente!
+FirstTimeWizardToadlet.step1Title=Asitente de instalaci?n! - Amigos y extra?os
+FirstTimeWizardToadlet.step3Title=Asistente de instalacion! - Limites de ancho 
de banda
+FirstTimeWizardToadlet.step5Title=Asistente de instalaci?n! - Configuraci?n de 
Red
+FirstTimeWizardToadlet.step6Title=Asistente de instalacion! - Felicitaciones, 
el nodo ha sido configurado
+GIFFilter.invalidHeader=El archivo no contiene una cabecera GIF valida.
+GIFFilter.invalidHeaderTitle=Cabecera invalida
+GIFFilter.tooShort=El archivo es muy chico para ser un GIF.
+GIFFilter.tooShortTitle=Muy corto
+HTMLFilter.failedToParseLabel=El filtro HTML fallo al parsear la pagina
+IPDetectorPluginManager.direct=Al parecer estas conectado directamente a 
internet. Felicitaciones, seguramente vas a poder conectarte a cualquier otro 
nodo de Freenet.
+IPDetectorPluginManager.directTitle=Coneccion directa a internet detectada
+IPDetectorPluginManager.noConnectivityTitle=No hay conectividad UDP
+IPUndetectedUserAlert.unknownAddressTitle=Direcci?n externa desconocida
+InsertException.longError.10=Cancelado por el usuario
+InsertException.longError.3=Error Interno
+InsertException.shortError.1=Direccion URI invalida
+InsertException.shortError.10=Cancelado
+InsertException.shortError.3=Error interno
+InsertException.shortError.4=Sobrecarga o Tiempo de espera agotado
+InsertException.shortError.5=Ruta no encontrada
+JPEGFilter.tooShort=El archivo es muy chico para ser un JPEG.
+KnownUnsafeContentTypeException.dangerousLinksLabel=Links peligrosos:
+KnownUnsafeContentTypeException.dangerousMetadataLabel=Meta-datos peligrosos:
+KnownUnsafeContentTypeException.dangerousScriptsLabel=Script peligroso:
+LocalFileInsertToadlet.checkPathReadable=Checkear que la ruta especificada 
pueda ser leida por el usuario bajo el cual corre Freenet.
+LocalFileInsertToadlet.dirAccessDenied=No puedes acceder a este directorio
+LocalFileInsertToadlet.dirCannotBeRead=El directorio "${path}" no puede ser 
leido.
+LocalFileInsertToadlet.fileHeader=Archivo
+LocalFileInsertToadlet.insert=Insertar
+LocalFileInsertToadlet.sizeHeader=Tama?o
+LogConfigHandler.dirNameLong=Directorio en donde almancenar los logs
+LogConfigHandler.enabled=Activar logeo?
+LogConfigHandler.maxZippedLogsSize=M?ximo espacio en disco a usar para los 
logs viejos
+LogConfigHandler.maxZippedLogsSizeLong=Espacio m?ximo en disco a utilizar por 
logs viejos
+LogConfigHandler.rotationInterval=Intervalo en la rotaci?n de logs
+MeaningfulNodeNameUserAlert.noNodeNickTitle=El nombre de tu nodo no ha sido 
configurado
+N2NTMToadlet.failed=Mensaje no enviado: el contacto no esta conectado
+N2NTMToadlet.friends=Amigos
+N2NTMToadlet.returnToFriends=Volver a la lista de amigos
+N2NTMToadlet.sendMessage=Enviar mensaje de Nodo a Nodo
+N2NTMToadlet.sendMessageShort=Enviar mensaje
+N2NTMToadlet.sentTitle=Enviado
+N2NTMToadlet.tooLong=Los mensajes de nodo a nodo estan limitados a 1024 
caracteres
+N2NTMToadlet.tooLongTitle=Muy largo
+N2NTMUserAlert.delete=Borrar
+N2NTMUserAlert.header=De: ${from} (escrito ${composed} | enviado ${sent} | 
recibido ${received})
+N2NTMUserAlert.reply=Responder
+N2NTMUserAlert.title=Mensaje Nodo a Nodo ${number} de ${peername} (${peer})
+Node.bandwidthLimitMustBePositiveOrMinusOne=El limite de ancho de banda debe 
ser positivo o -1
+Node.bwlimitMustBePositive=El limite de ancho de banda debe ser positivo
+Node.databaseMemory=Limite de memoria a usar por el datastore
+Node.invalidStoreSize=El tama?o del store debe ser al menos de 32MB
+Node.nodeDir=Directorio del nodo
+Node.nodeName=Nombre para el nodo de Freenet
+Node.nodeNameLong=Nombre del nodo, solo visible a tus contactos/amigos.
+Node.port=Numero de puerto para FCP, (protocolo UDP)
+Node.storeSizeLong=Tamanio del datastore en bytes
+NodeClientCore.downloadDir=Directorio de descargas por defecto
+NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=Debe ser mayor a cero
+NodeClientCore.movingTempDirOnTheFlyNotSupported=Mover el directorio temporal 
mienras Freenet esta corriendo no esta soportado todavia
+NodeClientCore.tempDir=Directorio de archivos temporales
+NodeClientCore.tempDirLong=Nombre del directorio en donde poner los archivos 
temporales
+NodeIPDectector.inclLocalAddress=Incluir direccion de red local en la 
referencia del nodo
+NodeIPDectector.inclLocalAddressLong=Incluye la direccion de la red local (LAN 
y localhost) en la referencia del nodo. Solo sirve si ambos nodos que se estan 
tratand de conectar tienen esta opcion activada (allowLocalAddresses=true).
+NodeIPDectector.ipOverride=Forzar direccion IP
+NodeIPDectector.tempAddressHint=Direccion IP Temporaria
+NodeIPDectector.tempAddressHintLong=Direccion IP temporaria, pone aca tu 
direccion IP actual, es borrada despues de usarse
+NodeIPDetector.unknownHostErrorInIPOverride=Host desconocido: ${error}
+NodeStat.statsPersister=Archivo en donde guardar las estad?sticas del nodo
+NodeUpdateManager.enabled=Buscar y descaracar nuevas versiones
+NodeUpdateManager.enabledLong=Indica si el nodo debe buscar autom?ticamente 
nuevas versiones de Freenet. Si es as?, las nuevas versiones se detectaran y 
descargaran autom?ticamente, pero no necesariamente se instalaran. Esta 
configuraci?n se vuelve siempre a 'falso' a menos que el nodo corra dentro del 
wrapper.
+NodeUpdateManager.extURI=Llave para actualizar freenet-ext.jar
+NodeUpdateManager.extURILong=Llave que utilizara el nodo para buscar 
actualizaciones de freenet-ext.jar
+NodeUpdateManager.installNewVersions=Instalar autom?ticamente nuevas versiones
+NodeUpdateManager.installNewVersionsLong=Debe el nodo actualizarse a la ultima 
versi?n sin preguntar?
+NodeUpdateManager.invalidUpdateURI=Actualizacion Invalida URI: ${error}
+NodeUpdateManager.noUpdateWithoutWrapper=No se puede actualizar por que no se 
esta corriendo en nodo desde el wrapper
+NodeUpdateManager.revocationURI=Llave de revocaci?n
+NodeUpdateManager.revocationURILong=URI de la llave de revocaci?n. Si el nodo 
encuentra esta llave mostrara su contenido y desactivara la actualizaci?n 
automatica.
+NodeUpdateManager.updateCatastropheTitle=Falla catastr?fica en la 
actualizaci?n!
+NodeUpdateManager.updateFailedTitle=Fallo en la actualizaci?n!
+NodeUpdateManager.updateURI=Llave de actualizaci?n del nodo.
+NodeUpdateManager.updateURILong=Llave que utilizar? el nodo para buscar sus 
actualizaciones.
+OpennetUserAlert.warningTitle=Advertencia: Modo Promiscuo (opennet) Activado: 
Tu nodo se conectara a desconocidos.
+PNGFilter.invalidHeaderTitle=No es un PNG - cabecera invalida
+PeerManagerUserAlert.connErrorTitle=Error al conectar con algunos nodos
+PeerManagerUserAlert.noConnsTitle=No existen conexiones abiertas
+PeerManagerUserAlert.noPeersTitle=No se encontraron peers
+PeerManagerUserAlert.onlyFewConnsTitle=Solo ${count} coneccion(es) abiertas
+PeerManagerUserAlert.tooManyConnsTitle=Demasiadas conexiones abiertas
+PeerManagerUserAlert.tooManyDisconnectedTitle=Muchos nodos desconectados
+PeerManagerUserAlert.tooManyNeverConnectedTitle=Muchos nodos que no se 
conectaron todav?a
+PeerManagerUserAlert.tooManyPeersTitle=Mucho nodos
+PeerManagerUserAlert.tooOldNeverConnectedPeersTitle=Peer(s) nunca conectados 
muy antiguos
+PluginManager.loadedPlugins=Plugins a cargar al inicio
+PluginManager.loadedPluginsLong=Lista de plugins que se inician cuando arranca 
el nodo
+PluginToadlet.addPluginTitle=Agregar un plugin
+PluginToadlet.failedToLoadPlugin=Error al cargar el plugin
+PluginToadlet.internalNameTitle=Nombre interno
+PluginToadlet.loadPluginCommand=Cargar Plugin
+PluginToadlet.noWebInterface=El plugin no tiene interface web, asi que no hay 
nada que mostrar.
+PluginToadlet.noWebInterfaceTitle=El plugin no tiene interface web
+PluginToadlet.pluginList=Lista de Plugins
+PluginToadlet.pluginListTitle=Lista de Plugins
+PluginToadlet.pluginNotFoundTitle=Plugin no encontrado
+PluginToadlet.returnToPluginsWithLinks=${link}Volver${/link} a la lista de 
plugins.
+PluginToadlet.visit=Visitar
+PproxyToadlet.classNameTitle=Nombre de la Clase
+PproxyToadlet.internalIDTitle=ID Interno
+PproxyToadlet.loadPluginLabel=Cargar plugin:
+PproxyToadlet.noPlugins=No hay plugins cargados
+PproxyToadlet.pluginNotFoundReload=El plugins especificado no pudo ser 
encontrado para recargarse.
+PproxyToadlet.pluginNotFoundReloadTitle=Plugin no encontrado (recargando)
+PproxyToadlet.pluginUnloaded=Plugin detenido
+PproxyToadlet.pluginUnloadedWithName=El plugin ${name} ha sido detenido.
+PproxyToadlet.plugins=Plugins
+PproxyToadlet.pluginsWithNodeName=Plugins de ${name}
+PproxyToadlet.reload=Recargar
+PproxyToadlet.returnToPluginPage=Volver a la pagina de plugins
+PproxyToadlet.startedAtTitle=Empezado a las
+PproxyToadlet.unload=Descargar
+PproxyToadlet.unloadPluginTitle=Detener plugin?
+PproxyToadlet.unloadPluginWithName=Esta seguro que desea detener ${name}?
+QueueToadlet.DUinProgress=Lista actual de Subidas
+QueueToadlet.DinProgress=Descargas en progreso
+QueueToadlet.UinProgress=Subida en progreso
+QueueToadlet.change=Cambiar
+QueueToadlet.completedDU=Directorios de subida completados
+QueueToadlet.completedDinDownloadDirectory=Completado: Descargas en el 
directorio de descargas (${size})
+QueueToadlet.completedDinTempDirectory=Completados: Descargas completadas al 
directorio temporal (${size})
+QueueToadlet.completedDtoDisk=Descargas a disco completadas
+QueueToadlet.completedDtoTemp=Descargas completadas al directorio temporal
+QueueToadlet.completedU=Completados: Subidas (${size})
+QueueToadlet.completedUDirectory=Completados: Directorio de Subidas (${size})
+QueueToadlet.download=Descarga
+QueueToadlet.errorAccessDenied=Error, acceso denegado!
+QueueToadlet.errorAccessDeniedFile=La actual configuracion del nodo te prohibe 
subir el archivo "${file}".
+QueueToadlet.errorDToDisk=No se puede bajar a disco
+QueueToadlet.errorDToDiskConfig=La configuracion actual del nodo no te permite 
realizar descargas en el directorio de descargas.
+QueueToadlet.errorDownloadNotCompleted=Descarga No Completada
+QueueToadlet.errorDownloadNotFound=Descarga no encontrada
+QueueToadlet.errorDownloadNotFoundExplanation=La descarga no pudo ser 
encontrada. Puede ser que ya se haya eliminado?
+QueueToadlet.errorInvalidURI=URI invalida
+QueueToadlet.errorInvalidURIToD=La URI es invalida y no puede ser descargada.
+QueueToadlet.errorInvalidURIToU=No especificaste una URI valida para insertar 
el archivo
+QueueToadlet.errorNoFileOrCannotRead=El archivo no existe o no puede ser leido
+QueueToadlet.errorNoFileSelected=Ningun archivo seleccionado
+QueueToadlet.errorNoFileSelectedU=No seleccionaste un archivo para subir
+QueueToadlet.errorNoKey=No se especifico una key a bajar
+QueueToadlet.errorNoKeyToD=No especificaste una key para descargar!
+QueueToadlet.failedD=Descargas fallidas
+QueueToadlet.failedDU=Lista de subidas falladas
+QueueToadlet.failedToRemove=Error al eliminar ${id}: ${message}
+QueueToadlet.failedToRemoveId=Error al remover: ${id}
+QueueToadlet.failedToRemoveRequest=Error al eliminar el pedido
+QueueToadlet.failedToRestart=Error al reiniciar: ${id}
+QueueToadlet.failedToRestartRequest=Error al iniciar pedido
+QueueToadlet.failedU=Subidas fallidas
+QueueToadlet.fcpIsMissing=No se encuentra el servicio FCP
+QueueToadlet.fileName=Nombre del archivo
+QueueToadlet.files=Archivos
+QueueToadlet.follow=Follow Redirect
+QueueToadlet.globalQueueIsEmpty=La cola de descargas esta vacia
+QueueToadlet.identifier=Identificador
+QueueToadlet.insertAs=Insertar como:
+QueueToadlet.insertFile=Insertar archivo
+QueueToadlet.legend=Leyendas
+QueueToadlet.mimeType=Tipo MIME
+QueueToadlet.noTaskOnGlobalQueue=No hay ninguna tarea a realizar
+QueueToadlet.none=ninguna
+QueueToadlet.panicButton=Boton del Panico!
+QueueToadlet.panicButtonConfirmation=Eliminar todos los pedidos sin confirmar!
+QueueToadlet.persistence=Persistencia
+QueueToadlet.persistenceForever=para-siempre
+QueueToadlet.persistenceNone=ninguna
+QueueToadlet.persistenceReboot=reiniciar
+QueueToadlet.pleaseEnableFCP=Necesitas habilitas el servidor FCP para acceder 
a esta pagina
+QueueToadlet.priority=Prioridad
+QueueToadlet.priority0=emergencia
+QueueToadlet.priority1=muy alta
+QueueToadlet.priority2=alta
+QueueToadlet.priority3=media
+QueueToadlet.priority4=baja
+QueueToadlet.priority5=muy baja
+QueueToadlet.priority6=no termina mas
+QueueToadlet.progress=Progreso
+QueueToadlet.progressbarAccurate=Este indicador de progreso es exacto
+QueueToadlet.progressbarNotAccurate=El valor de este medidor de progreso puede 
llegar a cambiar mientras el proceso de descarga no haya finalizado
+QueueToadlet.reason=Razon
+QueueToadlet.remove=Eliminar
+QueueToadlet.requestNavigation=Barra de Navegacion
+QueueToadlet.restart=Reiniciar
+QueueToadlet.size=Tamanio
+QueueToadlet.starting=EMPEZANDO
+QueueToadlet.title=Cola de Descargas de ${nodeName}
+QueueToadlet.totalSize=Tamanio Total
+QueueToadlet.unknown=Desconocido
+QueueToadlet.warningUnsafeContent=Contenido potencialmente peligroso
+QueueToadlet.warningUnsafeContentExplanation=El archivo que estas intentando 
descargar no esta filtrado por el filtro de contenido de Freenet. Esto quiere 
decir que tu anonimato podria llegar a ser comprometido si abris este archivo.
+QueueToadlet.wipD=En progreso: Descargas (${size})
+QueueToadlet.wipDU=En progreso: Lista de subidas (${size})
+QueueToadlet.wipU=En progreso: Subidas (${size})
+SimpleToadletServer.cssNameLong=Nombre del CSS que FProxy va a usar
+SimpleToadletServer.illegalCSSName=El nombre del CSS no debe contener barras o 
comillas.
+StaticToadlet.pathNotFound=La ruta especificada no existe.
+StaticToadlet.pathNotFoundTitle=Ruta no encontrada
+StatisticsToadlet.cpus=CPUs disponibles: ${count}
+StatisticsToadlet.getLogs=Traer el ultimo archivo de log del nodo
+StatisticsToadlet.inputRate=Entrada: ${rate}/sec (of ${max})
+StatisticsToadlet.jvmVersion=Version de JavaVirtualMachine: ${version}
+StatisticsToadlet.noRequests=El nodo no esta procesando ningun pedido en este 
momento.
+StatisticsToadlet.osArch=Arquitectura del SO: ${arch}
+StatisticsToadlet.osVersion=Version del Sistema Operativo: ${version}
+StatisticsToadlet.outputRate=Salida: ${rate}/sec (of ${max})
+StatisticsToadlet.totalInput=Total Entrada: ${total} (${rate}/sec)
+StatisticsToadlet.totalOutput=Total Salida: ${total} (${rate}/sec)
+StatisticsToadlet.usedMemory=Memoria usada por Java: ${memory}
+StatisticsToadlet.versionTitle=Informacion sobre la version del nodo
+SymlinkerToadlet.symlinks=Links simbolicos en ToadletServer
+TextModeClientInterfaceServer.allowedHosts=Hosts permitidos
+TextModeClientInterfaceServer.allowedHostsLong=Hostnames o direcciones IP que 
les esta permitido conectar al TMCI. Puede ser una lista separada por coma de 
nombres de host, una direccion IP o del tipo 192.168.0.0/24 (este ultimo 
permite acceso a toda la LAN)
+TextModeClientInterfaceServer.bindTo=Direcci?n IP en donde iniciar
+TextModeClientInterfaceServer.bindToLong=Direccion IP en donde iniciar
+TextModeClientInterfaceServer.enableInputOutput=Activar en stdout/stdin?
+TextModeClientInterfaceServer.enableInputOutputLong=Activar TMCI en la 
entrada/salida estandar?
+TextModeClientInterfaceServer.enabled=Activar TMCI
+TextModeClientInterfaceServer.enabledLong=Activar TMCI, una interfaz de texto 
para manejar funcionalidades basicas del nodo.
+TextModeClientInterfaceServer.telnetPortNumber=Puerto de telnet
+TextModeClientInterfaceServer.telnetPortNumberLong=Numero de puerto en donde 
se va a abrir la TMCI
+Toadlet.cancel=Cancelar
+Toadlet.clickHere=Clickear aqui
+Toadlet.homepage=Pagina de Inicio
+Toadlet.internalErrorPleaseReport=Error interno: favor de reportar
+Toadlet.internalErrorTitle=Error interno
+Toadlet.nodeHomepage=Pagina de Inicio
+Toadlet.notSupportedTitle=No soportado
+Toadlet.notSupportedWithClass=Tu browser envio una peticion que Freenet 
(${class}) no pudo entender
+Toadlet.permRedirectWithReason=Redireccion permanente: ${reason}
+Toadlet.returnToNodeHomepage=Volver a la homepage del nodo
+Toadlet.returnToPrevPage=Volver a la pagina anterior
+Toadlet.tempRedirectWithReason=Redireccion temporal: ${reason}
+Toadlet.unauthorized=No esta permitido a acceder a esta pagina
+Toadlet.yes=Si
+ToadletContextImpl.methodNotAllowed=M?todo HTTP no permitido
+ToadletContextImpl.parseErrorWithError=Error de parseo: ${error}
+ToadletContextImpl.uriParseErrorTitle=Error de parseo en la URI
+TranslationToadlet.bracketUpdateTranslation=(actualizar la traduccion)
+TranslationToadlet.contributingToLabelWithLang=Estas contribuyendo con la 
traducci?n del idioma ${lang}:
+TranslationToadlet.downloadTranslationsFile=Descargar tu archivo de 
traducciones
+TranslationToadlet.hideAlreadyTranslated=Esconder entradas ya traducidas
+TranslationToadlet.remove=Eliminar
+TranslationToadlet.updateTranslationCommand=Actualizar la traduccion!
+UpdateDeployContext.updateFailedCannotDeleteOldConfig=No se puede borrar 
${old} por eso no se puede renombrar. Fallo en la actualizaci?n.
+UpdatedVersionAvailableUserAlert.alsoDownloadedNewExtJar=Tu nodo tambien bajo 
una nueva version de Freenet extra jar, version ${version}
+UpdatedVersionAvailableUserAlert.armed=El nodo se reiniciara autom?ticamente 
cuando finalize el proceso de descarga y verificaci?n de la actualizaci?n.
+UpdatedVersionAvailableUserAlert.clickToUpdateASAP=Clickea abajo para 
actualizar tu nodo ni bien se termine de verificar  la actualizaci?n..
+UpdatedVersionAvailableUserAlert.clickToUpdateNow=Clickea abajo para 
actualizar tu nodo inmediatamente.
+UpdatedVersionAvailableUserAlert.downloadedNewJar=El nodo bajo una nueva 
version de Freenet, version ${version}.
+UpdatedVersionAvailableUserAlert.fetchingNewBoth=Tu nodo esta bajando una 
nueva version de Freenet (node version ${nodeVersion} and extra jar version 
${extVersion}).
+UpdatedVersionAvailableUserAlert.fetchingNewExt=Tu nodo esta bajando una nueva 
version de Freenet (extra jar version ${extVersion}).
+UpdatedVersionAvailableUserAlert.fetchingNewNode=Tu nodo esta bajando una 
nueva version de Freenet (node version ${nodeVersion}).
+UpdatedVersionAvailableUserAlert.finalCheck=Tu nodo esta haciendo un checkeo 
final de seguridad de la actualizacion (${count} de ${max}, maximo tiempo 
restante ${time}).
+UpdatedVersionAvailableUserAlert.notLatest=Al parecer, tu nodo no esta 
corriendo la ultima version de Freenet disponible.
+UpdatedVersionAvailableUserAlert.updateASAPButton=Actualizar ni bien se pueda
+UpdatedVersionAvailableUserAlert.updateASAPQuestion=Queres que tu nodo se 
reinicie ni bien haya termino la actualizaci?n?
+UpdatedVersionAvailableUserAlert.updateNowButton=Actualizar ahora!
+UserAlert.apply=Aplicar
+UserAlert.hide=Ocultar
+UserAlert.reset=Resetear
+UserAlertManager.alertsOnHomepage=| Ver las alertas en ${link}la pagina de 
inicio de tu nodo${/link}.
+UserAlertManager.alertsTitle=Alertas pendientes
+UserAlertManager.criticalErrorCountLabel=Errores Cr?ticos:
+UserAlertManager.errorCountLabel=Errores:
+UserAlertManager.minorCountLabel=Menor:
+UserAlertManager.warningCountLabel=Alertas:
+WelcomeToadlet.activityTitle=Actividad
+WelcomeToadlet.confirmAddBookmarkTitle=Agregar un favorito
+WelcomeToadlet.confirmExternalLinkSubTitle=Confirmar enlace exterior
+WelcomeToadlet.confirmExternalLinkTitle=CUIDADO: Enlace externo
+WelcomeToadlet.databaseStatsSubTitle=Estad?sticas de la Base de Datos
+WelcomeToadlet.fetch=Bajar
+WelcomeToadlet.fetchKeyLabel=Bajar un archivo
+WelcomeToadlet.finInsertSuccessWithKey=El mensaje se interto con exito en 
${key}.
+WelcomeToadlet.fromHeader=De
+WelcomeToadlet.goToExternalLink=Ir al enlace especificado
+WelcomeToadlet.homepageFullTitleWithName=Freenet - Pagina inicial de FProxy 
del nodo ${name}
+WelcomeToadlet.ieWarning=Al parecer estas Microsoft Internet Explorer. Algunos 
de los Freesites que visites pueden comprometer tu anonimato!
+WelcomeToadlet.ieWarningTitle=Riesgo de Seguridad!
+WelcomeToadlet.insertFailedTitle=Insercion fallida
+WelcomeToadlet.insertSucceededTitle=Inserci?n exitosa
+WelcomeToadlet.messageHeader=Mensaje
+WelcomeToadlet.nodeUpdateConfirm=Desea actualizar su nodo de Freenet?
+WelcomeToadlet.nodeUpdateConfirmTitle=Confirmar actualizaci?n del nodo
+WelcomeToadlet.privateKeyHeader=Llave Privada
+WelcomeToadlet.publicKeyHeader=Llave publica
+WelcomeToadlet.restart=Reiniciar
+WelcomeToadlet.restartConfirm=Esta seguro que desea reiniciar su nodo de 
Freenet?
+WelcomeToadlet.restartConfirmTitle=Reiniciar el Nodo
+WelcomeToadlet.restartNode=Reiniciar el nodo
+WelcomeToadlet.restartingTitle=El nodo esta siendo reiniciado
+WelcomeToadlet.shutdown=Apagar
+WelcomeToadlet.shutdownConfirm=Seguro queres apagar tu nodo de Freenet?
+WelcomeToadlet.shutdownConfirmTitle=Apagar el nodo
+WelcomeToadlet.shutdownNode=Apagar el nodo
+WelcomeToadlet.subjectHeader=Titulo
+WelcomeToadlet.testnetWarningTitle=Modo Testnet (Red de Pruebas)
+WelcomeToadlet.thanks=Gracias por usar Freenet!
+WelcomeToadlet.update=Actualizar
+WelcomeToadlet.updatingTitle=Nodo Actualiz?ndose
+WelcomeToadlet.uriWouldHaveBeen=La URI deber?a haber sido: ${uri}
+WelcomeToadlet.versionHeader=Informaci?n de Versi?n y Control del Nodo
+End

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.fr.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.fr.properties    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.fr.properties    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -113,6 +113,8 @@
 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.disconnecting=D?connexion en cours (nous sommes en 
train de pr?venir ce noeud que nous le supprimons, cela peut prendre un peu de 
temps)
+DarknetConnectionsToadlet.disconnectingShort=D?connexion
 DarknetConnectionsToadlet.enterDescription=Entrez un commentaire :
 DarknetConnectionsToadlet.failedToAddNodeInternalError=Impossible de traiter 
ce texte comme une r?f?rence. Merci de signaler ceci aux d?veloppeurs :
 DarknetConnectionsToadlet.failedToAddNodeInternalErrorTitle=Ajout du noeud 
impossible : erreur interne
@@ -390,10 +392,13 @@
 IPDetectorPluginManager.restrictedTitle=Routeur NAT (restricted cone) d?tect?
 IPDetectorPluginManager.suggestForwardPort=Ce serait une bonne id?e de 
transf?rer le port UDP num?ro ${port} manuellement. Consultez 
http://wiki.freenetproject.org/FirewallAndRouterIssues .
 IPDetectorPluginManager.suggestForwardPortWithLink=Ce serait une bonne id?e de 
${link}transf?rer le port${/link} UDP num?ro ${port}) manuellement (? moins que 
vous ne l'ayez d?ja fait, Freenet ne peut pas d?tecter cela facilement).
+IPDetectorPluginManager.suggestForwardTwoPorts=Vous devriez ouvrir ces ports 
(UDP/${port1} et UDP/${port2}) manuellement. (Voir 
http://wiki.freenetproject.org/FirewallAndRouterIssues ).
 IPDetectorPluginManager.symmetric=Il semble que vous soyez derri?re un routeur 
NAT sym?trique ou un firewall. Vous pourrez sans doute vous connecter 
uniquement ? des gens ayant une connexion directe ? internet ou un routeur NAT 
"restricted cone".
 IPDetectorPluginManager.symmetricTitle=Firewall sym?trique d?tect?
 IPUndetectedUserAlert.detecting=Freenet essaie de d?tecter votre adresse IP 
externe. Si ?a prend plus de quelques minutes, c'est qu'il y a un probl?me...
 IPUndetectedUserAlert.detectingWithConfigLink=Freenet essaie de d?tecter votre 
adresse IP externe. Si ?a prend plus de quelques minutes, c'est qu'il y a un 
probl?me et vous pouvez renseigner le ${link}param?tre de configuration${/link} 
"Adresse IP temporaire". Ce serait une bonne id?e de transf?rer le port ${port} 
en UDP sur votre routeur afin qu'il soit facile de se connecter ? votre noeud.
+IPUndetectedUserAlert.suggestForwardPort=Il serait judicieux d'ouvrir le port 
${port} (UDP) sur votre routeur pour faciliter la connexion ? votre noeud.
+IPUndetectedUserAlert.suggestForwardTwoPorts=Il serait judicieux d'ouvrir les 
ports ${port1} et ${port2} (UDP) dans la configuration de votre routeur afin de 
faciliter les connexions ? votre noeud.
 IPUndetectedUserAlert.unknownAddress=Freenet n'a pas r?ussi ? d?terminer votre 
adresse IP externe (ou l'adresse IP de votre routeur NAT ou firewall). Vous 
pouvez quand m?me ?changer des r?f?rences avec d'autres gens, mais cela ne 
marchera que si ces gens ne sont pas derri?re un NAT ou un firewall. D?s que 
vous vous serez connect? ? une personne, Freenet sera en mesure de d?terminer 
votre adresse IP externe. Vous pouvez d?terminer vous-m?me votre adresse IP et 
renseigner le ${link}param?tre de configuration${/link} "Adresse IP 
temporaire". Ce serait une bonne id?e de transf?rer le port ${port} en UDP sur 
votre routeur afin qu'il soit facile de se connecter ? votre noeud.
 IPUndetectedUserAlert.unknownAddressTitle=Adresse externe inconnue
 IPUndetectedUserAlert.unknownAddressWithConfigLink=Freenet n'a pas r?ussi ? 
d?terminer votre adresse IP externe (ou l'adresse IP de votre routeur NAT ou 
firewall). Vous pouvez quand m?me ?changer des r?f?rences avec d'autres gens, 
mais cela ne marchera que si ces gens ne sont pas derri?re un NAT ou un 
firewall. D?s que vous vous serez connect? ? une personne, Freenet sera en 
mesure de d?terminer votre adresse IP externe. Vous pouvez d?terminer vous-m?me 
votre adresse IP et renseigner le ${link}param?tre de configuration${/link} 
"Adresse IP temporaire". Ce serait une bonne id?e de transf?rer le port ${port} 
en UDP sur votre routeur afin qu'il soit facile de se connecter ? votre noeud.
@@ -544,10 +549,6 @@
 Node.storeSizeLong=Taille du store en octets
 Node.swapRInterval=Intervalle entre les demandes de permutation (ms)
 Node.swapRIntervalLong=Intervale entre chaque demande de permutation, en 
millisecondes. Laissez tel quel !
-NodeClientCore.allowInsecureCHK=Autorisez les CHK non s?curis?es ?
-NodeClientCore.allowInsecureCHKLong=Avant la version 1010, toutes les CHK 
avaient un probl?me de s?curit? (crypt?es ? moiti? seulement). Accepter les 
anciennes CHKs ?
-NodeClientCore.allowInsecureSSK=Autoriser les SSK non-s?curis?es ?
-NodeClientCore.allowInsecureSSKLong=Avant la version 1010, toutes les SSK 
avaient un probl?me de s?curit? (crypt?es ? moiti?). Accepter les anciennes SSK 
?
 NodeClientCore.couldNotFindOrCreateDir=Impossible de trouver ou de cr?er le 
dossier
 NodeClientCore.downloadAllowedDirs=Les dossiers de t?l?chargement autoris?s 
sont
 NodeClientCore.downloadAllowedDirsLong=Liste de dossiers (s?par?s par des ';') 
o? le t?l?chargement est autoris?. "downloads" correspond au dossier de 
t?l?chargement, laissez-la vide pour n'autoriser aucun t?l?chargement sur le 
disque dur, "all" autorise les t?l?chargements partout !
@@ -555,8 +556,6 @@
 NodeClientCore.downloadDirLong=Dossier o? sauvegarder les fichiers t?l?charg?s 
par d?faut
 NodeClientCore.fileForClientStats=Fichier o? stocker les statistiques des 
clients
 NodeClientCore.fileForClientStatsLong=Fichier o? stocker les statistiques de 
transfert (utilis?es pour d?cider ? quelle fr?quence envoyer les requ?tes)
-NodeClientCore.ignoreTooManyPathComponents=Ignorer les chemins trop longs
-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
@@ -627,7 +626,7 @@
 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 connexions pour am?liorer la situation.
-PeerManagerUserAlert.tooHighBwlimitDelayTimeTitle=bwlimitDelayTime trop ?lec?
+PeerManagerUserAlert.tooHighBwlimitDelayTimeTitle=bwlimitDelayTime trop ?lev?
 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".
@@ -679,6 +678,7 @@
 PproxyToadlet.noPlugins=Aucun plugin charg?
 PproxyToadlet.pluginNotFoundReload=Le plugin demand? est introuvable.
 PproxyToadlet.pluginNotFoundReloadTitle=Plugin introuvable (rechargement)
+PproxyToadlet.pluginStopping=Arr?t du plugin
 PproxyToadlet.pluginUnloaded=Plugin enlev?
 PproxyToadlet.pluginUnloadedWithName=Le plugin ${name} a ?t? enlev?.
 PproxyToadlet.plugins=Plugins
@@ -745,7 +745,7 @@
 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.pleaseEnableFCP=Vous devez activer le serveur FCP pour acc?der ? 
cette page
 QueueToadlet.priority=Priorit?
 QueueToadlet.priority0=urgent
 QueueToadlet.priority1=tr?s ?lev?e

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.it.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.it.properties    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.it.properties    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -1,6 +1,6 @@
-BookmarkEditorToadlet.addBookmark=Aggiungi a Preferiti
+BookmarkEditorToadlet.addBookmark=Aggiungi ai Segnalibri
 BookmarkEditorToadlet.addCategory=Aggiungi Categoria
-BookmarkEditorToadlet.addNewBookmark=Aggiungi a Preferiti
+BookmarkEditorToadlet.addNewBookmark=Aggiungi nuovo segnalibro
 BookmarkEditorToadlet.addNewCategory=Nuova categoria
 BookmarkEditorToadlet.addedNewBookmark=Aggiunto alla lista dei preferiti.
 BookmarkEditorToadlet.addedNewBookmarkTitle=Aggiunto a Preferiti
@@ -15,8 +15,8 @@
 BookmarkEditorToadlet.deleteBookmark=Rimuovi da preferiti
 BookmarkEditorToadlet.deleteBookmarkConfirm=Si ? certi di voler eliminare 
${bookmark}?
 BookmarkEditorToadlet.deleteCategory=Elimina categoria
-BookmarkEditorToadlet.deleteCategoryConfirm=Si ? certi di voler eliminare 
${bookmark} con tutte le sottocategorie?
-BookmarkEditorToadlet.deleteSucceeded=Rimozione dalla lista di preferiti 
effettuata.
+BookmarkEditorToadlet.deleteCategoryConfirm=Conferma: eliminare ${bookmark} 
con tutte le sottocategorie?
+BookmarkEditorToadlet.deleteSucceeded=Rimozione effettuata.
 BookmarkEditorToadlet.deleteSucceededTitle=Eliminato
 BookmarkEditorToadlet.edit=Modifica
 BookmarkEditorToadlet.editBookmarkTitle=Modifica
@@ -28,7 +28,7 @@
 BookmarkEditorToadlet.keyLabel=Chiave :
 BookmarkEditorToadlet.moveDown=Gi?
 BookmarkEditorToadlet.moveUp=Su
-BookmarkEditorToadlet.myBookmarksTitle=I Miei Freesite Preferiti
+BookmarkEditorToadlet.myBookmarksTitle=Segnalibri
 BookmarkEditorToadlet.nameLabel=Nome :
 BookmarkEditorToadlet.paste=Incolla
 BookmarkEditorToadlet.pasteOrCancel=Clicka su un'icona incolla o cancella.
@@ -41,7 +41,7 @@
 BookmarkItem.bookmarkUpdatedWithLink=Il Sito ${link}${name}${/link} ? stato 
aggiornato all'edizione ${edition}.
 BookmarkItem.deleteBookmarkUpdateNotification=Elimina notifica
 BookmarkItem.unnamedBookmark=Senza Nome
-BookmarkManager.list=Preferiti
+BookmarkManager.list=Segnalibri
 BookmarkManager.listLong=Lista dei freesite preferiti
 BookmarkManager.malformedBookmark=Segnalibro Malformato
 BooleanOption.parseError=Boolean non riconosciuto: ${val} - provare vero o 
falso
@@ -72,7 +72,7 @@
 ConfigToadlet.title=Configurazione del Nodo Freenet
 ConfigToadlet.unauthorized=L'accesso a questa pagina e' interdetto.
 ConfigurablePersister.doesNotExistCannotCreate=Il file non esiste e non pu? 
essere creato
-ConfigurablePersister.existsCannotReadWrite=Il file esiste e non pu? essere 
letto/scritto
+ConfigurablePersister.existsCannotReadWrite=Il file esiste ma non pu? essere 
letto/scritto
 ContentDataFilter.unknownCharset=La pagina che sta per essere visualizzata 
utilizza un formato di caratteri (chaset) di tipo sconosciuto. Questo rende 
impossibile filtrare la pagina, il che potrebbe compromettere l'anonimato 
dell'utente.
 ContentDataFilter.unknownCharsetTitle=Charset sconosciuto!
 ContentDataFilter.warningUnknownCharsetTitle=Attenzione: charset sconosciuto 
(${charset})
@@ -81,7 +81,7 @@
 ContentFilter.imageGifReadAdvice=Immagine GIF - probabilmente non pericoloso
 ContentFilter.imageGifWriteAdvice=Immagine GIF - probabilmente non pericoloso 
ma sarebbe opportuno eliminare tutti i commenti
 ContentFilter.imageIcoReadAdvice=File icona: probabilmente non pericoloso
-ContentFilter.imageIcoWriteAdvice=File icona - probabilmente non pericoloso 
(potrebbe contenere altri dati dovuti all'immagine riportata?)
+ContentFilter.imageIcoWriteAdvice=File icona - probabilmente non pericoloso 
(ma potrebbe contenere altri dati)
 ContentFilter.imageJpegReadAdvice=Immagine JPEG - probabilmente non pericoloso
 ContentFilter.imageJpegWriteAdvice=Immagine JPEG - probabilmente non 
pericoloso ma pu? contenere dati EXIF
 ContentFilter.imagePngReadAdvice=Immagine PNG - probabilmente non pericoloso
@@ -90,7 +90,7 @@
 ContentFilter.textCssWriteAdvice=CSS (cascading style sheet, solitamente usato 
con HTML) - pu? contenere metadati, controllare manualmente
 ContentFilter.textHtmlReadAdvice=HTML - Non pericoloso se filtrato
 ContentFilter.textHtmlWriteAdvice=HTML - pu? contenere metadati di tipo 
pericoloso, ecc; si consiglia di controllare manualmente
-ContentFilter.textPlainReadAdvice=Formato testo semlice (plain text)- non ? 
pericoloso a meno che il browser utilizzato non sia particolarmente "stupido" 
(per esempio Internet Explorer)
+ContentFilter.textPlainReadAdvice=Formato testo semplice (plain text)- non ? 
pericoloso a meno che il browser utilizzato non sia particolarmente "stupido" 
(per esempio Internet Explorer)
 ContentFilter.textPlainWriteAdvice=Testo semplice (plain text) - non 
pericoloso a meno che l'utente vi includa informazioni compromettenti
 DarknetConnectionsToadlet.activityInserts=Inserzioni: ${totalSenders} totale 
senders, ${CHKhandlers} CHK handlers, ${SSKhandlers} SSK handlers
 DarknetConnectionsToadlet.activityRequests=Richieste: ${totalSenders} totale 
senders, ${CHKhandlers} CHK handlers, ${SSKhandlers} SSK handlers
@@ -107,8 +107,8 @@
 DarknetConnectionsToadlet.cancel=Cancella
 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.cantParseWrongEnding=Non ? stato possibile 
ricostruire le referenza: L'ultimo rigo dovrebbe contenere solo la parola End, 
esso contiene invece: ${end}
+DarknetConnectionsToadlet.clockProblem=L'orologio di sitema 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
@@ -120,8 +120,10 @@
 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.disconnecting=Disconnessione in corso (si sta 
procedendo alla rimozione del nodo; ? necessario informare il nodo in questione 
e questo richiede un po' di tempo)
+DarknetConnectionsToadlet.disconnectingShort=Disconnessione in corso
 DarknetConnectionsToadlet.enterDescription=Descrizione:
-DarknetConnectionsToadlet.failedToAddNodeInternalError=Dal testo analizzato 
non risulta la referenza di un nodo. Se possibile, riportare agli sviluppatori 
quanto segue:
+DarknetConnectionsToadlet.failedToAddNodeInternalError=Impossibile ricostruire 
la referenza di un nodo Freenet dal testo dato.. Se possibile, riportare agli 
sviluppatori quanto segue:
 DarknetConnectionsToadlet.failedToAddNodeInternalErrorTitle=Tentativo di 
aggiungere nodo fallito: Errore interno.
 DarknetConnectionsToadlet.failedToAddNodeTitle=Tentativo di aggiungere nodo 
fallito
 DarknetConnectionsToadlet.fcpDisabled=FCP non ? abilitato (per applicazioni 
client di Freenet come Frost e Thaw)
@@ -234,7 +236,7 @@
 FProxyToadlet.pathNotFoundTitle=Percorso non trovato
 FProxyToadlet.plugins=configura e gestisci plugin
 FProxyToadlet.pluginsTitle=Plugin
-FProxyToadlet.queue=gestisci richieste in coda
+FProxyToadlet.queue=gestisci coda richieste
 FProxyToadlet.queueTitle=Coda
 FProxyToadlet.retryNow=Riprova
 FProxyToadlet.sizeLabel=Dimensioni:
@@ -277,7 +279,7 @@
 FcpServer.portNumberLong=Numero della porta FCP.
 FetchException.longError.1=Troppi livelli di recursione negli archivii
 FetchException.longError.10=File non in archivio
-FetchException.longError.11=Troppe componenti di percorso - non un manifesto? 
Prova a eliminarne uno
+FetchException.longError.11=Troppe componenti di percorso. Prova a eliminarne 
uno
 FetchException.longError.12=Errore interno relativo ai file temporanei: 
potrebbe trattarsi di esaurimento dello spazio libero su disco rigido o di un 
problema relativo a permessi ed autorizzazioni
 FetchException.longError.13=Dati non trovati
 FetchException.longError.14=Percorso non trovato: non ? stato possobile 
trovare un numero di nodi sufficiente ad essere certi che i dati non esistono
@@ -383,18 +385,18 @@
 GIFFilter.notGif=Il file che si sta cercando di richiamare non ? un GIF. 
Potrebbe trattarsi di un file in un altro formato, ed il browser potrebbe fare 
qualcosa di pericoloso a cause della confusione generata dalla mancata 
corrispondenza; il file ? stato pertanto bloccato.
 GIFFilter.tooShort=Il file ? troppo piccolo per essere un GIF.
 GIFFilter.tooShortTitle=Troppo corto
-GenericReadFilterCallback.couldNotParseAbsoluteFreenetURI=Non ? stato 
possibile analizzare come URI Freenet assoluta
-GenericReadFilterCallback.couldNotParseFormURIWithError=Il filtro non ? 
riuscito ad analizzare la URI: ${error}
-GenericReadFilterCallback.couldNotParseRelativeFreenetURI=Non ? stato 
possibile analizzare come URI Freenet relativa.
-GenericReadFilterCallback.couldNotParseURIWithError=Il filtro non ? riuscito 
ad analizzare la URI: ${error}
-GenericReadFilterCallback.invalidFormURI=Forma URI non valida: punta ad una 
risorsa esterna
+GenericReadFilterCallback.couldNotParseAbsoluteFreenetURI=Non ? stato 
possibile ricostruire come URI assoluta di Freenet.
+GenericReadFilterCallback.couldNotParseFormURIWithError=Il filtro non ? 
riuscito ad ricostruire dalla URI: ${error}
+GenericReadFilterCallback.couldNotParseRelativeFreenetURI=Non ? stato 
possibile ricostruire come URI relativa di Freenet.
+GenericReadFilterCallback.couldNotParseURIWithError=Il filtro non ? riuscito a 
ricostruire la URI: ${error}
+GenericReadFilterCallback.invalidFormURI=Forma URI non valida perch? punta ad 
una risorsa esterna (non Freenet)
 GenericReadFilterCallback.invalidFormURIAttemptToEscape=Tentativo di evasione 
dalla struttura delle directory
 GenericReadFilterCallback.malformedAbsoluteURL=URL malformata (assoluto): 
${error}
-GenericReadFilterCallback.malformedRelativeURL=URL malformata (relative): 
${error}
+GenericReadFilterCallback.malformedRelativeURL=URL malformata (relativo): 
${error}
 GenericReadFilterCallback.protocolNotEscaped=Non ? un protocollo fuggitivo: 
${protocol}
 HTMLFilter.couldNotParseStyle=Non ? stato possibile abbinare lo stile 
dell'input
 HTMLFilter.deletedUnknownStyle=stile sconosciuto eliminato
-HTMLFilter.failedToParseLabel=Il filtro HTML non ? riuscidto ad analizzare la 
pagina
+HTMLFilter.failedToParseLabel=Il filtro HTML non ? riuscito a ricostruire la 
pagina
 HTMLFilter.tooManyNestedStyleOrScriptTags=Troppi tag 'style' annidati e/o 
troppi tag di scripting: analisi ambigua o non valida
 HTMLFilter.tooManyNestedStyleOrScriptTagsLong=Troppi tag </style> annidati - 
analisi ambigua o non valida. Non ? possibile filtrare in modo affidabile 
quindi i tag interni sono stai rimossi: questo pu? causare al browser qualche 
inconveniente nella visuallizzazione
 HTMLFilter.unknownTag=tag sconosciuto ${tag}
@@ -410,11 +412,14 @@
 IPDetectorPluginManager.restrictedTitle=E' stato rilevato un cono NAT ristretto
 IPDetectorPluginManager.suggestForwardPort=Potrebbe essere necessario 
configurare il 'port forwarding' della portav (Porta UDP numero${port}) 
manualmente. Vedi: http://wiki.freenetproject.org/FirewallAndRouterIssues 
(disponibile solo in inglese, per ora).
 IPDetectorPluginManager.suggestForwardPortWithLink=Potrebbe essere necessario 
${link}configurare il port forwarding${/link} (porta UDP numero ${port}) 
manualmente (oppure ci? ? gi? stato fatto: Freenet pu? avere delle difficolt? 
in questo tipo di rilevazione).
+IPDetectorPluginManager.suggestForwardTwoPorts=Sarebbe opportuno configurare 
manualmente il port forward sul router per le porte ${port1} e ${port2} (UDP). 
vedi http://wiki.freenetproject.org/FirewallAndRouterIssues (disponibile solo 
in inglese, per ora).
 IPDetectorPluginManager.symmetric=La connessione a Internet sembra avvenire 
attraverso un NAT simmetrico o un firewall. Il nodo potr? probabilmente 
connettersi soltanto ad utenti direttamente connessi a Internet, o che usano 
NAT a cono ristretto.
 IPDetectorPluginManager.symmetricTitle=Rilevato firewall simmetrico
 IPUndetectedUserAlert.detecting=Freenet sta cercando in questo momento di 
rilevare l'indirizzo IP esterno. Se questa operazione si protrae per pi? di 
qualche minuto, c'? qualcosa che non va...
 IPUndetectedUserAlert.detectingWithConfigLink=Freenet sta rilevando 
l'indirizzo IP esterno. Se l'operazione dura pi? di qualche minuto si pu? 
presumere che qualcosa non stia andando come dovrebbe e si pu? provare ad usare 
il Suggerimento Temporaneo per l'Indirizzo IP ${link}parametro di 
configurazione${/link}. Sarebbe inoltre auspicabile configurare il forwarding 
UDP della porta numero ${port} sul router in modo da facilitare le connessioni.
-IPUndetectedUserAlert.unknownAddress=Non ? stato possibile riolevare 
l'indirizzo IP esterno (o l'indirizzo IP del NAT-box o del firewall). Si pu? 
comunque scambiare refs con altri utenti, ma sar? possibile connettersi 
solamente a nodi che non si trovino a loro volta dietro un NAT o firewall. Non 
appaena ci si sar? connessi ad almeno un altro utente in questa maniera, sar? 
possibile determinare l'indirizzo IP esterno. E' possibile suggerire 
l'indirizzo IP usando 'Suggerimento Temporaneo per lIndirizzo IP' 
${link}parametro di configurazione${/link}. Sarebbe inoltre auspicabile 
configurasre il forwarding UDP della porta numero ${port} sul router, in modo 
da facilitare le connessioni..
+IPUndetectedUserAlert.suggestForwardPort=Sarebbe opportuno configurare il port 
forward sul router per le porte ${port1} e ${port2} (UDP) per facilitare le 
connessioni.
+IPUndetectedUserAlert.suggestForwardTwoPorts=Sarebbe opportuno configurare il 
port forward sul router per le porte ${port1} e ${port2} (UDP) per facilitare 
le connessioni.
+IPUndetectedUserAlert.unknownAddress=Non ? stato possibile rilevare 
l'indirizzo IP esterno (o l'indirizzo IP del NAT-box o del firewall). Si pu? 
comunque scambiare refs con altri utenti, ma sar? possibile connettersi 
solamente a nodi che non si trovino a loro volta dietro un NAT o firewall. Non 
appaena ci si sar? connessi ad almeno un altro utente in questa maniera, sar? 
possibile determinare l'indirizzo IP esterno. E' possibile suggerire 
l'indirizzo IP usando 'Suggerimento Temporaneo per lIndirizzo IP' 
${link}parametro di configurazione${/link}. Sarebbe inoltre auspicabile 
configurare il forwarding UDP della porta numero ${port} sul router in modo da 
facilitare le connessioni.
 IPUndetectedUserAlert.unknownAddressTitle=Indirizzo esterno sconosciuto
 IPUndetectedUserAlert.unknownAddressWithConfigLink=Freenet non ha potuto 
rilevare l'indirizzo IP esterno (o l'indirizzo IP del NAT-box o firewall). E' 
comunque possibile scambiare referenze con altri utenti, ma la connessione 
avverr? soltanto se l'altra parte non ? a sua volta dietro un NAT o firewall. 
Appena connesso ad almeno un utente in questo modo, Freenet riuscit? a 
determinare l'indirizzo IP esterno.E' possibile suggerire al nodo l'IP esterno 
usando il campo 'suggerimento provvisorio per l'indirizzo IP'  ${link}parametro 
di configurazione${/link}. Sarebbe inlotre auspicabile configurare il 
forwarding per UDP della porta ${port} sul router, in modo da rendere pi? 
facili le connessioni
 InsertException.longError.1=Caller ha fornito una URI che non ? possibile 
utilizzare"
@@ -427,7 +432,7 @@
 InsertException.longError.5=Non ? stato possibile propagare questa inserzione 
su un numero sufficiente di nodi (questo e' normale su network di piccole 
dimensioni: si pu? provare a richiamare il file comunque)
 InsertException.longError.6=Errori fatali in un'inserzione di splitfiles
 InsertException.longError.7=Non ? stato possibile inserire gli splitfile: 
numero di tentativi esaurito (errori nonfatali)
-InsertException.longError.8=Non ? stato possibile nemmeno inoltrare 
l'inserzione
+InsertException.longError.8=Non ? stato possibile far partire l'inserzione dal 
nodo locale.
 InsertException.longError.9=L' inserzione ? in conflitto con dati 
pre-esistenti e differenti alla stessa chiave
 InsertException.shortError.1=URI non valida
 InsertException.shortError.10=Annullato
@@ -441,7 +446,7 @@
 InsertException.shortError.7=Alcuni block hanno esaurito il numero di 
tentativi a disposizione
 InsertException.shortError.8=Non ? stato possibile inoltrare la richiesta
 InsertException.shortError.9=Conflitto con dati esistenti
-IntOption.parseError=Il valore specificato non pu? essere analizzato come 
32-bit integer : ${val}
+IntOption.parseError=Il valore specificato non pu? essere ricostruito come 
32-bit integer : ${val}
 JPEGFilter.notJpeg=Le dimensioni del file che si sta cercando di richiamare 
sono troppo piccole perch? si tratti du un'immagine JPEG. Potrebbe trattarsi di 
un file in un altro formato w il browser potrebbe fare qualcosa di sbagliato a 
causa della mancata corrispondenza, il file ? stato pertanto bloccato.
 JPEGFilter.tooShort=File troppo piccolo per un JPEG.
 JPEGFilter.tooShortTitle=Troppo corto
@@ -483,8 +488,8 @@
 LogConfigHandler.rotationInterval=Intervallo di rotazione dei log
 LogConfigHandler.rotationIntervalLong=Intervallo di rotazione dei log - 
Periodo di tempo allo scadere del quale i log vengono ruotati. Gli ultimi due 
log files vengono conservati (current.log e prev.log) insieme a diversi log 
file compressi fino al massimo impostato in maxZippedLogsSize
 LoggerHook.unrecognizedPriority=Nome di priorit? non riconosciuto: ${name}.
-LongOption.parseError=Il valore specificato non pu? essere analizzato come 
64-bit integer : ${val}
-MeaningfulNodeNameUserAlert.noNodeNick=Sembra che il nodo non conosca il 
nickname da usare. Usare qui un indirizzo e-mail o un nickname IRC ? 
generalmente una buona idea in quanto consente ai peers di identificare il 
nodo. (nota che soltanto i peer del darknet elencati alla pagina Amici possono 
vedere il nome del nodo, esso resta invece invisibile ai peer dell' opennet)
+LongOption.parseError=Il valore specificato non pu? essere ricostruito come 
64-bit integer : ${val}
+MeaningfulNodeNameUserAlert.noNodeNick=Sembra che il nodo non conosca il 
nickname da usare. Usare qui un indirizzo e-mail o un nickname IRC ? 
generalmente una buona idea in quanto consente ai peer di identificare il nodo. 
(nota che soltanto i peer del darknet elencati alla pagina Amici possono vedere 
il nome del nodo, esso resta invece invisibile agli sconosciuti)
 MeaningfulNodeNameUserAlert.noNodeNickTitle=Non ? stato definito un nome per 
il nodo.
 N2NTMToadlet.composingMessageLabel=Messaggio N2NTM  da inviare ai seguenti 
nodi:
 N2NTMToadlet.delayed=Il nodo interressato sta temporaneamente respingendo 
richieste; il messaggio potrebbe giungere in lieve ritardo.
@@ -537,12 +542,12 @@
 Node.extraPeerDir=Directory dei dati extra peer
 Node.extraPeerDirLong=Directory dove conservare dati extra
 Node.forceBigShrink=Esegui immediatamente grosse riduzioni di dimensione dello 
store
-Node.forceBigShrinkLong=Determina se eseguire immediatamente le riduzioni di 
dimensione dello store di misura superiore al 10% piuttosto che aspettare il 
prossimo riavvio del nodo. Le riduzioni on-line non preservano i dati 
utilizzati pi? di recente quindi l'uso di questa opzione non ? raccomandato; da 
usarsi solo nel caso in cui sia desiderabile un risultato immediato.
+Node.forceBigShrinkLong=Determina se eseguire immediatamente le riduzioni di 
dimensione in misura superiore al 10% del magazzino (store), piuttosto che 
aspettare il prossimo riavvio del nodo. Le riduzioni on-line non preservano i 
dati utilizzati per ultimi quindi l'uso di questa opzione non ? raccomandato; 
da usarsi solo nel caso in cui sia desiderabile un risultato immediato.
 Node.inBWLimit=Limite ampiezza di banda in entrata (bytes per secondo)
 Node.inBWLimitLong=Limite dell'ampiezza di banda in entrata (bytes/sec); il 
nodo cerca di non eccedere tale limite; -1 siglifica quatto volte il limite 
impostato per l'ampiezza di banda in uscita (outputBandwidthLimit)
 Node.invalidStoreSize=Lo store non pu? essere di dimensioni inferiori a 32MB
 Node.l10nLanguage=Lingua dell' interfaccia grafica
-Node.l10nLanguageLong=Cambia la lingua in cui messaggi sono visualizzati. Per 
motivi tecnici, alcune frasi e messaggi non possono essere visibili in versione 
tradotta fino al prossimo riavvio.
+Node.l10nLanguageLong=Cambia la lingua in cui messaggi sono visualizzati. 
Alcune frasi e messaggi saranno visibili in versione tradotta dopo il prossimo 
riavvio del nodo.
 Node.maxHTL=HTL massimo
 Node.maxHTLLong=HTL massimo (PER USO ESCLUSIVO DEGLI SVILUPPATORI!)
 Node.mustBePositive=Il valore di config deve essere positivo
@@ -551,26 +556,22 @@
 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.oneConnectionPerIPLong=Non permettere pi? di una connessione per 
indirizzo? Questo render? leggermente pi? difficile eseguire un attacco 
connettendosi al 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 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.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 (cio? manualmente aggiugere peers conosciuti e fidati).
 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
+Node.storeDirectory=Directory magazzino (store)
 Node.storeDirectoryLong=Directory contenente gli store file
 Node.storeMaxMemTooHigh=Dare a BDB pi? dell' 80% della memoria RAM disponibile 
non e' una buona idea!
 Node.storeSize=Dimensioni dello store in bytes
-Node.storeSizeLong=Dimensioni dello store in bytes
+Node.storeSizeLong=Dimensioni del magazzino (store) in bytes
 Node.swapRInterval=Intervallo tra le richieste di scambio (swap) in 
millisecondi
 Node.swapRIntervalLong=Intervallo tra richieste di scambio (swap) in 
millisecondi. Non toccare!
-NodeClientCore.allowInsecureCHK=Permettere l'uso di chiavi CHK non sicure?
-NodeClientCore.allowInsecureCHKLong=Prima di 1010, le chiavi CHK non erano 
sicure perch? venivano criptate solo per met?. Questa impostazione specifica se 
l'uso di vecchie chiavi CHK ? permesso o meno.
-NodeClientCore.allowInsecureSSK=Permettere l' uso di chiavi SSK non sicure?
-NodeClientCore.allowInsecureSSKLong=Prima di 1010, le chiavi SSK non erano 
sicure perch? venivano criptate solo per met?. Questa impostazione specifica se 
l'uso di vecchie chiavi SSK ? permesso o meno
 NodeClientCore.couldNotFindOrCreateDir=Non ? stato possibile trovare o creare 
la direcory
 NodeClientCore.downloadAllowedDirs=Directory dove il download ? consentito
 NodeClientCore.downloadAllowedDirsLong=Si pu? usare una lista di directory 
dove dove il download e' permesso, separate da 'punto e virgola' (;). 
"downloads" significa downloadsDir, vuoto (nessuna directory specificata) 
significa che il download su disco non e' permesso, "all" significa download 
permessi in tutte le directory AVVERTENZA; Se impostato su "all" ("tutte"), 
ogni utente potr? scaricare files  in qualsiasi directory.
@@ -578,14 +579,12 @@
 NodeClientCore.downloadDirLong=Directory predefinita dove salvare i file 
scaricati
 NodeClientCore.fileForClientStats=File contenente statistiche del client
 NodeClientCore.fileForClientStatsLong=File dove conservare le statistiche 
relative al client throttling (utilizzato per stabilire l'intervallo tra le 
richieste)
-NodeClientCore.ignoreTooManyPathComponents=Ignora eccesso di componenti nel 
percorso
-NodeClientCore.ignoreTooManyPathComponentsLong=Se impostato su 'vero' (true) 
il nodo non produrr? errori dovuti all' eccesso di subdir senza senso 
(/bla/bla/bla): le vecchie chiavi CHK possono includere un' 'appendice' 
aggiunta dopo l'inserzione originale,spesso il nome del file. Ci? ? ormai 
obsoleto, adesso possiamo includere i nomi direttamente. Abilitare questa 
opzione solo in caso di necessit?, per esempio compatibilit? con vecchie 
applicazioni.
-NodeClientCore.lazyResume=Caricare completamente le richieste persistenti dopo 
aver avviato il nodo? (Fa maggiore uso di memoria)
+NodeClientCore.lazyResume=Caricare completamente le richieste persistenti dopo 
aver completato l'avvio del nodo? (Fa maggiore uso di memoria ma avvia il nodo 
pi? velocemente)
 NodeClientCore.lazyResumeLong=Le richieste persistenti possono essere caricate 
durante l'avviamento del nodo, oppure si possono iscrivere i dati in memoria e 
lasciare che vengano caricati completamente solo dopo che il processo di avvio 
? terminato. Ci? riduce tempo di avviamento ma fa maggior uso di memoria
 NodeClientCore.maxUSKFetchers=Numero massimo di USK fetchers ammessi
 NodeClientCore.maxUSKFetchersLong=Numero massimo di USK fetchers ammessi
 NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=Deve essere maggiore di zero
-NodeClientCore.movingTempDirOnTheFlyNotSupported=Spostare la directory dei 
file temporanei non ? possible mentre il nodo ? in funzione
+NodeClientCore.movingTempDirOnTheFlyNotSupported=Non ? possible cambiare la 
directory dei file temporanei mentre il nodo ? in funzione
 NodeClientCore.persistentTempDir=Directory dei file temporanei persistenti
 NodeClientCore.persistentTempDirLong=Directory dei temp file persistenti
 NodeClientCore.tempDir=Directory dei file temporanei
@@ -607,16 +606,16 @@
 NodeStat.freeHeapBytesThresholdLong=Il nodo respinge richieste per mantenere 
la quota di free heap bytes impostata
 NodeStat.freeHeapPercentThreshold=Soglia percentuale free heap
 NodeStat.freeHeapPercentThresholdLong=Respingendo nuove richieste, il nodo 
mantiene al di sopra della soglia la  percentuale di max heap bytes ammessa
-NodeStat.memCheck=Abilita Verifica di Memoria
+NodeStat.memCheck=Abilita la Verifica di Memoria
 NodeStat.memCheckLong=Abilita verifica della memoria Scrive un messaggio nel 
log file. La verifica della memoria deve essere abilitata perche' 
aggressiveGCModificator abbia effetto
 NodeStat.statsPersister=File contenente le statistiche del nodo
 NodeStat.statsPersisterLong=File contenente statistiche: NON si tratta delle 
statistiche del client. Queste statistiche servono al nodo per decidere se 
accettare o rifiutare nuove richieste. Non eliminare!
 NodeStat.threadLimit=Limite thread
-NodeStat.threadLimitLong=Il nodo limita l'uso di thread al valore specificato; 
per farlo respinge richieste
+NodeStat.threadLimitLong=Il nodo respinge richieste per limitare l'uso di 
thread al valore specificato.
 NodeStats.mustBePercentValueNotFull=Questo valore deve essere espresso in 
precentuale, compresa tra 0 e 99.
 NodeStats.valueTooLow=Questo valore ? troppo basso per quella impostazione: ? 
necessario aumentarlo.
 NodeUpdateManager.enabled=Verifica disponibilit? e scarica nuove versioni
-NodeUpdateManager.enabledLong=Verifica e scarica automaticamente nuove 
versioni di Freenet: le nuove versioni verranno scaricate ma non 
necessariamente installate. L'impostazione torna su 'false' tranne nel caso in 
cui il nodo giri all'interno del wrapper.
+NodeUpdateManager.enabledLong=Verifica e scarica automaticamente nuove 
versioni di Freenet: le nuove versioni verranno scaricate ma non 
necessariamente installate. L'impostazione torna sempre su 'false' tranne nel 
caso in cui il nodo giri all'interno del wrapper.
 NodeUpdateManager.extURI=Dove cercare versioni aggiornate di freenet-ext.jar?
 NodeUpdateManager.extURILong=Dove cercare aggiornamenti per freenet-ext.jar?
 NodeUpdateManager.installNewVersions=Installa nuove versioni automaticamente
@@ -624,7 +623,7 @@
 NodeUpdateManager.invalidExtURI=Ext URI non valida: ${error}
 NodeUpdateManager.invalidRevocationURI=URI di revoca non valida: ${error}
 NodeUpdateManager.invalidUpdateURI=URI di aggiornamento non valida: ${error}
-NodeUpdateManager.noUpdateWithoutWrapper=Non ? stato possibile completare 
l'aggiornamento perch? il nodo non sta girando entro il wrapper
+NodeUpdateManager.noUpdateWithoutWrapper=Non ? stato possibile completare 
l'aggiornamento perch? il nodo non sta girando dentro il wrapper
 NodeUpdateManager.revocationURI=Dove cercare la chiave di revoca?
 NodeUpdateManager.revocationURILong=URI per la chiave di revoca. Se viene 
trovata il nodo ne mostra il contenuto e disabilita l'aggiornamento automatico.
 NodeUpdateManager.updateCatastropheTitle=Fallimento Catastrofico dell' 
Aggiornamento!
@@ -646,7 +645,7 @@
 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!
 PeerManagerUserAlert.noPeersTestnet=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. Trattandosi di un nodo testnet, ci si pu? connettere 
a irc.freenode.net canale #freenet-refs e chiedere chi vuole connettersi.
-PeerManagerUserAlert.noPeersTitle=Nessun peer
+PeerManagerUserAlert.noPeersTitle=Nessun peer trovato
 PeerManagerUserAlert.oneConn=Questo nodo ha una sola connessione. Il 
rendimento ne risentir? in modo notevole, e l'utente non disporra di anonimato 
e "negabilit? plausibile" se quell' unico nodo al quale si ? conessi dovesse 
essere operato da un avversario. Il nodo risulter? attaccato al network come 
una "foglia all' albero" e non contribuir? alla salute generale del network 
stesso. Per un corretto funzionamento del nodo ? necessario che almeno tre peer 
e (idealmente 5-10) siano connessi in qualsiasi momento.
 PeerManagerUserAlert.onlyFewConnsTitle=Soltanto ${count} connessione/i aperta/e
 PeerManagerUserAlert.tooHighBwlimitDelayTime=Questo nodo deve aspettare troppo 
termpo per ottenere della banda (${delay}ms > ${max}ms). Aumentare l'ampiezza 
di banda in uscita per miglirare la situazione.
@@ -677,7 +676,7 @@
 PluginManager.loadedOnStartupLong=Classpath, nome e locazione dei plugin da 
caricare all'avvio
 PluginManager.loadedPlugins=Plugin da caricare all'avvio
 PluginManager.loadedPluginsLong=Lista dei plugin da avviare all'avvio del nodo
-PluginManager.pluginReqNewerJVM=Per usare il plugin ${name} ? necessario 
installare una JVM pi? recente di quella attualmente in uso. Installare Sun 
Java 1.5 o rimuovere il plugin.
+PluginManager.pluginReqNewerJVM=Per usare il plugin ${name} ? necessario 
installare una JVM pi? recente di quella attualmente in uso. Installare Sun 
Java 1.5 o superiore, o rimuovere il plugin.
 PluginManager.pluginReqNewerJVMTitle=Il plugin ${name} richiede l'uso di una 
JVM pi? aggiornata
 PluginToadlet.addPluginTitle=Aggiungi plugin
 PluginToadlet.failedToLoadPlugin=Caricamento plugin fallito.
@@ -703,13 +702,14 @@
 PproxyToadlet.noPlugins=Nessun plugin caricato
 PproxyToadlet.pluginNotFoundReload=Non ? stato possibile localizzare il plugin 
specificato per poterlo ricaricare.
 PproxyToadlet.pluginNotFoundReloadTitle=Plugin non trovato (ricaricamento in 
corso)
+PproxyToadlet.pluginStopping=Disconnessione plugin in corso
 PproxyToadlet.pluginUnloaded=Plugin terminato
 PproxyToadlet.pluginUnloadedWithName=Il plugin ${name} ? stato terminato.
 PproxyToadlet.plugins=Plugin
 PproxyToadlet.pluginsWithNodeName=Plugin di ${name}
 PproxyToadlet.reload=Ricarica
 PproxyToadlet.returnToPluginPage=Torna alla pagina dei plugin
-PproxyToadlet.startedAtTitle=Avviato alle
+PproxyToadlet.startedAtTitle=Avviato:
 PproxyToadlet.unload=Termina
 PproxyToadlet.unloadPluginTitle=Termina plugin?
 PproxyToadlet.unloadPluginWithName=Si ? certi di voler terminare ${name}?
@@ -720,7 +720,7 @@
 QueueToadlet.completedDU=Upload directory completati
 QueueToadlet.completedDinDownloadDirectory=Download completati (directory dei 
download) (${size})
 QueueToadlet.completedDinTempDirectory=Download Completati (directory 
temporanea) (${size})
-QueueToadlet.completedDtoDisk=Download completati su disco
+QueueToadlet.completedDtoDisk=Download completati
 QueueToadlet.completedDtoTemp=Download completati (directory temporanea)
 QueueToadlet.completedU=Upload completati: (${size})
 QueueToadlet.completedUDirectory=Upload directory completati (${size})
@@ -803,9 +803,9 @@
 QueueToadlet.wipU=Upload in corso: (${size})
 RequestStarterGroup.scheduler=Policy di priorit? dello schedulatore
 RequestStarterGroup.schedulerLong=Imposta lo schema delle policy di priorit? 
usate dallo schedulatore.
-RevocationKeyFoundUserAlert.text=La chiave di revoca dell' autoupdater ? stata 
rilevata sul network. Questo significa che il nostro sistema du aggiornamento 
automatico ? stato probabilmente COMPROMESSO! Di conseguenza, ? stato 
disabilitato, onde prevenrite l'intallazione automatica di "robaccia". Si 
raccomanda di controllare la disponibilit? di nuovi aggiornamenti al sito del 
progetto. Controllare che il sito non sia stato falsificato!. Il messaggio di 
revoca ? il seguente: ${message}.
+RevocationKeyFoundUserAlert.text=? stata rilevata la presenza sul network 
della chiave di revoca dell' aggiornamento automatico. Questo significa che il 
nostro sistema di aggiornamento automatico ? stato probabilmente COMPROMESSO. 
L'aggiornamento automatico ? stato conseguentemente disabilitato onde 
prevenrite l'intallazione automatica di "robaccia". Si raccomanda di 
controllare la disponibilit? di nuovi aggiornamenti al sito del progetto. 
Controllare che il sito non sia stato falsificato!. Il messaggio di revoca ? il 
seguente: ${message}.
 RevocationKeyFoundUserAlert.title=La chiave privata del progetto ? stata 
compromessa!
-ShortOption.parseError=Il valore specificato non pu? essere analizzato come 
16-bit integer : ${val}
+ShortOption.parseError=Il valore specificato non pu? essere ricostruito come 
16-bit integer : ${val}
 SimpleToadletServer.advancedMode=Abilita modalit? avanzata
 SimpleToadletServer.advancedModeLong=Mostra informazioni cho possono 
interessare solo utenti avanzati o sviluppatori. Nella maggiornaza dei casi 
dovrebbe essere impostata su 'false'
 SimpleToadletServer.allowedFullAccess=Host ai quali ? consentito pieno accesso 
a FProxy (leggere l'avvertenza)
@@ -836,22 +836,22 @@
 StaticToadlet.pathInvalidChars=La URI specificata contiene caratteri non 
permessi.
 StaticToadlet.pathNotFound=Il percorso specificato non esiste.
 StaticToadlet.pathNotFoundTitle=Percorso non trovato
-StatisticsToadlet.activityInserts=Inserzioni: ${totalSenders} total senders, 
${CHKhandlers} CHK handlers, ${SSKhandlers} SSK handlers
-StatisticsToadlet.activityRequests=Richieste: ${totalSenders} total senders, 
${CHKhandlers} CHK handlers, ${SSKhandlers} SSK handlers
+StatisticsToadlet.activityInserts=Inserzioni: ${totalSenders} totale mittenti, 
${CHKhandlers} CHK handlers, ${SSKhandlers} SSK handlers
+StatisticsToadlet.activityRequests=Richieste: ${totalSenders} Totale mittenti, 
${CHKhandlers} CHK handlers, ${SSKhandlers} SSK handlers
 StatisticsToadlet.allocMemory=Memoria Java allocata: ${memory}
 StatisticsToadlet.bandwidthTitle=Ampiezza di banda
 StatisticsToadlet.cpus=CPU disponibili: ${count}
 StatisticsToadlet.getLogs=Ultimo logfile del nodo
 StatisticsToadlet.inputRate=Input: ${rate}/second (of ${max}/second)
-StatisticsToadlet.jeDumpButton=Genera JE Dump
+StatisticsToadlet.jeDumpButton=Generare JE Dump
 StatisticsToadlet.jvmInfoTitle=Info JVM
-StatisticsToadlet.jvmVendor=Vendor JVM: ${vendor}
+StatisticsToadlet.jvmVendor=Fornitore di JVM: ${vendor}
 StatisticsToadlet.jvmVersion=Versione JVM: ${version}
-StatisticsToadlet.maxMemory=Memoria Java massima: ${memory}
-StatisticsToadlet.noRequests=Il nodo non sta elaborando alcuna richiesta in 
questo momento.
+StatisticsToadlet.maxMemory=Max Memoria Java: ${memory}
+StatisticsToadlet.noRequests=Al momento il nodo non sta elaborando alcuna 
richiesta.
 StatisticsToadlet.osArch=Architettura OS: ${arch}
-StatisticsToadlet.osName=Nome OS: ${name}
-StatisticsToadlet.osVersion=Versione OS: ${version}
+StatisticsToadlet.osName=Sistema Operativo: ${name}
+StatisticsToadlet.osVersion=Versione Sistema Operativo: ${version}
 StatisticsToadlet.outputRate=Output: ${rate}/second (of ${max}/second)
 StatisticsToadlet.payloadOutput=Output Carico: ${total} (${rate}/second) 
(${percent}%)
 StatisticsToadlet.peerStatsTitle=Statistiche peer
@@ -859,12 +859,12 @@
 StatisticsToadlet.threads=Threads in esecuzione: ${running}/${max}
 StatisticsToadlet.totalInput=Input Totale: ${total} (${rate}/second)
 StatisticsToadlet.totalOutput=Output Totale: ${total} (${rate}/second)
-StatisticsToadlet.transferringRequests=Trasferimento Richieste: uscita 
${senders}, receiving ${receivers}
+StatisticsToadlet.transferringRequests=Trasferimento Richieste: uscita 
${senders}, entrata ${receivers}
 StatisticsToadlet.usedMemory=Memoria Java in uso: ${memory}
 StatisticsToadlet.versionTitle=Info Versione Nodo
 SymlinkerToadlet.symlinks=Link simbolici in ToadletServer
 SymlinkerToadlet.symlinksLong=Lista di "alias#target" che va a formare un 
gruppo di link simbolici
-TestnetHandler.cannotEnableDisableOnTheFly=Abilitare o disabilitare la 
modalit? testnet "al volo" non ? possibile; ? necessario riavviare il nodo ed 
ottenere nuove connessioni.
+TestnetHandler.cannotEnableDisableOnTheFly=Non ? possibile abilitare o 
disabilitare la modalit? testnet "al volo";  ? necessario riavviare il nodo ed 
ottenere nuove connessioni.
 TestnetHandler.enable=Abilita modalit? testnet (PERICOLO)
 TestnetHandler.enableLong=Abilita la modalit? testnet (PERICOLO). La modalit? 
testnet elimina l'anonimato per poter permettere agli sviluppatori di fare il 
debugging del nodo
 TestnetHandler.port=Porta testnet
@@ -899,16 +899,16 @@
 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.headersLineTooLong=Rigo troppo lungo dalla ricostruzione di 
headers
 ToadletContextImpl.methodNotAllowed=Metodo HTTP Non Permesso
 ToadletContextImpl.noContentLengthInPOST=Nessun content-length in POST
 ToadletContextImpl.noSuchToadlet=Nessuna Toadlet con quel nome
-ToadletContextImpl.parseErrorWithError=Errore di analisi: ${error}
+ToadletContextImpl.parseErrorWithError=Errore nella ricostruzione ${error}
 ToadletContextImpl.uriParseErrorTitle=Errore di Analisi URI
 TranslationToadlet.bracketRemoveOverride=(Elimina questa chiave di traduzione)
 TranslationToadlet.bracketTranslateIt=(traducilo nella tua lingua)
 TranslationToadlet.bracketUpdateTranslation=(aggiorna la traduzione)
-TranslationToadlet.confirmRemoveOverride=Si ? certi di voler eliminare la 
chiave di traduzione : (${key} - ${value}) ?
+TranslationToadlet.confirmRemoveOverride=Conferma: eliminare la chiave di 
traduzione : (${key} - ${value}) ?
 TranslationToadlet.contributingToLabelWithLang=Stai attualmente contribuendo 
alla traduzione ${lang}:
 TranslationToadlet.currentTranslationLabel=Traduzione attuale
 TranslationToadlet.downloadTranslationsFile=Scarica il file di traduzione
@@ -961,12 +961,12 @@
 WelcomeToadlet.activityTitle=Attivit? in corso
 WelcomeToadlet.arkFetchCount=ARK Fetchers: ${total}
 WelcomeToadlet.confirmAddBookmarkSubTitle=Conferma aggiungi a Preferiti
-WelcomeToadlet.confirmAddBookmarkTitle=Aggiungi ai Preferiti
+WelcomeToadlet.confirmAddBookmarkTitle=Aggiungi ai Segnalibri
 WelcomeToadlet.confirmAddBookmarkWithKey=Confermare che si vuole aggiungere la 
chiave ${key} ai preferiti ed aggiugnere la descrizione desiderata:
 WelcomeToadlet.confirmExternalLinkSubTitle=Conferma link esterno
 WelcomeToadlet.confirmExternalLinkTitle=ATTENZIONE: Link esterno
 WelcomeToadlet.confirmExternalLinkWithURL=Confermare che l'indirizzo da 
visitare sia ${url}. ATTENZIONE: Si sta per lasciare FREENET! Clickare su 
questo link METTERA' in serio pericolo l'anonimato dell'utente. Si raccomanda 
fortemente di non farlo.
-WelcomeToadlet.confirmFIN=Vsi vuole inserire il seguente messaggio Frost?
+WelcomeToadlet.confirmFIN=Inserire il messaggio Frost riportato di seguito?
 WelcomeToadlet.databaseStatsSubTitle=Statistiche Database
 WelcomeToadlet.databaseStatsTitle=Ricevi statistiche JE
 WelcomeToadlet.disabledAlert=Allarme non abilitato
@@ -988,8 +988,9 @@
 WelcomeToadlet.insertSucceededTitle=Inserzione Completata
 WelcomeToadlet.insertedTitle=Inserzione
 WelcomeToadlet.keyInsertedSuccessfullyWithKeyAndName=La chiave 
${link}${name}${/link} ? stata inserita.
+WelcomeToadlet.keyRequestLabel=Chiave:
 WelcomeToadlet.messageHeader=Messaggio
-WelcomeToadlet.nodeUpdateConfirm=Si ? certi di voler aggiornare il nodo 
Freenet?
+WelcomeToadlet.nodeUpdateConfirm=Conferma: aggiornare il nodo Freenet?
 WelcomeToadlet.nodeUpdateConfirmTitle=Conferma Aggiorna Nodo
 WelcomeToadlet.post=Post
 WelcomeToadlet.privateKeyHeader=Chiave Privata
@@ -1006,7 +1007,7 @@
 WelcomeToadlet.shutdownConfirmTitle=Arresta Nodo
 WelcomeToadlet.shutdownDone=Nodo Freenet arrestato..
 WelcomeToadlet.shutdownNode=Arresta il nodo
-WelcomeToadlet.splitfileErrorLabel=Errore splitfile-specifico:
+WelcomeToadlet.splitfileErrorLabel=Errore relativo a splitfile:
 WelcomeToadlet.startIndexHeader=Indice dal quale iniziare
 WelcomeToadlet.subjectHeader=Oggetto
 WelcomeToadlet.targetBoardHeader=Board obiettivo

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.no.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.no.properties    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.no.properties    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -419,10 +419,6 @@
 Node.storeMaxMemTooHigh=? gi mer enn 80% av minnet ditt til BDB er antagelig 
ikke det du vil gj?re!
 Node.storeSize=Lagerst?rrelse i byte
 Node.storeSizeLong=Lagerst?rrelse i byte
-NodeClientCore.allowInsecureCHK=Tillat usikre CHKer?
-NodeClientCore.allowInsecureCHKLong=F?r 1010 var alle CHKer usikre (bare 
halvveis kryptert). Tillat gamle CHKer?
-NodeClientCore.allowInsecureSSK=Tillat usikre SSKer?
-NodeClientCore.allowInsecureSSKLong=F?r 1010 var alle SSKer usikre (bare 
halvveis kryptert). Tillat gamle SSKer?
 NodeClientCore.couldNotFindOrCreateDir=Kunne ikke finne eller opprette mappe
 NodeClientCore.maxUSKFetchersMustBeGreaterThanZero=M? v?re st?rre enn null
 NodeIPDectector.inclLocalAddress=Inkluder lokal adresse i noderef

Modified: branches/freenet-jfk/src/freenet/l10n/freenet.l10n.se.properties
===================================================================
--- branches/freenet-jfk/src/freenet/l10n/freenet.l10n.se.properties    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/l10n/freenet.l10n.se.properties    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -363,10 +363,6 @@
 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

Modified: branches/freenet-jfk/src/freenet/node/CHKInsertSender.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/CHKInsertSender.java  2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/CHKInsertSender.java  2007-09-23 
09:58:34 UTC (rev 15282)
@@ -37,6 +37,7 @@
                }

                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        try {
                                bt.send(executor);
                                if(bt.failedDueToOverload()) {
@@ -199,6 +200,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
         short origHTL;
        synchronized (this) {
             origHTL = htl;
@@ -637,6 +639,7 @@
        private class CompletionWaiter implements Runnable {

                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        if(logMINOR) Logger.minor(this, "Starting "+this);

                        // Wait for the request to reach a terminal stage.

Modified: branches/freenet-jfk/src/freenet/node/DNSRequester.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/DNSRequester.java     2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/DNSRequester.java     2007-09-23 
09:58:34 UTC (rev 15282)
@@ -30,6 +30,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
         while(true) {
             try {
                 realRun();

Modified: branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java  2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/DarknetPeerNode.java  2007-09-23 
09:58:34 UTC (rev 15282)
@@ -221,7 +221,8 @@
                                status == 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF ||
                                status == 
PeerManager.PEER_NODE_STATUS_CONN_ERROR ||
                                status == PeerManager.PEER_NODE_STATUS_TOO_NEW 
||
-                               status == PeerManager.PEER_NODE_STATUS_TOO_OLD)
+                               status == PeerManager.PEER_NODE_STATUS_TOO_OLD 
||
+                               status == 
PeerManager.PEER_NODE_STATUS_DISCONNECTING)
                        return status;
                if(isListenOnly)
                        return PeerManager.PEER_NODE_STATUS_LISTEN_ONLY;

Modified: branches/freenet-jfk/src/freenet/node/FNPPacketMangler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/FNPPacketMangler.java 2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/FNPPacketMangler.java 2007-09-23 
09:58:34 UTC (rev 15282)
@@ -171,12 +171,13 @@
                                // Try with unverified key
                                if(tryProcess(buf, offset, length, 
opn.getUnverifiedKeyTracker())) return;
                        }
-                       if(length > Node.SYMMETRIC_KEY_LENGTH /* iv */ + 
HASH_LENGTH + 2) {
+                       if(length > Node.SYMMETRIC_KEY_LENGTH /* iv */ + 
HASH_LENGTH + 2 && !node.isStopping()) {
                                // Might be an auth packet
-                               if(tryProcessAuth(buf, offset, length, opn, 
peer)) return;
+                               if(tryProcessAuth(buf, offset, length, opn, 
peer, false)) return;
                        }
                }
                PeerNode[] peers = crypto.getPeerNodes();
+               // Existing connection, changed IP address?
                if(length > HASH_LENGTH + RANDOM_BYTES_LENGTH + 4 + 6) {
                        for(int i=0;i<peers.length;i++) {
                                pn = peers[i];
@@ -198,13 +199,27 @@
                                }
                        }
                }
+               if(node.isStopping()) return;
+               // Disconnected node connecting on a new IP address?
                if(length > Node.SYMMETRIC_KEY_LENGTH /* iv */ + HASH_LENGTH + 
2) {
                        for(int i=0;i<peers.length;i++) {
                                pn = peers[i];
                                if(pn == opn) continue;
-                               if(tryProcessAuth(buf, offset, length, pn, 
peer)) return;
+                               if(tryProcessAuth(buf, offset, length, pn, 
peer,false)) return;
                        }
                }
+               OpennetManager opennet = node.getOpennet();
+               if(opennet != null) {
+                       // Try old opennet connections.
+                       if(opennet.wantPeer(null, false)) {
+                               // We want a peer.
+                               // Try old connections.
+                               PeerNode[] oldPeers = opennet.getOldPeers();
+                               for(int i=0;i<oldPeers.length;i++) {
+                                       if(tryProcessAuth(buf, offset, length, 
oldPeers[i], peer, true)) return;
+                               }
+                       }
+               }
                Logger.normal(this,"Unmatchable packet from "+peer);
        }

@@ -217,7 +232,7 @@
         * @param peer The Peer to send a reply to
         * @return True if we handled a negotiation packet, false otherwise.
         */
-       private boolean tryProcessAuth(byte[] buf, int offset, int length, 
PeerNode opn, Peer peer) {
+       private boolean tryProcessAuth(byte[] buf, int offset, int length, 
PeerNode opn, Peer peer, boolean oldOpennetPeer) {
                BlockCipher authKey = opn.incomingSetupCipher;
                if(logMINOR) Logger.minor(this, "Decrypt key: 
"+HexUtil.bytesToHex(opn.incomingSetupKey)+" for "+peer+" : "+opn+" in 
tryProcessAuth");
                // Does the packet match IV E( H(data) data ) ?
@@ -261,7 +276,7 @@

                if(Arrays.equals(realHash, hash)) {
                        // Got one
-                       processDecryptedAuth(payload, opn, peer);
+                       processDecryptedAuth(payload, opn, peer, 
oldOpennetPeer);
                        opn.reportIncomingBytes(length);
                        return true;
                } else {
@@ -274,7 +289,7 @@
         * Process a decrypted, authenticated auth packet.
         * @param payload The packet payload, after it has been decrypted.
         */
-       private void processDecryptedAuth(byte[] payload, PeerNode pn, Peer 
replyTo) {
+       private void processDecryptedAuth(byte[] payload, PeerNode pn, Peer 
replyTo, boolean oldOpennetPeer) {
                if(logMINOR) Logger.minor(this, "Processing decrypted auth 
packet from "+replyTo+" for "+pn);
                if(pn.isDisabled()) {
                        if(logMINOR) Logger.minor(this, "Won't connect to a 
disabled peer ("+pn+ ')');
@@ -355,10 +370,10 @@
                                // Verify the packet, then complete
                                // Format: IV E_K ( H(data) data )
                                // Where data = [ long: bob's startup number ]
-                               processSignedDHTwoOrThree(2, payload, pn, 
replyTo, true);
+                               processSignedDHTwoOrThree(2, payload, pn, 
replyTo, true, oldOpennetPeer);
                        } else if(packetType == 3) {
                                // We are Alice
-                               processSignedDHTwoOrThree(3, payload, pn, 
replyTo, false);
+                               processSignedDHTwoOrThree(3, payload, pn, 
replyTo, false, oldOpennetPeer);
                        }
                }
                else if (negType==2){
@@ -1100,8 +1115,11 @@
         * Data
         * 
         * May decrypt in place.
+        * @param oldOpennetPeer If true, the peer we are negotiating with is 
not in
+        * the primary routing table, it needs to be promoted from the list of 
old opennet
+        * nodes.
         */
-       private DiffieHellmanContext processSignedDHTwoOrThree(int phase, 
byte[] payload, PeerNode pn, Peer replyTo, boolean sendCompletion) {
+       private DiffieHellmanContext processSignedDHTwoOrThree(int phase, 
byte[] payload, PeerNode pn, Peer replyTo, boolean sendCompletion, boolean 
oldOpennetPeer) {
                if(logMINOR) Logger.minor(this, "Handling signed stage 
"+phase+" auth packet");
                // Get context, cipher, IV
                DiffieHellmanContext ctx = (DiffieHellmanContext) 
pn.getKeyAgreementSchemeContext();
@@ -1159,6 +1177,22 @@

                // Success!
                long bootID = Fields.bytesToLong(data);
+
+               // Promote if necessary
+               boolean dontWant = false;
+               if(oldOpennetPeer) {
+                       OpennetManager opennet = node.getOpennet();
+                       if(opennet == null) {
+                               Logger.normal(this, "Dumping incoming 
old-opennet peer as opennet just turned off: "+pn+".");
+                               return null;
+                       }
+                       if(!opennet.wantPeer(pn, true)) {
+                               Logger.normal(this, "No longer want peer "+pn+" 
- dumping it after connecting");
+                               dontWant = true;
+                       }
+                       // wantPeer will call node.peers.addPeer(), we don't 
have to.
+               }
+
                // Send the completion before parsing the data, because this is 
easiest
                // Doesn't really matter - if it fails, we get loads of errors 
anyway...
                // Only downside is that the other side might still think we 
are connected for a while.
@@ -1168,7 +1202,10 @@
                if(pn.completedHandshake(bootID, data, 8, data.length-8, 
cipher, encKey, replyTo, phase == 2)) {
                        if(sendCompletion)
                                sendSignedDHCompletion(3, ctx.getCipher(), pn, 
replyTo, ctx);
-                       pn.maybeSendInitialMessages();
+                       if(dontWant)
+                               node.peers.disconnect(pn, true, false);
+                       else
+                               pn.maybeSendInitialMessages();
                } else {
                        Logger.error(this, "Handshake not completed");
                }
@@ -1472,6 +1509,8 @@
                        tracker.destForgotPacket(realSeqNo);
                }

+               tracker.pn.receivedPacket(false); // Must keep the connection 
open, even if it's an ack packet only and on an incompatible connection - we 
may want to do a UOM transfer e.g.
+
                if(seqNumber == -1) {
                        if(logMINOR) Logger.minor(this, "Returning because 
seqno = "+seqNumber);
                        return;
@@ -1479,8 +1518,7 @@
                // No sequence number == no messages

                if((seqNumber != -1) && tracker.alreadyReceived(seqNumber)) {
-                       tracker.queueAck(seqNumber);
-                       tracker.pn.receivedPacket(false);
+                       tracker.queueAck(seqNumber); // Must keep the 
connection open!
                        Logger.error(this, "Received packet twice 
("+seqNumber+") from "+tracker.pn.getPeer()+": "+seqNumber+" 
("+TimeUtil.formatTime((long) tracker.pn.pingAverage.currentValue(), 2, true)+" 
ping avg)");
                        return;
                }
@@ -2290,4 +2328,4 @@
                }
                return data;
        }
-}
\ No newline at end of file
+}

Modified: branches/freenet-jfk/src/freenet/node/GlobalProbe.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/GlobalProbe.java      2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/GlobalProbe.java      2007-09-23 
09:58:34 UTC (rev 15282)
@@ -39,6 +39,7 @@
        }

        public void run() {
+               freenet.support.Logger.OSThread.logPID(this);
                synchronized(this) {
                        lastLocation = 0.0;
                        double prevLoc = lastLocation;

Modified: branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java  
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/IPDetectorPluginManager.java  
2007-09-23 09:58:34 UTC (rev 15282)
@@ -2,6 +2,9 @@

 import java.net.InetAddress;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 import java.util.Vector;

 import freenet.io.comm.FreenetInetAddress;
@@ -10,7 +13,11 @@
 import freenet.node.useralerts.ProxyUserAlert;
 import freenet.node.useralerts.UserAlert;
 import freenet.pluginmanager.DetectedIP;
+import freenet.pluginmanager.ForwardPort;
+import freenet.pluginmanager.ForwardPortCallback;
+import freenet.pluginmanager.ForwardPortStatus;
 import freenet.pluginmanager.FredPluginIPDetector;
+import freenet.pluginmanager.FredPluginPortForward;
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.OOMHandler;
@@ -20,7 +27,7 @@
  * Tracks all known IP address detection plugins, and runs them when 
appropriate.
  * Normally there would only be one, but sometimes there may be more than one.
  */
-public class IPDetectorPluginManager {
+public class IPDetectorPluginManager implements ForwardPortCallback {

        public class MyUserAlert implements UserAlert {

@@ -59,7 +66,17 @@
                        if(!suggestPortForward) return text;
                        StringBuffer sb = new StringBuffer();
                        sb.append(text);
-                       sb.append(l10n("suggestForwardPort", "port", 
Integer.toString(node.getDarknetPortNumber())));
+                       // FIXME we should support any number of ports, UDP or 
TCP, and pick them up from the node as we do with the forwarding plugin ... 
that would be a bit of a pain for L10n though ...
+                       int darknetPort = node.getDarknetPortNumber();
+                       int opennetPort = node.getOpennetFNPPort();
+                       sb.append(" ");
+                       if(opennetPort <= 0) {
+                               sb.append(l10n("suggestForwardPort", "port", 
Integer.toString(darknetPort)));
+                       } else {
+                               sb.append(l10n("suggestForwardTwoPorts", new 
String[] { "port1", "port2" }, 
+                                               new String[] { 
Integer.toString(darknetPort), Integer.toString(opennetPort) }));
+                       }
+                       
                        return sb.toString();
                }

@@ -93,6 +110,7 @@
        private final NodeIPDetector detector;
        private final Node node;
        FredPluginIPDetector[] plugins;
+       FredPluginPortForward[] portForwardPlugins;
        private final MyUserAlert noConnectionAlert;
        private final MyUserAlert symmetricAlert;
        private final MyUserAlert portRestrictedAlert;
@@ -104,6 +122,7 @@
        IPDetectorPluginManager(Node node, NodeIPDetector detector) {
                logMINOR = Logger.shouldLog(Logger.MINOR, getClass());
                plugins = new FredPluginIPDetector[0];
+               portForwardPlugins = new FredPluginPortForward[0];
                this.node = node;
                this.detector = detector;
                noConnectionAlert = new MyUserAlert( 
l10n("noConnectivityTitle"), l10n("noConnectivity"), 
@@ -152,6 +171,7 @@
                }
                node.getTicker().queueTimedJob(new Runnable() {
                        public void run() {
+                               freenet.support.Logger.OSThread.logPID(this);
                                tryMaybeRun();
                        }
                }, 60*1000);
@@ -160,7 +180,7 @@
        /**
         * Register a plugin.
         */
-       public void register(FredPluginIPDetector d) {
+       public void registerDetectorPlugin(FredPluginIPDetector d) {
                if(d == null) throw new NullPointerException();
                synchronized(this) {
                        FredPluginIPDetector[] newPlugins = new 
FredPluginIPDetector[plugins.length+1];
@@ -175,7 +195,7 @@
        /**
         * Remove a plugin.
         */
-       public void remove(FredPluginIPDetector d) {
+       public void unregisterDetectorPlugin(FredPluginIPDetector d) {
                synchronized(this) {
                        int count = 0;
                        for(int i=0;i<plugins.length;i++) {
@@ -185,7 +205,7 @@
                        FredPluginIPDetector[] newPlugins = new 
FredPluginIPDetector[plugins.length - count];
                        int x = 0;
                        for(int i=0;i<plugins.length;i++) {
-                               if(newPlugins[i] != d) newPlugins[x++] = 
plugins[i];
+                               if(plugins[i] != d) newPlugins[x++] = 
plugins[i];
                        }
                        plugins = newPlugins;
                }
@@ -475,6 +495,7 @@
        public class DetectorRunner implements Runnable {

                public void run() {
+                       freenet.support.Logger.OSThread.logPID(this);
                        try {
                                realRun();
                        } catch (OutOfMemoryError e) {
@@ -611,4 +632,67 @@
                return plugins.length == 0;
        }

+       public void registerPortForwardPlugin(FredPluginPortForward forward) {
+               if(forward == null) throw new NullPointerException();
+               synchronized(this) {
+                       FredPluginPortForward[] newForwardPlugins = new 
FredPluginPortForward[portForwardPlugins.length+1];
+                       System.arraycopy(portForwardPlugins, 0, 
newForwardPlugins, 0, portForwardPlugins.length);
+                       newForwardPlugins[portForwardPlugins.length] = forward;
+                       portForwardPlugins = newForwardPlugins;
+               }
+               if(logMINOR) Logger.minor(this, "Registering a new port forward 
plugin : " + forward);
+               forward.onChangePublicPorts(node.getPublicInterfacePorts(), 
this);
+       }
+
+       /**
+        * Remove a plugin.
+        */
+       public void unregisterPortForwardPlugin(FredPluginPortForward forward) {
+               synchronized(this) {
+                       int count = 0;
+                       for(int i=0;i<portForwardPlugins.length;i++) {
+                               if(portForwardPlugins[i] == forward) count++;
+                       }
+                       if(count == 0) return;
+                       FredPluginPortForward[] newPlugins = new 
FredPluginPortForward[portForwardPlugins.length - count];
+                       int x = 0;
+                       for(int i=0;i<portForwardPlugins.length;i++) {
+                               if(portForwardPlugins[i] != forward) 
newPlugins[x++] = portForwardPlugins[i];
+                       }
+                       portForwardPlugins = newPlugins;
+               }
+       }
+
+       void notifyPortChange(Set newPorts) {
+               FredPluginPortForward[] plugins;
+               synchronized(this) {
+                       plugins = portForwardPlugins;
+               }
+               for(int i=0;i<plugins.length;i++)
+                       plugins[i].onChangePublicPorts(newPorts, this);
+       }
+
+       public void portForwardStatus(Map statuses) {
+               Set currentPorts = node.getPublicInterfacePorts();
+               Iterator i = currentPorts.iterator();
+               while(i.hasNext()) {
+                       ForwardPort p = (ForwardPort) i.next();
+                       ForwardPortStatus status = (ForwardPortStatus) 
statuses.get(p);
+                       if(status == null) continue;
+                       if(status.status == ForwardPortStatus.DEFINITE_SUCCESS) 
{
+                               Logger.normal(this, "Succeeded forwarding 
"+p.name+" port "+p.portNumber+" for "+p.protocol+" - port forward definitely 
succeeded "+status.reasonString);
+                       } else if(status.status == 
ForwardPortStatus.PROBABLE_SUCCESS) {
+                               Logger.normal(this, "Probably succeeded 
forwarding "+p.name+" port "+p.portNumber+" for "+p.protocol+" - port forward 
probably succeeded "+status.reasonString);
+                       } else if(status.status == 
ForwardPortStatus.MAYBE_SUCCESS) {
+                               Logger.normal(this, "Maybe succeeded forwarding 
"+p.name+" port "+p.portNumber+" for "+p.protocol+" - port forward may have 
succeeded but strongly recommend out of band verification 
"+status.reasonString);
+                       } else if(status.status == 
ForwardPortStatus.DEFINITE_FAILURE) {
+                               Logger.error(this, "Failed forwarding 
"+p.name+" port "+p.portNumber+" for "+p.protocol+" - port forward definitely 
failed "+status.reasonString);
+                       } else if(status.status == 
ForwardPortStatus.PROBABLE_FAILURE) {
+                               Logger.error(this, "Probably failed forwarding 
"+p.name+" port "+p.portNumber+" for "+p.protocol+" - port forward probably 
failed "+status.reasonString);
+                       }
+                       // Not much more we can do / want to do for now
+                       // FIXME use status.externalPort.
+               }
+       }
+       
 }

Modified: branches/freenet-jfk/src/freenet/node/InsertHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/InsertHandler.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/InsertHandler.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -71,6 +71,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
         try {
                realRun();
                } catch (OutOfMemoryError e) {
@@ -400,6 +401,7 @@
     public class DataReceiver implements Runnable {

         public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                synchronized(this) {
                        receiveStarted = true;
                }

Modified: branches/freenet-jfk/src/freenet/node/KeyTracker.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/KeyTracker.java       2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/KeyTracker.java       2007-09-23 
09:58:34 UTC (rev 15282)
@@ -441,8 +441,7 @@
      */
     public synchronized void receivedPacket(int seqNumber) {
         logMINOR = Logger.shouldLog(Logger.MINOR, this);
-       if(logMINOR) Logger.minor(this, "Received packet "+seqNumber);
-               pn.receivedPacket(false);
+       if(logMINOR) Logger.minor(this, "Received packet "+seqNumber+" from 
"+pn.shortToString());
         if(seqNumber == -1) return;
         // FIXME delete this log statement
         if(logMINOR) Logger.minor(this, "Still received packet: "+seqNumber);

Modified: branches/freenet-jfk/src/freenet/node/LocationManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/LocationManager.java  2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/LocationManager.java  2007-09-23 
09:58:34 UTC (rev 15282)
@@ -20,6 +20,7 @@
 import freenet.support.Logger;
 import freenet.support.ShortBuffer;
 import freenet.support.TimeSortedHashtable;
+import freenet.support.math.BootstrappingDecayingRunningAverage;

 /**
  * @author amphibian
@@ -49,18 +50,33 @@
     }

     static final int TIMEOUT = 60*1000;
+    static final int SWAP_MAX_HTL = 10;
+    /** Number of swap evaluations, either incoming or outgoing, between 
resetting our location. 
+     * There is a 2 in SWAP_RESET chance that a reset will occur on one or 
other end of a swap request. */
+    static final int SWAP_RESET = 4000;
+       // FIXME vary automatically
+    static final int SEND_SWAP_INTERVAL = 8000;
+    /** The average time between sending a swap request, and completion. */
+    final BootstrappingDecayingRunningAverage averageSwapTime;
+    /** Minimum swap delay */
+    static final int MIN_SWAP_TIME = 
Node.MIN_INTERVAL_BETWEEN_INCOMING_SWAP_REQUESTS;
+    /** Maximum swap delay */
+    static final int MAX_SWAP_TIME = 60*1000;
     private static boolean logMINOR;
     final RandomSource r;
     final SwapRequestSender sender;
-    SwapRequestInterval interval;
-    Node node;
+    final Node node;
     long timeLastSuccessfullySwapped;

-    public LocationManager(RandomSource r) {
+    public LocationManager(RandomSource r, Node node) {
         loc = r.nextDouble();
         sender = new SwapRequestSender();
         this.r = r;
+        this.node = node;
         recentlyForwardedIDs = new Hashtable();
+        // FIXME persist to disk!
+        averageSwapTime = new 
BootstrappingDecayingRunningAverage(SEND_SWAP_INTERVAL, 0, Integer.MAX_VALUE, 
20, null);
+        
         logMINOR = Logger.shouldLog(Logger.MINOR, this);
     }

@@ -117,10 +133,8 @@
      * Start a thread to send FNPSwapRequests every second when
      * we are not locked.
      */
-    public void startSender(Node n, SwapRequestInterval interval) {
-        this.node = n;
-        this.interval = interval;
-        n.executor.execute(sender, "SwapRequest sender");
+    public void startSender() {
+        node.executor.execute(sender, "SwapRequest sender");
     }

     /**
@@ -129,15 +143,14 @@
      */
     public class SwapRequestSender implements Runnable {

-        int sendInterval = 2000;
-        
         public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
             while(true) {
                 try {
                     long startTime = System.currentTimeMillis();
                     double nextRandom = r.nextDouble();
                     while(true) {
-                        int sleepTime = interval.getValue();
+                        int sleepTime = getSendSwapInterval();
                         sleepTime *= nextRandom;
                         sleepTime = Math.min(sleepTime, Integer.MAX_VALUE);
                         long endTime = startTime + (int)sleepTime;
@@ -178,13 +191,13 @@
                                     node.writeNodeFile();
                                 }
                             } finally {
-                                unlock();
+                                unlock(false);
                             }
-                        } else unlock();
+                        } else unlock(false);
                     } else {
                         continue;
                     }
-                    // Check the 
+                    // Send a swap request
                     startSwapRequest();
                 } catch (Throwable t) {
                     Logger.error(this, "Caught "+t, t);
@@ -202,7 +215,16 @@
                 "Outgoing swap request handler for port 
"+node.getDarknetPortNumber());
     }

-    /**
+    public int getSendSwapInterval() {
+       int interval = (int) averageSwapTime.currentValue();
+       if(interval < MIN_SWAP_TIME)
+               interval = MIN_SWAP_TIME;
+       if(interval > MAX_SWAP_TIME)
+               interval = MAX_SWAP_TIME;
+       return interval;
+       }
+
+       /**
      * Similar to OutgoingSwapRequestHandler, except that we did
      * not initiate the SwapRequest.
      */
@@ -223,8 +245,10 @@
         }

         public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
             MessageDigest md = SHA256.getMessageDigest();

+            boolean reachedEnd = false;
             try {
             // We are already locked by caller
             // Because if we can't get lock they need to send a reject
@@ -347,11 +371,21 @@
                if(logMINOR) Logger.minor(this, "Didn't swap: "+myLoc+" <-> 
"+hisLoc+" - "+uid);
                 noSwaps++;
             }
+            
+            reachedEnd = true;
+            
+            // Randomise our location every 2*SWAP_RESET swap attempts, 
whichever way it went.
+            if(node.random.nextInt(SWAP_RESET) == 0) {
+                setLocation(node.random.nextDouble());
+                announceLocChange();
+                node.writeNodeFile();
+            }
+
             SHA256.returnMessageDigest(md);
         } catch (Throwable t) {
             Logger.error(this, "Caught "+t, t);
         } finally {
-            unlock();
+            unlock(reachedEnd); // we only count the time taken by our 
outgoing swap requests
             removeRecentlyForwardedItem(item);
         }
         }
@@ -369,8 +403,10 @@
         RecentlyForwardedItem item;

         public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
             long uid = r.nextLong();            
             if(!lock()) return;
+            boolean reachedEnd = false;
             try {
                 startedSwaps++;
                 // We can't lock friends_locations, so lets just
@@ -388,7 +424,7 @@

                 byte[] myHash = SHA256.digest(myValue);

-                Message m = DMT.createFNPSwapRequest(uid, myHash, 6);
+                Message m = DMT.createFNPSwapRequest(uid, myHash, 
SWAP_MAX_HTL);

                 PeerNode pn = node.peers.getRandomPeer();
                 if(pn == null) {
@@ -527,10 +563,20 @@
                        if(logMINOR) Logger.minor(this, "Didn't swap: "+myLoc+" 
<-> "+hisLoc+" - "+uid);
                     noSwaps++;
                 }
+                
+                reachedEnd = true;
+                
+                // Randomise our location every 2*SWAP_RESET swap attempts, 
whichever way it went.
+                if(node.random.nextInt(SWAP_RESET) == 0) {
+                    setLocation(node.random.nextDouble());
+                    announceLocChange();
+                    node.writeNodeFile();
+                }
+
             } catch (Throwable t) {
                 Logger.error(this, "Caught "+t, t);
             } finally {
-                unlock();
+                unlock(reachedEnd);
                 if(item != null)
                     removeRecentlyForwardedItem(item);
             }
@@ -575,7 +621,10 @@
         return true;
     }

-    synchronized void unlock() {
+    /**
+     * Unlock the node for swapping.
+     * @param logSwapTime If true, log the swap time. */
+    synchronized void unlock(boolean logSwapTime) {
         if(!locked)
             throw new IllegalStateException("Unlocking when not locked!");
         locked = false;
@@ -584,6 +633,7 @@
                Logger.minor(this, "Unlocking on port 
"+node.getDarknetPortNumber());
                Logger.minor(this, "lockTime: "+lockTime);
         }
+        averageSwapTime.report(lockTime);
     }

     /**
@@ -617,6 +667,9 @@
         // all multiplied together

         // Dump
+       
+       if(Math.abs(hisLoc - myLoc) <= Double.MIN_VALUE * 2)
+               return false; // Probably swapping with self

         StringBuffer sb = new StringBuffer();

@@ -705,19 +758,21 @@
      */
     public boolean handleSwapRequest(Message m) {
         PeerNode pn = (PeerNode)m.getSource();
-        long uid = m.getLong(DMT.UID);
-        Long luid = new Long(uid);
-        long oid = uid+1;
-        // We have two separate IDs so we can deal with two visits
-        // separately. This is because we want it to be as random 
-        // as possible.
-        // This means we can and should check for the same ID being
-        // sent twice.
+        long oldID = m.getLong(DMT.UID);
+        Long luid = new Long(oldID);
+        long newID = oldID+1;
+        /**
+         * UID is used to record the state i.e. UID x, came in from node a, 
forwarded to node b.
+         * We increment it on each hop, because in order for the node 
selection to be as random as
+         * possible we *must allow loops*! I.e. the same swap chain may pass 
over the same node 
+         * twice or more. However, if we get a request with either the 
incoming or the outgoing 
+         * UID, we can safely kill it as it's clearly the result of a bug.
+         */
         RecentlyForwardedItem item = (RecentlyForwardedItem) 
recentlyForwardedIDs.get(luid);
         if(item != null) {
                if(logMINOR) Logger.minor(this, "Rejecting - same ID as 
previous request");
             // Reject
-            Message reject = DMT.createFNPSwapRejected(uid);
+            Message reject = DMT.createFNPSwapRejected(oldID);
             try {
                 pn.sendAsync(reject, null, 0, null);
             } catch (NotConnectedException e) {
@@ -729,7 +784,7 @@
         if(pn.shouldRejectSwapRequest()) {
                if(logMINOR) Logger.minor(this, "Advised to reject SwapRequest 
by PeerNode - rate limit");
             // Reject
-            Message reject = DMT.createFNPSwapRejected(uid);
+            Message reject = DMT.createFNPSwapRejected(oldID);
             try {
                 pn.sendAsync(reject, null, 0, null);
             } catch (NotConnectedException e) {
@@ -738,16 +793,21 @@
             swapsRejectedRateLimit++;
             return true;
         }
-        if(logMINOR) Logger.minor(this, "SwapRequest from "+pn+" - uid="+uid);
-        int htl = m.getInt(DMT.HTL)-1;
+        if(logMINOR) Logger.minor(this, "SwapRequest from "+pn+" - 
uid="+oldID);
+        int htl = m.getInt(DMT.HTL);
+        if(htl > SWAP_MAX_HTL) {
+               Logger.error(this, "Bogus swap HTL: "+htl+" from "+pn+" 
uid="+oldID);
+               htl = SWAP_MAX_HTL;
+        }
+        htl--;
         // Either forward it or handle it
-        if(htl == 0) {
-               if(logMINOR) Logger.minor(this, "Accepting?... "+uid);
+        if(htl <= 0) {
+               if(logMINOR) Logger.minor(this, "Accepting?... "+oldID);
             // Accept - handle locally
             if(!lock()) {
-               if(logMINOR) Logger.minor(this, "Can't obtain lock on "+uid+" - 
rejecting to "+pn);
+               if(logMINOR) Logger.minor(this, "Can't obtain lock on "+oldID+" 
- rejecting to "+pn);
                 // Reject
-                Message reject = DMT.createFNPSwapRejected(uid);
+                Message reject = DMT.createFNPSwapRejected(oldID);
                 try {
                     pn.sendAsync(reject, null, 0, null);
                 } catch (NotConnectedException e1) {
@@ -757,30 +817,30 @@
                 return true;
             }
             try {
-                item = addForwardedItem(uid, oid, pn, null);
+                item = addForwardedItem(oldID, newID, pn, null);
                 // Locked, do it
                 IncomingSwapRequestHandler isrh =
                     new IncomingSwapRequestHandler(m, pn, item);
-                if(logMINOR) Logger.minor(this, "Handling... "+uid);
+                if(logMINOR) Logger.minor(this, "Handling... "+oldID);
                 node.executor.execute(isrh, "Incoming swap request handler for 
port "+node.getDarknetPortNumber());
                 return true;
             } catch (Error e) {
-                unlock();
+                unlock(false);
                 throw e;
             } catch (RuntimeException e) {
-                unlock();
+                unlock(false);
                 throw e;
             }
         } else {
             m.set(DMT.HTL, htl);
-            m.set(DMT.UID, oid);
-            if(logMINOR) Logger.minor(this, "Forwarding... "+uid);
+            m.set(DMT.UID, newID);
+            if(logMINOR) Logger.minor(this, "Forwarding... "+oldID);
             while(true) {
                 // Forward
                 PeerNode randomPeer = node.peers.getRandomPeer(pn);
                 if(randomPeer == null) {
-                       if(logMINOR) Logger.minor(this, "Late reject "+uid);
-                    Message reject = DMT.createFNPSwapRejected(uid);
+                       if(logMINOR) Logger.minor(this, "Late reject "+oldID);
+                    Message reject = DMT.createFNPSwapRejected(oldID);
                     try {
                         pn.sendAsync(reject, null, 0, null);
                     } catch (NotConnectedException e1) {
@@ -789,14 +849,14 @@
                     swapsRejectedNowhereToGo++;
                     return true;
                 }
-                if(logMINOR) Logger.minor(this, "Forwarding "+uid+" to 
"+randomPeer);
-                item = addForwardedItem(uid, oid, pn, randomPeer);
+                if(logMINOR) Logger.minor(this, "Forwarding "+oldID+" to 
"+randomPeer);
+                item = addForwardedItem(oldID, newID, pn, randomPeer);
                 item.successfullyForwarded = false;
                 try {
                     // Forward the request.
                     // Note that we MUST NOT send this blocking as we are on 
the
                     // receiver thread.
-                    randomPeer.sendAsync(m, new 
MyCallback(DMT.createFNPSwapRejected(uid), pn, item), 0, null);
+                    randomPeer.sendAsync(m, new 
MyCallback(DMT.createFNPSwapRejected(oldID), pn, item), 0, null);
                 } catch (NotConnectedException e) {
                     // Try a different node
                     continue;
@@ -1145,4 +1205,8 @@
        public synchronized double getLocChangeSession() {
                return locChangeSession;
        }
+       
+       public int getAverageSwapTime() {
+               return (int) averageSwapTime.currentValue();
+       }
 }

Modified: branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/LoggingConfigHandler.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -302,6 +302,7 @@
                }

                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        fileLoggerHook.waitForSwitch();
                        delete(logDir);
                }

Modified: branches/freenet-jfk/src/freenet/node/MemoryChecker.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/MemoryChecker.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/MemoryChecker.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -32,6 +32,7 @@
        }

        public void run() {
+               freenet.support.Logger.OSThread.logPID(this);
                if(!goon){
                        Logger.normal(this, "Goon is false ; killing 
MemoryChecker");
                        return;

Modified: branches/freenet-jfk/src/freenet/node/Node.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/Node.java     2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/node/Node.java     2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -25,6 +25,7 @@
 import java.util.Locale;
 import java.util.MissingResourceException;
 import java.util.Random;
+import java.util.Set;

 import org.spaceroots.mantissa.random.MersenneTwister;
 import org.tanukisoftware.wrapper.WrapperManager;
@@ -80,6 +81,7 @@
 import freenet.node.useralerts.OpennetUserAlert;
 import freenet.node.useralerts.TimeSkewDetectedUserAlert;
 import freenet.node.useralerts.UserAlert;
+import freenet.pluginmanager.ForwardPort;
 import freenet.pluginmanager.PluginManager;
 import freenet.store.BerkeleyDBFreenetStore;
 import freenet.store.FreenetStore;
@@ -103,6 +105,7 @@
 import freenet.support.api.LongCallback;
 import freenet.support.api.ShortCallback;
 import freenet.support.api.StringCallback;
+import freenet.support.transport.ip.HostnameSyntaxException;

 /**
  * @author amphibian
@@ -333,13 +336,11 @@
        final LRUHashtable cachedPubKeys;
        final boolean testnetEnabled;
        final TestnetHandler testnetHandler;
-       final StaticSwapRequestInterval swapInterval;
        public final DoubleTokenBucket outputThrottle;
        private int outputBandwidthLimit;
        private int inputBandwidthLimit;
        boolean inputLimitDefault;
        public static final short DEFAULT_MAX_HTL = (short)10;
-       public static final int DEFAULT_SWAP_INTERVAL = 2000;
        private short maxHTL;
        /** Type identifier for fproxy node to node messages, as sent on 
DMT.nodeToNodeMessage's */
        public static final int N2N_MESSAGE_TYPE_FPROXY = 1;
@@ -405,7 +406,11 @@
                                // Just keep the first one with the correct 
port number.
                                Peer p;
                                try {
-                                       p = new Peer(udp[i], false);
+                                       p = new Peer(udp[i], false, true);
+                               } catch (HostnameSyntaxException e) {
+                                       Logger.error(this, "Invalid hostname or 
IP Address syntax error while parsing darknet node reference: "+udp[i]);
+                                       System.err.println("Invalid hostname or 
IP Address syntax error while parsing darknet node reference: "+udp[i]);
+                                       continue;
                                } catch (PeerParseException e) {
                                        IOException e1 = new IOException();
                                        e1.initCause(e);
@@ -540,7 +545,7 @@
                random.nextBytes(buffer);
                this.fastWeakRandom = new MersenneTwister(buffer);
                cachedPubKeys = new LRUHashtable();
-               lm = new LocationManager(random);
+               lm = new LocationManager(random, this);

                try {
                        localhostAddress = InetAddress.getByName("127.0.0.1");
@@ -671,21 +676,6 @@
                        ibwLimit = obwLimit * 4;
                }

-               // SwapRequestInterval
-               
-               nodeConfig.register("swapRequestSendInterval", 
DEFAULT_SWAP_INTERVAL, sortOrder++, true, false,
-                               "Node.swapRInterval", "Node.swapRIntervalLong",
-                               new IntCallback() {
-                                       public int get() {
-                                               return 
swapInterval.fixedInterval;
-                                       }
-                                       public void set(int val) throws 
InvalidConfigValueException {
-                                               swapInterval.set(val);
-                                       }
-               });
-               
-               swapInterval = new 
StaticSwapRequestInterval(nodeConfig.getInt("swapRequestSendInterval"));
-               
                // Testnet.
                // Cannot be enabled/disabled on the fly.
                // If enabled, forces certain other config options.
@@ -770,7 +760,7 @@

                // Then read the peers
                peers = new PeerManager(this);
-               peers.tryReadPeers(new File(nodeDir, 
"peers-"+getDarknetPortNumber()).getPath(), darknetCrypto, null, false);
+               peers.tryReadPeers(new File(nodeDir, 
"peers-"+getDarknetPortNumber()).getPath(), darknetCrypto, null, false, false);
                peers.writePeers();
                peers.updatePMUserAlert();

@@ -802,6 +792,7 @@
                                }
                                if(val) o.start();
                                else o.stop();
+                               
ipDetector.ipDetectorManager.notifyPortChange(getPublicInterfacePorts());
                        }
                });

@@ -962,6 +953,9 @@
                envConfig.setTxnWriteNoSync(true);
                envConfig.setLockTimeout(600*1000*1000); // should be long 
enough even for severely overloaded nodes!
                // Note that the above is in *MICRO*seconds.
+               envConfig.setConfigParam("je.log.faultReadSize", "6144");
+               envConfig.setConfigParam("je.evictor.lruOnly", "false");  //Is 
not a mutable config option and must be set before opening of environment.
+               envConfig.setConfigParam("je.evictor.nodesPerScan", "100");  
//Is not a mutable config option and must be set before opening of environment.

                File dbDir = new File(storeDir, 
"database-"+getDarknetPortNumber());
                dbDir.mkdirs();
@@ -982,6 +976,8 @@
                                reconstructFile.delete();
                                throw new DatabaseException();
                        }
+                       // Auto-recovery can take a long time
+                       WrapperManager.signalStarting(60*60*1000);
                        env = new Environment(dbDir, envConfig);
                        mutableConfig = env.getConfig();
                } catch (DatabaseException e) {
@@ -1122,9 +1118,6 @@
                }
                envMutableConfig.setCacheSize(databaseMaxMemory);
                // 
http://www.oracle.com/technology/products/berkeley-db/faq/je_faq.html#35
-               // FIXME is this the correct place to set these parameters?
-               envMutableConfig.setConfigParam("je.evictor.lruOnly", "false");
-               envMutableConfig.setConfigParam("je.evictor.nodesPerScan", 
"100");

                try {
                        storeEnvironment.setMutableConfig(envMutableConfig);
@@ -1272,8 +1265,6 @@

        public void start(boolean noSwaps) throws NodeInitException {

-               if(!noSwaps)
-                       lm.startSender(this, this.swapInterval);
                dispatcher.start(nodeStats); // must be before usm
                dnsr.start();
                ps.start(nodeStats);
@@ -1302,6 +1293,9 @@
 //             pluginManager3 = new 
freenet.plugin_new.PluginManager(pluginManagerConfig);

                ipDetector.start();
+               
+               // Start sending swaps
+               lm.startSender();

                // Node Updater
                try{
@@ -2247,7 +2241,11 @@
                if(key == null) return null;
                clientSSK.setPublicKey(key);
                SSKBlock block = fetch((NodeSSK)clientSSK.getNodeKey(), 
dontPromote);
-               if(block == null) return null;
+               if(block == null) {
+                       if(logMINOR)
+                               Logger.minor(this, "Could not find key for 
"+clientSSK+" (dontPromote="+dontPromote+")");
+                       return null;
+               }
                // Move the pubkey to the top of the LRU, and fix it if it
                // was corrupt.
                cacheKey(clientSSK.pubKeyHash, key, false);
@@ -2279,15 +2277,33 @@
                }
        }

+       public synchronized boolean isStopping() {
+               return isStopping;
+       }
+       
        /**
         * Get the node into a state where it can be stopped safely
         * May be called twice - once in exit (above) and then again
         * from the wrapper triggered by calling System.exit(). Beware!
         */
-       public synchronized void park() {
-               if(isStopping) return;
-               isStopping = true;
+       public void park() {
+               synchronized(this) {
+                       if(isStopping) return;
+                       isStopping = true;
+               }

+               try {
+                       Message msg = DMT.createFNPDisconnect(false, false, -1, 
new ShortBuffer(new byte[0]));
+                       peers.localBroadcast(msg, true);
+               } catch (Throwable t) {
+                       try {
+                               // E.g. if we haven't finished startup
+                               Logger.error(this, "Failed to tell peers we are 
going down: "+t, t);
+                       } catch (Throwable t1) {
+                               // Ignore. We don't want to mess up the exit 
process!
+                       }
+               }
+               
                config.store();

                // TODO: find a smarter way of doing it not involving any 
casting
@@ -2310,7 +2326,7 @@
        }

        public void removePeerConnection(PeerNode pn) {
-               peers.disconnect(pn);
+               peers.disconnect(pn, true, false);
        }

        public void onConnectedPeer() {
@@ -2338,14 +2354,20 @@
         */
        public void receivedNodeToNodeMessage(Message m) {
          PeerNode src = (PeerNode) m.getSource();
+         int type = ((Integer) 
m.getObject(DMT.NODE_TO_NODE_MESSAGE_TYPE)).intValue();
+         ShortBuffer messageData = (ShortBuffer) 
m.getObject(DMT.NODE_TO_NODE_MESSAGE_DATA);
+         receivedNodeToNodeMessage(src, type, messageData, false);
+       }
+       
+       public void receivedNodeToNodeMessage(PeerNode src, int type, 
ShortBuffer messageData, boolean partingMessage) {
          if(!(src instanceof DarknetPeerNode)) {
-               Logger.error(this, "Got N2NTM from opennet node ?!?!?!: "+m+" 
from "+src);
+               Logger.error(this, "Got N2NTM from opennet node ?!?!?!: from 
"+src);
                return;
          }
-         DarknetPeerNode source = (DarknetPeerNode)m.getSource();
-         int type = ((Integer) 
m.getObject(DMT.NODE_TO_NODE_MESSAGE_TYPE)).intValue();
+         DarknetPeerNode source = (DarknetPeerNode)src;
+         
          if(type == Node.N2N_MESSAGE_TYPE_FPROXY) {
-               ShortBuffer messageData = (ShortBuffer) 
m.getObject(DMT.NODE_TO_NODE_MESSAGE_DATA);
+               
                Logger.normal(this, "Received N2NM from 
'"+source.getPeer()+"'");
                SimpleFieldSet fs = null;
                try {
@@ -2513,6 +2535,14 @@
                return lm.getLocChangeSession();
        }

+       public int getAverageOutgoingSwapTime() {
+               return lm.getAverageSwapTime();
+       }
+
+       public int getSendSwapInterval() {
+               return lm.getSendSwapInterval();
+       }
+       
        public int getNumberOfRemotePeerLocationsSeenInSwaps() {
                return lm.numberOfRemotePeerLocationsSeenInSwaps;
        }
@@ -2684,4 +2714,23 @@
                return passOpennetRefsThroughDarknet;
        }

+       /**
+        * Get the set of public ports that need to be forwarded. These are 
internal
+        * ports, not necessarily external - they may be rewritten by the NAT.
+        * @return A Set of ForwardPort's to be fed to port forward plugins.
+        */
+       public Set getPublicInterfacePorts() {
+               HashSet set = new HashSet();
+               // FIXME IPv6 support
+               set.add(new ForwardPort("darknet", false, 
ForwardPort.PROTOCOL_UDP_IPV4, darknetCrypto.portNumber));
+               OpennetManager opennet = this.opennet;
+               if(opennet != null) {
+                       NodeCrypto crypto = opennet.crypto;
+                       if(crypto != null) {
+                               set.add(new ForwardPort("opennet", false, 
ForwardPort.PROTOCOL_UDP_IPV4, crypto.portNumber));
+                       }
+               }
+               return set;
+       }
+
 }

Modified: branches/freenet-jfk/src/freenet/node/NodeClientCore.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/NodeClientCore.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/NodeClientCore.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -241,8 +241,6 @@
                                        }

                });
-               if(node.lastVersion <= 1024)
-                       nodeConfig.fixOldDefault("downloadAllowedDirs", 
"downloads");
                
setDownloadAllowedDirs(nodeConfig.getStringArr("downloadAllowedDirs"));

                nodeConfig.register("uploadAllowedDirs", new String[] {"all"}, 
sortOrder++, true, true, "NodeClientCore.uploadAllowedDirs", 
@@ -278,22 +276,22 @@

                // FIXME remove this code, the new behaviour should be handled 
by all clients

-               nodeConfig.register("ignoreTooManyPathComponents", false, 
sortOrder++, true, false, "NodeClientCore.ignoreTooManyPathComponents", 
-                               
"NodeClientCore.ignoreTooManyPathComponentsLong", new BooleanCallback() {
+//             nodeConfig.register("ignoreTooManyPathComponents", false, 
sortOrder++, true, false, "NodeClientCore.ignoreTooManyPathComponents", 
+//                             
"NodeClientCore.ignoreTooManyPathComponentsLong", new BooleanCallback() {

-                                       public boolean get() {
-                                               return 
ignoreTooManyPathComponents;
-                                       }
+//                                     public boolean get() {
+//                                             return 
ignoreTooManyPathComponents;
+//                                     }

-                                       public void set(boolean val) throws 
InvalidConfigValueException {
-                                               
synchronized(NodeClientCore.this) {
-                                                       
ignoreTooManyPathComponents = val;
-                                               }
-                                       }
-                       
-               });
+//                                     public void set(boolean val) throws 
InvalidConfigValueException {
+//                                             
synchronized(NodeClientCore.this) {
+//                                                     
ignoreTooManyPathComponents = val;
+//                                             }
+//                                     }
+//                     
+//             });

-               ignoreTooManyPathComponents = 
nodeConfig.getBoolean("ignoreTooManyPathComponents");
+               ignoreTooManyPathComponents = false;

                nodeConfig.register("lazyResume", false, sortOrder++, true, 
false, "NodeClientCore.lazyResume",
                                "NodeClientCore.lazyResumeLong", new 
BooleanCallback() {
@@ -327,40 +325,6 @@
                maxBackgroundUSKFetchers = 
nodeConfig.getInt("maxBackgroundUSKFetchers");


-               // FIXME remove and remove related code when we can just block 
them.
-               // REDFLAG normally we wouldn't use static variables to carry 
important non-final data, but in this
-               // case it's temporary code which will be removed before 0.7.0.
-               
-               nodeConfig.register("allowInsecureCHKs", false, sortOrder++, 
true, false, "NodeClientCore.allowInsecureCHK", 
"NodeClientCore.allowInsecureCHKLong",
-                               new BooleanCallback() {
-
-                                       public boolean get() {
-                                               return 
Key.ALLOW_INSECURE_CLIENT_CHKS;
-                                       }
-
-                                       public void set(boolean val) throws 
InvalidConfigValueException {
-                                               Key.ALLOW_INSECURE_CLIENT_CHKS 
= val;
-                                       }
-                       
-               });
-               
-               Key.ALLOW_INSECURE_CLIENT_CHKS = 
nodeConfig.getBoolean("allowInsecureCHKs");
-               
-               nodeConfig.register("allowInsecureSSKs", false, sortOrder++, 
true, false, "NodeClientCore.allowInsecureSSK", 
"NodeClientCore.allowInsecureSSKLong",
-                               new BooleanCallback() {
-
-                                       public boolean get() {
-                                               return 
Key.ALLOW_INSECURE_CLIENT_SSKS;
-                                       }
-
-                                       public void set(boolean val) throws 
InvalidConfigValueException {
-                                               Key.ALLOW_INSECURE_CLIENT_SSKS 
= val;
-                                       }
-                       
-               });
-               
-               Key.ALLOW_INSECURE_CLIENT_SSKS = 
nodeConfig.getBoolean("allowInsecureSSKs");
-               
                // This is all part of construction, not of start().
                // Some plugins depend on it, so it needs to be *created* 
before they are started.


Modified: branches/freenet-jfk/src/freenet/node/NodeCrypto.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/NodeCrypto.java       2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/NodeCrypto.java       2007-09-23 
09:58:34 UTC (rev 15282)
@@ -188,9 +188,7 @@
                        if(privARK != null) {
                                FreenetURI uri = new FreenetURI(privARK);
                                ark = InsertableClientSSK.create(uri);
-                               if(ark.isInsecure() || s == null) {
-                                       if(ark.isInsecure())
-                                               System.out.println("Creating 
new ARK, old is insecure");
+                               if(s == null) {
                                        ark = null;
                                        myARKNumber = 0;
                                } else {

Modified: branches/freenet-jfk/src/freenet/node/NodeDispatcher.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/NodeDispatcher.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/NodeDispatcher.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -87,6 +87,9 @@
                        return handleTime(m, source);
                } else if(spec == DMT.FNPVoid) {
                        return true;
+               } else if(spec == DMT.FNPDisconnect) {
+                       handleDisconnect(m, source);
+                       return true;
                } else if(spec == DMT.nodeToNodeMessage) {
                        node.receivedNodeToNodeMessage(m);
                        return true;
@@ -144,6 +147,25 @@
                return false;
        }

+       private void handleDisconnect(Message m, PeerNode source) {
+               source.disconnected();
+               // If true, remove from active routing table, likely to be down 
for a while.
+               // Otherwise just dump all current connection state and keep 
trying to connect.
+               boolean remove = m.getBoolean(DMT.REMOVE);
+               if(remove)
+                       node.peers.disconnect(source, false, false);
+               // If true, purge all references to this node. Otherwise, we 
can keep the node
+               // around in secondary tables etc in order to more easily 
reconnect later. 
+               // (Mostly used on opennet)
+               // Not used at the moment - FIXME
+               boolean purge = m.getBoolean(DMT.PURGE);
+               // Process parting message
+               int type = m.getInt(DMT.NODE_TO_NODE_MESSAGE_TYPE);
+               ShortBuffer messageData = (ShortBuffer) 
m.getObject(DMT.NODE_TO_NODE_MESSAGE_DATA);
+               if(messageData.getLength() == 0) return;
+               node.receivedNodeToNodeMessage(source, type, messageData, true);
+       }
+
        private boolean handleTime(Message m, PeerNode source) {
                long delta = m.getLong(DMT.TIME) - System.currentTimeMillis();
                source.setTimeDelta(delta);
@@ -529,7 +551,8 @@
         * request which we should handle anyway.
         * @param cb
         * @param locsNotVisited 
-        * @param maxDistance 
+        * @param maxDistance Don't route to any nodes further away from the 
target than this distance.
+        * Note that it is a distance, NOT A LOCATION.
         * @param dontReject If true, don't reject the request, simply return 
false and the caller will handle it.
         * @return True unless we rejected the request (due to load, route not 
found etc), or would have if it weren't for dontReject.
         */
@@ -818,6 +841,7 @@
                        for(int i=0;i<locsNotVisited.length;i++)
                                notVisitedList.add(new 
Double(locsNotVisited[i]));
                }
+               // notVisitedList == locsNotVisited

                // Find it
                ProbeContext ctx;
@@ -852,37 +876,21 @@

                // Maybe fork

-               
-               
                try {
-                       double furthestDist = 0.0;
-                       if(notVisitedList.size() > 0) {
+                       if(locsNotVisited.length > 0) {
                                if(ctx.forkCount < MAX_FORKS) {
                                        ctx.forkCount++;

-                                       Double[] locs = (Double[]) 
notVisitedList.toArray(new Double[notVisitedList.size()]);
-                                       Arrays.sort(locs, new Comparator() {
-                                               public int compare(Object arg0, 
Object arg1) {
-                                                       double d0 = ((Double) 
arg0).doubleValue();
-                                                       double d1 = ((Double) 
arg1).doubleValue();
-                                                       double dist0 = 
Location.distance(d0, target, true);
-                                                       double dist1 = 
Location.distance(d1, target, true);
-                                                       if(dist0 < dist1) 
return -1; // best at the beginning
-                                                       if(dist0 > dist1) 
return 1;
-                                                       return 0; // should not 
happen
-                                               }
-                                       });
+                                       double[] dists = new 
double[locsNotVisited.length];
+                                       for(int i=0;i<dists.length;i++) {
+                                               dists[i] = 
Location.distance(locsNotVisited[i], target, true);
+                                       }
+                                       Arrays.sort(dists);

-                                       double mustBeBetterThan = 
((Double)locs[Math.min(3,locs.length)]).doubleValue();
+                                       double mustBeBetterThan = 
dists[Math.min(3,dists.length)];
+                                       double maxDistance = 
Location.distance(mustBeBetterThan, target, true);

-                                       for(int 
i=0;i<notVisitedList.size();i++) {
-                                               double loc = 
((Double)(notVisitedList.get(i))).doubleValue();
-                                               double dist = 
Location.distance(loc, target);
-                                               if(dist > furthestDist) {
-                                                       furthestDist = dist;
-                                               }
-                                       }
-                                       if(innerHandleProbeRequest(src, id, 
lid, target, best, nearest, ctx.htl, counter, false, false, false, false, null, 
notVisitedList, mustBeBetterThan, true, linearCounter, "backtracking"))
+                                       if(innerHandleProbeRequest(src, id, 
lid, target, best, nearest, ctx.htl, counter, false, false, false, false, null, 
notVisitedList, maxDistance, true, linearCounter, "backtracking"))
                                                return true;
                                }
                        }

Modified: branches/freenet-jfk/src/freenet/node/NodeIPDetector.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/NodeIPDetector.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/NodeIPDetector.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -11,15 +11,18 @@
 import freenet.config.SubConfig;
 import freenet.io.comm.FreenetInetAddress;
 import freenet.io.comm.Peer;
+import freenet.io.comm.PeerParseException;
 import freenet.l10n.L10n;
 import freenet.node.useralerts.IPUndetectedUserAlert;
 import freenet.node.useralerts.SimpleUserAlert;
 import freenet.node.useralerts.UserAlert;
 import freenet.pluginmanager.DetectedIP;
 import freenet.pluginmanager.FredPluginIPDetector;
+import freenet.pluginmanager.FredPluginPortForward;
 import freenet.support.Logger;
 import freenet.support.api.BooleanCallback;
 import freenet.support.api.StringCallback;
+import freenet.support.transport.ip.HostnameSyntaxException;
 import freenet.support.transport.ip.IPAddressDetector;
 import freenet.support.transport.ip.IPUtil;

@@ -303,6 +306,17 @@
                                        redetectAddress();
                                        return;
                                }
+                               // Try making a dummy Peer, which will allow us 
to do a syntax check on the given hostname/IP address
+                               try {
+                                       String hostAndDummyPort = val + 
":8888";  // add a dummy port so our string can be parsed by Peer's constructor
+                                       new Peer(hostAndDummyPort, false, true);
+                               } catch (HostnameSyntaxException e) {
+                                       throw new 
InvalidConfigValueException(l10n("unknownHostErrorInIPOverride", "error", 
"hostname or IP address syntax error"));
+                               } catch (PeerParseException e) {
+                                       throw new 
InvalidConfigValueException(l10n("unknownHostErrorInIPOverride", "error", 
"parse error"));
+                               } catch (UnknownHostException e) {
+                                       throw new 
InvalidConfigValueException(l10n("unknownHostErrorInIPOverride", "error", 
e.getMessage()));
+                               }
                                FreenetInetAddress addr;
                                try {
                                        addr = new FreenetInetAddress(val, 
false);
@@ -410,9 +424,13 @@
        }

        public void registerIPDetectorPlugin(FredPluginIPDetector detector) {
-               ipDetectorManager.register(detector);
-       } // FIXME what about unloading?
+               ipDetectorManager.registerDetectorPlugin(detector);
+       }

+       public void unregisterIPDetectorPlugin(FredPluginIPDetector detector) {
+               ipDetectorManager.unregisterDetectorPlugin(detector);
+       }
+       
        public synchronized boolean isDetecting() {
                return !(hasDetectedPM && hasDetectedIAD);
        }
@@ -440,5 +458,12 @@
                                
node.clientCore.alerts.unregister(maybeSymmetricAlert);
                }
        }
-       
+
+       public void registerPortForwardPlugin(FredPluginPortForward forward) {
+               ipDetectorManager.registerPortForwardPlugin(forward);
+       }
+
+       public void unregisterPortForwardPlugin(FredPluginPortForward forward) {
+               ipDetectorManager.unregisterPortForwardPlugin(forward);
+       }
 }

Modified: branches/freenet-jfk/src/freenet/node/NodePinger.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/NodePinger.java       2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/NodePinger.java       2007-09-23 
09:58:34 UTC (rev 15282)
@@ -32,6 +32,7 @@
        final Node node;

        public void run() {
+           //freenet.support.OSThread.RealOSThread.logPID(this);
                try {
                        recalculateMean(node.peers.connectedPeers);
                } finally {

Modified: branches/freenet-jfk/src/freenet/node/NodeStarter.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/NodeStarter.java      2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/NodeStarter.java      2007-09-23 
09:58:34 UTC (rev 15282)
@@ -30,13 +30,12 @@
  *     A class to tie the wrapper and the node (needed for self-restarting 
support)
  *
  */
-public class NodeStarter
-    implements WrapperListener
+public class NodeStarter implements WrapperListener
 {
     private Node node;
        private static LoggingConfigHandler logConfigHandler;
        //TODO: cleanup
-       public static int RECOMMENDED_EXT_BUILD_NUMBER = 13;
+       public static int RECOMMENDED_EXT_BUILD_NUMBER = 17;

        /*
                (File.separatorChar == '\\') &&
@@ -331,7 +330,7 @@
         */
        public static Node createTestNode(int port, String testName, boolean 
doClient, 
                        boolean doSwapping, boolean disableProbabilisticHTLs, 
short maxHTL,
-                       int dropProb, int swapInterval, RandomSource random, 
Executor executor) throws NodeInitException {
+                       int dropProb, RandomSource random, Executor executor) 
throws NodeInitException {

                File baseDir = new File(testName);
                File portDir = new File(baseDir, Integer.toString(port));
@@ -356,7 +355,6 @@
                configFS.putSingle("node.nodeDir", portDir.toString());
                configFS.put("node.maxHTL", maxHTL);
                configFS.put("node.testingDropPacketsEvery", dropProb);
-               configFS.put("node.swapRequestSendInterval", swapInterval);

                PersistentConfig config = new PersistentConfig(configFS);


Modified: branches/freenet-jfk/src/freenet/node/NodeStats.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/NodeStats.java        2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/NodeStats.java        2007-09-23 
09:58:34 UTC (rev 15282)
@@ -508,22 +508,6 @@
                return null;
        }

-       private TimeDecayingRunningAverage getSuccessfulBytes(boolean isSSK, 
boolean isInsert, boolean isReceived) {
-               if(isSSK) {
-                       if(isInsert) {
-                               return isReceived ? 
successfulSskInsertBytesReceivedAverage : successfulSskInsertBytesSentAverage;
-                       } else {
-                               return isReceived ? 
successfulSskFetchBytesReceivedAverage : successfulSskFetchBytesSentAverage;
-                       }
-               } else {
-                       if(isInsert) {
-                               return isReceived ? 
successfulChkInsertBytesReceivedAverage : successfulChkInsertBytesSentAverage;
-                       } else {
-                               return isReceived ? 
successfulChkFetchBytesReceivedAverage : successfulChkFetchBytesSentAverage;
-                       }
-               }
-       }
-
        private void dumpByteCostAverages() {
                Logger.minor(this, "Byte cost averages: REMOTE:"+
                                " CHK insert 
"+remoteChkInsertBytesSentAverage.currentValue()+ '/' 
+remoteChkInsertBytesReceivedAverage.currentValue()+

Modified: branches/freenet-jfk/src/freenet/node/OpennetManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/OpennetManager.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/OpennetManager.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -21,6 +21,7 @@
 import freenet.support.LRUQueue;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
+import freenet.support.transport.ip.HostnameSyntaxException;

 /**
  * Central location for all things opennet.
@@ -37,6 +38,11 @@
        /** Our peers. PeerNode's are promoted when they successfully fetch a 
key. Normally we take
         * the bottom peer, but if that isn't eligible to be dropped, we 
iterate up the list. */
        private final LRUQueue peersLRU;
+       /** Old peers. Opennet peers which we dropped but would still like to 
talk to
+        * if we have no other option. */
+       private final LRUQueue oldPeers;
+       /** Maximum number of old peers */
+       static final int MAX_OLD_PEERS = 50;
        /** Time at which last dropped a peer */
        private long timeLastDropped;
        /** Number of successful CHK requests since last added a node */
@@ -86,7 +92,8 @@
                        }
                }
                peersLRU = new LRUQueue();
-               node.peers.tryReadPeers(new File(node.nodeDir, 
"openpeers-"+crypto.portNumber).toString(), crypto, this, true);
+               oldPeers = new LRUQueue();
+               node.peers.tryReadPeers(new File(node.nodeDir, 
"openpeers-"+crypto.portNumber).toString(), crypto, this, true, false);
                OpennetPeerNode[] nodes = node.peers.getOpennetPeers();
                Arrays.sort(nodes, new Comparator() {
                        public int compare(Object arg0, Object arg1) {
@@ -112,6 +119,8 @@
                        peersLRU.push(nodes[i]);
                dropExcessPeers();
                writeFile(nodeFile, backupNodeFile);
+               // Read old peers
+               node.peers.tryReadPeers(new File(node.nodeDir, 
"openpeers-old-"+crypto.portNumber).toString(), crypto, this, true, true);
        }

        private void writeFile(File orig, File backup) {
@@ -158,7 +167,11 @@
                                // Just keep the first one with the correct 
port number.
                                Peer p;
                                try {
-                                       p = new Peer(udp[i], false);
+                                       p = new Peer(udp[i], false, true);
+                               } catch (HostnameSyntaxException e) {
+                                       Logger.error(this, "Invalid hostname or 
IP Address syntax error while parsing opennet node reference: "+udp[i]);
+                                       System.err.println("Invalid hostname or 
IP Address syntax error while parsing opennet node reference: "+udp[i]);
+                                       continue;
                                } catch (PeerParseException e) {
                                        IOException e1 = new IOException();
                                        e1.initCause(e);
@@ -207,6 +220,17 @@
        /** When did we last offer our noderef to some other node? */
        private long timeLastOffered;

+       void forceAddPeer(PeerNode nodeToAddNow, boolean addAtLRU) {
+               synchronized(this) {
+                       if(addAtLRU)
+                               peersLRU.pushLeast(nodeToAddNow);
+                       else
+                               peersLRU.push(nodeToAddNow);
+                       oldPeers.remove(nodeToAddNow);
+               }
+               dropExcessPeers();
+       }
+       
        /**
         * Trim the peers list and possibly add a new node. Note that if we are 
not adding a new node,
         * we will only return true every MIN_TIME_BETWEEN_OFFERS, to prevent 
problems caused by many
@@ -224,17 +248,24 @@
                        if(peersLRU.size() < MAX_PEERS) {
                                if(nodeToAddNow != null) {
                                        if(logMINOR) Logger.minor(this, "Added 
opennet peer "+nodeToAddNow+" as opennet peers list not full");
-                                       peersLRU.push(nodeToAddNow);
-                                       // Always take OpennetManager lock 
before PeerManager
-                                       node.peers.addPeer(nodeToAddNow);
+                                       if(addAtLRU)
+                                               
peersLRU.pushLeast(nodeToAddNow);
+                                       else
+                                               peersLRU.push(nodeToAddNow);
+                                       oldPeers.remove(nodeToAddNow);
                                } else {
                                        if(logMINOR) Logger.minor(this, "Want 
peer because not enough opennet nodes");
                                }
                                timeLastOffered = System.currentTimeMillis();
-                               return true;
+                               ret = true;
                        }
                        noDisconnect = successCount < 
MIN_SUCCESS_BETWEEN_DROP_CONNS;
                }
+               if(ret) {
+                       if(nodeToAddNow != null)
+                               node.peers.addPeer(nodeToAddNow, true); // Add 
to peers outside the OM lock
+                       return true;
+               }
                Vector dropList = new Vector();
                synchronized(this) {
                        boolean hasDisconnected = false;
@@ -245,7 +276,7 @@
                        } else while(peersLRU.size() > MAX_PEERS - 
(nodeToAddNow == null ? 0 : 1)) {
                                PeerNode toDrop;
                                // can drop peers which are over the limit
-                               toDrop = peerToDrop(noDisconnect && 
nodeToAddNow != null && peersLRU.size() == MAX_PEERS);
+                               toDrop = peerToDrop(noDisconnect && 
nodeToAddNow != null && peersLRU.size() >= MAX_PEERS);
                                if(toDrop == null) {
                                        if(logMINOR)
                                                Logger.minor(this, "No more 
peers to drop, cannot accept peer"+(nodeToAddNow == null ? "" : 
nodeToAddNow.toString()));
@@ -262,6 +293,7 @@
                        if(ret) {
                                long now = System.currentTimeMillis();
                                if(nodeToAddNow != null) {
+                                       // Here we can't avoid nested locks. So 
always take the OpennetManager lock first.
                                        if(!node.peers.addPeer(nodeToAddNow)) {
                                                // Can't add it, already 
present (some sort of race condition)
                                                PeerNode readd = (PeerNode) 
dropList.remove(dropList.size()-1);
@@ -274,7 +306,10 @@
                                                        
peersLRU.pushLeast(nodeToAddNow);
                                                else
                                                        
peersLRU.push(nodeToAddNow);
-                                               if(logMINOR) Logger.minor(this, 
"Added opennet peer "+nodeToAddNow+" after clearing "+dropList.size()+" 
items");                                        
+                                               if(logMINOR) Logger.minor(this, 
"Added opennet peer "+nodeToAddNow+" after clearing "+dropList.size()+" items");
+                                               oldPeers.remove(nodeToAddNow);
+                                               // Always take OpennetManager 
lock before PeerManager
+                                               
node.peers.addPeer(nodeToAddNow, true);
                                        }
                                        if(!dropList.isEmpty())
                                                timeLastDropped = now;
@@ -295,7 +330,7 @@
                for(int i=0;i<dropList.size();i++) {
                        OpennetPeerNode pn = (OpennetPeerNode) dropList.get(i);
                        if(logMINOR) Logger.minor(this, "Dropping LRU opennet 
peer: "+pn);
-                       node.peers.disconnect(pn);
+                       node.peers.disconnect(pn, true, true);
                }
                return ret;
        }
@@ -306,7 +341,7 @@
                        toDrop = peerToDrop(false);
                        if(toDrop == null) return;
                        peersLRU.remove(toDrop);
-                       node.peers.disconnect(toDrop);
+                       node.peers.disconnect(toDrop, true, true);
                }
        }

@@ -357,11 +392,48 @@
                        }
                }
                if(!wantPeer(pn, false)) // Start at top as it just succeeded
-                       node.peers.disconnect(pn);
+                       node.peers.disconnect(pn, true, false);
        }

-       public synchronized void onRemove(OpennetPeerNode pn) {
-               peersLRU.remove(pn);
+       public void onRemove(OpennetPeerNode pn) {
+               synchronized (this) {
+                       peersLRU.remove(pn);
+                       oldPeers.push(pn);
+                       while (oldPeers.size() > MAX_OLD_PEERS)
+                               oldPeers.pop();
+               }
+               pn.disconnected();
        }

+       synchronized PeerNode[] getOldPeers() {
+               return (PeerNode[]) oldPeers.toArrayOrdered(new 
PeerNode[oldPeers.size()]);
+       }
+       
+       synchronized PeerNode[] getUnsortedOldPeers() {
+               return (PeerNode[]) oldPeers.toArray(new 
PeerNode[oldPeers.size()]);
+       }
+       
+       /**
+        * Add an old opennet node - a node which might try to reconnect, and 
which we should accept
+        * if we are desperate.
+        * @param pn The node to add to the old opennet nodes LRU.
+        */
+       synchronized void addOldOpennetNode(PeerNode pn) {
+               oldPeers.push(pn);
+       }
+
+       String getOldPeersFilename() {
+               return new File(node.nodeDir, 
"openpeers-old-"+crypto.portNumber).toString();
+       }
+
+       synchronized int countOldOpennetPeers() {
+               return oldPeers.size();
+       }
+
+       PeerNode randomOldOpennetNode() {
+               PeerNode[] nodes = getUnsortedOldPeers();
+               if(nodes.length == 0) return null;
+               return nodes[node.random.nextInt(nodes.length)];
+       }
+
 }

Modified: branches/freenet-jfk/src/freenet/node/PacketSender.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/PacketSender.java     2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/PacketSender.java     2007-09-23 
09:58:34 UTC (rev 15282)
@@ -28,8 +28,22 @@

        private static boolean logMINOR;

+       /** Maximum time we will queue a message for in millseconds */
        static final int MAX_COALESCING_DELAY = 200;

+       /** If opennet is enabled, and there are fewer than this many 
connections,
+        * we MAY attempt to contact old opennet peers (opennet peers we have 
+        * dropped from the routing table but kept around in case we can't 
connect). */
+       static final int MIN_CONNECTIONS_TRY_OLD_OPENNET_PEERS = 5;
+       
+       /** We send connect attempts to old-opennet-peers no more than once 
every
+        * this many milliseconds. */
+       static final int MIN_OLD_OPENNET_CONNECT_DELAY_NO_CONNS = 10*1000;
+       
+       /** We send connect attempts to old-opennet-peers no more than once 
every
+        * this many milliseconds. */
+       static final int MIN_OLD_OPENNET_CONNECT_DELAY = 60*1000;
+       
     final LinkedList resendPackets;
     /** ~= Ticker :) */
     private final TreeMap timedJobsByTime;
@@ -41,6 +55,7 @@
     long lastReceivedPacketFromAnyNode;
     /** For watchdog. 32-bit to avoid locking. */
     volatile int lastTimeInSeconds;
+    private long timeLastSentOldOpennetConnectAttempt;

     private Vector rpiTemp;
     private int[] rpiIntTemp;
@@ -70,6 +85,7 @@
     private class Watchdog implements Runnable {

        public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                // Do not lock anything, or we may be caught up with a 
lost-lock deadlock.
                while(true) {
                        try {
@@ -120,6 +136,7 @@
        if(now < transition) {
                queueTimedJob(new Runnable() {
                        public void run() {
+                                   
freenet.support.Logger.OSThread.logPID(this);
                                PeerNode[] nodes = node.peers.myPeers;
                                for(int i=0;i<nodes.length;i++) {
                                        PeerNode pn = nodes[i];
@@ -139,6 +156,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
         while(true) {
             lastReceivedPacketFromAnyNode = lastReportedNoPackets;
             try {
@@ -180,20 +198,20 @@
             PeerNode pn = nodes[i];
             lastReceivedPacketFromAnyNode =
                 Math.max(pn.lastReceivedPacketTime(), 
lastReceivedPacketFromAnyNode);
-                       pn.maybeOnConnect();
+            pn.maybeOnConnect();
             if(pn.isConnected()) {
-                // Is the node dead?
-                if(now - pn.lastReceivedPacketTime() > 
pn.maxTimeBetweenReceivedPackets()) {
-                       Logger.normal(this, "Disconnecting from "+pn+" - 
haven't received packets recently");
-                    pn.disconnected();
-                    continue;
-                } else if(pn.isRoutable() && pn.noLongerRoutable()) {
-                       // we don't disconnect but we mark it incompatible
-                       pn.invalidate();
-                       pn.setPeerNodeStatus(now);
-                       Logger.normal(this, "shouldDisconnectNow has returned 
true : marking the peer as incompatible");
-                       continue;
-                }
+               // Is the node dead?
+               if(now - pn.lastReceivedPacketTime() > 
pn.maxTimeBetweenReceivedPackets()) {
+                       Logger.normal(this, "Disconnecting from "+pn+" - 
haven't received packets recently");
+                       pn.disconnected();
+                       continue;
+               } else if(pn.isRoutable() && pn.noLongerRoutable()) {
+                       // we don't disconnect but we mark it incompatible
+                       pn.invalidate();
+                       pn.setPeerNodeStatus(now);
+                       Logger.normal(this, "shouldDisconnectNow has returned 
true : marking the peer as incompatible");
+                       continue;
+               }

                 boolean mustSend = false;

@@ -303,6 +321,26 @@
                        Logger.error(this, "tempNow is more than 5 seconds past 
oldTempNow ("+(tempNow - oldTempNow)+") in PacketSender working with 
"+pn.userToString());
                oldTempNow = tempNow;
        }
+        
+        // Consider sending connect requests to our opennet old-peers.
+        // No point if they are NATed, of course... but we don't know whether 
they are.
+        OpennetManager om = node.getOpennet();
+        if(om != null) {
+               int connCount = node.peers.quickCountConnectedPeers();
+               int minDelay = connCount == 0 ? 
MIN_OLD_OPENNET_CONNECT_DELAY_NO_CONNS : MIN_OLD_OPENNET_CONNECT_DELAY;
+               if(now - timeLastSentOldOpennetConnectAttempt > minDelay &&
+                               connCount <= 
MIN_CONNECTIONS_TRY_OLD_OPENNET_PEERS &&
+                               om.countOldOpennetPeers() > 0 &&
+                               now - node.startupTime > 
OpennetManager.DROP_STARTUP_DELAY) {
+               PeerNode pn = om.randomOldOpennetNode();
+               if(pn != null) {
+                       pn.getOutgoingMangler().sendHandshake(pn);
+                       timeLastSentOldOpennetConnectAttempt = now;
+                       if(pn.noContactDetails() && node.getPeerNodes().length 
> 0 && connCount > 0 && node.random.nextBoolean())
+                               pn.startARKFetcher();
+               }
+               }
+        }

         if(now - lastClearedOldSwapChains > 10000) {
             node.lm.clearOldSwapChains();

Modified: branches/freenet-jfk/src/freenet/node/PeerManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/PeerManager.java      2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/PeerManager.java      2007-09-23 
09:58:34 UTC (rev 15282)
@@ -22,6 +22,8 @@
 import java.util.Vector;
 import java.util.ArrayList;

+import freenet.io.comm.AsyncMessageCallback;
+import freenet.io.comm.DMT;
 import freenet.io.comm.FreenetInetAddress;
 import freenet.io.comm.Message;
 import freenet.io.comm.NotConnectedException;
@@ -30,6 +32,7 @@
 import freenet.io.comm.ReferenceSignatureVerificationException;
 import freenet.node.useralerts.PeerManagerUserAlert;
 import freenet.support.Logger;
+import freenet.support.ShortBuffer;
 import freenet.support.SimpleFieldSet;

 /**
@@ -71,6 +74,8 @@
        private static final long peerNodeStatusLogInterval = 5000;
        /** PeerNode statuses, by status */
        private final HashMap peerNodeStatuses;
+       /** DarknetPeerNode statuses, by status */
+       private final HashMap peerNodeStatusesDarknet;
        /** PeerNode routing backoff reasons, by reason */
        private final HashMap peerNodeRoutingBackoffReasons;
        /** Next time to update routableConnectionStats */
@@ -89,6 +94,7 @@
        public static final int PEER_NODE_STATUS_LISTEN_ONLY = 10;
        public static final int PEER_NODE_STATUS_CLOCK_PROBLEM = 11;
        public static final int PEER_NODE_STATUS_CONN_ERROR = 12;
+       public static final int PEER_NODE_STATUS_DISCONNECTING = 13;

     /**
      * Create a PeerManager by reading a list of peers from
@@ -100,6 +106,7 @@
         Logger.normal(this, "Creating PeerManager");
         logMINOR = Logger.shouldLog(Logger.MINOR, this);
                peerNodeStatuses = new HashMap();
+               peerNodeStatusesDarknet = new HashMap();
                peerNodeRoutingBackoffReasons = new HashMap();
         System.out.println("Creating PeerManager");
         myPeers = new PeerNode[0];
@@ -107,7 +114,17 @@
         this.node = node;
     }

-    void tryReadPeers(String filename, NodeCrypto crypto, OpennetManager 
opennet, boolean isOpennet) {
+    /**
+     * Attempt to read a file full of noderefs. Try the file as named first, 
then the .bak if it is empty or
+     * otherwise doesn't work.
+     * @param filename The filename to read from. If this doesn't work, we try 
the .bak file.
+     * @param crypto The cryptographic identity which these nodes are 
connected to.
+     * @param opennet The opennet manager for the nodes. Only needed (for 
constructing the nodes) if isOpennet.
+     * @param isOpennet Whether the file contains opennet peers.
+     * @param oldOpennetPeers If true, don't add the nodes to the routing 
table, pass them to the opennet
+     * manager as "old peers" i.e. inactive nodes which may try to reconnect.
+     */
+    void tryReadPeers(String filename, NodeCrypto crypto, OpennetManager 
opennet, boolean isOpennet, boolean oldOpennetPeers) {
        synchronized(writePeersSync) {
                if(isOpennet) {
                        openFilename = filename;
@@ -120,7 +137,7 @@
         File backupFile = new File(filename+".bak");
         // Try to read the node list from disk
        if(peersFile.exists()) {
-               if(readPeers(peersFile, mangler, crypto, opennet, isOpennet)) {
+               if(readPeers(peersFile, mangler, crypto, opennet, isOpennet, 
oldOpennetPeers)) {
                    String msg;
                    if(isOpennet) {
                            msg = "Read "+getOpennetPeers().length+" peers from 
"+peersFile;
@@ -134,7 +151,7 @@
                }
        // Try the backup
        if(backupFile.exists()) {
-               if(readPeers(backupFile, mangler, crypto, opennet, isOpennet)) {
+               if(readPeers(backupFile, mangler, crypto, opennet, isOpennet, 
oldOpennetPeers)) {
                    String msg;
                    if(isOpennet) {
                            msg = "Read "+getOpennetPeers().length+" peers from 
"+backupFile;
@@ -150,7 +167,7 @@
        }               
        }

-       private boolean readPeers(File peersFile, OutgoingPacketMangler 
mangler, NodeCrypto crypto, OpennetManager opennet, boolean isOpennet) {
+       private boolean readPeers(File peersFile, OutgoingPacketMangler 
mangler, NodeCrypto crypto, OpennetManager opennet, boolean isOpennet, boolean 
oldOpennetPeers) {
        boolean gotSome = false;
        FileInputStream fis;
                try {
@@ -184,7 +201,10 @@
                        Logger.error(this, "Could not parse peer: "+e2+ '\n' 
+fs.toString(),e2);
                     continue;
                                }
-                addPeer(pn);
+                if(oldOpennetPeers)
+                       opennet.addOldOpennetNode(pn);
+                else
+                       addPeer(pn, true);
                 gotSome = true;
             }
         } catch (EOFException e) {
@@ -201,24 +221,54 @@
        }

        public boolean addPeer(PeerNode pn) {
-       synchronized(this) {
-        for(int i=0;i<myPeers.length;i++) {
-            if(myPeers[i].equals(pn)) {
-               return false;
-            }
-        }
-        PeerNode[] newMyPeers = new PeerNode[myPeers.length+1];
-        System.arraycopy(myPeers, 0, newMyPeers, 0, myPeers.length);
-        newMyPeers[myPeers.length] = pn;
-        myPeers = newMyPeers;
-        Logger.normal(this, "Added "+pn);
-       }
-       this.addPeerNodeStatus(pn.getPeerNodeStatus(), pn);
-       pn.setPeerNodeStatus(System.currentTimeMillis());
-        updatePMUserAlert();
-        return true;
-    }
+               return addPeer(pn, false);
+       }
+       
+       /**
+        * Add a peer.
+        * @param pn The node to add to the routing table.
+        * @param ignoreOpennet If true, don't check for opennet peers. If 
false, check for opennet peers and if so,
+        * if opennet is enabled auto-add them to the opennet LRU, otherwise 
fail.
+        * @return True if the node was successfully added. False if it was 
already present, or if we tried to add
+        * an opennet peer when opennet was disabled.
+        */
+       boolean addPeer(PeerNode pn, boolean ignoreOpennet) {
+               synchronized (this) {
+                       for (int i = 0; i < myPeers.length; i++) {
+                               if (myPeers[i].equals(pn)) {
+                                       return false;
+                               }
+                       }
+                       PeerNode[] newMyPeers = new PeerNode[myPeers.length + 
1];
+                       System.arraycopy(myPeers, 0, newMyPeers, 0, 
myPeers.length);
+                       newMyPeers[myPeers.length] = pn;
+                       myPeers = newMyPeers;
+                       Logger.normal(this, "Added " + pn);
+               }
+               this.addPeerNodeStatus(pn.getPeerNodeStatus(), pn);
+               pn.setPeerNodeStatus(System.currentTimeMillis());
+               updatePMUserAlert();
+               if((!ignoreOpennet) && pn instanceof OpennetPeerNode) {
+                       OpennetManager opennet = node.getOpennet();
+                       if(opennet != null) {
+                               opennet.forceAddPeer(pn, true);
+                       } else {
+                               Logger.error(this, "Adding opennet peer when no 
opennet enabled!!!: "+pn+" - removing...");
+                               removePeer(pn);
+                               return false;
+                       }
+               }
+               
+               return true;
+       }

+       synchronized boolean havePeer(PeerNode pn) {
+               for(int i=0;i<myPeers.length;i++) {
+                       if(myPeers[i] == pn) return true;
+               }
+               return false;
+       }
+       
     private boolean removePeer(PeerNode pn) {
        synchronized(this) {
        boolean isInPeers = false;
@@ -364,9 +414,52 @@
     /**
      * Disconnect from a specified node
      */
-    public void disconnect(PeerNode pn){
-       if(removePeer(pn))
-               writePeers();
+    public void disconnect(final PeerNode pn, boolean sendDisconnectMessage, 
final boolean waitForAck) {
+       synchronized(this) {
+               if(!havePeer(pn)) return;
+       }
+       pn.notifyDisconnecting();
+       if(sendDisconnectMessage) {
+               Message msg = DMT.createFNPDisconnect(true, false, -1, new 
ShortBuffer(new byte[0]));
+                       try {
+                               pn.sendAsync(msg, new AsyncMessageCallback() {
+                                       boolean done = false;
+                                       public void acknowledged() {
+                                               done();
+                                       }
+                                       public void disconnected() {
+                                               done();
+                                       }
+                                       public void fatalError() {
+                                               done();
+                                       }
+                                       public void sent() {
+                                               if(!waitForAck) done();
+                                       }
+                                       void done() {
+                                               synchronized(this) {
+                                                       if(done) return;
+                                                       done = true;
+                                               }
+                                       if(removePeer(pn))
+                                               writePeers();
+                                       }
+                               }, 0, null);
+                       } catch (NotConnectedException e) {
+                       if(removePeer(pn))
+                               writePeers();
+                       return;
+                       }
+                       node.getTicker().queueTimedJob(new Runnable() {
+                               public void run() {
+                               if(removePeer(pn))
+                                       writePeers();
+                               }
+                       }, 60*1000);
+       } else {
+               if(removePeer(pn))
+                       writePeers();
+       }
     }

     class LocationUIDPair implements Comparable {
@@ -570,8 +663,8 @@
                PeerNode nbo = _closerPeer(pn, routedTo, notIgnored, loc, 
ignoreSelf, true, minVersion, null, maxDistance);
                if(nbo != null) {
                        
node.nodeStats.routingMissDistance.report(Location.distance(best, 
nbo.getLocation()));
-                       int numberOfConnected = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CONNECTED);
-                       int numberOfRoutingBackedOff = 
getPeerNodeStatusSize(PEER_NODE_STATUS_ROUTING_BACKED_OFF);
+                       int numberOfConnected = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CONNECTED, false);
+                       int numberOfRoutingBackedOff = 
getPeerNodeStatusSize(PEER_NODE_STATUS_ROUTING_BACKED_OFF, false);
                        if (numberOfRoutingBackedOff + numberOfConnected > 0 ) {
                                node.nodeStats.backedOffPercent.report((double) 
numberOfRoutingBackedOff / (double) (numberOfRoutingBackedOff + 
numberOfConnected));
                        }
@@ -593,6 +686,7 @@
      * than we are, and is not included in the provided set.
         * @param addUnpickedLocsTo Add all locations we didn't choose which we 
could have routed to to 
         * this array. Remove the location of the peer we pick from it.
+        * @param maxDistance If a node is further away from the target than 
this distance, ignore it.
      */
     private PeerNode _closerPeer(PeerNode pn, Set routedTo, Set notIgnored, 
double target, boolean ignoreSelf, boolean ignoreBackedOff, int minVersion, 
Vector addUnpickedLocsTo, double maxDistance) {
         PeerNode[] peers;  
@@ -734,8 +828,12 @@
        synchronized(writePeersSync) {
                if(darkFilename != null)
                        writePeersInner(darkFilename, getDarknetPeers());
-               if(openFilename != null)
-                       writePeersInner(openFilename, getOpennetPeers());
+               OpennetManager om = node.getOpennet();
+               if(om != null) {
+                       if(openFilename != null)
+                               writePeersInner(openFilename, 
getOpennetPeers());
+                       writePeersInner(om.getOldPeersFilename(), 
om.getOldPeers());
+               }
        }
     }

@@ -814,17 +912,22 @@
         */
        public void updatePMUserAlert() {
                if(ua == null) return;
-               int conns, peers;
+               int peers, darknetPeers;
                synchronized(this) {
-                       conns = this.connectedPeers.length;
                        peers = this.myPeers.length;
+                       darknetPeers = this.getDarknetPeers().length;
                }
                synchronized(ua) {
-                       ua.conns = conns;
+                       ua.darknetConns = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CONNECTED, true) +
+                               
getPeerNodeStatusSize(PEER_NODE_STATUS_ROUTING_BACKED_OFF, true);
+                       ua.conns = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CONNECTED, false) +
+                               
getPeerNodeStatusSize(PEER_NODE_STATUS_ROUTING_BACKED_OFF, false);
+                       ua.darknetPeers = darknetPeers;
+                       ua.disconnDarknetPeers = darknetPeers - ua.darknetConns;
                        ua.peers = peers;
-                       ua.neverConn = 
getPeerNodeStatusSize(PEER_NODE_STATUS_NEVER_CONNECTED);
-                       ua.clockProblem = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CLOCK_PROBLEM);
-                       ua.connError = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CONN_ERROR);
+                       ua.neverConn = 
getPeerNodeStatusSize(PEER_NODE_STATUS_NEVER_CONNECTED, true);
+                       ua.clockProblem = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CLOCK_PROBLEM, false);
+                       ua.connError = 
getPeerNodeStatusSize(PEER_NODE_STATUS_CONN_ERROR, true);
                }
                if(anyConnectedPeers())
                        node.onConnectedPeer();
@@ -926,6 +1029,7 @@
                int numberOfBursting = 0;
                int numberOfClockProblem = 0;
                int numberOfConnError = 0;
+               int numberOfDisconnecting = 0;

                PeerNodeStatus[] pns = getPeerNodeStatuses();

@@ -967,12 +1071,15 @@
                        case PEER_NODE_STATUS_CONN_ERROR:
                                numberOfConnError++;
                                break;
+                       case PEER_NODE_STATUS_DISCONNECTING:
+                               numberOfDisconnecting++;
+                               break;
                        default:
                                Logger.error(this, "Unknown peer status value : 
"+pns[i].getStatusValue());
                                break;
                        }
                }
-               Logger.normal(this, "Connected: "+numberOfConnected+"  Routing 
Backed Off: "+numberOfRoutingBackedOff+"  Too New: "+numberOfTooNew+"  Too Old: 
"+numberOfTooOld+"  Disconnected: "+numberOfDisconnected+"  Never Connected: 
"+numberOfNeverConnected+"  Disabled: "+numberOfDisabled+"  Bursting: 
"+numberOfBursting+"  Listening: "+numberOfListening+"  Listen Only: 
"+numberOfListenOnly+"  Clock Problem: "+numberOfClockProblem+"  Connection 
Problem: "+numberOfConnError);
+               Logger.normal(this, "Connected: "+numberOfConnected+"  Routing 
Backed Off: "+numberOfRoutingBackedOff+"  Too New: "+numberOfTooNew+"  Too Old: 
"+numberOfTooOld+"  Disconnected: "+numberOfDisconnected+"  Never Connected: 
"+numberOfNeverConnected+"  Disabled: "+numberOfDisabled+"  Bursting: 
"+numberOfBursting+"  Listening: "+numberOfListening+"  Listen Only: 
"+numberOfListenOnly+"  Clock Problem: "+numberOfClockProblem+"  Connection 
Problem: "+numberOfConnError+"  Disconnecting: "+numberOfDisconnecting);
                nextPeerNodeStatusLogTime = now + peerNodeStatusLogInterval;
                }
        }
@@ -982,33 +1089,41 @@
         */
        public void addPeerNodeStatus(int pnStatus, PeerNode peerNode) {
                Integer peerNodeStatus = new Integer(pnStatus);
+               addPeerNodeStatuses(pnStatus, peerNode, peerNodeStatus, 
peerNodeStatuses);
+               if(!peerNode.isOpennet())
+                       addPeerNodeStatuses(pnStatus, peerNode, peerNodeStatus, 
peerNodeStatusesDarknet);
+       }
+
+       private void addPeerNodeStatuses(int pnStatus, PeerNode peerNode, 
Integer peerNodeStatus, HashMap statuses) {
                HashSet statusSet = null;
-               synchronized(peerNodeStatuses) {
-                       if(peerNodeStatuses.containsKey(peerNodeStatus)) {
-                               statusSet = (HashSet) 
peerNodeStatuses.get(peerNodeStatus);
+               synchronized(statuses) {
+                       if(statuses.containsKey(peerNodeStatus)) {
+                               statusSet = (HashSet) 
statuses.get(peerNodeStatus);
                                if(statusSet.contains(peerNode)) {
                                        Logger.error(this, 
"addPeerNodeStatus(): identity '"+peerNode.getIdentityString()+"' already in 
peerNodeStatuses as "+peerNode.getPeer()+" with status 
'"+PeerNode.getPeerNodeStatusString(peerNodeStatus.intValue())+"'");
                                        return;
                                }
-                               peerNodeStatuses.remove(peerNodeStatus);
+                               statuses.remove(peerNodeStatus);
                        } else {
                                statusSet = new HashSet();
                        }
                        if(logMINOR) Logger.minor(this, "addPeerNodeStatus(): 
adding PeerNode for '"+peerNode.getIdentityString()+"' with status 
'"+PeerNode.getPeerNodeStatusString(peerNodeStatus.intValue())+"'");
                        statusSet.add(peerNode);
-                       peerNodeStatuses.put(peerNodeStatus, statusSet);
+                       statuses.put(peerNodeStatus, statusSet);
                }
        }

        /**
         * How many PeerNodes have a particular status?
+        * @param darknet If true, only count darknet nodes, if false, count 
all nodes.
         */
-       public int getPeerNodeStatusSize(int pnStatus) {
+       public int getPeerNodeStatusSize(int pnStatus, boolean darknet) {
                Integer peerNodeStatus = new Integer(pnStatus);
                HashSet statusSet = null;
-               synchronized(peerNodeStatuses) {
-                       if(peerNodeStatuses.containsKey(peerNodeStatus)) {
-                               statusSet = (HashSet) 
peerNodeStatuses.get(peerNodeStatus);
+               HashMap statuses = darknet ? peerNodeStatusesDarknet : 
this.peerNodeStatuses;
+               synchronized(statuses) {
+                       if(statuses.containsKey(peerNodeStatus)) {
+                               statusSet = (HashSet) 
statuses.get(peerNodeStatus);
                        } else {
                                statusSet = new HashSet();
                        }
@@ -1021,15 +1136,21 @@
         */
        public void removePeerNodeStatus(int pnStatus, PeerNode peerNode) {
                Integer peerNodeStatus = new Integer(pnStatus);
+               removePeerNodeStatus(pnStatus, peerNodeStatus, peerNode, 
peerNodeStatuses);
+               if(!peerNode.isOpennet())
+                       removePeerNodeStatus(pnStatus, peerNodeStatus, 
peerNode, peerNodeStatusesDarknet);
+       }
+
+       private void removePeerNodeStatus(int pnStatus, Integer peerNodeStatus, 
PeerNode peerNode, HashMap statuses) {
                HashSet statusSet = null;
-               synchronized(peerNodeStatuses) {
-                       if(peerNodeStatuses.containsKey(peerNodeStatus)) {
-                               statusSet = (HashSet) 
peerNodeStatuses.get(peerNodeStatus);
+               synchronized(statuses) {
+                       if(statuses.containsKey(peerNodeStatus)) {
+                               statusSet = (HashSet) 
statuses.get(peerNodeStatus);
                                if(!statusSet.contains(peerNode)) {
                                        Logger.error(this, 
"removePeerNodeStatus(): identity '"+peerNode.getIdentityString()+"' not in 
peerNodeStatuses with status 
'"+PeerNode.getPeerNodeStatusString(peerNodeStatus.intValue())+"'", new 
Exception("debug"));
                                        return;
                                }
-                               peerNodeStatuses.remove(peerNodeStatus);
+                               statuses.remove(peerNodeStatus);
                        } else {
                                statusSet = new HashSet();
                        }
@@ -1037,7 +1158,7 @@
                        if(statusSet.contains(peerNode)) {
                                statusSet.remove(peerNode);
                        }
-                       peerNodeStatuses.put(peerNodeStatus, statusSet);
+                       statuses.put(peerNodeStatus, statusSet);
                }
        }

@@ -1235,4 +1356,10 @@

                return null;
        }
+
+       public int quickCountConnectedPeers() {
+               PeerNode[] conns = connectedPeers;
+               if(conns == null) return 0;
+               return connectedPeers.length;
+       }
 }

Modified: branches/freenet-jfk/src/freenet/node/PeerNode.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/PeerNode.java 2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/node/PeerNode.java 2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -60,6 +60,7 @@
 import freenet.support.math.RunningAverage;
 import freenet.support.math.SimpleRunningAverage;
 import freenet.support.math.TimeDecayingRunningAverage;
+import freenet.support.transport.ip.HostnameSyntaxException;

 /**
  * @author amphibian
@@ -299,6 +300,9 @@
         * long time, but without preventing it from being GC'ed. */
        final WeakReference myRef;

+       /** The node is being disconnected, but it may take a while. */
+       private boolean disconnecting;
+       
     private static boolean logMINOR;

     /**
@@ -364,15 +368,19 @@
         try {
                String physical[]=fs.getAll("physical.udp");
                if(physical==null) {
-                       // Be tolerant of nonexistent domains.
-                       Peer p = new Peer(fs.get("physical.udp"), true);
-                       if(p != null)
-                               nominalPeer.addElement(p);
+                       // Leave it empty
                } else {
                        for(int i=0;i<physical.length;i++) {
-                                       Peer p = new Peer(physical[i], true);
-                                   if(!nominalPeer.contains(p)) 
-                                       nominalPeer.addElement(p);
+                               Peer p;
+                               try {
+                                               p = new Peer(physical[i], true, 
true);
+                                       } catch (HostnameSyntaxException e) {
+                                               Logger.error(this, "Invalid 
hostname or IP Address syntax error while parsing peer reference: 
"+physical[i]);
+                                               System.err.println("Invalid 
hostname or IP Address syntax error while parsing peer reference: 
"+physical[i]);
+                                               continue;
+                                       }
+                                       if(!nominalPeer.contains(p)) 
+                                               nominalPeer.addElement(p);
                        }
                }
         } catch (Exception e1) {
@@ -462,9 +470,9 @@
                                "\nFor:       "+getPeer());

         try {
-            incomingSetupCipher = new Rijndael(256,256,false);
+            incomingSetupCipher = new Rijndael(256,256);
             incomingSetupCipher.initialize(incomingSetupKey);
-            outgoingSetupCipher = new Rijndael(256,256,false);
+            outgoingSetupCipher = new Rijndael(256,256);
             outgoingSetupCipher.initialize(outgoingSetupKey);
         } catch (UnsupportedCipherException e1) {
             Logger.error(this, "Caught: "+e1);
@@ -1372,13 +1380,27 @@
     /**
      * Called when we have completed a handshake, and have a new session key.
      * Creates a new tracker and demotes the old one. Deletes the old one if
-     * the bootID isn't recognized.
-     * @param thisBootID The boot ID of the peer we have just connected to
-     * @param data Byte array from which to read the new noderef
-     * @param offset Offset to start reading at
-     * @param length Number of bytes to read
-     * @param encKey
-     * @param replyTo
+     * the bootID isn't recognized, since if the node has restarted we cannot
+     * recover old messages. In more detail:
+     * <ul>
+     * <li>Process the new noderef (check if it's valid, pick up any new 
information etc).</li>
+     * <li>Handle version conflicts (if the node is too old, or we are too 
old, we mark it as 
+     * non-routable, but some messages will still be exchanged e.g. Update 
Over Mandatory stuff).</li>
+     * <li>Deal with key trackers (if we just got message 4, the new key 
tracker becomes current; 
+     * if we just got message 3, it's possible that our message 4 will be lost 
in transit, so we 
+     * make the new tracker unverified. It will be promoted to current if we 
get a packet on it..
+     * if the node has restarted, we dump the old key trackers, otherwise 
current becomes previous).</li>
+     * <li>Complete the connection process: update the node's status, send 
initial messages, update
+     * the last-received-packet timestamp, etc.</li>
+     * @param thisBootID The boot ID of the peer we have just connected to.
+     * This is simply a random number regenerated on every startup of the node.
+     * We use it to determine whether the node has restarted since we last saw 
+     * it.
+     * @param data Byte array from which to read the new noderef.
+     * @param offset Offset to start reading at.
+     * @param length Number of bytes to read.
+     * @param encKey The new session key.
+     * @param replyTo The IP the handshake came in on.
      * @return True unless we rejected the handshake, or it failed to parse.
      */
     public boolean completedHandshake(long thisBootID, byte[] data, int 
offset, int length, BlockCipher encCipher, byte[] encKey, Peer replyTo, boolean 
unverified) {
@@ -1739,11 +1761,23 @@
         try {
                String physical[]=fs.getAll("physical.udp");
                if(physical==null) {
-                       Peer p = new Peer(fs.get("physical.udp"), true);
-                       nominalPeer.addElement(p);
+                       try {
+                               Peer p = new Peer(fs.get("physical.udp"), true, 
true);
+                               nominalPeer.addElement(p);
+                               } catch (HostnameSyntaxException e) {
+                                       Logger.error(this, "Invalid hostname or 
IP Address syntax error while parsing peer reference: "+fs.get("physical.udp"));
+                                       System.err.println("Invalid hostname or 
IP Address syntax error while parsing peer reference: "+fs.get("physical.udp"));
+                               }
                } else {
                        for(int i=0;i<physical.length;i++) {
-                                       Peer p = new Peer(physical[i], true);
+                               Peer p;
+                               try {
+                                               p = new Peer(physical[i], true, 
true);
+                                       } catch (HostnameSyntaxException e) {
+                                               Logger.error(this, "Invalid 
hostname or IP Address syntax error while parsing peer reference: 
"+physical[i]);
+                                               System.err.println("Invalid 
hostname or IP Address syntax error while parsing peer reference: 
"+physical[i]);
+                                               continue;
+                                       }
                                    if(!nominalPeer.contains(p)) {
                                        if(oldNominalPeer.contains(p)) {
                                                // Do nothing
@@ -2324,6 +2358,8 @@
                        return "CLOCK PROBLEM";
                if(status == PeerManager.PEER_NODE_STATUS_CONN_ERROR)
                        return "CONNECTION ERROR";
+               if(status == PeerManager.PEER_NODE_STATUS_DISCONNECTING)
+                       return "DISCONNECTING";
                return "UNKNOWN STATUS";
        }

@@ -2355,11 +2391,15 @@
                        return "peer_listen_only";
                if(status == PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM)
                        return "peer_clock_problem";
+               if(status == PeerManager.PEER_NODE_STATUS_DISCONNECTING)
+                       return "peer_disconnecting";
                return "peer_unknown_status";
        }

        protected synchronized int getPeerNodeStatus(long now, long 
routingBackedOffUntil) {
                checkConnectionsAndTrackers();
+               if(disconnecting)
+                       return PeerManager.PEER_NODE_STATUS_DISCONNECTING;
                if(isRoutable()) {  // Function use also updates 
timeLastConnected and timeLastRoutable
                        peerNodeStatus = PeerManager.PEER_NODE_STATUS_CONNECTED;
                        if(now < routingBackedOffUntil) {
@@ -2688,4 +2728,13 @@

        /** Called when the peer is removed from the PeerManager */
        public abstract void onRemove();
+
+       /** Called when a delayed disconnect is occurring. Tell the node that 
it is being disconnected, but
+        * that the process may take a while. */
+       public void notifyDisconnecting() {
+               synchronized(this) {
+                       disconnecting = true;
+               }
+               setPeerNodeStatus(System.currentTimeMillis());
+       }
 }

Modified: branches/freenet-jfk/src/freenet/node/PeerNodeStatus.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/PeerNodeStatus.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/PeerNodeStatus.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -3,7 +3,6 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.node;

-import java.util.Hashtable;
 import java.util.Map;

 import freenet.clients.http.DarknetConnectionsToadlet;
@@ -46,6 +45,8 @@

        private final boolean isFetchingARK;

+       private final boolean isOpennet;
+
        private final double averagePingTime;

        private final boolean publicInvalidVersion;
@@ -100,6 +101,7 @@
                this.connected = peerNode.isConnected();
                this.routable = peerNode.isRoutable();
                this.isFetchingARK = peerNode.isFetchingARK();
+               this.isOpennet = peerNode.isOpennet();
                this.averagePingTime = peerNode.averagePingTime();
                this.publicInvalidVersion = peerNode.publicInvalidVersion();
                this.publicReverseInvalidVersion = 
peerNode.publicReverseInvalidVersion();
@@ -291,6 +293,13 @@
        }

        /**
+        * @return the isOpennet
+        */
+       public boolean isOpennet() {
+               return isOpennet;
+       }
+
+       /**
         * @return the simpleVersion
         */
        public String getSimpleVersion() {

Modified: branches/freenet-jfk/src/freenet/node/Persister.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/Persister.java        2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/Persister.java        2007-09-23 
09:58:34 UTC (rev 15282)
@@ -41,6 +41,7 @@
        }

        public void run() {
+               freenet.support.Logger.OSThread.logPID(this);
                try {
                        persistThrottle();
                } catch (OutOfMemoryError e) {

Modified: branches/freenet-jfk/src/freenet/node/RequestHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/RequestHandler.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/RequestHandler.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -73,6 +73,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
        boolean thrown = false;
         try {
                realRun();
@@ -147,9 +148,7 @@
                node.addTransferringRequestHandler(uid);
                if(bt.send(node.executor)) {
                        status = RequestSender.SUCCESS; // for byte logging
-                       if(source.isOpennet()) {
-                               finishOpennetNoRelay();
-                       }
+                               finishOpennetNoRelayChecked();
                }
             }
             return;
@@ -188,7 +187,7 @@
                        finalTransferFailed = true;
                } else {
                                // Successful CHK transfer, maybe path fold
-                               finishOpennet();
+                               finishOpennetChecked();
                }
                                status = rs.getStatus();
                    return;
@@ -262,23 +261,48 @@
         }
        }

-       private void finishOpennet() {
-               if(!(node.passOpennetRefsThroughDarknet() || 
source.isOpennet())) return;
+       private void finishOpennetChecked() {
+               if(!(node.passOpennetRefsThroughDarknet() || 
source.isOpennet())) {
+                       Message msg = DMT.createFNPOpennetCompletedAck(uid);
+                       try {
+                               source.sendAsync(msg, null, 0, this);
+                       } catch (NotConnectedException e) {
+                               // Oh well...
+                       }
+                       return;
+               }
+               finishOpennetInner();
+       }
+       
+       private void finishOpennetInner() {
                byte[] noderef = rs.waitForOpennetNoderef();
                if(noderef == null) {
-                       finishOpennetNoRelay();
+                       finishOpennetNoRelayInner();
                        return;
                }

                if(node.random.nextInt(OpennetManager.RESET_PATH_FOLDING_PROB) 
== 0) {
-                       finishOpennetNoRelay();
+                       finishOpennetNoRelayInner();
                        return;
                }

        finishOpennetRelay(noderef);
     }

-    private void finishOpennetNoRelay() {
+       private void finishOpennetNoRelayChecked() {
+               if(!(node.passOpennetRefsThroughDarknet() || 
source.isOpennet())) {
+                       Message msg = DMT.createFNPOpennetCompletedAck(uid);
+                       try {
+                               source.sendAsync(msg, null, 0, this);
+                       } catch (NotConnectedException e) {
+                               // Oh well...
+                       }
+                       return;
+               }
+               finishOpennetNoRelayInner();
+       }
+       
+    private void finishOpennetNoRelayInner() {
        if(logMINOR)
                Logger.minor(this, "Finishing opennet: sending own reference");
                OpennetManager om = node.getOpennet();
@@ -345,6 +369,13 @@
                                        // Oh well...
                                }
                        }
+               } else {
+                       Message msg = DMT.createFNPOpennetCompletedAck(uid);
+                       try {
+                               source.sendAsync(msg, null, 0, this);
+                       } catch (NotConnectedException e) {
+                               // Oh well...
+                       }
                }
     }


Modified: branches/freenet-jfk/src/freenet/node/RequestSender.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/RequestSender.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/RequestSender.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -117,6 +117,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
         if((key instanceof NodeSSK) && (pubKey == null)) {
                pubKey = ((NodeSSK)key).getPubKey();
         }
@@ -691,7 +692,8 @@

                OpennetManager om = node.getOpennet();
        try {
-                       if(om != null /* prevent race */ && 
!node.addNewOpennetNode(ref)) {
+                       if(om == null || 
+                                       (om != null /* prevent race */ && 
!node.addNewOpennetNode(ref))) {
                                // If we don't want it let somebody else have it
                                synchronized(this) {
                                        opennetNoderef = noderef;
@@ -751,7 +753,9 @@
                                        wait(OPENNET_TIMEOUT);
                                } catch (InterruptedException e) {
                                        // Ignore
+                                       continue;
                                }
+                               return null;
                }
        }
     }

Modified: branches/freenet-jfk/src/freenet/node/RequestStarter.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/RequestStarter.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/RequestStarter.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -142,6 +142,7 @@
        }

        public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
                while(true) {
                        try {
                                realRun();
@@ -162,6 +163,7 @@
                }

                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        if(!req.send(core, sched))
                                Logger.normal(this, "run() not able to send a 
request");
                        if(Logger.shouldLog(Logger.MINOR, this)) 

Modified: branches/freenet-jfk/src/freenet/node/SSKInsertHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/SSKInsertHandler.java 2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/SSKInsertHandler.java 2007-09-23 
09:58:34 UTC (rev 15282)
@@ -75,6 +75,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
         try {
                realRun();
                } catch (OutOfMemoryError e) {

Modified: branches/freenet-jfk/src/freenet/node/SSKInsertSender.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/SSKInsertSender.java  2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/SSKInsertSender.java  2007-09-23 
09:58:34 UTC (rev 15282)
@@ -101,6 +101,7 @@
     }

        public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
         short origHTL = htl;
         node.addInsertSender(myKey, htl, this);
         try {

Modified: branches/freenet-jfk/src/freenet/node/TestnetHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/TestnetHandler.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/TestnetHandler.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -75,6 +75,7 @@
        private int testnetPort;

        public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
                while(true){
                        // Set up server socket
                        try {
@@ -128,6 +129,7 @@
                }

                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
                        InputStream is = null;
                        OutputStream os = null;

Modified: branches/freenet-jfk/src/freenet/node/TestnetStatusUploader.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/TestnetStatusUploader.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/TestnetStatusUploader.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -45,6 +45,7 @@
        private Socket client;

        public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        //thread loop

                        while(true){

Modified: branches/freenet-jfk/src/freenet/node/TextModeClientInterface.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/TextModeClientInterface.java  
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/TextModeClientInterface.java  
2007-09-23 09:58:34 UTC (rev 15282)
@@ -95,6 +95,7 @@
     }

     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
        try {
                realRun();
        } catch (IOException e) {
@@ -337,6 +338,7 @@
        // FIXME run on separate thread
        n.ps.queueTimedJob(new Runnable() {
                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        n.getNodeUpdater().arm();
                }
        }, 0);
@@ -357,6 +359,7 @@
                while(bis.available() > 0){
                        outsb.append((char)bis.read());
                }
+               bis.close();
                output.data.free();
        } catch (IOException e) {
                outsb.append("Bucket error?: " + e.getMessage());
@@ -895,7 +898,7 @@
         } else if(uline.startsWith("PLUGLIST")) {
                outsb.append(n.pluginManager.dumpPlugins());
         } else if(uline.startsWith("PLUGKILL:")) {
-               
n.pluginManager.killPlugin(line.substring("PLUGKILL:".length()).trim());
+               
n.pluginManager.killPlugin(line.substring("PLUGKILL:".length()).trim(), 
60*1000);
         } else {
                if(uline.length() > 0)
                        printHeader(out);

Modified: 
branches/freenet-jfk/src/freenet/node/TextModeClientInterfaceServer.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/TextModeClientInterfaceServer.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/TextModeClientInterfaceServer.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -63,7 +63,7 @@

                SubConfig TMCIConfig = new SubConfig("console", config);

-               TMCIConfig.register("enabled", true, 1, true, true /* FIXME 
only because can't be changed on the fly */, 
"TextModeClientInterfaceServer.enabled", 
"TextModeClientInterfaceServer.enabledLong", new TMCIEnabledCallback(core));
+               TMCIConfig.register("enabled", false, 1, true, true /* FIXME 
only because can't be changed on the fly */, 
"TextModeClientInterfaceServer.enabled", 
"TextModeClientInterfaceServer.enabledLong", new TMCIEnabledCallback(core));
                TMCIConfig.register("bindTo", "127.0.0.1", 2, true, false, 
"TextModeClientInterfaceServer.bindTo", 
"TextModeClientInterfaceServer.bindToLong", new TMCIBindtoCallback(core));
                TMCIConfig.register("allowedHosts", 
"127.0.0.1,0:0:0:0:0:0:0:1", 2, true, false, 
"TextModeClientInterfaceServer.allowedHosts", 
"TextModeClientInterfaceServer.allowedHostsLong", new 
TMCIAllowedHostsCallback(core));
                TMCIConfig.register("port", 2323, 1, true, false, 
"TextModeClientInterfaceServer.telnetPortNumber", 
"TextModeClientInterfaceServer.telnetPortNumberLong", new 
TCMIPortNumberCallback(core));
@@ -208,6 +208,7 @@
      * Read commands, run them
      */
     public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
        while(true) {
                int curPort = port;
                String tempBindTo = this.bindTo;

Modified: branches/freenet-jfk/src/freenet/node/Version.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/Version.java  2007-09-23 08:04:32 UTC 
(rev 15281)
+++ branches/freenet-jfk/src/freenet/node/Version.java  2007-09-23 09:58:34 UTC 
(rev 15282)
@@ -24,17 +24,17 @@
        public static final String protocolVersion = "1.0";

        /** The build number of the current revision */
-       private static final int buildNumber = 1054;
+       private static final int buildNumber = 1064;

        /** Oldest build of Fred we will talk to */
-       private static final int oldLastGoodBuild = 1049;
-       private static final int newLastGoodBuild = 1054;
+       private static final int oldLastGoodBuild = 1058;
+       private static final int newLastGoodBuild = 1064;
        static final long transitionTime;

        static {
                final Calendar _cal = 
Calendar.getInstance(TimeZone.getTimeZone("GMT"));
                // year, month - 1 (or constant), day, hour, minute, second
-               _cal.set( 2007, Calendar.AUGUST, 14, 0, 0, 0 );
+               _cal.set( 2007, Calendar.SEPTEMBER, 28, 0, 0, 0 );
                transitionTime = _cal.getTimeInMillis();
        }


Modified: branches/freenet-jfk/src/freenet/node/fcp/AllDataMessage.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/AllDataMessage.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/AllDataMessage.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -17,12 +17,15 @@
        final long dataLength;
        final boolean global;
        final String identifier;
+       final long startupTime, completionTime;

-       public AllDataMessage(Bucket bucket, String identifier, boolean global) 
{
+       public AllDataMessage(Bucket bucket, String identifier, boolean global, 
long startupTime, long completionTime) {
                this.bucket = bucket;
                this.dataLength = bucket.size();
                this.identifier = identifier;
                this.global = global;
+               this.startupTime = startupTime;
+               this.completionTime = completionTime;
        }

        long dataLength() {
@@ -31,9 +34,11 @@

        public SimpleFieldSet getFieldSet() {
                SimpleFieldSet fs = new SimpleFieldSet(true);
-               fs.putSingle("DataLength", Long.toString(dataLength));
+               fs.put("DataLength", dataLength);
                fs.putSingle("Identifier", identifier);
                if(global) fs.putSingle("Global", "true");
+               fs.put("StartupTime", startupTime);
+               fs.put("CompletionTime", completionTime);
                return fs;
        }


Modified: branches/freenet-jfk/src/freenet/node/fcp/ClientGet.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/ClientGet.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/ClientGet.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -77,7 +77,7 @@
                        File returnFilename, File returnTempFilename) throws 
IdentifierCollisionException, NotAllowedException {
                super(uri, identifier, verbosity, null, globalClient, prioClass,
                                (persistRebootOnly ? 
ClientRequest.PERSIST_REBOOT : ClientRequest.PERSIST_FOREVER),
-                                               null, true);
+                               null, true);

                fctx = new FetchContext(client.defaultFetchContext, 
FetchContext.IDENTICAL_MASK, false);
                fctx.eventProducer.addEventListener(this);
@@ -124,11 +124,11 @@
                                ret.free();
                                throw e;
                        }
-               getter = new ClientGetter(this, 
client.core.requestStarters.chkFetchScheduler, 
client.core.requestStarters.sskFetchScheduler, uri, fctx, priorityClass, 
client.lowLevelClient, returnBucket, null);
-               if(persistenceType != PERSIST_CONNECTION) {
-                       FCPMessage msg = persistentTagMessage();
-                       client.queueClientRequestMessage(msg, 0);
-               }
+                       getter = new ClientGetter(this, 
client.core.requestStarters.chkFetchScheduler, 
client.core.requestStarters.sskFetchScheduler, uri, fctx, priorityClass, 
client.lowLevelClient, returnBucket, null);
+                       if(persistenceType != PERSIST_CONNECTION) {
+                               FCPMessage msg = persistentTagMessage();
+                               client.queueClientRequestMessage(msg, 0);
+                       }
        }

        public ClientGet(FCPConnectionHandler handler, ClientGetMessage 
message) throws IdentifierCollisionException, MessageInvalidException {
@@ -147,12 +147,12 @@
                // Has already been checked
                fctx.maxOutputLength = message.maxSize;
                fctx.maxTempLength = message.maxTempSize;
-               
+
                if(message.allowedMIMETypes != null) {
                        fctx.allowedMIMETypes = new HashSet();
                        for(int i=0;i<message.allowedMIMETypes.length;i++) 
fctx.allowedMIMETypes.add(message.allowedMIMETypes[i]);
                }
-               
+
                this.returnType = message.returnType;
                this.binaryBlob = message.binaryBlob;
                Bucket ret = null;
@@ -194,16 +194,16 @@
                                ret.free();
                                throw e;
                        }
-               getter = new ClientGetter(this, 
client.core.requestStarters.chkFetchScheduler, 
-                               client.core.requestStarters.sskFetchScheduler, 
uri, fctx, priorityClass, 
-                               client.lowLevelClient, binaryBlob ? new 
NullBucket() : returnBucket, 
-                                               binaryBlob ? returnBucket : 
null);
-               if(persistenceType != PERSIST_CONNECTION) {
-                       FCPMessage msg = persistentTagMessage();
-                       client.queueClientRequestMessage(msg, 0);
-                       if(handler != null && (!handler.isGlobalSubscribed()))
-                               handler.outputHandler.queue(msg);
-               }
+                       getter = new ClientGetter(this, 
client.core.requestStarters.chkFetchScheduler, 
+                                       
client.core.requestStarters.sskFetchScheduler, uri, fctx, priorityClass, 
+                                       client.lowLevelClient, binaryBlob ? new 
NullBucket() : returnBucket, 
+                                                       binaryBlob ? 
returnBucket : null);
+                       if(persistenceType != PERSIST_CONNECTION) {
+                               FCPMessage msg = persistentTagMessage();
+                               client.queueClientRequestMessage(msg, 0);
+                               if(handler != null && 
(!handler.isGlobalSubscribed()))
+                                       handler.outputHandler.queue(msg);
+                       }
        }

        /**
@@ -296,21 +296,21 @@
                        fctx.allowedMIMETypes = new HashSet();
                        for(int i=0;i<allowed.length;i++) 
fctx.allowedMIMETypes.add(allowed[i]);
                }
-               
+
                getter = new ClientGetter(this, 
client.core.requestStarters.chkFetchScheduler, 
                                client.core.requestStarters.sskFetchScheduler, 
uri, 
                                fctx, priorityClass, client.lowLevelClient, 
                                binaryBlob ? new NullBucket() : returnBucket, 
-                               binaryBlob ? returnBucket : null);
+                                               binaryBlob ? returnBucket : 
null);

                if(persistenceType != PERSIST_CONNECTION) {
                        FCPMessage msg = persistentTagMessage();
                        client.queueClientRequestMessage(msg, 0);
                }
-               
+
                if(finished){
                        if(succeeded) 
-                               allDataPending = new 
AllDataMessage(returnBucket, identifier, global);
+                               allDataPending = new 
AllDataMessage(returnBucket, identifier, global, startupTime, completionTime);
                        else
                                started = true;
                }
@@ -381,17 +381,20 @@
                                Logger.error(this, "onSuccess called twice for 
"+this+" ("+identifier+ ')');
                                return; // We might be called twice; ignore it 
if so.
                        }
+                       started = true;
                        if(returnType == ClientGetMessage.RETURN_TYPE_DIRECT) {
                                // Send all the data at once
                                // FIXME there should be other options
-                               adm = new AllDataMessage(returnBucket, 
identifier, global);
+                               // FIXME: CompletionTime is set on finish() : 
we need to give it current time here
+                               // but it means we won't always return the same 
value to clients... Does it matter ?
+                               adm = new AllDataMessage(returnBucket, 
identifier, global, startupTime, System.currentTimeMillis());
                                if(persistenceType == PERSIST_CONNECTION)
                                        adm.setFreeOnSent();
                                dontFree = true;
-                       /* 
-                        * } else if(returnType == 
ClientGetMessage.RETURN_TYPE_NONE) {
+                               /* 
+                                * } else if(returnType == 
ClientGetMessage.RETURN_TYPE_NONE) {
                                // Do nothing
-                        */
+                                */
                        } else if(returnType == 
ClientGetMessage.RETURN_TYPE_DISK) {
                                // Write to temp file, then rename over filename
                                FileOutputStream fos = null;
@@ -494,11 +497,12 @@
        }

        public void onFailure(FetchException e, ClientGetter state) {
-        if(finished) return;
+               if(finished) return;
                synchronized(this) {
                        succeeded = false;
                        getFailedMessage = new GetFailedMessage(e, identifier, 
global);
                        finished = true;
+                       started = true;
                }
                if(Logger.shouldLog(Logger.MINOR, this))
                        Logger.minor(this, "Caught "+e, e);
@@ -519,26 +523,26 @@
        public void onGeneratedURI(FreenetURI uri, BaseClientPutter state) {
                // Ignore
        }
-    
-    public void requestWasRemoved() {
-        // if request is still running, send a GetFailed with code=cancelled
-        if( !finished ) {
-            synchronized(this) {
-                succeeded = false;
-                finished = true;
-                FetchException cancelled = new 
FetchException(FetchException.CANCELLED);
-                getFailedMessage = new GetFailedMessage(cancelled, identifier, 
global);
-            }
-            trySendDataFoundOrGetFailed(null);
-        }
-        // notify client that request was removed
-        FCPMessage msg = new PersistentRequestRemovedMessage(getIdentifier(), 
global);
-        client.queueClientRequestMessage(msg, 0);

-        freeData();
-        finish();
-    }
+       public void requestWasRemoved() {
+               // if request is still running, send a GetFailed with 
code=cancelled
+               if( !finished ) {
+                       synchronized(this) {
+                               succeeded = false;
+                               finished = true;
+                               FetchException cancelled = new 
FetchException(FetchException.CANCELLED);
+                               getFailedMessage = new 
GetFailedMessage(cancelled, identifier, global);
+                       }
+                       trySendDataFoundOrGetFailed(null);
+               }
+               // notify client that request was removed
+               FCPMessage msg = new 
PersistentRequestRemovedMessage(getIdentifier(), global);
+               client.queueClientRequestMessage(msg, 0);

+               freeData();
+               finish();
+       }
+
        public void receive(ClientEvent ce) {
                // Don't need to lock, verbosity is final and finished is never 
unset.
                if(finished) return;
@@ -594,6 +598,10 @@
                }
                fs.putSingle("Global", Boolean.toString(client.isGlobalQueue));
                fs.put("BinaryBlob", binaryBlob);
+               fs.put("StartupTime", startupTime);
+               if(finished)
+                       fs.put("CompletionTime", completionTime);
+
                return fs;
        }


Modified: branches/freenet-jfk/src/freenet/node/fcp/ClientPut.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/ClientPut.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/ClientPut.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -6,6 +6,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.security.MessageDigest;
@@ -231,7 +232,9 @@
                                md.update(salt.getBytes("UTF-8"));
                        } catch (UnsupportedEncodingException e) {}
                        try {
-                               SHA256.hash(data.getInputStream(), md);
+                               InputStream is = data.getInputStream();
+                               SHA256.hash(is, md);
+                               is.close();
                        } catch (IOException e) {
                                SHA256.returnMessageDigest(md);
                                Logger.error(this, "Got IOE: " +e.getMessage(), 
e);

Modified: branches/freenet-jfk/src/freenet/node/fcp/ClientPutBase.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/ClientPutBase.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/ClientPutBase.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -30,7 +30,7 @@
        private int VERBOSITY_SPLITFILE_PROGRESS = 1;
        private int VERBOSITY_PUT_FETCHABLE = 256;
        private int VERBOSITY_COMPRESSION_START_END = 512;
-       
+
        // Stuff waiting for reconnection
        /** Has the request succeeded? */
        protected boolean succeeded;
@@ -48,12 +48,12 @@

        /** Whether to force an early generation of the CHK */
        protected final boolean earlyEncode;
-       
+
        protected final FreenetURI publicURI;
-       
+
        public final static String SALT = "Salt";
        public final static String FILE_HASH = "FileHash";
-       
+
        public ClientPutBase(FreenetURI uri, String identifier, int verbosity, 
FCPConnectionHandler handler, 
                        short priorityClass, short persistenceType, String 
clientToken, boolean global, boolean getCHKOnly,
                        boolean dontCompress, int maxRetries, boolean 
earlyEncode) throws MalformedURLException {
@@ -125,7 +125,7 @@
                        cancel();
                // otherwise ignore
        }
-       
+
        public void onSuccess(BaseClientPutter state) {
                synchronized(this) {
                        // Including this helps with certain bugs...
@@ -141,7 +141,7 @@
        }

        public void onFailure(InsertException e, BaseClientPutter state) {
-        if(finished) return;
+               if(finished) return;
                synchronized(this) {
                        finished = true;
                        putFailedMessage = new PutFailedMessage(e, identifier, 
global);
@@ -161,25 +161,25 @@
                }
                trySendGeneratedURIMessage(null);
        }
-    
-    public void requestWasRemoved() {
-        // if request is still running, send a PutFailed with code=cancelled
-        if( !finished ) {
-            synchronized(this) {
-                finished = true;
-                InsertException cancelled = new 
InsertException(InsertException.CANCELLED);
-                putFailedMessage = new PutFailedMessage(cancelled, identifier, 
global);
-            }
-            trySendFinalMessage(null);
-        }
-        // notify client that request was removed
-        FCPMessage msg = new PersistentRequestRemovedMessage(getIdentifier(), 
global);
-        client.queueClientRequestMessage(msg, 0);

-        freeData();
-        finish();
-    }
+       public void requestWasRemoved() {
+               // if request is still running, send a PutFailed with 
code=cancelled
+               if( !finished ) {
+                       synchronized(this) {
+                               finished = true;
+                               InsertException cancelled = new 
InsertException(InsertException.CANCELLED);
+                               putFailedMessage = new 
PutFailedMessage(cancelled, identifier, global);
+                       }
+                       trySendFinalMessage(null);
+               }
+               // notify client that request was removed
+               FCPMessage msg = new 
PersistentRequestRemovedMessage(getIdentifier(), global);
+               client.queueClientRequestMessage(msg, 0);

+               freeData();
+               finish();
+       }
+
        public void receive(ClientEvent ce) {
                if(finished) return;
                if(ce instanceof SplitfileProgressEvent) {
@@ -215,18 +215,18 @@
                        trySendProgressMessage(msg, VERBOSITY_PUT_FETCHABLE, 
null);
                }
        }
-       
+
        private void trySendFinalMessage(FCPConnectionOutputHandler handler) {
-               
+
                FCPMessage msg;
                synchronized (this) {
                        if(succeeded) {
-                               msg = new PutSuccessfulMessage(identifier, 
global, generatedURI);
+                               msg = new PutSuccessfulMessage(identifier, 
global, generatedURI, startupTime, completionTime);
                        } else {
                                msg = putFailedMessage;
                        }
                }
-               
+
                if(msg == null) {
                        Logger.error(this, "Trying to send null message on 
"+this, new Exception("error"));
                } else {
@@ -258,7 +258,7 @@
                else
                        client.queueClientRequestMessage(msg, verbosity);
        }
-       
+
        public void sendPendingMessages(FCPConnectionOutputHandler handler, 
boolean includePersistentRequest, boolean includeData, boolean onlyData) {
                if(persistenceType == PERSIST_CONNECTION) {
                        Logger.error(this, "WTF? 
persistenceType="+persistenceType, new Exception("error"));
@@ -268,7 +268,7 @@
                        FCPMessage msg = persistentTagMessage();
                        handler.queue(msg);
                }
-               
+
                boolean generated = false;
                FCPMessage msg = null;
                boolean fin = false;
@@ -306,9 +306,13 @@
                        // Should have a putFailedMessage... unless there is a 
race condition.
                        fs.put("PutFailed", 
putFailedMessage.getFieldSet(false));
                fs.putSingle("Global", Boolean.toString(client.isGlobalQueue));
+               fs.put("StartupTime", startupTime);
+               if(finished)
+                       fs.put("CompletionTime", completionTime);
+               
                return fs;
        }
-       
+
        protected abstract String getTypeName();

        public synchronized double getSuccessFraction() {
@@ -320,7 +324,7 @@
                        return -1;
        }

-       
+
        public synchronized double getTotalBlocks() {
                if(progressMessage != null) {
                        if(progressMessage instanceof SimpleProgressMessage)
@@ -329,7 +333,7 @@
                } else
                        return -1;
        }
-       
+
        public synchronized double getMinBlocks() {
                if(progressMessage != null) {
                        if(progressMessage instanceof SimpleProgressMessage)
@@ -338,7 +342,7 @@
                } else
                        return -1;
        }
-       
+
        public synchronized double getFailedBlocks() {
                if(progressMessage != null) {
                        if(progressMessage instanceof SimpleProgressMessage)
@@ -347,7 +351,7 @@
                } else
                        return -1;
        }
-       
+
        public synchronized double getFatalyFailedBlocks() {
                if(progressMessage != null) {
                        if(progressMessage instanceof SimpleProgressMessage)
@@ -356,7 +360,7 @@
                } else
                        return -1;
        }
-       
+
        public synchronized double getFetchedBlocks() {
                if(progressMessage != null) {
                        if(progressMessage instanceof SimpleProgressMessage)
@@ -365,12 +369,12 @@
                } else
                        return -1;
        }
-       
+
        public synchronized boolean isTotalFinalized() {
                if(!(progressMessage instanceof SimpleProgressMessage)) return 
false;
                else return 
((SimpleProgressMessage)progressMessage).isTotalFinalized();
        }
-       
+
        public synchronized String getFailureReason() {
                if(putFailedMessage == null)
                        return null;
@@ -379,7 +383,7 @@
                        s += ": "+putFailedMessage.extraDescription;
                return s;
        }
-       
+
        public void setVarsRestart() {
                synchronized(this) {
                        finished = false;

Modified: branches/freenet-jfk/src/freenet/node/fcp/ClientPutDir.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/ClientPutDir.java 2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/ClientPutDir.java 2007-09-23 
09:58:34 UTC (rev 15282)
@@ -5,11 +5,13 @@

 import java.io.File;
 import java.io.IOException;
+import java.io.FileNotFoundException;
 import java.net.MalformedURLException;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Vector;

+import freenet.client.DefaultMIMETypes;
 import freenet.client.FetchException;
 import freenet.client.FetchResult;
 import freenet.client.InsertException;
@@ -61,6 +63,66 @@
                if(logMINOR) Logger.minor(this, "Putting dir "+identifier+" : 
"+priorityClass);
        }

+       /**
+       *       Puts a disk dir
+       */
+       public ClientPutDir(FCPClient client, FreenetURI uri, String 
identifier, int verbosity, short priorityClass, short persistenceType, String 
clientToken, boolean getCHKOnly, boolean dontCompress, int maxRetries, File 
dir, String defaultName, boolean allowUnreadableFiles, boolean global, boolean 
earlyEncode) throws FileNotFoundException, IdentifierCollisionException, 
MalformedURLException {
+               super(uri, identifier, verbosity , null, client, priorityClass, 
persistenceType, clientToken, global, getCHKOnly, dontCompress, maxRetries, 
earlyEncode);
+
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+               this.manifestElements = makeDiskDirManifest(dir, "", 
allowUnreadableFiles);
+               this.defaultName = defaultName;
+               makePutter();
+               if(persistenceType != PERSIST_CONNECTION) {
+                       client.register(this, false);
+                       FCPMessage msg = persistentTagMessage();
+                       client.queueClientRequestMessage(msg, 0);
+               }
+               if(putter != null) {
+                       numberOfFiles = putter.countFiles();
+                       totalSize = putter.totalSize();
+               } else {
+                       numberOfFiles = -1;
+                       totalSize = -1;
+               }
+               if(logMINOR) Logger.minor(this, "Putting dir "+identifier+" : 
"+priorityClass);
+       }
+
+       private HashMap makeDiskDirManifest(File dir, String prefix, boolean 
allowUnreadableFiles) throws FileNotFoundException {
+
+               HashMap map = new HashMap();
+               File[] files = dir.listFiles();
+               
+               if(files == null)
+                       throw new IllegalArgumentException("No such directory");
+
+               for(int i=0; i < files.length; i++) {
+
+                       File f = files[i];
+                       if (f.exists() && f.canRead()) {
+                               if(f.isFile()) {
+                                       FileBucket bucket = new FileBucket(f, 
true, false, false, false, false);
+                                       if(logMINOR)
+                                               Logger.minor(this, "Add file : 
" + f.getAbsolutePath());
+                                       
+                                       map.put(f.getName(), new 
ManifestElement(f.getName(), prefix + f.getName(), bucket, 
DefaultMIMETypes.guessMIMEType(f.getName(), true), f.length()));
+                               } else if(f.isDirectory()) {
+                                       if(logMINOR)
+                                               Logger.minor(this, "Add dir : " 
+ f.getAbsolutePath());
+                                       
+                                       map.put(f.getName(), 
makeDiskDirManifest(f, prefix + f.getName() + "/", allowUnreadableFiles));
+                               } else {
+                                       if(!allowUnreadableFiles)
+                                               throw new 
FileNotFoundException("Not a file and not a directory : " + f);
+                               }
+                       } else if (!allowUnreadableFiles)
+                               throw new FileNotFoundException("The file does 
not exist or is unreadable : " + f);
+                       
+               }
+
+               return map;
+       }
+       
        private void makePutter() {
                SimpleManifestPutter p;
                try {
@@ -73,6 +135,8 @@
                putter = p;
        }

+
+
        public ClientPutDir(SimpleFieldSet fs, FCPClient client) throws 
PersistenceParseException, IOException {
                super(fs, client);
                logMINOR = Logger.shouldLog(Logger.MINOR, this);

Modified: branches/freenet-jfk/src/freenet/node/fcp/ClientRequest.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/ClientRequest.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/ClientRequest.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -37,6 +37,10 @@
        protected String clientToken;
        /** Is the request on the global queue? */
        protected final boolean global;
+       /** Timestamp : startup time */
+       protected final long startupTime;
+       /** Timestamp : completion time */
+       protected long completionTime = Long.MAX_VALUE;

        public ClientRequest(FreenetURI uri2, String identifier2, int 
verbosity2, FCPConnectionHandler handler, 
                        FCPClient client, short priorityClass2, short 
persistenceType2, String clientToken2, boolean global) {
@@ -56,8 +60,9 @@
                else
                        origHandler = null;
                this.client = client;
+               this.startupTime = System.currentTimeMillis();
        }
-       
+
        public ClientRequest(FreenetURI uri2, String identifier2, int 
verbosity2, FCPConnectionHandler handler, 
                        short priorityClass2, short persistenceType2, String 
clientToken2, boolean global) {
                this.uri = uri2;
@@ -80,6 +85,7 @@
                } else {
                        client = handler.getClient();
                }
+               this.startupTime = System.currentTimeMillis();
        }

        public ClientRequest(SimpleFieldSet fs, FCPClient client2) throws 
MalformedURLException {
@@ -98,20 +104,25 @@
                clientToken = fs.get("ClientToken");
                finished = Fields.stringToBool(fs.get("Finished"), false);
                global = Fields.stringToBool(fs.get("Global"), false);
+               final String stime = fs.get("StartupTime");
+               this.startupTime = stime == null ? System.currentTimeMillis() : 
Fields.parseLong(stime);
+               final String ctime = fs.get("CompletionTime");
+               if(ctime != null)
+                       completionTime = Fields.parseLong(ctime);
        }

        /** Lost connection */
        public abstract void onLostConnection();
-       
+
        /** Send any pending messages for a persistent request e.g. after 
reconnecting */
        public abstract void sendPendingMessages(FCPConnectionOutputHandler 
handler, boolean includePersistentRequest, boolean includeData, boolean 
onlyData);

        // Persistence
-       
+
        public static final short PERSIST_CONNECTION = 0;
        public static final short PERSIST_REBOOT = 1;
        public static final short PERSIST_FOREVER = 2;
-       
+
        public static String persistenceTypeString(short type) {
                switch(type) {
                case PERSIST_CONNECTION:
@@ -212,13 +223,13 @@
        }

        protected abstract ClientRequester getClientRequest();
-       
+
        /** Completed request dropped off the end without being acknowledged */
        public void dropped() {
                cancel();
                freeData();
        }
-       
+
        /** Return the priority class */
        public short getPriority(){
                return priorityClass;
@@ -229,13 +240,14 @@

        /** Request completed. But we may have to stick around until we are 
acked. */
        protected void finish() {
+               completionTime = System.currentTimeMillis();
                if(persistenceType == ClientRequest.PERSIST_CONNECTION)
                        origHandler.finishedClientRequest(this);
                else
                        client.server.forceStorePersistentRequests();
                client.finishedClientRequest(this);
        }
-       
+
        /**
         * Write a persistent request to disk.
         * @throws IOException 
@@ -249,14 +261,14 @@
                SimpleFieldSet fs = getFieldSet();
                fs.writeTo(w);
        }
-       
+
        /**
         * Get a SimpleFieldSet representing this request.
         */
        public abstract SimpleFieldSet getFieldSet() throws IOException;

        public abstract double getSuccessFraction();
-       
+
        public abstract double getTotalBlocks();
        public abstract double getMinBlocks();
        public abstract double getFetchedBlocks();
@@ -269,7 +281,7 @@
         * Has the total number of blocks to insert been determined yet?
         */
        public abstract boolean isTotalFinalized();
-       
+
        public void onMajorProgress() {
                if(persistenceType != ClientRequest.PERSIST_CONNECTION) {
                        if(client != null)
@@ -281,7 +293,7 @@
        public abstract void start();

        protected boolean started;
-       
+
        public boolean isStarted() {
                return started;
        }
@@ -292,67 +304,78 @@

        public abstract boolean restart();

-    protected abstract FCPMessage persistentTagMessage();
+       protected abstract FCPMessage persistentTagMessage();

-    /**
-     * Called after a ModifyPersistentRequest.
-     * Sends a PersistentRequestModified message to clients if any value 
changed. 
-     */
-    public void modifyRequest(String newClientToken, short newPriorityClass) {
+       /**
+        * Called after a ModifyPersistentRequest.
+        * Sends a PersistentRequestModified message to clients if any value 
changed. 
+        */
+       public void modifyRequest(String newClientToken, short 
newPriorityClass) {

-        boolean clientTokenChanged = false;
-        boolean priorityClassChanged = false;
-        
-        if(newClientToken != null) {
-            if( clientToken != null ) {
-                if( !newClientToken.equals(clientToken) ) {
-                    this.clientToken = newClientToken; // token changed
-                    clientTokenChanged = true;
-                }
-            } else {
-                this.clientToken = newClientToken; // first time the token is 
set
-                clientTokenChanged = true;
-            }
-        }
+               boolean clientTokenChanged = false;
+               boolean priorityClassChanged = false;

-        if(newPriorityClass >= 0 && newPriorityClass != priorityClass) {
-            this.priorityClass = newPriorityClass;
-            getClientRequest().setPriorityClass(priorityClass);
-            priorityClassChanged = true;
-        }
+               if(newClientToken != null) {
+                       if( clientToken != null ) {
+                               if( !newClientToken.equals(clientToken) ) {
+                                       this.clientToken = newClientToken; // 
token changed
+                                       clientTokenChanged = true;
+                               }
+                       } else {
+                               this.clientToken = newClientToken; // first 
time the token is set
+                               clientTokenChanged = true;
+                       }
+               }

-        if( clientTokenChanged || priorityClassChanged ) {
-            if(persistenceType != ClientRequest.PERSIST_CONNECTION) {
-                if(client != null) {
-                    client.server.forceStorePersistentRequests();
-                }
-            }
-        } else {
-            return; // quick return, nothing was changed
-        }
+               if(newPriorityClass >= 0 && newPriorityClass != priorityClass) {
+                       this.priorityClass = newPriorityClass;
+                       getClientRequest().setPriorityClass(priorityClass);
+                       priorityClassChanged = true;
+               }

-        // this could become too complex with more parameters, but for now its 
ok
-        final PersistentRequestModifiedMessage modifiedMsg;
-        if( clientTokenChanged && priorityClassChanged ) {
-            modifiedMsg = new PersistentRequestModifiedMessage(identifier, 
global, priorityClass, clientToken);
-        } else if( priorityClassChanged ) {
-            modifiedMsg = new PersistentRequestModifiedMessage(identifier, 
global, priorityClass);
-        } else if( clientTokenChanged ) {
-            modifiedMsg = new PersistentRequestModifiedMessage(identifier, 
global, clientToken);
-        } else {
-            return; // paranoia, we should not be here if nothing was changed!
-        }
-        client.queueClientRequestMessage(modifiedMsg, 0);
-    }
+               if( clientTokenChanged || priorityClassChanged ) {
+                       if(persistenceType != ClientRequest.PERSIST_CONNECTION) 
{
+                               if(client != null) {
+                                       
client.server.forceStorePersistentRequests();
+                               }
+                       }
+               } else {
+                       return; // quick return, nothing was changed
+               }

-    /**
-     * Called after a RemovePersistentRequest. Send a PersistentRequestRemoved 
to the clients.
-     */
-    public abstract void requestWasRemoved();
-    
+               // this could become too complex with more parameters, but for 
now its ok
+               final PersistentRequestModifiedMessage modifiedMsg;
+               if( clientTokenChanged && priorityClassChanged ) {
+                       modifiedMsg = new 
PersistentRequestModifiedMessage(identifier, global, priorityClass, 
clientToken);
+               } else if( priorityClassChanged ) {
+                       modifiedMsg = new 
PersistentRequestModifiedMessage(identifier, global, priorityClass);
+               } else if( clientTokenChanged ) {
+                       modifiedMsg = new 
PersistentRequestModifiedMessage(identifier, global, clientToken);
+               } else {
+                       return; // paranoia, we should not be here if nothing 
was changed!
+               }
+               client.queueClientRequestMessage(modifiedMsg, 0);
+       }
+
+       /**
+        * Called after a RemovePersistentRequest. Send a 
PersistentRequestRemoved to the clients.
+        */
+       public abstract void requestWasRemoved();
+
        /** Utility method for storing details of a possibly encrypted bucket. 
*/
        protected void bucketToFS(SimpleFieldSet fs, String name, boolean 
includeSize, Bucket data) {
                SerializableToFieldSetBucket bucket = 
(SerializableToFieldSetBucket) data;
                fs.put(name, bucket.toFieldSet());
        }
+
+       public void restartAsync() {
+               synchronized(this) {
+                       this.started = false;
+               }
+               client.core.getExecutor().execute(new Runnable() {
+                       public void run() {
+                               restart();
+                       }
+               }, "Restarting "+this);
+       }
 }

Modified: branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionHandler.java 
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionHandler.java 
2007-09-23 09:58:34 UTC (rev 15282)
@@ -63,6 +63,7 @@
        final BucketFactory bf;
        final HashMap requestsByIdentifier;
        protected final String connectionIdentifier;
+       static boolean logMINOR;

        // We are confident that the given client can access those
        private final HashMap checkedDirectories = new HashMap();
@@ -70,6 +71,7 @@
        private final HashMap inTestDirectories = new HashMap();

        public FCPConnectionHandler(Socket s, FCPServer server) {
+               logMINOR = Logger.shouldLog(Logger.MINOR, this);
                this.sock = s;
                this.server = server;
                isClosed = false;
@@ -249,25 +251,28 @@
                ClientPutDir cp = null;
                FCPMessage failedMessage = null;
                boolean persistent = message.persistenceType != 
ClientRequest.PERSIST_CONNECTION;
+               // We need to track non-persistent requests anyway, so we may 
as well check
+               boolean success;
                synchronized(this) {
                        if(isClosed) return;
-                       // We need to track non-persistent requests anyway, so 
we may as well check
-                       boolean success;
                        if(!persistent)
                                success = true;
                        else
                                success = !requestsByIdentifier.containsKey(id);
-                       if(success) {
-                               try {
-                                       cp = new ClientPutDir(this, message, 
buckets);
-                               } catch (IdentifierCollisionException e) {
-                                       success = false;
-                               } catch (MalformedURLException e) {
-                                       failedMessage = new 
ProtocolErrorMessage(ProtocolErrorMessage.FREENET_URI_PARSE_ERROR, true, null, 
id, message.global);
+               }
+               if(success) {
+                       try {
+                               cp = new ClientPutDir(this, message, buckets);
+                       } catch (IdentifierCollisionException e) {
+                               success = false;
+                       } catch (MalformedURLException e) {
+                               failedMessage = new 
ProtocolErrorMessage(ProtocolErrorMessage.FREENET_URI_PARSE_ERROR, true, null, 
id, message.global);
+                       }
+                       if(!persistent) {
+                               synchronized(this) {
+                                       requestsByIdentifier.put(id, cp);
                                }
-                               if(!persistent)
-                                       requestsByIdentifier.put(id, cp);
-                               
+                               // FIXME register non-persistent requests in 
the constructors also, we already register persistent ones...
                        }
                        if(!success) {
                                Logger.normal(this, "Identifier collision on 
"+this);
@@ -276,6 +281,8 @@
                }
                if(failedMessage != null) {
                        outputHandler.queue(failedMessage);
+                       if(cp != null)
+                               cp.cancel();
                        return;
                } else {
                        // Register before starting, because it may complete 
immediately, and if it does,
@@ -315,13 +322,16 @@
         * @return boolean : allowed or not
         */
        protected boolean allowDDAFrom(File filename, boolean writeRequest) {
-               String parentDirectory = 
FileUtil.getCanonicalFile(filename).getPath();
+               String parentDirectory = 
FileUtil.getCanonicalFile(filename).getParent();
                DirectoryAccess da = null;

                synchronized (checkedDirectories) {
                                da = (DirectoryAccess) 
checkedDirectories.get(parentDirectory);
                }

+               if(logMINOR)
+                       Logger.minor(this, "Checking DDA: "+da+" for 
"+parentDirectory);
+               
                if(writeRequest)
                        return (da == null ? 
server.isDownloadDDAAlwaysAllowed() : da.canWrite);
                else
@@ -340,6 +350,9 @@
                synchronized (checkedDirectories) {
                                checkedDirectories.put(path, da);
                }
+               
+               if(logMINOR)
+                       Logger.minor(this, "DDA: read="+read+" write="+write+" 
for "+path);
        }

        /**

Modified: 
branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionInputHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionInputHandler.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionInputHandler.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -27,6 +27,7 @@
        }

        public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
                try {
                        realRun();
                } catch (IOException e) {
@@ -66,11 +67,12 @@
                                is.close();
                                return;
                        }
-                       if(messageType.equals("")) continue;
+                       if(messageType.equals(""))
+                               continue;
                        fs = new SimpleFieldSet(lis, 4096, 128, true, true, 
true, true);

                        // check for valid endmarker
-                       if (fs.getEndMarker() != null && 
(!fs.getEndMarker().startsWith("End")) && (!"Data".equals(fs.getEndMarker()))) {
+                       if (!firstMessage && fs.getEndMarker() != null && 
(!fs.getEndMarker().startsWith("End")) && (!"Data".equals(fs.getEndMarker()))) {
                                FCPMessage err = new 
ProtocolErrorMessage(ProtocolErrorMessage.MESSAGE_PARSE_ERROR, false, "Invalid 
end marker: "+fs.getEndMarker(), fs.get("Identifer"), fs.getBoolean("Global", 
false));
                                handler.outputHandler.queue(err);
                                continue;
@@ -83,15 +85,24 @@
                                msg = FCPMessage.create(messageType, fs, 
handler.bf, handler.server.core.persistentTempBucketFactory);
                                if(msg == null) continue;
                        } catch (MessageInvalidException e) {
-                               FCPMessage err = new 
ProtocolErrorMessage(e.protocolCode, false, e.getMessage(), e.ident, e.global);
-                               handler.outputHandler.queue(err);
+                               if(firstMessage) {
+                                       FCPMessage err = new 
ProtocolErrorMessage(ProtocolErrorMessage.CLIENT_HELLO_MUST_BE_FIRST_MESSAGE, 
true, null, null, false);
+                                       handler.outputHandler.queue(err);
+                                       handler.close();
+                                       is.close();
+                                       return;
+                               } else {
+                                       FCPMessage err = new 
ProtocolErrorMessage(e.protocolCode, false, e.getMessage(), e.ident, e.global);
+                                       handler.outputHandler.queue(err);
+                               }
                                continue;
                        }
                        if(firstMessage && !(msg instanceof 
ClientHelloMessage)) {
                                FCPMessage err = new 
ProtocolErrorMessage(ProtocolErrorMessage.CLIENT_HELLO_MUST_BE_FIRST_MESSAGE, 
true, null, null, false);
                                handler.outputHandler.queue(err);
                                handler.close();
-                               continue;
+                               is.close();
+                               return;
                        }
                        if(msg instanceof BaseDataCarryingMessage) {
                                // FIXME tidy up - coalesce with above and 
below try { } catch (MIE) {}'s?
@@ -118,7 +129,10 @@
                                continue;
                        }
                        firstMessage = false;
-                       if(handler.isClosed()) return;
+                       if(handler.isClosed()) {
+                               is.close();
+                               return;
+                       }
                }
        }
 }

Modified: 
branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionOutputHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionOutputHandler.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/FCPConnectionOutputHandler.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -26,6 +26,7 @@
        }

        public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
                try {
                        realRun();
                } catch (IOException e) {

Modified: branches/freenet-jfk/src/freenet/node/fcp/FCPServer.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/FCPServer.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/FCPServer.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -151,6 +151,7 @@
        }

        public void run() {
+           freenet.support.Logger.OSThread.logPID(this);
                while(true) {
                        try {
                                realRun();
@@ -383,8 +384,8 @@

                AssumeDDADownloadIsAllowedCallback cb4;
                AssumeDDAUploadIsAllowedCallback cb5;
-               fcpConfig.register("assumeDownloadDDAIsAllowed", true, 
sortOrder++, true, false, "FcpServer.assumeDownloadDDAIsAllowed", 
"FcpServer.assumeDownloadDDAIsAllowedLong", cb4 = new 
AssumeDDADownloadIsAllowedCallback());
-               fcpConfig.register("assumeUploadDDAIsAllowed", true, 
sortOrder++, true, false, "FcpServer.assumeUploadDDAIsAllowed", 
"FcpServer.assumeUploadDDAIsAllowedLong", cb5 = new 
AssumeDDAUploadIsAllowedCallback());
+               fcpConfig.register("assumeDownloadDDAIsAllowed", false, 
sortOrder++, true, false, "FcpServer.assumeDownloadDDAIsAllowed", 
"FcpServer.assumeDownloadDDAIsAllowedLong", cb4 = new 
AssumeDDADownloadIsAllowedCallback());
+               fcpConfig.register("assumeUploadDDAIsAllowed", false, 
sortOrder++, true, false, "FcpServer.assumeUploadDDAIsAllowed", 
"FcpServer.assumeUploadDDAIsAllowedLong", cb5 = new 
AssumeDDAUploadIsAllowedCallback());

                FCPServer fcp = new FCPServer(fcpConfig.getString("bindTo"), 
fcpConfig.getString("allowedHosts"), 
fcpConfig.getString("allowedHostsFullAccess"), fcpConfig.getInt("port"), node, 
core, persistentDownloadsEnabled, persistentDownloadsDir, 
persistentDownloadsInterval, fcpConfig.getBoolean("enabled"), 
fcpConfig.getBoolean("assumeDownloadDDAIsAllowed"), 
fcpConfig.getBoolean("assumeUploadDDAIsAllowed"));

@@ -525,6 +526,7 @@
                }

                public void run() {
+                   freenet.support.Logger.OSThread.logPID(this);
                        while(true) {
                                long startTime = System.currentTimeMillis();
                                try {
@@ -665,7 +667,7 @@
                                throw new IOException(e.toString());
                        }
                        for(int i=0;i<count;i++) {
-                               WrapperManager.signalStarting(5*60*1000);  // 5 
minutes per request
+                               WrapperManager.signalStarting(20*60*1000);  // 
20 minutes per request; must be >ds lock timeout (10 minutes)
                                System.out.println("Loading persistent request 
"+(i+1)+" of "+count+"..."); // humans count from 1..
                                ClientRequest.readAndRegister(br, this);
                        }

Modified: branches/freenet-jfk/src/freenet/node/fcp/PutSuccessfulMessage.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/fcp/PutSuccessfulMessage.java 
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/fcp/PutSuccessfulMessage.java 
2007-09-23 09:58:34 UTC (rev 15282)
@@ -12,11 +12,14 @@
        public final String identifier;
        public final boolean global;
        public final FreenetURI uri;
+       public final String startupTime, completionTime;

-       public PutSuccessfulMessage(String identifier, boolean global, 
FreenetURI uri) {
+       public PutSuccessfulMessage(String identifier, boolean global, 
FreenetURI uri, long startupTime, long completionTime) {
                this.identifier = identifier;
                this.global = global;
                this.uri = uri;
+               this.startupTime = String.valueOf(startupTime);
+               this.completionTime = String.valueOf(completionTime);
        }

        public SimpleFieldSet getFieldSet() {
@@ -26,6 +29,8 @@
                // FIXME debug and remove!
                if(uri != null)
                        fs.putSingle("URI", uri.toString());
+               fs.putSingle("StartupTime", startupTime);
+               fs.putSingle("CompletionTime", completionTime);
                return fs;
        }


Modified: branches/freenet-jfk/src/freenet/node/simulator/RealNodePingTest.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/simulator/RealNodePingTest.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/simulator/RealNodePingTest.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -33,8 +33,8 @@
         RandomSource random = NodeStarter.globalTestInit("pingtest");
         // Create 2 nodes
         Executor executor = new PooledExecutor();
-        Node node1 = NodeStarter.createTestNode(5001, "pingtest", false, 
false, true, Node.DEFAULT_MAX_HTL, 0, Node.DEFAULT_SWAP_INTERVAL, random, 
executor);
-        Node node2 = NodeStarter.createTestNode(5002, "pingtest", false, 
false, true, Node.DEFAULT_MAX_HTL, 0, Node.DEFAULT_SWAP_INTERVAL, random, 
executor);
+        Node node1 = NodeStarter.createTestNode(5001, "pingtest", false, 
false, true, Node.DEFAULT_MAX_HTL, 0, random, executor);
+        Node node2 = NodeStarter.createTestNode(5002, "pingtest", false, 
false, true, Node.DEFAULT_MAX_HTL, 0, random, executor);
         // Connect
         node1.connect(node2);
         node2.connect(node1);

Modified: 
branches/freenet-jfk/src/freenet/node/simulator/RealNodeRequestInsertTest.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/node/simulator/RealNodeRequestInsertTest.java  
    2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/node/simulator/RealNodeRequestInsertTest.java  
    2007-09-23 09:58:34 UTC (rev 15282)
@@ -54,7 +54,7 @@
         Executor executor = new PooledExecutor();
         for(int i=0;i<NUMBER_OF_NODES;i++) {
             nodes[i] = 
-               NodeStarter.createTestNode(5001+i, wd, false, true, true, 
MAX_HTL, 20 /* 5% */, Node.DEFAULT_SWAP_INTERVAL, random, executor);
+               NodeStarter.createTestNode(5001+i, wd, false, true, true, 
MAX_HTL, 20 /* 5% */, random, executor);
             Logger.normal(RealNodeRoutingTest.class, "Created node "+i);
         }
         SimpleFieldSet refs[] = new SimpleFieldSet[NUMBER_OF_NODES];

Modified: 
branches/freenet-jfk/src/freenet/node/simulator/RealNodeRoutingTest.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/simulator/RealNodeRoutingTest.java    
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/simulator/RealNodeRoutingTest.java    
2007-09-23 09:58:34 UTC (rev 15282)
@@ -48,7 +48,7 @@
         Executor executor = new PooledExecutor();
         for(int i=0;i<NUMBER_OF_NODES;i++) {
             nodes[i] = 
-               NodeStarter.createTestNode(5001+i, wd, false, true, true, 
MAX_HTL, 0 /* no dropped packets */, /* Node.DEFAULT_SWAP_INTERVAL */ 0, 
random, executor);
+               NodeStarter.createTestNode(5001+i, wd, false, true, true, 
MAX_HTL, 0 /* no dropped packets */, random, executor);
             Logger.normal(RealNodeRoutingTest.class, "Created node "+i);
         }
         Logger.normal(RealNodeRoutingTest.class, "Created "+NUMBER_OF_NODES+" 
nodes");

Modified: branches/freenet-jfk/src/freenet/node/updater/NodeUpdateManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/updater/NodeUpdateManager.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/updater/NodeUpdateManager.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -14,7 +14,6 @@
 import freenet.io.comm.NotConnectedException;
 import freenet.keys.FreenetURI;
 import freenet.l10n.L10n;
-import freenet.node.ExtVersion;
 import freenet.node.Node;
 import freenet.node.NodeInitException;
 import freenet.node.NodeStarter;
@@ -594,16 +593,20 @@
        void onDownloadedNewJar(boolean isExt) {
                synchronized(this) {
                        if(isExt) {
-                               if(extUpdater.getFetchedVersion() > 
ExtVersion.buildNumber) {
+                               if(extUpdater.getFetchedVersion() > 
NodeStarter.extBuildNumber) {
                                        hasNewExtJar = true;
                                        startedFetchingNextExtJar = -1;
                                        gotJarTime = System.currentTimeMillis();
+                                       if(logMINOR)
+                                               Logger.minor(this, "Got ext 
jar: "+extUpdater.getFetchedVersion());
                                }
                        } else {
                                if(mainUpdater.getFetchedVersion() > 
Version.buildNumber()) {
                                        hasNewMainJar = true;
                                        startedFetchingNextMainJar = -1;
                                        gotJarTime = System.currentTimeMillis();
+                                       if(logMINOR)
+                                               Logger.minor(this, "Got main 
jar: "+mainUpdater.getFetchedVersion());
                                }
                        }
                }

Modified: branches/freenet-jfk/src/freenet/node/updater/NodeUpdater.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/updater/NodeUpdater.java      
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/updater/NodeUpdater.java      
2007-09-23 09:58:34 UTC (rev 15282)
@@ -212,8 +212,8 @@
                                }
                        }
                        this.fetchedVersion = fetchedVersion;
+                       System.out.println("Found "+fetchedVersion);
                        if(fetchedVersion > currentVersion) {
-                               System.out.println("Found "+fetchedVersion);
                                Logger.normal(this, "Found version " + 
fetchedVersion + ", setting up a new UpdatedVersionAvailableUserAlert");
                        }
                        this.cg = null;

Modified: 
branches/freenet-jfk/src/freenet/node/updater/UpdateOverMandatoryManager.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/node/updater/UpdateOverMandatoryManager.java   
    2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/node/updater/UpdateOverMandatoryManager.java   
    2007-09-23 09:58:34 UTC (rev 15282)
@@ -223,8 +223,8 @@
        protected void sendUOMRequestMain(final PeerNode source) {
                synchronized(this) {
                        if(nodesAskedSendMainJar.size() + 
nodesSendingMainJar.size() >= MAX_NODES_SENDING_MAIN_JAR) {
-                               nodesOfferedMainJar.add(source);
-                               System.err.println("Offered main jar by 
"+source.userToString()+" (already fetching, but will use this offer if our 
current fetches fail).");
+                               if(nodesOfferedMainJar.add(source))
+                                       System.err.println("Offered main jar by 
"+source.userToString()+" (already fetching from 
"+nodesSendingMainJar.size()+"), but will use this offer if our current fetches 
fail).");
                                return;
                        } else {
                                if(nodesSendingMainJar.contains(source)) {

Modified: 
branches/freenet-jfk/src/freenet/node/useralerts/IPUndetectedUserAlert.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/useralerts/IPUndetectedUserAlert.java 
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/useralerts/IPUndetectedUserAlert.java 
2007-09-23 09:58:34 UTC (rev 15282)
@@ -30,7 +30,7 @@
                if(node.ipDetector.isDetecting())
                        return l10n("detecting");
                else
-                       return l10n("unknownAddress", "port", 
Integer.toString(node.getDarknetPortNumber()));
+                       return l10n("unknownAddress", "port", 
Integer.toString(node.getDarknetPortNumber())) + ' ' + 
textPortForwardSuggestion();
        }

        private String l10n(String key) {
@@ -41,14 +41,19 @@
                return L10n.getString("IPUndetectedUserAlert."+key, pattern, 
value);
        }

+       private String l10n(String key, String[] patterns, String[] values) {
+               return L10n.getString("IPUndetectedUserAlert."+key, patterns, 
values);
+       }
+
        public HTMLNode getHTMLText() {
                SubConfig sc = node.config.get("node");
                Option o = sc.getOption("tempIPAddressHint");

                HTMLNode textNode = new HTMLNode("div");
                L10n.addL10nSubstitution(textNode, 
"IPUndetectedUserAlert."+(node.ipDetector.isDetecting() ? 
"detectingWithConfigLink" : "unknownAddressWithConfigLink"), 
-                               new String[] { "link", "/link", "port" }, 
-                               new String[] { "<a href=\"/config/\">", "</a>", 
Integer.toString(node.getDarknetPortNumber()) });
+                               new String[] { "link", "/link" }, 
+                               new String[] { "<a href=\"/config/\">", "</a>" 
});
+               addPortForwardSuggestion(textNode);
                HTMLNode formNode = textNode.addChild("form", new String[] { 
"action", "method" }, new String[] { "/config/", "post" });
                formNode.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "hidden", "formPassword", 
node.clientCore.formPassword });
                HTMLNode listNode = formNode.addChild("ul", "class", "config");
@@ -60,6 +65,30 @@
                return textNode;
        }

+       private void addPortForwardSuggestion(HTMLNode textNode) {
+               // FIXME we should support any number of ports, UDP or TCP, and 
pick them up from the node as we do with the forwarding plugin ... that would 
be a bit of a pain for L10n though ...
+               int darknetPort = node.getDarknetPortNumber();
+               int opennetPort = node.getOpennetFNPPort();
+               if(opennetPort <= 0) {
+                       textNode.addChild("#", " "+l10n("suggestForwardPort", 
"port", Integer.toString(darknetPort)));
+               } else {
+                       textNode.addChild("#", " 
"+l10n("suggestForwardTwoPorts", new String[] { "port1", "port2" }, 
+                                       new String[] { 
Integer.toString(darknetPort), Integer.toString(opennetPort) }));
+               }
+       }
+
+       private String textPortForwardSuggestion() {
+               // FIXME we should support any number of ports, UDP or TCP, and 
pick them up from the node as we do with the forwarding plugin ... that would 
be a bit of a pain for L10n though ...
+               int darknetPort = node.getDarknetPortNumber();
+               int opennetPort = node.getOpennetFNPPort();
+               if(opennetPort <= 0) {
+                       return l10n("suggestForwardPort", "port", 
Integer.toString(darknetPort));
+               } else {
+                       return " "+l10n("suggestForwardTwoPorts", new String[] 
{ "port1", "port2" }, 
+                                       new String[] { 
Integer.toString(darknetPort), Integer.toString(opennetPort) });
+               }
+       }
+
        public short getPriorityClass() {
                if(node.ipDetector.isDetecting())
                        return UserAlert.WARNING;

Modified: 
branches/freenet-jfk/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
   2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/node/useralerts/MeaningfulNodeNameUserAlert.java
   2007-09-23 09:58:34 UTC (rev 15282)
@@ -44,8 +44,11 @@
                formNode.addChild("input", new String[] { "type", "name", 
"value" }, new String[] { "hidden", "formPassword", 
node.clientCore.formPassword });
                HTMLNode listNode = formNode.addChild("ul", "class", "config");
                HTMLNode itemNode = listNode.addChild("li");
-               itemNode.addChild("span", "class", "configshortdesc", 
o.getShortDesc()).addChild("input", new String[] { "type", "name", "value" }, 
new String[] { "text", sc.getPrefix() + ".name", o.getValueString() });
-               itemNode.addChild("span", "class", "configlongdesc", 
o.getLongDesc());
+               itemNode.addChild("span", new String[]{ "class", "title", 
"style" },
+                               new String[]{ "configshortdesc", 
L10n.getString("ConfigToadlet.defaultIs", new String[] { "default" }, new 
String[] { o.getDefault() }), 
+                               "cursor: help;" 
}).addChild(L10n.getHTMLNode(o.getShortDesc()));
+               itemNode.addChild("input", new String[] { "type", "class", 
"alt", "name", "value" }, new String[] { "text", "config", o.getShortDesc(), 
"node.name", o.getValueString() });
+               itemNode.addChild("span", "class", 
"configlongdesc").addChild(L10n.getHTMLNode(o.getLongDesc()));
                formNode.addChild("input", new String[] { "type", "value" }, 
new String[] { "submit", L10n.getString("UserAlert.apply") });
                formNode.addChild("input", new String[] { "type", "value" }, 
new String[] { "reset", L10n.getString("UserAlert.reset") });


Modified: 
branches/freenet-jfk/src/freenet/node/useralerts/PeerManagerUserAlert.java
===================================================================
--- branches/freenet-jfk/src/freenet/node/useralerts/PeerManagerUserAlert.java  
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/node/useralerts/PeerManagerUserAlert.java  
2007-09-23 09:58:34 UTC (rev 15282)
@@ -15,10 +15,13 @@
        public int neverConn = 0;
        public int clockProblem = 0;
        public int connError = 0;
+       public int disconnDarknetPeers = 0;
        boolean isValid=true;
        int bwlimitDelayTime = 1;
        int nodeAveragePingTime = 1;
        long oldestNeverConnectedPeerAge = 0;
+       public int darknetConns = 0;
+       public int darknetPeers = 0;

        /** How many connected peers we need to not get alert about not enough 
*/
        static final int MIN_CONN_ALERT_THRESHOLD = 3;
@@ -65,7 +68,7 @@
                        return l10n("clockProblemTitle");
                if(connError > MIN_CONN_ERROR_ALERT_THRESHOLD)
                        return l10n("connErrorTitle");
-               if((peers - conns) > MAX_DISCONN_PEER_ALERT_THRESHOLD)
+               if(disconnDarknetPeers > MAX_DISCONN_PEER_ALERT_THRESHOLD)
                        return l10n("tooManyDisconnectedTitle");
                if(conns > MAX_CONN_ALERT_THRESHOLD)
                        return l10n("tooManyConnsTitle");
@@ -116,7 +119,7 @@
                        s = l10n("clockProblem", "count", 
Integer.toString(clockProblem));
                } else if(connError > MIN_CONN_ERROR_ALERT_THRESHOLD) {
                        s = l10n("connError", "count", 
Integer.toString(connError));
-               } else if((peers - conns) > MAX_DISCONN_PEER_ALERT_THRESHOLD){
+               } else if(disconnDarknetPeers > 
MAX_DISCONN_PEER_ALERT_THRESHOLD){
                        s = l10n("tooManyDisconnected", new String[] { "count", 
"max" }, 
                                        new String[] { 
Integer.toString(disconnected), 
Integer.toString(MAX_DISCONN_PEER_ALERT_THRESHOLD)});
                } else if(conns > MAX_CONN_ALERT_THRESHOLD) {
@@ -188,7 +191,7 @@
                        alertNode.addChild("#", l10n("clockProblem", "count", 
Integer.toString(clockProblem)));
                } else if(connError > MIN_CONN_ERROR_ALERT_THRESHOLD) {
                        alertNode.addChild("#", l10n("connError", "count", 
Integer.toString(connError)));
-               } else if ((peers - conns) > MAX_DISCONN_PEER_ALERT_THRESHOLD) {
+               } else if (disconnDarknetPeers > 
MAX_DISCONN_PEER_ALERT_THRESHOLD) {
                        alertNode.addChild("#", l10n("tooManyDisconnected", new 
String[] { "count", "max" }, new String[] { Integer.toString(disconnected), 
Integer.toString(MAX_DISCONN_PEER_ALERT_THRESHOLD)}));
                } else if (conns > MAX_CONN_ALERT_THRESHOLD) {
                        alertNode.addChild("#", l10n("tooManyConns", new 
String[] { "count", "max" }, 
@@ -213,7 +216,7 @@
                if((peers == 0) ||
                                (conns == 0) ||
                                (neverConn > 
MAX_NEVER_CONNECTED_PEER_ALERT_THRESHOLD) ||
-                               ((peers - conns) > 
MAX_DISCONN_PEER_ALERT_THRESHOLD) ||
+                               (disconnDarknetPeers > 
MAX_DISCONN_PEER_ALERT_THRESHOLD) ||
                                (conns > MAX_CONN_ALERT_THRESHOLD) ||
                                (peers > MAX_PEER_ALERT_THRESHOLD) ||
                                (n.bwlimitDelayAlertRelevant && 
(bwlimitDelayTime > NodeStats.MAX_BWLIMIT_DELAY_TIME_ALERT_THRESHOLD)) ||
@@ -230,7 +233,7 @@
                return ((peers == 0) ||
                                (conns < 3) ||
                                (neverConn > 
MAX_NEVER_CONNECTED_PEER_ALERT_THRESHOLD) ||
-                               ((peers - conns) > 
MAX_DISCONN_PEER_ALERT_THRESHOLD) ||
+                               (disconnDarknetPeers > 
MAX_DISCONN_PEER_ALERT_THRESHOLD) ||
                                (conns > MAX_CONN_ALERT_THRESHOLD) ||
                                (peers > MAX_PEER_ALERT_THRESHOLD) ||
                                (clockProblem > 
MIN_CLOCK_PROBLEM_PEER_ALERT_THRESHOLD) ||

Modified: 
branches/freenet-jfk/src/freenet/node/useralerts/UpdatedVersionAvailableUserAlert.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/node/useralerts/UpdatedVersionAvailableUserAlert.java
      2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/node/useralerts/UpdatedVersionAvailableUserAlert.java
      2007-09-23 09:58:34 UTC (rev 15282)
@@ -120,7 +120,7 @@
                                                
sb.append(l10n("fetchingNewNode", "nodeVersion", 
Integer.toString(updater.fetchingNewMainJarVersion())));
                                } else {
                                        if(fetchingNewExt)
-                                               
sb.append(l10n("fetchingNewExt", "extVersion", 
Integer.toString(updater.fetchingNewMainJarVersion())));
+                                               
sb.append(l10n("fetchingNewExt", "extVersion", 
Integer.toString(updater.fetchingNewExtJarVersion())));
                                }
                                sb.append(l10n("updateASAPQuestion"));
                                formText = l10n("updateASAPButton");

Copied: branches/freenet-jfk/src/freenet/pluginmanager/ForwardPort.java (from 
rev 15281, trunk/freenet/src/freenet/pluginmanager/ForwardPort.java)
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/ForwardPort.java             
                (rev 0)
+++ branches/freenet-jfk/src/freenet/pluginmanager/ForwardPort.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -0,0 +1,43 @@
+package freenet.pluginmanager;
+
+/**
+ * A public Internet Protocol port on the node which needs to be forwarded if 
the
+ * node is NATed.
+ * @author toad
+ */
+public class ForwardPort {
+
+       /** Name of the interface e.g. "opennet" */
+       public final String name;
+       /** IPv4 vs IPv6? */
+       public final boolean isIP6;
+       /** Protocol number. See constants. */
+       public final int protocol;
+       public static final int PROTOCOL_UDP_IPV4 = 17;
+       public static final int PROTOCOL_TCP_IPV4 = 6;
+       /** Port number to forward */
+       public final int portNumber;
+       // We don't currently support binding to a specific internal interface.
+       // It would be complicated: Different interfaces may be on different 
LANs,
+       // and an IGD is normally on only one LAN.
+       private final int hashCode;
+       
+       public ForwardPort(String name, boolean isIP6, int protocol, int 
portNumber) {
+               this.name = name;
+               this.isIP6 = isIP6;
+               this.protocol = protocol;
+               this.portNumber = portNumber;
+               hashCode = name.hashCode() | (isIP6 ? 1 : 0) | protocol | 
portNumber;
+       }
+       
+       public int hashCode() {
+               return hashCode;
+       }
+       
+       public boolean equals(Object o) {
+               if(o == this) return true;
+               if(!(o instanceof ForwardPort)) return false;
+               ForwardPort f = (ForwardPort) o;
+               return (f.name.equals(name)) && f.isIP6 == isIP6 && f.protocol 
== protocol && f.portNumber == portNumber;
+       }
+}

Copied: branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortCallback.java 
(from rev 15281, 
trunk/freenet/src/freenet/pluginmanager/ForwardPortCallback.java)
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortCallback.java     
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortCallback.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -0,0 +1,14 @@
+package freenet.pluginmanager;
+
+import java.util.Map;
+
+/**
+ * Callback called by port forwarding plugins to indicate success or failure.
+ * @author toad
+ */
+public interface ForwardPortCallback {
+       
+       /** Called to indicate status on one or more forwarded ports. */
+       public void portForwardStatus(Map /*<ForwardPort,ForwardPortStatus>*/ 
statuses);
+
+}

Copied: branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortStatus.java 
(from rev 15281, trunk/freenet/src/freenet/pluginmanager/ForwardPortStatus.java)
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortStatus.java       
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/pluginmanager/ForwardPortStatus.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -0,0 +1,33 @@
+package freenet.pluginmanager;
+
+public class ForwardPortStatus {
+       
+       public final int status;
+       /** The port forward definitely succeeded. */
+       public static final int DEFINITE_SUCCESS = 3;
+       /** The port forward probably succeeded. I.e. it succeeded unless there 
was
+        * for example hostile action on the part of the router. */
+       public static final int PROBABLE_SUCCESS = 2;
+       /** The port forward may have succeeded. Or it may not have. We should 
+        * definitely try to check out of band. See UP&P: Many routers say 
they've
+        * forwarded the port when they haven't. */
+       public static final int MAYBE_SUCCESS = 1;
+       /** The port forward is in progress */
+       public static final int IN_PROGRESS = 0;
+       /** The port forward probably failed */
+       public static final int PROBABLE_FAILURE = -1;
+       /** The port forward definitely failed. */
+       public static final int DEFINITE_FAILURE = -2;
+       
+       public final String reasonString;
+       
+       /** Some plugins may need to change the external port. They can return 
it
+        * to the node here. */
+       public final int externalPort;
+       
+       public ForwardPortStatus(int status, String reason, int externalPort) {
+               this.status = status;
+               this.reasonString = reason;
+               this.externalPort = externalPort;
+       }
+}

Copied: 
branches/freenet-jfk/src/freenet/pluginmanager/FredPluginPortForward.java (from 
rev 15281, trunk/freenet/src/freenet/pluginmanager/FredPluginPortForward.java)
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/FredPluginPortForward.java   
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/pluginmanager/FredPluginPortForward.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -0,0 +1,21 @@
+package freenet.pluginmanager;
+
+import java.util.Set;
+
+/**
+ * Interface for port forwarding plugins.
+ * @author toad
+ */
+public interface FredPluginPortForward {
+
+       /**
+        * Called when Fred's list of public ports changes, and just after 
loading the
+        * plugin.
+        * @param ports The set of ports that need to be forwarded from the 
outside 
+        * world through the NAT or firewall.
+        * @param cb Callback to be called with success/failure of each 
forward. Some
+        * plugins may return a probabilistic success e.g. with UP&P.
+        */
+       public void onChangePublicPorts(Set/*<ForwardPort>*/ ports, 
ForwardPortCallback cb);
+       
+}

Modified: branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/pluginmanager/PluginHandler.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -20,57 +20,53 @@
         * @param plug
         */
        public static PluginInfoWrapper startPlugin(PluginManager pm, String 
filename, FredPlugin plug, PluginRespirator pr) {
-               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
+               final PluginInfoWrapper pi = new PluginInfoWrapper(plug, 
filename);
+               final PluginStarter ps = new PluginStarter(pr, pi);
                ps.setPlugin(pm, plug);
-               // 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);
+               // We must start the plugin *after startup has finished*
+               Runnable job;
+               if(!pi.isThreadlessPlugin()) {
+                       final Thread t = new Thread(ps);
+                       t.setDaemon(true);
+                       pi.setThread(t);
+                       job = new Runnable() {
+                               public void run() {
+                                       t.start();
+                               }
+                       };
+               } else {
+                       job = ps;
+               }
+               // Run immediately after startup
+               pm.getTicker().queueTimedJob(job, 0);
                return pi;
        }

-       private static class PluginStarter extends Thread {
-               private Object plugin = null;
+       private static class PluginStarter implements Runnable {
+               private FredPlugin plugin = null;
                private PluginRespirator pr;
                private PluginManager pm = null;
+               final PluginInfoWrapper pi;

-               public PluginStarter(PluginRespirator pr) {
+               public PluginStarter(PluginRespirator pr, PluginInfoWrapper pi) 
{
                        this.pr = pr;
-                       setDaemon(true);
+                       this.pi = pi;
                }

-               public void setPlugin(PluginManager pm, Object plugin) {
+               public void setPlugin(PluginManager pm, FredPlugin plugin) {
                        this.plugin = plugin;
                        this.pm = pm;
                }

                public void run() {
-                       int seconds = 120; // give up after 2 min
-                       while (plugin == null) {
-                               // 1s polling
-                               try {
-                                       Thread.sleep(1000);
-                               } catch (InterruptedException e) {
-                               }
-                               if (seconds-- <= 0)
-                                       return;
-                       }
-                       
+                       boolean threadless = plugin instanceof 
FredPluginThreadless;
                        if (plugin instanceof FredPlugin) {
                                try {
+                                       if(!threadless) // Have to do it now 
because threaded
+                                               pm.register(plugin, pi);
                                        ((FredPlugin)plugin).runPlugin(pr);
+                                       if(threadless) // Don't want it to 
receive callbacks until after it has the PluginRespirator, else get NPEs
+                                               pm.register(plugin, pi);
                                } catch (OutOfMemoryError e) {
                                        OOMHandler.handleOOM(e);
                                } catch (Throwable t) {
@@ -78,8 +74,10 @@
                                        System.err.println("Caught Throwable 
while running plugin: "+t);
                                        t.printStackTrace();
                                }
-                               if(!(plugin instanceof FredPluginThreadless))
-                                       pm.removePlugin(this);
+                               if(!threadless) {
+                                       pi.unregister(pm); // If not already 
unregistered
+                                       pm.removePlugin(pi);
+                               }
                        } else {
                                // If not FredPlugin, then the whole thing is 
aborted,
                                // and then this method will return, killing 
the thread

Modified: branches/freenet-jfk/src/freenet/pluginmanager/PluginInfoWrapper.java
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/PluginInfoWrapper.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/pluginmanager/PluginInfoWrapper.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -3,6 +3,7 @@
 import java.util.Date;
 import java.util.HashSet;

+import freenet.support.Logger;
 import freenet.support.StringArray;

 public class PluginInfoWrapper {
@@ -17,25 +18,34 @@
        private boolean isPproxyPlugin;
        private boolean isThreadlessPlugin;
        private boolean isIPDetectorPlugin;
+       private boolean isPortForwardPlugin;
        private String filename;
-       private HashSet toadletLinks=new HashSet(); 
+       private HashSet toadletLinks=new HashSet();
+       private boolean stopping = false;
+       private boolean unregistered = false;
        //public String 

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

+       void setThread(Thread ps) {
+               if(thread != null)
+                       throw new IllegalStateException("Already set a thread");
+               thread = ps;
+               thread.setName(threadName);
+       }
+       
        public String toString() {
                return "ID: \"" +threadName + "\", Name: "+ className +", 
Started: " + (new Date(start)).toString();
        }
@@ -52,35 +62,74 @@
                return plug.getClass().getName();
        }

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

-       public boolean addPluginToadletSymlink(String linkfrom){
-               synchronized (toadletLinks) {
-                       if (toadletLinks.size() < 1)
-                               toadletLinks = new HashSet();
-                       return toadletLinks.add(linkfrom);
-               }
+       public synchronized boolean addPluginToadletSymlink(String linkfrom){
+               if (toadletLinks.size() < 1)
+                       toadletLinks = new HashSet();
+               return toadletLinks.add(linkfrom);
        }

-       public boolean removePluginToadletSymlink(String linkfrom){
-               synchronized (toadletLinks) {
-                       if (toadletLinks.size() < 1)
-                               return false;
-                       return toadletLinks.remove(linkfrom);
-               }
+       public synchronized boolean removePluginToadletSymlink(String linkfrom){
+               if (toadletLinks.size() < 1)
+                       return false;
+               return toadletLinks.remove(linkfrom);
        }

-       public void stopPlugin() {
+       /**
+        * Tell the plugin to quit. Interrupt it if it's a thread-based plugin 
which
+        * might be sleeping. Then call removePlugin() on it on the manager - 
either
+        * now, if it's threadless, or after it terminates, if it's thread 
based.
+        * @param manager The plugin manager object.
+        * @param maxWaitTime If a plugin is thread-based, we can wait for it to
+        * terminate. Set to -1 if you don't want to wait at all, 0 to wait 
forever
+        * or else a value in milliseconds.
+        **/
+       public void stopPlugin(PluginManager manager, int maxWaitTime) {
+               unregister(manager);
                plug.terminate();
-               thread.interrupt();
+               synchronized(this) {
+                       stopping = true;
+               }
+               if(thread != null) {
+                       thread.interrupt();
+                       // Will be removed when the thread exits.
+                       if(maxWaitTime >= 0) {
+                               try {
+                                       thread.join(maxWaitTime);
+                               } catch (InterruptedException e) {
+                                       Logger.normal(this, "stopPlugin 
interrupted while join()ed to terminating plugin thread - maybe one plugin 
stopping another???");
+                               }
+                               if(thread.isAlive()) {
+                                       String error = "Waited for "+thread+" 
for "+plug+" to exit for "+maxWaitTime+"ms, and it is still alive!";
+                                       Logger.error(this, error);
+                                       System.err.println(error);
+                                       // Dump the thread? Would require 
post-1.4 features...
+                               }
+                       }
+               } else {
+                       // Remove immediately
+                       manager.removePlugin(this);
+               }
        }

-       public boolean sameThread(Thread t){
-               return (t == thread);
+       /**
+        * Unregister the plugin from any user interface or other callbacks it 
may be
+        * registered with. Call this before manager.removePlugin(): the plugin 
becomes
+        * unvisitable immediately, but it may take time for it to shut down 
completely.
+        */
+       void unregister(PluginManager manager) {
+               synchronized(this) {
+                       if(unregistered) return;
+                       unregistered = true;
+               }
+               manager.unregisterPluginToadlet(this);
+               if(isIPDetectorPlugin)
+                       
manager.node.ipDetector.unregisterIPDetectorPlugin((FredPluginIPDetector)plug);
+               if(isPortForwardPlugin)
+                       
manager.node.ipDetector.unregisterPortForwardPlugin((FredPluginPortForward)plug);
        }

        public boolean isPproxyPlugin() {
@@ -99,8 +148,11 @@
                return isIPDetectorPlugin;
        }

-       public Thread getThread() {
-               return thread;
+       public boolean isPortForwardPlugin() {
+               return isPortForwardPlugin;
        }

+       public synchronized boolean isStopping() {
+               return stopping;
+       }
 }

Modified: branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java
===================================================================
--- branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/pluginmanager/PluginManager.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -4,13 +4,10 @@
 package freenet.pluginmanager;

 import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.JarURLConnection;
-import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -34,7 +31,6 @@
 import freenet.support.URIPreEncoder;
 import freenet.support.api.HTTPRequest;
 import freenet.support.api.StringArrCallback;
-import freenet.support.io.FileUtil;

 public class PluginManager {

@@ -48,17 +44,17 @@
         *
         */

-       private HashMap toadletList;
-       private HashMap pluginInfo;
+       private final HashMap toadletList;
+       private final Vector/*<PluginInfoWrapper>*/ pluginWrappers;
        private PluginRespirator pluginRespirator = null;
-       private final Node node;
+       final Node node;
        private final NodeClientCore core;
        SubConfig pmconfig;
        private boolean logMINOR;

        public PluginManager(Node node) {
-               pluginInfo = new HashMap();
                toadletList = new HashMap();
+               pluginWrappers = new Vector();
                this.node = node;
                this.core = node.clientCore;
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
@@ -68,15 +64,15 @@
                // Start plugins in the config
                pmconfig.register("loadplugin", null, 9, true, false, 
"PluginManager.loadedOnStartup", "PluginManager.loadedOnStartupLong",
                                new StringArrCallback() {
-                                       public String[] get() {
-                                               return getConfigLoadString();
-                                       }
-                                       public void set(String[] val) throws 
InvalidConfigValueException {
-                                               //if(storeDir.equals(new 
File(val))) return;
-                                               // FIXME
-                                               throw new 
InvalidConfigValueException(L10n.getString("PluginManager.cannotSetOnceLoaded"));
-                                       }
-                               });
+                       public String[] get() {
+                               return getConfigLoadString();
+                       }
+                       public void set(String[] val) throws 
InvalidConfigValueException {
+                               //if(storeDir.equals(new File(val))) return;
+                               // FIXME
+                               throw new 
InvalidConfigValueException(L10n.getString("PluginManager.cannotSetOnceLoaded"));
+                       }
+               });

                String fns[] = pmconfig.getStringArr("loadplugin");
                if (fns != null) {
@@ -93,7 +89,7 @@
                  for (int i = 0 ; i < fns.length ; i++)
                  System.err.println("Load: " + StringArrOption.decode(fns[i]));
                  System.err.println("=================================");
-                 */
+                */
        }

        private String[] getConfigLoadString() {
@@ -120,18 +116,9 @@
                try {
                        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 (pluginWrappers) {
+                               pluginWrappers.add(pi);
                        }
-
-                       synchronized (pluginInfo) {
-                               pluginInfo.put(pi.getThreadName(), pi);
-                       }
                        Logger.normal(this, "Plugin loaded: " + filename);
                } catch (PluginNotFoundException e) {
                        Logger.normal(this, "Loading plugin failed (" + 
filename + ')', e);
@@ -144,14 +131,28 @@
                                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();
        }

+       void register(FredPlugin plug, PluginInfoWrapper pi) {
+               // handles FProxy? If so, register
+
+               if (pi.isPproxyPlugin())
+                       registerToadlet(plug);
+
+               if(pi.isIPDetectorPlugin()) {
+                       
node.ipDetector.registerIPDetectorPlugin((FredPluginIPDetector) plug);
+               }
+               if(pi.isPortForwardPlugin()) {
+                       
node.ipDetector.registerPortForwardPlugin((FredPluginPortForward) plug);
+               }
+       }
+
        private String l10n(String key, String pattern, String value) {
                return L10n.getString("PluginManager."+key, pattern, value);
        }
@@ -164,32 +165,26 @@
                Logger.normal(this, "Added HTTP handler for 
/plugins/"+pl.getClass().getName()+ '/');
        }

-       public void removePlugin(Thread t) {
-               Object removeKey = null;
-               synchronized (pluginInfo) {
-                       Iterator it = pluginInfo.keySet().iterator();
-                       while (it.hasNext() && (removeKey == null)) {
-                               Object key = it.next();
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginInfo.get(key);
-                               if (pi.sameThread(t)) {
-                                       removeKey = key;
-                                       synchronized (toadletList) {
-                                               try {
-                                                       
toadletList.remove(pi.getPluginClassName());
-                                                       Logger.normal(this, 
"Removed HTTP handler for /plugins/"+
-                                                                       
pi.getPluginClassName()+ '/', new Exception("debug"));
-                                               } catch (Throwable ex) {
-                                                       Logger.error(this, 
"removing Plugin", ex);
-                                               }
-                                       }
-                               }
-                       }
+       /**
+        * Remove a plugin from the plugin list.
+        */
+       public void removePlugin(PluginInfoWrapper pi) {
+               synchronized (pluginWrappers) {
+                       if(!pluginWrappers.remove(pi)) return;
+               }
+               core.storeConfig();
+       }

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

        public void addToadletSymlinks(PluginInfoWrapper pi) {
@@ -233,10 +228,9 @@

        public String dumpPlugins() {
                StringBuffer out= new StringBuffer();
-               synchronized (pluginInfo) {
-                       Iterator it = pluginInfo.keySet().iterator();
-                       while (it.hasNext()) {
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginInfo.get(it.next());
+               synchronized (pluginWrappers) {
+                       for(int i=0;i<pluginWrappers.size();i++) {
+                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginWrappers.get(i);
                                out.append(pi.toString());
                                out.append('\n');
                        }
@@ -246,10 +240,9 @@

        public Set getPlugins() {
                HashSet out = new HashSet();
-               synchronized (pluginInfo) {
-                       Iterator it = pluginInfo.keySet().iterator();
-                       while (it.hasNext()) {
-                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginInfo.get(it.next());
+               synchronized (pluginWrappers) {
+                       for(int i=0;i<pluginWrappers.size();i++) {
+                               PluginInfoWrapper pi = (PluginInfoWrapper) 
pluginWrappers.get(i);
                                out.add(pi);
                        }
                }
@@ -263,7 +256,7 @@
                }
                /*if (handler == null)
                  return null;
-                 */
+                */

                if (handler instanceof FredPluginHTTP)
                        return ((FredPluginHTTP)handler).handleHTTPGet(request);
@@ -278,7 +271,7 @@
                }
                /*if (handler == null)
                  return null;
-                 */
+                */

                if (handler instanceof FredPluginHTTP)
                        return 
((FredPluginHTTP)handler).handleHTTPPost(request);
@@ -286,22 +279,19 @@
                throw new NotFoundPluginHTTPException("Plugin not found!", 
"/plugins");
        }

-       public void killPlugin(String name) {
+       public void killPlugin(String name, int maxWaitTime) {
                PluginInfoWrapper pi = null;
                boolean found = false;
-               synchronized (pluginInfo) {
-                       Iterator it = pluginInfo.keySet().iterator();
-                       while (it.hasNext() && !found) {
-                               pi = (PluginInfoWrapper) 
pluginInfo.get(it.next());
+               synchronized (pluginWrappers) {
+                       for(int i=0;i<pluginWrappers.size() && !found;i++) {
+                               pi = (PluginInfoWrapper) pluginWrappers.get(i);
                                if (pi.getThreadName().equals(name))
                                        found = true;
                        }
                }
-               if (found)
-                       if (pi.isThreadlessPlugin())
-                               removePlugin(pi.getThread());
-                       else
-                               pi.stopPlugin();
+               if (found) {
+                       pi.stopPlugin(this, maxWaitTime);
+               }
        }


@@ -315,189 +305,170 @@
         * @return                      An instanciated object of the plugin
         * @throws PluginNotFoundException      If anything goes wrong.
         */
-       private FredPlugin LoadPlugin(String filename) throws 
PluginNotFoundException {
+       private FredPlugin LoadPlugin(String origFilename)
+       throws PluginNotFoundException {
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                Class cls = null;
-               if (filename.endsWith("*")) {
-                       filename = 
"*@http://downloads.freenetproject.org/alpha/plugins/"; +
-                               filename.substring(filename.lastIndexOf(".")+1, 
filename.length()-1) +
-                               ".jar.url";
-                       //System.out.println(filename);
-                       if(logMINOR) Logger.minor(this, "Rewritten to 
"+filename);
-               } if (filename.endsWith("#")) {
-                       if(filename.indexOf('@') > -1) {
-                               Logger.error(this, "We don't allow downloads 
from anywhere else but our server");
-                               return null;
+               for (int tries = 0; (tries <= 5) && (cls == null); tries++) {
+                       String filename = origFilename;
+                       if (filename.endsWith("*")) {
+                               filename = 
"*@http://downloads.freenetproject.org/alpha/plugins/";
+                                               + 
filename.substring(filename.lastIndexOf(".") + 1,
+                                                               
filename.length() - 1) + ".jar.url";
+                               if (logMINOR)
+                                       Logger.minor(this, "Rewritten to " + 
filename);
                        }
-                       String pluginname = filename.substring(0, 
filename.length()-1);
-                       filename = null;
-
-                       URL url;
-                       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();
-                               
-                               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;
-                               }
+                               BufferedReader in = null;
+                               InputStream is = null;
+                               if ((filename.indexOf("@") >= 0)) {
+                                       boolean assumeURLRedirect = true;
+                                       // Open from external file
+                                       try {
+                                               String realURL = null;
+                                               String realClass = 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;
-                       } catch (FileNotFoundException e) {
-                               Logger.error(this, "FileNotFoundException has 
occured : "+ e, e);
-                               return null;
-                       } catch (IOException ioe) {
-                               System.out.println("Caught :"+ioe.getMessage());
-                               ioe.printStackTrace();
-                               return null;
-                       } finally {
-                               try {
-                                       if(is != null) is.close();
-                               } catch (IOException ioe) {}
-                       }
-                       if(filename == null)
-                               return null;
-               }
+                                               // Load the jar-file
+                                               String[] parts = 
filename.split("@");
+                                               if (parts.length != 2) {
+                                                       throw new 
PluginNotFoundException(
+                                                       "Could not split at 
\"@\".");
+                                               }
+                                               realClass = parts[0];
+                                               realURL = parts[1];
+                                               if (logMINOR)
+                                                       Logger.minor(this, 
"Class: " + realClass + " URL: "
+                                                                       + 
realURL);

-               BufferedReader in = null;
-               InputStream is = null;
-               if ((filename.indexOf("@") >= 0)) {
-                       boolean assumeURLRedirect = true;
-                       // Open from external file
-                       for (int tries = 0 ; (tries <= 5) && (cls == null) ; 
tries++)
-                               try {
-                                       String realURL = null;
-                                       String realClass = null;
+                                               if (filename.endsWith(".url")) {
+                                                       if (!assumeURLRedirect) 
{
+                                                               // Load the 
txt-file
+                                                               URL url = new 
URL(parts[1]);
+                                                               URLConnection 
uc = url.openConnection();
+                                                               in = new 
BufferedReader(new InputStreamReader(
+                                                                               
uc.getInputStream()));

-                                       // Load the jar-file
-                                       String[] parts = filename.split("@");
-                                       if (parts.length != 2) {
-                                               throw new 
PluginNotFoundException("Could not split at \"@\".");
-                                       }
-                                       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
-                                                       URL url = new 
URL(parts[1]);
-                                                       URLConnection uc = 
url.openConnection();
-                                                       in = new BufferedReader(
-                                                                       new 
InputStreamReader(uc.getInputStream()));
-
-                                                       realURL = in.readLine();
-                                                       if(realURL == null)
-                                                               throw new 
PluginNotFoundException("Initialization error: " + url +
-                                                                               
" isn't a plugin loading url!");
-                                                       realURL = 
realURL.trim();
-                                                       if(logMINOR) 
Logger.minor(this, "Loaded new URL: "+realURL+" from .url file");
-                                                       in.close();
+                                                               realURL = 
in.readLine();
+                                                               if (realURL == 
null)
+                                                                       throw 
new PluginNotFoundException(
+                                                                               
        "Initialization error: "
+                                                                               
        + url
+                                                                               
        + " isn't a plugin loading url!");
+                                                               realURL = 
realURL.trim();
+                                                               if (logMINOR)
+                                                                       
Logger.minor(this, "Loaded new URL: "
+                                                                               
        + realURL + " from .url file");
+                                                               in.close();
+                                                       }
+                                                       assumeURLRedirect = 
!assumeURLRedirect;
                                                }
-                                               assumeURLRedirect = 
!assumeURLRedirect;
-                                       }

-                                       // Load the class inside file
-                                       URL[] serverURLs = new URL[]{new 
URL(realURL)};
-                                       ClassLoader cl = new 
URLClassLoader(serverURLs);
+                                               // 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("*")) {

-                                       // 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();

-                                               // 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...
+                                                       
jarConnection.setUseCaches(false);
+                                                       JarFile jf = 
jarConnection.getJarFile();
+                                                       // URLJarFile jf = new 
URLJarFile(new File(liburi));
+                                                       // is =
+                                                       // 
jf.getInputStream(jf.getJarEntry("META-INF/MANIFEST.MF"));

-                                               URL url = new 
URL("jar:"+realURL+"!/");
-                                               JarURLConnection jarConnection 
= (JarURLConnection)url.openConnection();
-                                               // Java seems to cache even 
file: urls...
-                                               
jarConnection.setUseCaches(false);
-                                               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")));

-                                               //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();

-                                               //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;
-                                               while ((line = 
in.readLine())!=null) {
-                                                       //      
System.err.println(line + "\t\t\t" + realClass);
-                                                       if 
(line.startsWith("Plugin-Main-Class: ")) {
-                                                               realClass = 
line.substring("Plugin-Main-Class: ".length()).trim();
-                                                               if(logMINOR) 
Logger.minor(this, "Found plugin main class "+realClass+" from manifest");
+                                                       is = 
jf.getInputStream(jf
+                                                                       
.getJarEntry("META-INF/MANIFEST.MF"));
+                                                       in = new 
BufferedReader(new InputStreamReader(is));
+                                                       String line;
+                                                       while ((line = 
in.readLine()) != null) {
+                                                               // 
System.err.println(line + "\t\t\t" +
+                                                               // realClass);
+                                                               if 
(line.startsWith("Plugin-Main-Class: ")) {
+                                                                       
realClass = line.substring(
+                                                                               
        "Plugin-Main-Class: ".length())
+                                                                               
        .trim();
+                                                                       if 
(logMINOR)
+                                                                               
Logger.minor(this,
+                                                                               
                "Found plugin main class "
+                                                                               
                + realClass
+                                                                               
                + " from manifest");
+                                                               }
                                                        }
+                                                       // 
System.err.println("Real classname: " +
+                                                       // realClass);
                                                }
-                                               //System.err.println("Real 
classname: " + realClass);
-                                       }

-                                       cls = cl.loadClass(realClass);
+                                               cls = cl.loadClass(realClass);

-                               } catch (Exception e) {
-                                       if (tries >= 5)
-                                               throw new 
PluginNotFoundException("Initialization error:"
-                                                               + filename, e);
-
+                                       } finally {
+                                               try {
+                                                       if (is != null)
+                                                               is.close();
+                                                       if (in != null)
+                                                               in.close();
+                                               } catch (IOException ioe) {
+                                               }
+                                       }
+                               } else {
+                                       // Load class
                                        try {
-                                               Thread.sleep(100);
-                                       } catch (Exception ee) {}
-                               } finally {
-                                       try {
-                                               if(is != null)
-                                                       is.close();
-                                               if(in != null)
-                                                       in.close();
-                                       } catch (IOException ioe){}
+                                               cls = Class.forName(filename);
+                                       } catch (ClassNotFoundException e) {
+                                               throw new 
PluginNotFoundException(filename);
+                                       }
                                }
-               } else {
-                       // Load class
-                       try {
-                               cls = Class.forName(filename);
-                       } catch (ClassNotFoundException e) {
-                               throw new PluginNotFoundException(filename);
+
+                               if (cls == null)
+                                       throw new 
PluginNotFoundException("Unknown error");
+                       } catch (Exception e) {
+                               Logger.normal(this, "Failed to load plugin " + 
filename + " : "
+                                               + e, e);
+                               if (tries >= 5)
+                                       throw new 
PluginNotFoundException("Initialization error:"
+                                                       + filename, e);
+
+                               try {
+                                       Thread.sleep(100);
+                               } catch (Exception ee) {
+                               }
                        }
                }

-               if(cls == null)
-                       throw new PluginNotFoundException("Unknown error");
-
                // Class loaded... Objectize it!
                Object o = null;
                try {
                        o = cls.newInstance();
                } catch (Exception e) {
-                       throw new PluginNotFoundException("Could not re-create 
plugin:" +
-                                       filename, e);
+                       throw new PluginNotFoundException("Could not re-create 
plugin:"
+                                       + origFilename, e);
                }

                // See if we have the right type
                if (!(o instanceof FredPlugin)) {
-                       throw new PluginNotFoundException("Not a plugin: " + 
filename);
+                       throw new PluginNotFoundException("Not a plugin: " + 
origFilename);
                }

-               return (FredPlugin)o;
+               return (FredPlugin) o;
        }
-       
+
        Ticker getTicker() {
                return node.getTicker();
        }

Modified: branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java
===================================================================
--- branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java  
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/store/BerkeleyDBFreenetStore.java  
2007-09-23 09:58:34 UTC (rev 15282)
@@ -305,6 +305,7 @@
                secDbConfig.setKeyCreator(accessTimeKeyCreator);
                try {
                try {
+                       System.err.println("Opening access times database for 
"+prefix);
                        atime = environment.openSecondaryDatabase
                                                                (null, 
prefix+"CHK_accessTime", chkDB, secDbConfig);
                        // The below is too slow to be useful, because 
SecondaryDatabase.count() isn't optimised.
@@ -316,7 +317,7 @@
 //                             throw new DatabaseException("Needs 
repopulation");
 //                     }
                } catch (DatabaseException e) {
-                       
WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 
5*60*1000+chkDB.count()*100)));
+                       
WrapperManager.signalStarting((int)(Math.min(Integer.MAX_VALUE, 
5*60*1000L+chkDB.count()*100L)));
                        // Of course it's not a solution but a quick fix
                        // Integer.MAX_VALUE seems to trigger an overflow or 
whatever ...
                        // Either we find out what the maximum value is and we 
do a static method somewhere ensuring
@@ -1794,8 +1795,8 @@
        private void checkSecondaryDatabaseError(Throwable ex) {
                String msg = ex.getMessage();
                if(ex instanceof DatabaseException) {
-                       if(msg != null && (msg.indexOf("missing key in the 
primary database") > -1) ||
-                                       msg.indexOf("the primary record 
contains a key that is not present in the secondary") > -1) {
+                       if(msg != null && ((msg.indexOf("missing key in the 
primary database") > -1) ||
+                                       msg.indexOf("the primary record 
contains a key that is not present in the secondary") > -1)) {
                                try {
                                        fixSecondaryFile.createNewFile();
                                } catch (IOException e) {

Modified: branches/freenet-jfk/src/freenet/support/CPUInformation/CPUID.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/CPUInformation/CPUID.java  
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/CPUInformation/CPUID.java  
2007-09-23 09:58:34 UTC (rev 15282)
@@ -233,15 +233,26 @@
                                                return "Athlon (Barton)";
                                }
                        }
-                       if(getCPUFamily() == 15){
-                               if(getCPUExtendedFamily() == 0){
+                       if(getCPUFamily() == 15){ // Must check Extended Family
+                               if(getCPUExtendedFamily() == 0){ // AMD K8
+                                       // This just tells us socket type and 
chip die technology
+                                       // see BrandID both the ID and NN 
portions
+                                       // If you need to determine a specific 
chip brand
                                        switch(getCPUModel()){
                                                case 4:
                                                        return "Athlon 64";
                                                case 5:
                                                        return "Athlon 64 FX 
Opteron";
+                                               case 7:
+                                                       return "Athlon 64 (0.13 
um 939)";
+                                               case 8:
+                                                       return "Athlon 64 (0.13 
um 754)";
+                                               case 11:
+                                                       return "Athlon 64 (0.13 
um 939)";
                                                case 12:
-                                                       return "AMD Athlon(tm) 
64 Processor 3000+";
+                                                       return "Athlon 64 (0.13 
um 754)";
+                                               case 15:
+                                                       return "Athlon 64 (0.13 
um 939)";
                                        }
                                }
                        }
@@ -373,6 +384,7 @@
                System.out.println("**CPUInfo**");
                System.out.println("CPU Vendor: " + getCPUVendorID());
                System.out.println("CPU Family: " + getCPUFamily());
+               System.out.println("CPU Extended Family: " + 
getCPUExtendedFamily());
                System.out.println("CPU Model: " + getCPUModel());
                System.out.println("CPU Stepping: " + getCPUStepping());
                System.out.println("CPU Flags: " + getCPUFlags());

Modified: branches/freenet-jfk/src/freenet/support/HTMLNode.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/HTMLNode.java      2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/HTMLNode.java      2007-09-23 
09:58:34 UTC (rev 15282)
@@ -8,8 +8,12 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;

 public class HTMLNode {
+       
+       private static final Pattern namePattern = 
Pattern.compile("^[a-zA-Z][a-zA-Z_0-9]+$");

        protected final String name;

@@ -36,13 +40,18 @@
        }

        public HTMLNode(String name, String[] attributeNames, String[] 
attributeValues, String content) {
+               
+               Matcher nameMatcher = namePattern.matcher(name);
+               
+               assert nameMatcher.matches();
+               
                this.name = name.toLowerCase(Locale.ENGLISH);
                if ((attributeNames != null) && (attributeValues != null)) {
                        if (attributeNames.length != attributeValues.length) {
                                throw new IllegalArgumentException("attribute 
names and values differ");
                        }
                        for (int attributeIndex = 0, attributeCount = 
attributeNames.length; attributeIndex < attributeCount; attributeIndex++) {
-                               attributes.put(attributeNames[attributeIndex], 
attributeValues[attributeIndex]);
+                               addAttribute(attributeNames[attributeIndex], 
attributeValues[attributeIndex]);
                        }
                }
                if (content != null && !name.equals("#") && !name.equals("%")) {
@@ -60,6 +69,10 @@
        }

        public void addAttribute(String attributeName, String attributeValue) {
+               if (attributeName == null)
+                       throw new IllegalArgumentException("Cannot add an 
attribute with a null name");
+               if (attributeValue == null)
+                       throw new IllegalArgumentException("Cannot add an 
attribute with a null value");
                attributes.put(attributeName, attributeValue);
        }


Modified: branches/freenet-jfk/src/freenet/support/LRUQueue.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/LRUQueue.java      2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/LRUQueue.java      2007-09-23 
09:58:34 UTC (rev 15282)
@@ -106,14 +106,27 @@
         }
     }

+    /**
+     * Return the objects in the queue as an array in an arbitrary and 
meaningless
+     * order.
+     */
        public synchronized Object[] toArray() {
                return hash.keySet().toArray();
        }

+    /**
+     * Return the objects in the queue as an array in an arbitrary and 
meaningless
+     * order.
+        * @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[] toArray(Object[] array) {
                return hash.keySet().toArray(array);
        }

+       /**
+        * Return the objects in the queue as an array. The least recently used 
object
+        * is in [0], the most recently used object is in [array.length-1].
+        */
        public synchronized Object[] toArrayOrdered() {
                Object[] array = new Object[list.size()];
                int x = 0;
@@ -124,6 +137,8 @@
        }

        /**
+        * Return the objects in the queue as an array. The least recently used 
object
+        * is in [0], the most recently used object is in [array.length-1].
         * @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) {

Modified: branches/freenet-jfk/src/freenet/support/Logger.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/Logger.java        2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/Logger.java        2007-09-23 
09:58:34 UTC (rev 15282)
@@ -1,5 +1,12 @@
 package freenet.support;

+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.regex.PatternSyntaxException;
+
 import freenet.support.LoggerHook.InvalidThresholdException;

 /**
@@ -8,6 +15,182 @@
 */
 public abstract class Logger {

+       public final static class OSThread {
+               
+               public static boolean getPIDEnabled = false;
+               public static boolean getPPIDEnabled = false;
+               public static boolean logToFileEnabled = false;
+               public static int logToFileVerbosity = DEBUG;
+               public static boolean logToStdOutEnabled = false;
+               public static boolean procSelfStatEnabled = false;
+       
+               /**
+                * Get the thread's process ID or return -1 if it's unavailable 
for some reason
+                */
+               public synchronized static int getPID(Object o) {
+                       if(!getPIDEnabled) {
+                               return -1;
+                       }
+                       return getPIDFromProcSelfStat(o);
+               }
+       
+               /**
+                * Get the thread's parent process ID or return -1 if it's 
unavailable for some reason
+                */
+               public synchronized static int getPPID(Object o) {
+                       if(!getPPIDEnabled) {
+                               return -1;
+                       }
+                       return getPPIDFromProcSelfStat(o);
+               }
+       
+               /**
+                * Get a specified field from /proc/self/stat or return null if
+                * it's unavailable for some reason.
+                */
+               public synchronized static String getFieldFromProcSelfStat(int 
fieldNumber, Object o) {
+                       String readLine = null;
+       
+                       if(!procSelfStatEnabled) {
+                               return null;
+                       }
+       
+                       // read /proc/self/stat and parse for the specified 
field
+                       BufferedReader br = null;
+                       FileReader fr = null;
+                       File procFile = new File("/proc/self/stat");
+                       if(procFile.exists()) {
+                               try {
+                                       fr = new FileReader(procFile);
+                                       br = new BufferedReader(fr);
+                               } catch (FileNotFoundException e1) {
+                                       logStatic(o, "'/proc/self/stat' not 
found", logToFileVerbosity);
+                                       procSelfStatEnabled = false;
+                                       fr = null;
+                               }
+                               if(null != br) {
+                                       try {
+                                               readLine = br.readLine();
+                                       } catch (IOException e) {
+                                               error(o, "Caught IOException in 
br.readLine() of OSThread.getFieldFromProcSelfStat()", e);
+                                               readLine = null;
+                                       }
+                                       if(null != readLine) {
+                                               try {
+                                                       String[] procFields = 
readLine.trim().split(" ");
+                                                       if(4 <= 
procFields.length) {
+                                                               return 
procFields[ fieldNumber ];
+                                                       }
+                                               } catch (PatternSyntaxException 
e) {
+                                                       error(o, "Caught 
PatternSyntaxException in readLine.trim().split(\" \") of 
OSThread.getFieldFromProcSelfStat() while parsing '"+readLine+"'", e);
+                                               }
+                                       }
+                               }
+                       }
+                       return null;
+               }
+       
+               /**
+                * Get the thread's process ID using the /proc/self/stat method 
or
+                * return -1 if it's unavailable for some reason.  This is an 
ugly
+                * hack required by Java to get the OS process ID of a thread on
+                * Linux without using JNI.
+                */
+               public synchronized static int getPIDFromProcSelfStat(Object o) 
{
+                       int pid = -1;
+       
+                       if(!getPIDEnabled) {
+                               return -1;
+                       }
+                       if(!procSelfStatEnabled) {
+                               return -1;
+                       }
+                       String pidString = getFieldFromProcSelfStat(0, o);
+                       if(null == pidString) {
+                               return -1;
+                       }
+                       try {
+                               pid = Integer.parseInt( pidString.trim() );
+                       } catch (NumberFormatException e) {
+                               error(o, "Caught NumberFormatException in 
Integer.parseInt() of OSThread.getPIDFromProcSelfStat() while parsing 
'"+pidString+"'", e);
+                       }
+                       return pid;
+               }
+       
+               /**
+                * Get the thread's parent process ID using the /proc/self/stat
+                * method or return -1 if it's unavailable for some reason.  
This
+                * is ugly hack required by Java to get the OS parent process 
ID of
+                * a thread on Linux without using JNI.
+                */
+               public synchronized static int getPPIDFromProcSelfStat(Object 
o) {
+                       int ppid = -1;
+       
+                       if(!getPPIDEnabled) {
+                               return -1;
+                       }
+                       if(!procSelfStatEnabled) {
+                               return -1;
+                       }
+                       String ppidString = getFieldFromProcSelfStat(3, o);
+                       if(null == ppidString) {
+                               return -1;
+                       }
+                       try {
+                               ppid = Integer.parseInt( ppidString.trim() );
+                       } catch (NumberFormatException e) {
+                               error(o, "Caught NumberFormatException in 
Integer.parseInt() of OSThread.getPPIDFromProcSelfStat() while parsing 
'"+ppidString+"'", e);
+                       }
+                       return ppid;
+               }
+       
+               /**
+                * Log the thread's process ID or return -1 if it's unavailable 
for some reason
+                */
+               public synchronized static int logPID(Object o) {
+                       if(!getPIDEnabled) {
+                               return -1;
+                       }
+                       int pid = getPID(o);
+                       String msg;
+                       if(-1 != pid) {
+                               msg = "This thread's OS PID is " + pid;
+                       } else {
+                               msg = "This thread's OS PID could not be 
determined";
+                       }
+                       if(logToStdOutEnabled) {
+                               System.out.println(msg + ": " + o);
+                       }
+                       if(logToFileEnabled) {
+                               logStatic(o, msg, logToFileVerbosity);
+                       }
+                       return pid;
+               }
+       
+               /**
+                * Log the thread's process ID or return -1 if it's unavailable 
for some reason
+                */
+               public synchronized static int logPPID(Object o) {
+                       if(!getPPIDEnabled) {
+                               return -1;
+                       }
+                       int ppid = getPPID(o);
+                       String msg;
+                       if(-1 != ppid) {
+                               msg = "This thread's OS PPID is " + ppid;
+                       } else {
+                               msg = "This thread's OS PPID could not be 
determined";
+                       }
+                       if(logToStdOutEnabled) {
+                               System.out.println(msg + ": " + o);
+                       }
+                       if(logToFileEnabled) {
+                               logStatic(o, msg, logToFileVerbosity);
+                       }
+                       return ppid;
+               }
+       }
+
        /** These indicate the verbosity levels for calls to log() * */

        /** This message indicates an error which prevents correct 
functionality* */

Copied: branches/freenet-jfk/src/freenet/support/OSThread.java (from rev 15281, 
trunk/freenet/src/freenet/support/OSThread.java)
===================================================================

Modified: branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArray.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArray.java       
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArray.java       
2007-09-23 09:58:34 UTC (rev 15282)
@@ -40,6 +40,8 @@
                if(logMINOR)
                        Logger.minor(this, "Adding "+item+" to RGA "+rga+" for 
"+client);
                rga.add(item);
+               if(logMINOR)
+                       Logger.minor(this, "Size now "+grabArrays.length+" on 
"+this);
        }

        /**
@@ -69,7 +71,7 @@
                        int x = rand.nextInt(grabArrays.length);
                        RemoveRandomWithObject rga = grabArrays[x];
                        if(logMINOR)
-                               Logger.minor(this, "Picked "+x+" of 
"+grabArrays.length+" : "+rga+" : "+rga.getObject());
+                               Logger.minor(this, "Picked "+x+" of 
"+grabArrays.length+" : "+rga+" : "+rga.getObject()+" on "+this);
                        RandomGrabArrayItem item = rga.removeRandom();
                        if(logMINOR)
                                Logger.minor(this, "RGA has picked 
"+x+"/"+grabArrays.length+": "+item+

Modified: 
branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArrayWithObject.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArrayWithObject.java 
    2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/support/SectoredRandomGrabArrayWithObject.java 
    2007-09-23 09:58:34 UTC (rev 15282)
@@ -15,4 +15,8 @@
                return object;
        }

+       public String toString() {
+               return super.toString()+":"+object;
+       }
+       
 }

Modified: branches/freenet-jfk/src/freenet/support/TimeSortedHashtable.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/TimeSortedHashtable.java   
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/TimeSortedHashtable.java   
2007-09-23 09:58:34 UTC (rev 15282)
@@ -32,7 +32,7 @@
        private class MyComparator implements Comparator {

                public int compare(Object arg0, Object arg1) {
-                       if(arg0 instanceof Long && arg1 instanceof Long) return 
((Long)arg0).compareTo(arg1);
+                       if(arg0 instanceof Long && arg1 instanceof Long) return 
((Long)arg0).compareTo((Long)arg1);
                        if(arg0 instanceof Element && arg1 instanceof Element) 
return ((Element)arg0).compareTo(arg1);
                        // Comparing a Long with an Element, because we are 
searching for an Element by the value of a Long.
                        // Hence we do not need to consider the element value.

Modified: branches/freenet-jfk/src/freenet/support/URLEncoder.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/URLEncoder.java    2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/URLEncoder.java    2007-09-23 
09:58:34 UTC (rev 15282)
@@ -19,7 +19,9 @@
         *
         * @param  URL  String to encode
         * @param force List of characters (in the form of a string) which must 
be encoded as well as the built-in.
-        * @return      HTML-safe version of string
+        * @param ascii If true, encode all foreign letters, if false, leave 
them as is. Set to true if you are
+        * passing to something that needs ASCII (e.g. HTTP headers), set to 
false if you are using in an HTML page.
+        * @return      Encoded version of string
         */
        public final static String encode(String URL, String force, boolean 
ascii) {
                StringBuffer enc = new StringBuffer(URL.length());
@@ -48,7 +50,15 @@
                return enc.toString();
        }

-       public static String encode(String s) {
+       /**
+        * Encode a string for inclusion in a URI.
+        *
+        * @param  URL  String to encode
+        * @param ascii If true, encode all foreign letters, if false, leave 
them as is. Set to true if you are
+        * passing to something that needs ASCII (e.g. HTTP headers), set to 
false if you are using in an HTML page.
+        * @return      Encoded version of string
+        */
+       public static String encode(String s, boolean ascii) {
                return encode(s, null, false);
        }


Modified: branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/io/BaseFileBucket.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -413,7 +413,7 @@
                File file = getFile();
                if ((deleteOnFree() || forceFree) && file.exists()) {
                        Logger.debug(this,
-                               "Deleting bucket " + file.getName());
+                               "Deleting bucket " + file.getName(), new 
Exception("debug"));
                        deleteFile();
                        if (file.exists())
                                Logger.error(this,

Modified: branches/freenet-jfk/src/freenet/support/io/FileUtil.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/FileUtil.java   2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/io/FileUtil.java   2007-09-23 
09:58:34 UTC (rev 15282)
@@ -15,6 +15,7 @@
 import java.io.InputStreamReader;

 import freenet.client.DefaultMIMETypes;
+import freenet.support.Logger;

 final public class FileUtil {

@@ -76,7 +77,6 @@
                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;
@@ -86,7 +86,7 @@
                try {
                        fis = new FileInputStream(file);
                        bis = new BufferedInputStream(fis);
-                       isr = new InputStreamReader(bis);
+                       isr = new InputStreamReader(bis, "UTF-8");

                        char[] buf = new char[4096];
                        int length = 0;
@@ -106,33 +106,34 @@
        }

        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");
+               File file = File.createTempFile("temp", ".tmp", 
target.getParentFile());
+               if(Logger.shouldLog(Logger.MINOR, FileUtil.class))
+                       Logger.minor(FileUtil.class, "Writing to "+file+" to be 
renamed to "+target);

                try {
-                       bis = new BufferedInputStream(input);
-                       dis = new DataInputStream(bis);
+                       dis = new DataInputStream(input);
                        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);
+                               fos.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);
+               if(file.renameTo(target))
+                       return true;
+               else {
+                       file.delete();
+                       return false;
+               }
        }

        public static String sanitize(String s) {

Modified: 
branches/freenet-jfk/src/freenet/support/io/LineReadingInputStream.java
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/LineReadingInputStream.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/src/freenet/support/io/LineReadingInputStream.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -20,10 +20,14 @@

        /**
         * Read a \n or \r\n terminated line of UTF-8 or ISO-8859-1.
+        * @param maxLength The maximum length of a line. If a line is longer 
than this, we throw IOException rather
+        * than keeping on reading it forever.
+        * @param bufferSize The initial size of the read buffer.
+        * @param utf If true, read as UTF-8, if false, read as ISO-8859-1.
         */
        public String readLine(int maxLength, int bufferSize, boolean utf) 
throws IOException {
                if(maxLength < bufferSize)
-                       bufferSize = maxLength;
+                       bufferSize = maxLength + 1; // Buffer too big, shrink 
it (add 1 for the optional \r)
                if(buf == null)
                        buf = new byte[Math.max(Math.min(128,maxLength), 
Math.min(1024, bufferSize))];
                int ctr = 0;

Copied: branches/freenet-jfk/src/freenet/support/io/NullBucketFactory.java 
(from rev 15281, trunk/freenet/src/freenet/support/io/NullBucketFactory.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/io/NullBucketFactory.java          
                (rev 0)
+++ branches/freenet-jfk/src/freenet/support/io/NullBucketFactory.java  
2007-09-23 09:58:34 UTC (rev 15282)
@@ -0,0 +1,14 @@
+package freenet.support.io;
+
+import java.io.IOException;
+
+import freenet.support.api.Bucket;
+import freenet.support.api.BucketFactory;
+
+public class NullBucketFactory implements BucketFactory {
+
+       public Bucket makeBucket(long size) throws IOException {
+               return new NullBucket();
+       }
+
+}

Modified: 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
   2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/support/io/PaddedEphemerallyEncryptedBucket.java
   2007-09-23 09:58:34 UTC (rev 15282)
@@ -32,8 +32,6 @@
        private SoftReference /* <Rijndael> */ aesRef;
        /** The decryption key. */
        private final byte[] key;
-       /** Broken (old) encryption? */
-       private final boolean brokenEncryption;
        private long dataLength;
        private boolean readOnly;
        private int lastOutputStream;
@@ -50,7 +48,6 @@
                this.origRandom = origRandom;
                this.bucket = bucket;
                if(bucket.size() != 0) throw new 
IllegalArgumentException("Bucket must be empty");
-               brokenEncryption = false;
                byte[] tempKey = new byte[32];
                origRandom.nextBytes(tempKey);
                this.key = tempKey;
@@ -70,13 +67,12 @@
         * @param origRandom
         * @throws IOException 
         */
-       public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize, 
long knownSize, byte[] key, RandomSource origRandom, boolean oldCrypto) throws 
IOException {
+       public PaddedEphemerallyEncryptedBucket(Bucket bucket, int minSize, 
long knownSize, byte[] key, RandomSource origRandom) throws IOException {
                if(bucket.size() < knownSize)
                        throw new IOException("Bucket "+bucket+" is too small 
on disk - knownSize="+knownSize+" but bucket.size="+bucket.size()+" for 
"+bucket);
                this.dataLength = knownSize;
                this.origRandom = origRandom;
                this.bucket = bucket;
-               brokenEncryption = oldCrypto;
                if(key.length != 32) throw new IllegalArgumentException("Key 
wrong length: "+key.length);
                this.key = key;
                this.minPaddedSize = minSize;
@@ -101,7 +97,6 @@
                tmp = fs.get("DecryptKey");
                if(tmp == null)
                        throw new CannotCreateFromFieldSetException("No key");
-               brokenEncryption = fs.get("CryptoType") == null;
                key = HexUtil.hexToBytes(tmp);
                if(key.length != 32) throw new IllegalArgumentException("Key 
wrong length: "+key.length);
                tmp = fs.get("MinPaddedSize");
@@ -298,7 +293,7 @@
                        if(aes != null) return aes;
                }
                try {
-                       aes = new Rijndael(256, 256, false);
+                       aes = new Rijndael(256, 256);
                } catch (UnsupportedCipherException e) {
                        throw new Error(e);
                }
@@ -364,8 +359,6 @@
                        return null;
                }
                fs.put("MinPaddedSize", minPaddedSize);
-               if(!brokenEncryption)
-                       fs.putSingle("CryptoType", "aes256");
                return fs;
        }


Modified: 
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
   2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
   2007-09-23 09:58:34 UTC (rev 15282)
@@ -47,7 +47,7 @@
                                FileBucket fb = new FileBucket(fnam, false, 
true, false, false, true);
                                try {
                                        PaddedEphemerallyEncryptedBucket eb = 
-                                               new 
PaddedEphemerallyEncryptedBucket(fb, 1024, len, decryptKey, random, true);
+                                               new 
PaddedEphemerallyEncryptedBucket(fb, 1024, len, decryptKey, random);
                                        return eb;
                                } catch (IOException e) {
                                        throw new 
CannotCreateFromFieldSetException("Cannot create from old-format fieldset: "+e, 
e);

Copied: 
branches/freenet-jfk/src/freenet/support/transport/ip/HostnameSyntaxException.java
 (from rev 15281, 
trunk/freenet/src/freenet/support/transport/ip/HostnameSyntaxException.java)
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/transport/ip/HostnameSyntaxException.java
                          (rev 0)
+++ 
branches/freenet-jfk/src/freenet/support/transport/ip/HostnameSyntaxException.java
  2007-09-23 09:58:34 UTC (rev 15282)
@@ -0,0 +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. */
+package freenet.support.transport.ip;
+
+/**
+ * Thrown to indicate an invalid DNS hostname syntax.
+ */
+public class HostnameSyntaxException extends Exception {
+       private static final long serialVersionUID = -1;
+}

Copied: branches/freenet-jfk/src/freenet/support/transport/ip/HostnameUtil.java 
(from rev 15281, 
trunk/freenet/src/freenet/support/transport/ip/HostnameUtil.java)
===================================================================
--- branches/freenet-jfk/src/freenet/support/transport/ip/HostnameUtil.java     
                        (rev 0)
+++ branches/freenet-jfk/src/freenet/support/transport/ip/HostnameUtil.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -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.support.transport.ip;
+
+import freenet.io.AddressIdentifier;
+import freenet.support.Logger;
+
+public class HostnameUtil {
+
+       public static boolean isValidHostname(String hn, boolean 
allowIPAddress) {
+               if(allowIPAddress) {    
+                       // debugging log messages because AddressIdentifier 
doesn't appear to handle all IPv6 literals correctly, such as 
"fe80::204:1234:dead:beef"
+                       AddressIdentifier.AddressType addressType = 
AddressIdentifier.getAddressType(hn, true);
+                       Logger.debug(null, "Address type of '"+hn+"' appears to 
be '"+addressType+ '\'');
+                       if(!addressType.toString().equals("Other")) {
+                               // the address typer thinks it's either an IPv4 
or IPv6 IP address
+                               return true;
+                       }
+               }
+               // NOTE: It is believed that this code supports PUNYCODE based
+               //       ASCII Compatible Encoding (ACE) IDNA labels as
+               //       described in RFC3490.  Such an assertion has not be
+               //       thoroughly tested.
+               
if(!hn.matches("(?:[-!#\\$%&'\\*+\\\\/0-9=?A-Z^_`a-z{|}]+\\.)+[a-zA-Z]{2,6}")) {
+                       System.err.println("Failed to match "+hn+" as a 
hostname or IPv4/IPv6 IP address");
+                       return false;
+               }
+               return true;
+       }
+
+}

Modified: 
branches/freenet-jfk/src/freenet/support/transport/ip/IPAddressDetector.java
===================================================================
--- 
branches/freenet-jfk/src/freenet/support/transport/ip/IPAddressDetector.java    
    2007-09-23 08:04:32 UTC (rev 15281)
+++ 
branches/freenet-jfk/src/freenet/support/transport/ip/IPAddressDetector.java    
    2007-09-23 09:58:34 UTC (rev 15282)
@@ -75,9 +75,6 @@
                Enumeration interfaces = null;
                try {
                        interfaces = 
java.net.NetworkInterface.getNetworkInterfaces();
-               } catch (NoClassDefFoundError e) {
-                       addrs.add(oldDetect());
-                       old = true;
                } catch (SocketException e) {
                        Logger.error(
                                this,
@@ -211,6 +208,7 @@
        }

        public void run() {
+               freenet.support.Logger.OSThread.logPID(this);
                while(true) {
                        try {
                                Thread.sleep(interval);

Copied: branches/freenet-jfk/test/freenet/crypt/DSATest.java (from rev 15281, 
trunk/freenet/test/freenet/crypt/DSATest.java)
===================================================================
--- branches/freenet-jfk/test/freenet/crypt/DSATest.java                        
        (rev 0)
+++ branches/freenet-jfk/test/freenet/crypt/DSATest.java        2007-09-23 
09:58:34 UTC (rev 15282)
@@ -0,0 +1,243 @@
+/*
+ * 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.crypt;
+
+import java.math.BigInteger;
+import junit.framework.TestCase;
+import net.i2p.util.NativeBigInteger;
+
+/**
+ * Test case for {@link freenet.crypt.DSA} class.
+ *
+ * @author Alberto Bacchelli &lt;sback at freenetproject.org&gt;
+ */
+public class DSATest extends TestCase{
+    
+    
/*-------------FIPS-EXAMPLE-CONSTANTS---------------------------------------
+     * These are the values as they appear in the Appendix 5
+     * "Example of the DSA" of FIPS PUB 186-2.
+     * We can consider them sure examples */
+    private static final BigInteger FIPS_P = new NativeBigInteger(
+                                
"8df2a494492276aa3d25759bb06869cbeac0d83afb8d0cf7"+
+                                
"cbb8324f0d7882e5d0762fc5b7210eafc2e9adac32ab7aac"+
+                                "49693dfbf83724c2ec0736ee31c80291",16);
+    private static final BigInteger FIPS_Q = new NativeBigInteger(
+                                "c773218c737ec8ee993b4f2ded30f48edace915f",16);
+    private static final BigInteger FIPS_G = new NativeBigInteger(
+                                
"626d027839ea0a13413163a55b4cb500299d5522956cefcb"+
+                                
"3bff10f399ce2c2e71cb9de5fa24babf58e5b79521925c9c"+
+                                "c42e9f6f464b088cc572af53e6d78802",16);
+    private static final BigInteger FIPS_X = new NativeBigInteger(
+                                "2070b3223dba372fde1c0ffc7b2e3b498b260614",16);
+    private static final BigInteger FIPS_Y = new NativeBigInteger(
+                                
"19131871d75b1612a819f29d78d1b0d7346f7aa77bb62a85"+
+                                
"9bfd6c5675da9d212d3a36ef1672ef660b8c7c255cc0ec74"+
+                                "858fba33f44c06699630a76b030ee333",16);
+    private static final BigInteger FIPS_K = new NativeBigInteger(
+                                "358dad571462710f50e254cf1a376b2bdeaadfbf",16);
+    private static final BigInteger FIPS_K_INV = new NativeBigInteger(
+                                "0d5167298202e49b4116ac104fc3f415ae52f917",16);
+    private static final BigInteger FIPS_SHA1_M = new NativeBigInteger(
+                                "a9993e364706816aba3e25717850c26c9cd0d89d",16);
+    private static final BigInteger FIPS_R = new NativeBigInteger(
+                                "8bac1ab66410435cb7181f95b16ab97c92b341c0",16);
+    private static final BigInteger FIPS_S = new NativeBigInteger(
+                                "41e2345f1f56df2458f426d155b4ba2db6dcd8c8",16);
+    private static final DSAGroup FIPS_DSA_GROUP = 
+                    new DSAGroup(FIPS_P,FIPS_Q,FIPS_G);
+    private static final DSAPrivateKey FIPS_DSA_PRIVATE_KEY = 
+                    new DSAPrivateKey(FIPS_X);
+    private static final DSAPublicKey FIPS_DSA_PUBLIC_KEY =
+                    new DSAPublicKey(FIPS_DSA_GROUP,FIPS_Y);
+    private static final DSASignature FIPS_DSA_SIGNATURE = 
+                    new DSASignature(FIPS_R,FIPS_S);
+    
/*------------------------------------------------------------------------*/
+    
+    private RandomSource randomSource;
+    
+    public DSATest(String testName) { 
+        super(testName); 
+    }
+
+    protected void setUp() throws Exception {
+        randomSource = new DummyRandomSource();
+    }
+
+    protected void tearDown() throws Exception {}
+    
+    /**
+     * Test of verify and sign method consistency using FIPS examples.*/
+    public void testSignAndVerify() {
+        DSASignature aSignature = 
+                DSA.sign(FIPS_DSA_GROUP, FIPS_DSA_PRIVATE_KEY, FIPS_K, 
FIPS_SHA1_M, randomSource);
+        
+        
assertTrue(DSA.verify(FIPS_DSA_PUBLIC_KEY,aSignature,FIPS_SHA1_M,false));
+    }
+    
+    /** Test of verify(DSAPublicKey kp,
+                       DSASignature sig,
+                       BigInteger m, boolean forceMod)
+     * method comparing it with the DSA values
+     * based on FIPS examples */
+    public void testVerify() {
+        
assertTrue(DSA.verify(FIPS_DSA_PUBLIC_KEY,FIPS_DSA_SIGNATURE,FIPS_SHA1_M,false));
+    }
+    
+    /**
+     * Test sign method consistency
+     * It performs two signature of the same message
+     * and verifies if they are identical */
+    public void testSameSignConsistency() {
+        DSASignature firstSignature = 
+                DSA.sign(FIPS_DSA_GROUP, FIPS_DSA_PRIVATE_KEY, FIPS_K, 
FIPS_SHA1_M, randomSource);
+        
+        DSASignature secondSignature = 
+                DSA.sign(FIPS_DSA_GROUP, FIPS_DSA_PRIVATE_KEY, FIPS_K, 
FIPS_SHA1_M, randomSource);
+        
+        assertEquals(firstSignature.getR(),secondSignature.getR());
+        assertEquals(firstSignature.getS(),secondSignature.getS());
+    }
+
+    /**
+     * Test sign(DSAGroup g, DSAPrivateKey x, BigInteger m,RandomSource r)
+     * method, using a q value that is too small [shorter than 
DSAGroup.Q_BIT_LENGTH]
+     * to generate a correct k value */
+    public void testSignSmallQValue(){
+        try {
+            
DSA.sign(FIPS_DSA_GROUP,FIPS_DSA_PRIVATE_KEY,FIPS_SHA1_M,randomSource);
+            fail("Exception Error Not Thrown!");
+        } catch (IllegalArgumentException anException) {
+            assertNotNull(anException);
+        }
+    }
+    
+    /**
+     * Test sign(DSAGroup g, DSAPrivateKey x,
+                       BigInteger r, BigInteger kInv, 
+                       BigInteger m, RandomSource random)
+     * method comparing it with the DSASignature
+     * based on FIPS examples */
+    public void testSign_grp_pvtKey_r_kInv_m_rand() {
+        DSASignature aSignature =
+                
DSA.sign(FIPS_DSA_GROUP,FIPS_DSA_PRIVATE_KEY,FIPS_R,FIPS_K_INV,FIPS_SHA1_M,randomSource);
+        
+        assertEquals(aSignature.getR(),FIPS_R);
+        assertEquals(aSignature.getS(),FIPS_S);
+    }
+    
+    /**
+     * Test sign(DSAGroup g,
+                       DSAPrivateKey x,
+                       BigInteger k, 
+                       BigInteger m,
+                       RandomSource random)
+     * method comparing it with the DSASignature
+     * based on FIPS examples */
+    public void testSign_grp_pvtKey_k_m_rand() {
+        BigInteger x = FIPS_X;
+        DSAPrivateKey aTestPrivateKey;
+        DSASignature anotherSignature;
+        
+        DSASignature aSignature =
+                
DSA.sign(FIPS_DSA_GROUP,FIPS_DSA_PRIVATE_KEY,FIPS_K,FIPS_SHA1_M,randomSource);
+        
+        BigInteger generatedR = aSignature.getR();
+        BigInteger generatedS = aSignature.getS();
+        
+        assertEquals(generatedR,FIPS_R);
+        assertEquals(generatedS,FIPS_S);
+        
+        
+        for(int i = 0; i <= FIPS_X.bitCount(); i++) {
+            x = x.flipBit(i);
+            if (x.compareTo(FIPS_X) != 0) {
+                aTestPrivateKey = new DSAPrivateKey(x);
+                anotherSignature = 
DSA.sign(FIPS_DSA_GROUP,aTestPrivateKey,FIPS_K,FIPS_SHA1_M,randomSource);
+                assertFalse(generatedR.compareTo(anotherSignature.getR()) == 0 
&& 
+                            generatedS.compareTo(anotherSignature.getS()) == 
0);
+            }
+        }
+    }
+    
+    
+    /**
+     * Test sign(DSAGroup g, DSAPrivateKey x, BigInteger m,
+                       RandomSource r)
+     * method using verify method.
+     * As the verify test passed, then we can consider it 
+     * a tested way to verify this sign method accuracy, 
+     * since it is impossible to test it using
+     * FIPS Examples [as they are based on SHA1 and thus they use
+     * q values that are shorter than current DSAGroup.Q_BIT_LENGTH].*/
+    public void testSign_grp_pvtKey_m_rand() {
+        DSAGroup aDSAgroup = Global.DSAgroupBigA;
+        
+        DSAPrivateKey aDSAPrivKey=new DSAPrivateKey(aDSAgroup,randomSource);
+        DSAPublicKey aDSAPubKey=new DSAPublicKey(aDSAgroup,aDSAPrivKey);
+        DSASignature aSignature=
+                
DSA.sign(aDSAgroup,aDSAPrivKey,aDSAPrivKey.getX(),randomSource);
+        
+        assertTrue(DSA.verify(aDSAPubKey,aSignature,aDSAPrivKey.getX(),false));
+    }
+    
+    /* The following tests still generates problem,
+     * they are commented so they could be useful to
+     * check for bugs
+     */
+    
+    /*
+    public void testSign_border() {
+        BigInteger k = BigInteger.ONE;
+        BigInteger q = Global.DSAgroupBigA.getQ().add(BigInteger.ONE);
+        BigInteger p = q;
+        BigInteger g = p.add(BigInteger.ONE);
+        
+        DSAGroup aDSAgroup = new DSAGroup(p,q,g);
+        
+        DSAPrivateKey aDSAPrivKey=new DSAPrivateKey(aDSAgroup,randomSource);
+        DSAPublicKey aDSAPubKey=new DSAPublicKey(aDSAgroup,aDSAPrivKey);
+               DSASignature aSignature=
+                DSA.sign(aDSAgroup,aDSAPrivKey,k,BigInteger.TEN,randomSource);
+        
+        System.out.println(aSignature.toLongString());
+        System.out.println(aDSAPrivKey.toLongString());
+        System.out.println(aDSAPubKey.toLongString());
+        
+        assertTrue(DSA.verify(aDSAPubKey,aSignature,BigInteger.TEN,false));
+    }
+    
+    /**
+     * Test sign(DSAGroup g, DSAPrivateKey x, BigInteger m,
+                       RandomSource r)
+     * method in a not valid case (i.e. *
+    public void testSign_border2() {
+        System.out.println("sign(grp,pvtKey,m,rand)");
+        
+        BigInteger q = BigInteger.ONE;
+        //BigInteger p = q;
+        //BigInteger g = (p.multiply(BigInteger.ONE)).add(BigInteger.ONE);
+        
+        DSAGroup aDSAgroup = new DSAGroup(Global.DSAgroupBigA.getP(),
+                                          q,Global.DSAgroupBigA.getG());
+        
+        DSAPrivateKey aDSAPrivKey=new DSAPrivateKey(aDSAgroup,randomSource);
+        DSAPublicKey aDSAPubKey=new DSAPublicKey(aDSAgroup,aDSAPrivKey);
+               DSASignature aSignature=
+                
DSA.sign(aDSAgroup,aDSAPrivKey,aDSAPrivKey.getX(),randomSource);
+        
+        assertTrue(DSA.verify(aDSAPubKey,aSignature,aDSAPrivKey.getX(),false));
+    } */
+}
\ No newline at end of file

Modified: branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java
===================================================================
--- branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java 2007-09-23 
08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/test/freenet/support/HTMLNodeTest.java 2007-09-23 
09:58:34 UTC (rev 15282)
@@ -15,6 +15,8 @@
  */
 package freenet.support;

+import java.util.List;
+
 import junit.framework.TestCase;

 /**
@@ -26,16 +28,16 @@

        private HTMLNode exampleNode;

-       //example node name that includes a not ASCII char [greek alpha]
+       //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]
+       //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]
+       //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]
+       //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 {
@@ -44,7 +46,7 @@
        }

        /**
-        * Test HTMLNode(String,String,String,String) constructor
+        * Tests HTMLNode(String,String,String,String) constructor
         * using non-ASCII chars
         */
        public void testNotAsciiHTMLNode_StringStringStringString() {
@@ -57,6 +59,29 @@
        }

        /**
+        * Tests HTMLNode(String,String[],String[],String) constructor
+        * verifying if all attributes are correctly inserted
+        */
+       public void testHTMLNode_AttributesArray() {
+               int size = 100;
+               String[] methodAttributesName = new String[size];
+               String[] methodAttributesValue = new String[size];
+               for (int i=0;i<size;i++) {
+                       methodAttributesName[i] = "AttributeName " + i;
+                       methodAttributesValue[i] = "Value " + i;
+               }
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME,
+                               methodAttributesName,methodAttributesValue,
+                               SAMPLE_NODE_CONTENT);
+               //checks presence
+               for(int i=0;i<size;i++)
+                       assertEquals(methodAttributesValue[i],
+                                       
methodHTMLNode.getAttribute(methodAttributesName[i]));
+               //checks size
+               assertEquals(size,methodHTMLNode.getAttributes().size());
+       }
+       
+       /**
         * Tests addAttribute(String,String) method
         * adding the same attribute many times
         * and verifying it keeps only one
@@ -148,6 +173,87 @@
        }

        /**
+        * Tests addChildren(String,String,String) method
+        * verifying if the child is correctly added
+        * and if it generates good output using generate() method.
+        */
+       public void testAddChild_StringStringString() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               methodHTMLNode.addChild(SAMPLE_NODE_NAME, 
+                               SAMPLE_ATTRIBUTE_NAME, SAMPLE_ATTRIBUTE_VALUE);
+               List childrenList = methodHTMLNode.children;
+               assertEquals(1,childrenList.size());
+               assertEquals(generateNoContentNodeOutput(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE),
+                               ((HTMLNode)childrenList.get(0)).generate());
+       }
+       
+       /**
+        * Tests addChildren(String,String,String,String) method
+        * verifying if the child is correctly added
+        * and if it generates good output using generate() method.
+        */
+       public void testAddChild_StringStringStringString() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               methodHTMLNode.addChild(SAMPLE_NODE_NAME, 
+                               SAMPLE_ATTRIBUTE_NAME, SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_NODE_CONTENT);
+               List childrenList = methodHTMLNode.children;
+               assertEquals(1,childrenList.size());
+               assertEquals(generateFullNodeOutput(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME, SAMPLE_ATTRIBUTE_VALUE, 
+                               SAMPLE_NODE_CONTENT),
+                                       
((HTMLNode)childrenList.get(0)).generate());
+       }
+       
+       /**
+        * Tests addChildren(String,String[],String[]) method
+        * verifying if the child is correctly added
+        * and the child attributes are corrects.
+        */
+       public void testAddChild_StringArrayArray() {
+               String[] methodAttributesNamesArray = 
{"firstName","secondName","thirdName"};
+               String[] methodAttributesValuesArray = 
{"firstValue","secondValue","thirdValue"};
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               methodHTMLNode.addChild(SAMPLE_NODE_NAME, 
+                               methodAttributesNamesArray, 
methodAttributesValuesArray);
+               testSingleChildAttributes(methodHTMLNode, 
+                               methodAttributesNamesArray, 
methodAttributesValuesArray);
+       }
+       
+       /**
+        * Tests addChildren(String,String[],String[],String) method
+        * verifying if the child is correctly added
+        * and the child attributes are corrects.
+        */
+       public void testAddChild_StringArrayArrayString() {
+               String[] methodAttributesNamesArray = 
{"firstName","secondName","thirdName"};
+               String[] methodAttributesValuesArray = 
{"firstValue","secondValue","thirdValue"};
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               methodHTMLNode.addChild(SAMPLE_NODE_NAME, 
+                               methodAttributesNamesArray, 
methodAttributesValuesArray,
+                               SAMPLE_NODE_CONTENT);
+               testSingleChildAttributes(methodHTMLNode, 
+                               methodAttributesNamesArray, 
methodAttributesValuesArray);
+       }
+       
+       /**
+        * Check the passed HTMLNode only child attributes
+        * @param aHTMLNode where we fetch the only child
+        * @param attibutesNames the attributes names to check
+        * @param attributesValues the attributes values to check
+        */
+       private void testSingleChildAttributes(HTMLNode aHTMLNode,String[] 
attibutesNames, String[] attributesValues) {
+               List childrenList = aHTMLNode.children;
+               assertEquals(1,childrenList.size());
+               HTMLNode childHTMLNode = (HTMLNode)childrenList.get(0);
+               
assertEquals(attibutesNames.length,childHTMLNode.getAttributes().size());
+               for(int i = 0 ; i<attibutesNames.length;i++)
+                       assertEquals(attributesValues[i],
+                                       
childHTMLNode.getAttribute(attibutesNames[i]));
+       }
+       
+       /**
         * Tests getContent() method using
         * common sample HTMLNode, and "#"
         * "%" named nodes
@@ -172,6 +278,175 @@
        }

        /**
+        * Tests getAttribute() method using
+        * common sample HTMLNode, and "#"
+        * "%" named nodes
+        */
+       public void testGetAttribute() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               assertNull(methodHTMLNode.getAttribute(SAMPLE_ATTRIBUTE_NAME));
+               
+               methodHTMLNode = new 
HTMLNode(SAMPLE_NODE_NAME,SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE);
+               
assertEquals(SAMPLE_ATTRIBUTE_VALUE,methodHTMLNode.getAttribute(SAMPLE_ATTRIBUTE_NAME));
+               methodHTMLNode = new 
HTMLNode("#",SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE);
+               
assertEquals(SAMPLE_ATTRIBUTE_VALUE,methodHTMLNode.getAttribute(SAMPLE_ATTRIBUTE_NAME));
+               methodHTMLNode = new 
HTMLNode("%",SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE);
+               
assertEquals(SAMPLE_ATTRIBUTE_VALUE,methodHTMLNode.getAttribute(SAMPLE_ATTRIBUTE_NAME));
+       }
+       
+       /**
+        * Tests getAttributes() and setAttribute(String,String)
+        * methods verifying if attributes are correctly
+        * inserted and fetched.
+        */
+       public void testAddGetAttributes() {
+               int attributesNumber = 100;
+               String methodAttributeName = "";
+               String counterString = "";
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               for (int i=0; i<attributesNumber; i++) {
+                       counterString = String.valueOf(i);
+                       methodAttributeName = "attribute " + counterString; 
+                       assertEquals(i,methodHTMLNode.getAttributes().size());
+                       
methodHTMLNode.addAttribute(methodAttributeName,counterString);
+                       
assertEquals(counterString,methodHTMLNode.getAttribute(methodAttributeName));
+                       
assertEquals(counterString,methodHTMLNode.getAttributes().get(methodAttributeName));
+               }
+       }
+       
+       /**
+        * Tests addAttribute(String,String) method
+        * trying to insert an attribute with a null
+        * as name value. It should rise an
+        * IllegalArgument exception 
+        */
+       public void testAddAttribute_nullAttributeName() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               try {
+                       
methodHTMLNode.addAttribute(null,SAMPLE_ATTRIBUTE_VALUE);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests addAttribute(String,String) method
+        * trying to insert an attribute with a null
+        * as attribute value. It should rise an
+        * IllegalArgument exception 
+        */
+       public void testAddAttribute_nullAttributeValue() {
+               HTMLNode methodHTMLNode = new HTMLNode(SAMPLE_NODE_NAME);
+               try {
+                       methodHTMLNode.addAttribute(SAMPLE_ATTRIBUTE_NAME,null);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests HTMLNode(String,String,String,String) and
+        * HTMLNode(String,String,String) constructors 
+        * trying to create a node that has attribute name 
+        * null. It should raise an IllegalArgument exception
+        */
+       public void testHTMLNode_nullAttributeName() {
+               try {
+                       new HTMLNode(SAMPLE_NODE_NAME,
+                                       null,SAMPLE_ATTRIBUTE_VALUE,
+                                       SAMPLE_NODE_CONTENT);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+               try {
+                       new HTMLNode(SAMPLE_NODE_NAME,
+                                       null,SAMPLE_ATTRIBUTE_VALUE);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests HTMLNode(String,String,String,String) and
+        * HTMLNode(String,String,String) constructors 
+        * trying to create a node that has attribute value 
+        * null. It should raise an IllegalArgument exception
+        */
+       public void testHTMLNode_nullAttributeValue() {
+               try {
+                       new HTMLNode(SAMPLE_NODE_NAME,
+                                       SAMPLE_ATTRIBUTE_NAME,null,
+                                       SAMPLE_NODE_CONTENT);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+               try {
+                       new HTMLNode(SAMPLE_NODE_NAME,
+                                       SAMPLE_ATTRIBUTE_NAME,null);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
+        * Tests HTMLNode(String,String[],String[],String) 
+        * constructor trying to create a node that has
+        * attributes name null. It should raise an
+        * IllegalArgument exception
+        */
+       public void testHTMLNodeArray_nullAttributeName() {
+               String[] methodAttributesNameArray = {"first",null,"after"};
+               String[] methodAttributesValueArray = {SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_ATTRIBUTE_VALUE,SAMPLE_ATTRIBUTE_VALUE};
+               testHTMLNodeArray_null(methodAttributesNameArray, 
methodAttributesValueArray);
+       }
+       
+       /**
+        * Tests HTMLNode(String,String[],String[],String) 
+        * constructor trying to create a node that has
+        * attributes value null. It should raise an
+        * IllegalArgument exception
+        */
+       public void testHTMLNodeArray_nullAttributeValue() {
+               String[] methodAttributesNameArray = {SAMPLE_ATTRIBUTE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_NAME};
+               String[] methodAttributesValueArray = {"first",null,"after"};
+               testHTMLNodeArray_null(methodAttributesNameArray, 
methodAttributesValueArray);
+       }
+       
+       /**
+        * Tests HTMLNode(String,String[],String[],String) 
+        * constructor trying to create a node that has
+        * different length for attributes names array and
+        * attributes values array. It should raise an
+        * IllegalArgument exception
+        */
+       public void testHTMLNode_attributeArrays_differentLengths() {
+               String[] methodAttributesNameArray = {SAMPLE_ATTRIBUTE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME};
+               String[] methodAttributesValueArray = {SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_ATTRIBUTE_VALUE,SAMPLE_ATTRIBUTE_VALUE};
+               testHTMLNodeArray_null(methodAttributesNameArray, 
methodAttributesValueArray);
+       }
+       
+       /**
+        * Tests if the passed arrays raise an IllegalArgumentException
+        * using them to create a new HTMLNode (i.e. one of the name or value
+        * must be null)
+        * @param attributesNames the array of attribute names
+        * @param attributesValues the array of attribute values
+        */
+       private void testHTMLNodeArray_null(String[] attributesNames, String[] 
attributesValues) {
+               try {
+                       new HTMLNode(SAMPLE_NODE_NAME,
+                                       attributesNames,attributesValues,
+                                       SAMPLE_NODE_CONTENT);
+                       fail("Expected Exception Error Not Thrown!"); } 
+               catch (IllegalArgumentException anException) {
+                       assertNotNull(anException); }
+       }
+       
+       /**
         * Fetches the first line of a String
         * @param aString the String to consider
         * @return the first line of the String
@@ -185,6 +460,58 @@

        /**
         * Tests generate() method with a
+        * HTMLNode that has "textarea","div","a"
+        * as node name, since they generates a different
+        * output from all other names.
+        */
+       public void testGenerate_fromHTMLNode_textareaDivA() {
+               HTMLNode methodHTMLNode;
+               String[] nodeNamesArray = {"textarea","div","a"};
+               for(int i=0;i<nodeNamesArray.length;i++) {
+                       methodHTMLNode = new HTMLNode(nodeNamesArray[i],
+                                       
SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE);
+                       assertEquals(generateFullNodeOutput(nodeNamesArray[i], 
+                                       
SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,""),
+                                       methodHTMLNode.generate());
+               }       
+       }
+       
+       /**
+        * Tests generate() method when the
+        * node has a special name
+        * (i.e. "div","form","input","script","table","tr","td")
+        * and a child
+        */
+       public void testGenerate_fromHTMLNodeWithChild_SpecialNames() {
+               HTMLNode methodHTMLNode;
+               String[] nodeNamesArray = {"div","form","input",
+                               "script","table","tr","td"};
+               HTMLNode methodChildNode = new HTMLNode(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_NODE_CONTENT);
+               for(int i=0;i<nodeNamesArray.length;i++) {
+                       methodHTMLNode = new HTMLNode(nodeNamesArray[i],
+                                       
SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
+                                       SAMPLE_NODE_CONTENT);
+                       methodHTMLNode.addChild(methodChildNode);
+                       
+                       assertEquals(("<"+nodeNamesArray[i]+" ").toLowerCase() 
+ 
+                                        SAMPLE_ATTRIBUTE_NAME + "=" +
+                                        "\""+SAMPLE_ATTRIBUTE_VALUE+"\">" + 
'\n' +
+                                        SAMPLE_NODE_CONTENT +
+                                        
+                                        //child
+                                        
generateFullNodeOutput(SAMPLE_NODE_NAME,
+                                                               
SAMPLE_ATTRIBUTE_NAME, SAMPLE_ATTRIBUTE_VALUE, 
+                                                               
SAMPLE_NODE_CONTENT) +
+                                        
+                                        
("</"+nodeNamesArray[i]+">").toLowerCase() + '\n',
+                                        methodHTMLNode.generate());
+               }
+       }
+       
+       /**
+        * Tests generate() method with a
         * HTMLNode with only the name.
         * The resulting string should be in the form:
         * <node_name />
@@ -218,10 +545,8 @@
        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+"\""+
-                                        " />",
+               assertEquals(generateNoContentNodeOutput(SAMPLE_NODE_NAME,
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE),
                                        methodHTMLNode.generate());
        }

@@ -243,6 +568,22 @@

        /**
         * Generates the correct output for the HTMLNode.generate() method
+        * when called from a single node having only a name and an attribute
+        * name and value
+        * @param aName the HTMLNode name
+        * @param aAttributeName the HTMLNode attribute name
+        * @param aAttributeValue the HTMLNode attribute value
+        * @return the correct output expected by HTMLNode.generate() method
+        */
+       private String generateNoContentNodeOutput(String aName, String 
aAttributeName, String aAttributeValue) {
+               return ("<"+aName+" ").toLowerCase() + 
+               aAttributeName + "=" +
+                "\""+aAttributeValue+"\""+
+                " />";
+       }
+       
+       /**
+        * 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
@@ -290,6 +631,19 @@
        }

        /**
+        * Tests generate() method with a
+        * HTMLNode that has "%" as name.
+        * The expected output is just the HTMLNode content
+        */
+       public void testGenerate_fromHTMLNode_percentName() {
+               HTMLNode methodHTMLNode = new HTMLNode("%",
+                               SAMPLE_ATTRIBUTE_NAME,SAMPLE_ATTRIBUTE_VALUE,
+                               SAMPLE_NODE_CONTENT);
+               assertEquals(SAMPLE_NODE_CONTENT,
+                               methodHTMLNode.generate());
+       }
+       
+       /**
         * Tests HTMLDoctype.generate() method
         * comparing the result with the expected
         * String. It is useful for regression tests.

Modified: branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java
===================================================================
--- branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java     
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/test/freenet/support/LRUHashtableTest.java     
2007-09-23 09:58:34 UTC (rev 15282)
@@ -175,7 +175,7 @@
        }

        /**
-        * Tests popKey() method pushing
+        * Tests popValue() method pushing
         * and popping objects and
         * verifying if their values are correctly 
         * (in a FIFO manner) fetched and the
@@ -193,6 +193,16 @@
                //the HashTable must be empty
                assertNull(methodLRUht.popKey());
        }
+       
+       /**
+        * Tests popValue() method
+        * popping a value from an empty
+        * LRUHashtable.
+        */
+       public void testPopValueFromEmpty() {
+               LRUHashtable methodLRUht = new LRUHashtable();
+               assertNull(methodLRUht.popValue());
+       }

        /**
         * Tests peekValue() method pushing

Modified: branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java
===================================================================
--- branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java        
2007-09-23 08:04:32 UTC (rev 15281)
+++ branches/freenet-jfk/test/freenet/support/URLEncoderDecoderTest.java        
2007-09-23 09:58:34 UTC (rev 15282)
@@ -78,7 +78,7 @@
                String[] encoded = new String[toEncode.length];
                //encoding
                for (int i = 0; i < encoded.length; i++)
-                       encoded[i] = URLEncoder.encode(toEncode[i]);
+                       encoded[i] = URLEncoder.encode(toEncode[i],false);
                //decoding
                for (int i = 0; i < encoded.length; i++)
                        retValue &= 
(URLDecoder.decode(encoded[i],false)).equals(toEncode[i]);


Reply via email to