Author: toad
Date: 2008-11-29 20:45:18 +0000 (Sat, 29 Nov 2008)
New Revision: 23990

Modified:
   branches/db4o/freenet/
   branches/db4o/freenet/build.xml
   branches/db4o/freenet/src/freenet/client/HighLevelSimpleClient.java
   branches/db4o/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java
   branches/db4o/freenet/src/freenet/io/comm/MessageCore.java
   branches/db4o/freenet/src/freenet/io/comm/MessageFilter.java
   branches/db4o/freenet/src/freenet/keys/FreenetURI.java
   branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties
   branches/db4o/freenet/src/freenet/node/Node.java
   branches/db4o/freenet/src/freenet/node/NodeDispatcher.java
   branches/db4o/freenet/src/freenet/node/NodeStarter.java
   branches/db4o/freenet/src/freenet/node/PeerNode.java
   branches/db4o/freenet/src/freenet/node/Version.java
   branches/db4o/freenet/src/freenet/node/updater/NodeUpdateManager.java
   branches/db4o/freenet/src/freenet/node/updater/NodeUpdater.java
   
branches/db4o/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
   branches/db4o/freenet/src/freenet/pluginmanager/PluginManager.java
   
branches/db4o/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java
   branches/db4o/freenet/src/freenet/support/BinaryBloomFilter.java
   branches/db4o/freenet/src/freenet/support/BloomFilter.java
   branches/db4o/freenet/src/freenet/support/CountingBloomFilter.java
   branches/db4o/freenet/src/freenet/support/TransferThread.java
   branches/db4o/freenet/test/freenet/support/io/MockInputStream.java
Log:
Merge 1186



Property changes on: branches/db4o/freenet
___________________________________________________________________
Modified: svn:mergeinfo
   - /trunk/freenet:19964-23920
   + /trunk/freenet:19964-23968

Modified: branches/db4o/freenet/build.xml
===================================================================
--- branches/db4o/freenet/build.xml     2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/build.xml     2008-11-29 20:45:18 UTC (rev 23990)
@@ -19,6 +19,7 @@
        <property name="freenet-ext.location" 
location="${lib}/freenet-ext.jar"/>
        <property name="javadoc" location="javadoc"/>
        <property name="svn.revision" value="@custom@"/>
+       <property name="minExtVersion" value="-1"/>
        <property name="CSSTokenizerFilter.relative.jflex" 
value="freenet/clients/http/filter/CSSTokenizerFilter.jflex"/>
        <property name="CSSTokenizerFilter.java" 
value="${src}/freenet/clients/http/filter/CSSTokenizerFilter.java"/>
        <property name="junit.location" value="/usr/share/java/junit.jar"/>
@@ -114,6 +115,7 @@
                        <manifest>
                                <attribute name="Main-Class" 
value="freenet/node/Node"/>
                                <attribute name="Built-By" 
value="${user.name}"/>
+                               <attribute name="Required-Ext-Version" 
value="${minExtVersion}"/>
                                <section name="common">
                                        <attribute name="Specification-Title" 
value="Freenet"/>
                                        <attribute name="Specification-Version" 
value="0.7pre"/>

Modified: branches/db4o/freenet/src/freenet/client/HighLevelSimpleClient.java
===================================================================
--- branches/db4o/freenet/src/freenet/client/HighLevelSimpleClient.java 
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/client/HighLevelSimpleClient.java 
2008-11-29 20:45:18 UTC (rev 23990)
@@ -42,7 +42,7 @@
        public FetchResult fetch(FreenetURI uri, long maxSize, RequestClient 
context) throws FetchException;

        /**
-        * Non-blocking fetch of a URI with a configurable max-size, context 
object, callback and context.
+        * Non-blocking fetch of a URI with a configurable max-size (in bytes), 
context object, callback and context.
         */
        public ClientGetter fetch(FreenetURI uri, long maxSize, RequestClient 
context, ClientCallback callback, FetchContext fctx) throws FetchException;


Modified: 
branches/db4o/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java
===================================================================
--- branches/db4o/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java  
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/clients/http/FirstTimeWizardToadlet.java  
2008-11-29 20:45:18 UTC (rev 23990)
@@ -573,7 +573,8 @@
                        String shortSize = null;
                        if(freeSpace / 20 > 1024 * 1024 * 1024) {
                                // If 20GB+ free, 5% of available disk space.
-                               shortSize = SizeUtil.formatSize(freeSpace / 20);
+                               // Maximum of 256GB. That's a 128MB bloom 
filter.
+                               shortSize = 
SizeUtil.formatSize(Math.min(freeSpace / 20, 256*1024*1024*1024L));
                        }else if(freeSpace / 10 > 1024 * 1024 * 1024) {
                                // If 10GB+ free, 10% of available disk space.
                                shortSize = SizeUtil.formatSize(freeSpace / 10);

Modified: branches/db4o/freenet/src/freenet/io/comm/MessageCore.java
===================================================================
--- branches/db4o/freenet/src/freenet/io/comm/MessageCore.java  2008-11-29 
20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/io/comm/MessageCore.java  2008-11-29 
20:45:18 UTC (rev 23990)
@@ -298,7 +298,7 @@
                        Logger.error(this, "addAsyncFilter() on a filter which 
is already matched: "+filter, new Exception("error"));
                        filter.clearMatched();
                }
-               filter.onStartWaiting();
+               filter.onStartWaiting(false);
                if(logMINOR) Logger.minor(this, "Adding async filter "+filter+" 
for "+callback);
                Message ret = null;
                if(filter.anyConnectionsDropped()) {
@@ -380,7 +380,7 @@
                        Logger.error(this, "waitFor() on a filter which is 
already matched: "+filter, new Exception("error"));
                        filter.clearMatched();
                }
-               filter.onStartWaiting();
+               filter.onStartWaiting(true);
                Message ret = null;
                if(filter.anyConnectionsDropped()) {
                        filter.onDroppedConnection(filter.droppedConnection());

Modified: branches/db4o/freenet/src/freenet/io/comm/MessageFilter.java
===================================================================
--- branches/db4o/freenet/src/freenet/io/comm/MessageFilter.java        
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/io/comm/MessageFilter.java        
2008-11-29 20:45:18 UTC (rev 23990)
@@ -60,15 +60,23 @@
         return new MessageFilter();
     }

-    void onStartWaiting() {
+    void onStartWaiting(boolean waitFor) {
        synchronized(this) {
+               /* We cannot wait on a MessageFilter with a callback, because 
onMatched() calls clearMatched()
+                * if we have a callback. The solution would be to:
+                * - Set a flag indicating we are waitFor()ing a filter here.
+                * - On matching a message (setMessage), call the callback 
immediately if not waitFor()ing.
+                * - If we are waitFor()ing, call the callback when we exit 
waitFor() (onStopWaiting()???).
+                */
+               if(waitFor && _callback != null)
+                       throw new IllegalStateException("Cannot wait on a 
MessageFilter with a callback!");
                if(!_setTimeout)
                        Logger.error(this, "No timeout set on filter "+this, 
new Exception("error"));
                if(_initialTimeout > 0 && _timeoutFromWait)
                        _timeout = System.currentTimeMillis() + _initialTimeout;
        }
        if(_or != null)
-               _or.onStartWaiting();
+               _or.onStartWaiting(waitFor);
     }

     /**
@@ -169,7 +177,6 @@

        public boolean match(Message m) {
                if ((_or != null) && (_or.match(m))) {
-                       _matched = true;
                        return true;
                }
                if ((_type != null) && (!_type.equals(m.getSpec()))) {
@@ -189,7 +196,6 @@
                        }
                }
                if(reallyTimedOut(System.currentTimeMillis())) return false;
-               _matched=true;
                return true;
        }

@@ -227,9 +233,11 @@
         return _message;
     }

-    public void setMessage(Message message) {
+    public synchronized void setMessage(Message message) {
         //Logger.debug(this, "setMessage("+message+") on "+this, new 
Exception("debug"));
         _message = message;
+        _matched = _message != null;
+        notifyAll();
     }

     public int getInitialTimeout() {
@@ -315,7 +323,6 @@
                        // Clear matched before calling callback in case we are 
re-added.
                        if(_callback != null)
                                clearMatched();
-                       notifyAll();
                }
                if(cb != null) {
                        cb.onMatched(msg);

Modified: branches/db4o/freenet/src/freenet/keys/FreenetURI.java
===================================================================
--- branches/db4o/freenet/src/freenet/keys/FreenetURI.java      2008-11-29 
20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/keys/FreenetURI.java      2008-11-29 
20:45:18 UTC (rev 23990)
@@ -860,4 +860,9 @@
                // All members are inline (arrays, ints etc), treated as 
values, so we can happily just call delete(this).
                container.delete(this);
        }
+
+       public FreenetURI sskForUSK() {
+               if(!keyType.equalsIgnoreCase("USK")) throw new 
IllegalStateException();
+               return new FreenetURI("SSK", docName+"-"+suggestedEdition, 
metaStr, routingKey, cryptoKey, extra, 0);
+       }
 }

Modified: branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties
===================================================================
--- branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties   
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/l10n/freenet.l10n.en.properties   
2008-11-29 20:45:18 UTC (rev 23990)
@@ -835,8 +835,8 @@
 PeerManagerUserAlert.clockProblem=${count} of your nodes are unable to connect 
because their system clock is more than 24 hours different to yours. Please 
check whether your computer has the correct time. Incorrect time will cause 
many node and client mechanisms to fail.
 PeerManagerUserAlert.noConns=This node has not been able to connect to any 
other nodes so far; it will not be able to function normally. Hopefully some of 
your peers will connect soon; if not, try to get some more peers. You need at 
least 3 peers at any time, and should aim for 5-10.
 PeerManagerUserAlert.noConnsTitle=No open connections
-PeerManagerUserAlert.noPeersDarknet=This node has no peers to connect to, 
therefore it will not be able to function normally. Ideally you should connect 
to peers run by people you know (if you are paranoid, then people you trust; if 
not, then at least people you've talked to). You need at least 3 connected 
peers at all times, and ideally 5-10. You could turn on insecure mode (opennet) 
to quickly and automatically get some connections.
-PeerManagerUserAlert.noPeersTestnet=This node has no peers to connect to, 
therefore it will not be able to function normally. Ideally you should connect 
to peers run by people you know (if you are paranoid, then people you trust; if 
not, then at least people you've talked to). You need at least 3 connected 
peers at all times, and ideally 5-10. You could turn on insecure mode (opennet) 
to quickly and automatically get some connections.
+PeerManagerUserAlert.noPeersDarknet=This node has no peers to connect to, 
therefore it will not be able to function normally. Ideally you should connect 
to peers run by people you know (if you are paranoid, then people you trust; if 
not, then at least people you've talked to). You need at least 3 connected 
peers at all times, and ideally 5-10. You could set the network security level 
to NORMAL or LOW to enable connecting to strangers and quickly get some 
connections.
+PeerManagerUserAlert.noPeersTestnet=This node has no peers to connect to, 
therefore it will not be able to function normally. Ideally you should connect 
to peers run by people you know (if you are paranoid, then people you trust; if 
not, then at least people you've talked to). You need at least 3 connected 
peers at all times, and ideally 5-10. You could set the network security level 
to NORMAL or LOW to enable connecting to strangers and quickly get some 
connections.
 PeerManagerUserAlert.noPeersTitle=No peers found
 PeerManagerUserAlert.oneConn=This node only has one connection. Performance 
will be impaired, and you have no anonymity nor even plausible deniability if 
that one person is malicious. Your node is attached to the network like a 
"leaf" and does not contribute to the network's health. Try to get at least 3 
(ideally more like 5-10) connected peers at any given time.
 PeerManagerUserAlert.onlyFewConnsTitle=Only ${count} open connection(s)

Modified: branches/db4o/freenet/src/freenet/node/Node.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/Node.java    2008-11-29 20:39:52 UTC 
(rev 23989)
+++ branches/db4o/freenet/src/freenet/node/Node.java    2008-11-29 20:45:18 UTC 
(rev 23990)
@@ -1566,7 +1566,11 @@

                storeType = nodeConfig.getString("storeType");

-               nodeConfig.register("storeSize", "1G", sortOrder++, false, 
true, "Node.storeSize", "Node.storeSizeLong", 
+               /*
+                * Very small initial store size, since the node will 
preallocate it when starting up for the first time,
+                * BLOCKING STARTUP, and since everyone goes through the wizard 
anyway...
+                */
+               nodeConfig.register("storeSize", "100M", sortOrder++, false, 
true, "Node.storeSize", "Node.storeSizeLong", 
                                new LongCallback() {

                                        @Override
@@ -1643,7 +1647,7 @@

                storeBloomFilterSize = 
nodeConfig.getInt("storeBloomFilterSize");
                if (storeBloomFilterSize == -1) 
-                       storeBloomFilterSize = (int) 
Math.min(maxTotalDatastoreSize / 2048, 268435456);
+                       storeBloomFilterSize = (int) 
Math.min(maxTotalDatastoreSize / 2048, Integer.MAX_VALUE);

                nodeConfig.register("storeBloomFilterCounting", true, 
sortOrder++, true, false,
                        "Node.storeBloomFilterCounting", 
"Node.storeBloomFilterCountingLong", new BooleanCallback() {
@@ -3083,6 +3087,10 @@
        }

        public int getNumRemoteSSKRequests() {
+//             synchronized(runningSSKGetUIDs) {
+//                     for(Long l : runningSSKGetUIDs)
+//                             Logger.minor(this, "Running remote SSK fetch: 
"+l);
+//             }
                return runningSSKGetUIDs.size();
        }


Modified: branches/db4o/freenet/src/freenet/node/NodeDispatcher.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/NodeDispatcher.java  2008-11-29 
20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/node/NodeDispatcher.java  2008-11-29 
20:45:18 UTC (rev 23990)
@@ -129,9 +129,13 @@
                } else if(spec == DMT.UOMSendingRevocation && 
source.isRealConnection()) {
                        return node.nodeUpdater.uom.handleSendingRevocation(m, 
source);
                } else if(spec == DMT.UOMRequestMain && 
source.isRealConnection()) {
-                       return node.nodeUpdater.uom.handleRequestMain(m, 
source);
+                       return node.nodeUpdater.uom.handleRequestJar(m, source, 
false);
+               } else if(spec == DMT.UOMRequestExtra && 
source.isRealConnection()) {
+                       return node.nodeUpdater.uom.handleRequestJar(m, source, 
true);
                } else if(spec == DMT.UOMSendingMain && 
source.isRealConnection()) {
                        return node.nodeUpdater.uom.handleSendingMain(m, 
source);
+               } else if(spec == DMT.UOMSendingExtra && 
source.isRealConnection()) {
+                       return node.nodeUpdater.uom.handleSendingExt(m, source);
                } else if(spec == DMT.FNPOpennetAnnounceRequest) {
                        return handleAnnounceRequest(m, source);
                } else if(spec == DMT.FNPRoutingStatus) {

Modified: branches/db4o/freenet/src/freenet/node/NodeStarter.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/NodeStarter.java     2008-11-29 
20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/node/NodeStarter.java     2008-11-29 
20:45:18 UTC (rev 23990)
@@ -33,6 +33,12 @@

        private Node node;
        private static LoggingConfigHandler logConfigHandler;
+       /** Freenet will not function at all without at least this build of 
freenet-ext.jar. 
+        * This will be included in the jar manifest file so we can check it 
when we download new builds. */
+       public final static int REQUIRED_EXT_BUILD_NUMBER = 24;
+       /** Freenet will function best with this build of freenet-ext.jar. 
+        * It may be required in the near future. The node will try to download 
it. 
+        * The node will not update to a later ext version than this, because 
that might be incompatible. */
        public final static int RECOMMENDED_EXT_BUILD_NUMBER = 26;
        /*
        (File.separatorChar == '\\') &&

Modified: branches/db4o/freenet/src/freenet/node/PeerNode.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/PeerNode.java        2008-11-29 
20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/node/PeerNode.java        2008-11-29 
20:45:18 UTC (rev 23990)
@@ -1877,9 +1877,13 @@
                        unroutableNewerVersion = newer;
                        unroutableOlderVersion = older;
                        bootIDChanged = (thisBootID != this.bootID);
-                       if(bootIDChanged && wasARekey)
+                       if(bootIDChanged && wasARekey) {
                                Logger.error(this, "Changed boot ID while 
rekeying! from " + bootID + " to " + thisBootID + " for " + getPeer());
-                       else if(bootIDChanged && logMINOR)
+                               wasARekey = false;
+                               connectedTime = now;
+                               countSelectionsSinceConnected = 0;
+                               sentInitialMessages = false;
+                       } else if(bootIDChanged && logMINOR)
                                Logger.minor(this, "Changed boot ID from " + 
bootID + " to " + thisBootID + " for " + getPeer());
                        this.bootID = thisBootID;
                        if(bootIDChanged) {
@@ -4027,6 +4031,16 @@
                return offeredMainJarVersion;
        }

+       private volatile long offeredExtJarVersion;
+       
+       public void setExtJarOfferedVersion(long extJarVersion) {
+               offeredExtJarVersion = extJarVersion;
+       }
+       
+       public long getExtJarOfferedVersion() {
+               return offeredExtJarVersion;
+       }
+
        /**
         * Maybe send something. A SINGLE PACKET.
         * Don't send everything at once, for two reasons:

Modified: branches/db4o/freenet/src/freenet/node/Version.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/Version.java 2008-11-29 20:39:52 UTC 
(rev 23989)
+++ branches/db4o/freenet/src/freenet/node/Version.java 2008-11-29 20:45:18 UTC 
(rev 23990)
@@ -24,17 +24,17 @@
        public static final String protocolVersion = "1.0";

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

        /** Oldest build of Fred we will talk to */
-       private static final int oldLastGoodBuild = 1181;
-       private static final int newLastGoodBuild = 1185;
+       private static final int oldLastGoodBuild = 1185;
+       private static final int newLastGoodBuild = 1186;
        static final long transitionTime;

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


Modified: branches/db4o/freenet/src/freenet/node/updater/NodeUpdateManager.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/updater/NodeUpdateManager.java       
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/node/updater/NodeUpdateManager.java       
2008-11-29 20:45:18 UTC (rev 23990)
@@ -235,9 +235,8 @@
                                        throw new 
InvalidConfigValueException(l10n("noUpdateWithoutWrapper"));
                                }
                                // Start it
-                               mainUpdater = new NodeUpdater(this, updateURI, 
false, Version.buildNumber(), "main-jar-");
-                               if(shouldUpdateExt)
-                                       extUpdater = new NodeUpdater(this, 
extURI, true, NodeStarter.extBuildNumber, "ext-jar-");
+                               mainUpdater = new NodeUpdater(this, updateURI, 
false, Version.buildNumber(), Integer.MAX_VALUE, "main-jar-");
+                               extUpdater = new NodeUpdater(this, extURI, 
true, NodeStarter.extBuildNumber, NodeStarter.RECOMMENDED_EXT_BUILD_NUMBER, 
"ext-jar-");
                        }
                }
                if(!enable) {
@@ -897,6 +896,16 @@
                }
                return updater.getBlobFile(version);
        }
+       
+       public File getExtBlob(int version) {
+               NodeUpdater updater;
+               synchronized(this) {
+                       if(hasBeenBlown) return null;
+                       updater = extUpdater;
+                       if(updater == null) return null;
+               }
+               return updater.getBlobFile(version);
+       }

        public synchronized long timeRemainingOnCheck() {
                long now = System.currentTimeMillis();
@@ -926,4 +935,8 @@
        protected long getStartedFetchingNextMainJarTimestamp() {
                return startedFetchingNextMainJar;
        }
+       
+       protected long getStartedFetchingNextExtJarTimestamp() {
+               return startedFetchingNextExtJar;
+       }
 }

Modified: branches/db4o/freenet/src/freenet/node/updater/NodeUpdater.java
===================================================================
--- branches/db4o/freenet/src/freenet/node/updater/NodeUpdater.java     
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/node/updater/NodeUpdater.java     
2008-11-29 20:45:18 UTC (rev 23990)
@@ -45,13 +45,14 @@
        private int fetchingVersion;
        private int fetchedVersion;
        private int writtenVersion;
+       private int maxDeployVersion;
        private boolean isRunning;
        private boolean isFetching;
        public final boolean extUpdate;
        private final String blobFilenamePrefix;
        private File tempBlobFile;

-       NodeUpdater(NodeUpdateManager manager, FreenetURI URI, boolean 
extUpdate, int current, String blobFilenamePrefix) {
+       NodeUpdater(NodeUpdateManager manager, FreenetURI URI, boolean 
extUpdate, int current, int max, String blobFilenamePrefix) {
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                this.manager = manager;
                this.node = manager.node;
@@ -65,6 +66,7 @@
                this.isFetching = false;
                this.extUpdate = extUpdate;
                this.blobFilenamePrefix = blobFilenamePrefix;
+               this.maxDeployVersion = max;

                FetchContext tempContext = core.makeClient((short) 0, 
true).getFetchContext();
                tempContext.allowSplitfiles = true;
@@ -95,6 +97,8 @@
                                return;
                        found = (int) key.suggestedEdition;

+                       if(found > maxDeployVersion) found = maxDeployVersion;
+                       
                        if(found <= availableVersion)
                                return;
                        Logger.minor(this, "Updating availableVersion from " + 
availableVersion + " to " + found + " and queueing an update");
@@ -143,8 +147,10 @@
                                                System.err.println("Starting " 
+ (extUpdate ? "freenet-ext.jar " : "") + "fetch for " + availableVersion);
                                        tempBlobFile =
                                                
File.createTempFile(blobFilenamePrefix + availableVersion + "-", ".fblob.tmp", 
manager.node.clientCore.getPersistentTempDir());
+                                       FreenetURI uri = 
URI.setSuggestedEdition(availableVersion);
+                                       uri = uri.sskForUSK();
                                        cg = new ClientGetter(this, 
core.requestStarters.chkFetchScheduler, core.requestStarters.sskFetchScheduler,
-                                               
URI.setSuggestedEdition(availableVersion), ctx, 
RequestStarter.IMMEDIATE_SPLITFILE_PRIORITY_CLASS,
+                                               uri, ctx, 
RequestStarter.IMMEDIATE_SPLITFILE_PRIORITY_CLASS,
                                                this, null, new 
FileBucket(tempBlobFile, false, false, false, false, false));
                                        toStart = cg;
                                }
@@ -235,7 +241,7 @@
                                                Logger.error(this, "Not able to 
rename binary blob for node updater: " + tempBlobFile + " -> " + blobFile + " - 
may not be able to tell other peers about this build");
                        }
                        this.fetchedVersion = fetchedVersion;
-                       System.out.println("Found " + fetchedVersion);
+                       System.out.println("Found " + (extUpdate ? "ext " : "") 
+ fetchedVersion);
                        if(fetchedVersion > currentVersion)
                                Logger.normal(this, "Found version " + 
fetchedVersion + ", setting up a new UpdatedVersionAvailableUserAlert");
                        this.cg = null;

Modified: 
branches/db4o/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java  
    2008-11-29 20:39:52 UTC (rev 23989)
+++ 
branches/db4o/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java  
    2008-11-29 20:45:18 UTC (rev 23990)
@@ -72,12 +72,18 @@
        private final HashSet<PeerNode> nodesSayKeyRevokedFailedTransfer;
        /** PeerNode's which have offered the main jar which we are not 
fetching it from right now */
        private final HashSet<PeerNode> nodesOfferedMainJar;
+       /** PeerNode's which have offered the ext jar which we are not fetching 
it from right now */
+       private final HashSet<PeerNode> nodesOfferedExtJar;
        /** PeerNode's we've asked to send the main jar */
        private final HashSet<PeerNode> nodesAskedSendMainJar;
+       /** PeerNode's we've asked to send the ext jar */
+       private final HashSet<PeerNode> nodesAskedSendExtJar;
        /** PeerNode's sending us the main jar */
        private final HashSet<PeerNode> nodesSendingMainJar;
+       /** PeerNode's sending us the ext jar */
+       private final HashSet<PeerNode> nodesSendingExtJar;
        // 2 for reliability, no more as gets very slow/wasteful
-       static final int MAX_NODES_SENDING_MAIN_JAR = 2;
+       static final int MAX_NODES_SENDING_JAR = 2;
        /** Maximum time between asking for the main jar and it starting to 
transfer */
        static final int REQUEST_MAIN_JAR_TIMEOUT = 60 * 1000;
        //** Grace time before we use UoM to update */
@@ -92,8 +98,11 @@
                nodesSayKeyRevoked = new HashSet<PeerNode>();
                nodesSayKeyRevokedFailedTransfer = new HashSet<PeerNode>();
                nodesOfferedMainJar = new HashSet<PeerNode>();
+               nodesOfferedExtJar = new HashSet<PeerNode>();
                nodesAskedSendMainJar = new HashSet<PeerNode>();
+               nodesAskedSendExtJar = new HashSet<PeerNode>();
                nodesSendingMainJar = new HashSet<PeerNode>();
+               nodesSendingExtJar = new HashSet<PeerNode>();
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
        }

@@ -105,7 +114,7 @@
         * @return True unless we don't want the message (in this case, always 
true).
         */
        public boolean handleAnnounce(Message m, final PeerNode source) {
-               String jarKey = m.getString(DMT.MAIN_JAR_KEY);
+               String mainJarKey = m.getString(DMT.MAIN_JAR_KEY);
                String extraJarKey = m.getString(DMT.EXTRA_JAR_KEY);
                String revocationKey = m.getString(DMT.REVOCATION_KEY);
                boolean haveRevocationKey = 
m.getBoolean(DMT.HAVE_REVOCATION_KEY);
@@ -124,7 +133,7 @@
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR) {
                        Logger.minor(this, "Update Over Mandatory offer from 
node " + source.getPeer() + " : " + source.userToString() + ":");
-                       Logger.minor(this, "Main jar key: " + jarKey + " 
version=" + mainJarVersion + " length=" + mainJarFileLength);
+                       Logger.minor(this, "Main jar key: " + mainJarKey + " 
version=" + mainJarVersion + " length=" + mainJarFileLength);
                        Logger.minor(this, "Extra jar key: " + extraJarKey + " 
version=" + extraJarVersion + " length=" + extraJarFileLength);
                        Logger.minor(this, "Revocation key: " + revocationKey + 
" found=" + haveRevocationKey + " length=" + revocationKeyFileLength + " last 
had 3 DNFs " + revocationKeyLastTried + " ms ago, " + revocationKeyDNFs + " 
DNFs so far");
                        Logger.minor(this, "Load stats: " + pingTime + "ms 
ping, " + delayTime + "ms bwlimit delay time");
@@ -218,6 +227,16 @@
                        return true; // Don't care if not enabled, except for 
the revocation URI

                long now = System.currentTimeMillis();
+               
+               handleMainJarOffer(now, mainJarFileLength, mainJarVersion, 
source, mainJarKey);
+               
+               handleExtJarOffer(now, extraJarFileLength, extraJarVersion, 
source, extraJarKey);
+               
+               return true;
+       }
+
+       private void handleMainJarOffer(long now, long mainJarFileLength, long 
mainJarVersion, PeerNode source, String jarKey) {
+               
                long started = 
updateManager.getStartedFetchingNextMainJarTimestamp();
                long whenToTakeOverTheNormalUpdater;
                if(started > 0)
@@ -247,14 +266,14 @@
                                try {
                                        FreenetURI mainJarURI = new 
FreenetURI(jarKey).setSuggestedEdition(mainJarVersion);
                                        
if(mainJarURI.equals(updateManager.updateURI.setSuggestedEdition(mainJarVersion)))
-                                               sendUOMRequestMain(source, 
true);
+                                               sendUOMRequest(source, true, 
false);
                                        else
                                                System.err.println("Node " + 
source.userToString() + " offered us a new main jar (version " + mainJarVersion 
+ ") but his key was different to ours:\n" +
                                                        "our key: " + 
updateManager.updateURI + "\nhis key:" + mainJarURI);
                                } catch(MalformedURLException e) {
                                        // Should maybe be a useralert?
-                                       Logger.error(this, "Node " + source + " 
sent us a UOMAnnounce claiming to have a new jar, but it had an invalid URI: " 
+ revocationKey + " : " + e, e);
-                                       System.err.println("Node " + 
source.userToString() + " sent us a UOMAnnounce claiming to have a new jar, but 
it had an invalid URI: " + revocationKey + " : " + e);
+                                       Logger.error(this, "Node " + source + " 
sent us a UOMAnnounce claiming to have a new ext jar, but it had an invalid 
URI: " + jarKey + " : " + e, e);
+                                       System.err.println("Node " + 
source.userToString() + " sent us a UOMAnnounce claiming to have a new ext jar, 
but it had an invalid URI: " + jarKey + " : " + e);
                                }
                        } else {
                                // Don't take up the offer. Add to 
nodesOfferedMainJar, so that we know where to fetch it from when we need it.
@@ -271,61 +290,134 @@
                                                
if(updateManager.hasNewMainJar())
                                                        return;
                                                
if(!updateManager.node.isOudated()) {
+                                                       Logger.error(this, "The 
update process seems to have been stuck for too long; let's switch to UoM! 
SHOULD NOT HAPPEN! (2) (ext)");
+                                                       System.out.println("The 
update process seems to have been stuck for too long; let's switch to UoM! 
SHOULD NOT HAPPEN! (2) (ext)");
+                                               }
+                                               maybeRequestMainJar();
+                                       }
+                               }, whenToTakeOverTheNormalUpdater - now);
+                       }
+               }
+               
+       }
+
+       private void handleExtJarOffer(long now, long extJarFileLength, long 
extJarVersion, PeerNode source, String jarKey) {
+               
+               long started = 
updateManager.getStartedFetchingNextExtJarTimestamp();
+               long whenToTakeOverTheNormalUpdater;
+               if(started > 0)
+                       whenToTakeOverTheNormalUpdater = started + GRACE_TIME;
+               else
+                       whenToTakeOverTheNormalUpdater = 
System.currentTimeMillis() + GRACE_TIME;
+               boolean isOutdated = updateManager.node.isOudated();
+               // if the new build is self-mandatory or if the "normal" 
updater has been trying to update for more than one hour
+               Logger.normal(this, "We received a valid UOMAnnounce : 
(isOutdated=" + isOutdated + " version=" + extJarVersion + " 
whenToTakeOverTheNormalUpdater=" + 
TimeUtil.formatTime(whenToTakeOverTheNormalUpdater - now) + ") file length " + 
extJarFileLength + " updateManager version " + 
updateManager.newExtJarVersion());
+               if(extJarVersion > NodeStarter.extBuildNumber && 
extJarFileLength > 0 &&
+                       extJarVersion > updateManager.newExtJarVersion()) {
+                       source.setExtJarOfferedVersion(extJarVersion);
+                       // Offer is valid.
+                       if(logMINOR)
+                               Logger.minor(this, "Offer is valid");
+                       if((isOutdated) || whenToTakeOverTheNormalUpdater < 
now) {
+                               // Take up the offer, subject to limits on 
number of simultaneous downloads.
+                               // If we have fetches running already, then 
sendUOMRequestMain() will add the offer to nodesOfferedMainJar,
+                               // so that if all our fetches fail, we can 
fetch from this node.
+                               if(!isOutdated) {
+                                       String howLong = 
TimeUtil.formatTime(now - started);
+                                       Logger.error(this, "The update process 
seems to have been stuck for " + howLong + "; let's switch to UoM! SHOULD NOT 
HAPPEN! (1) (ext)");
+                                       System.out.println("The update process 
seems to have been stuck for " + howLong + "; let's switch to UoM! SHOULD NOT 
HAPPEN! (1) (ext)");
+                               } else if(logMINOR)
+                                       Logger.minor(this, "Fetching via UOM as 
our build is deprecated");
+                               // Fetch it
+                               try {
+                                       FreenetURI extJarURI = new 
FreenetURI(jarKey).setSuggestedEdition(extJarVersion);
+                                       
if(extJarURI.equals(updateManager.extURI.setSuggestedEdition(extJarVersion)))
+                                               sendUOMRequest(source, true, 
true);
+                                       else
+                                               System.err.println("Node " + 
source.userToString() + " offered us a new ext jar (version " + extJarVersion + 
") but his key was different to ours:\n" +
+                                                       "our key: " + 
updateManager.extURI + "\nhis key:" + extJarURI);
+                               } catch(MalformedURLException e) {
+                                       // Should maybe be a useralert?
+                                       Logger.error(this, "Node " + source + " 
sent us a UOMAnnounce claiming to have a new ext jar, but it had an invalid 
URI: " + jarKey + " : " + e, e);
+                                       System.err.println("Node " + 
source.userToString() + " sent us a UOMAnnounce claiming to have a new jar, but 
it had an invalid URI: " + jarKey + " : " + e);
+                               }
+                       } else {
+                               // Don't take up the offer. Add to 
nodesOfferedMainJar, so that we know where to fetch it from when we need it.
+                               synchronized(this) {
+                                       nodesOfferedExtJar.add(source);
+                               }
+                               
updateManager.node.getTicker().queueTimedJob(new Runnable() {
+
+                                       public void run() {
+                                               if(updateManager.isBlown())
+                                                       return;
+                                               if(!updateManager.isEnabled())
+                                                       return;
+                                               if(updateManager.hasNewExtJar())
+                                                       return;
+                                               
if(!updateManager.node.isOudated()) {
                                                        Logger.error(this, "The 
update process seems to have been stuck for too long; let's switch to UoM! 
SHOULD NOT HAPPEN! (2)");
                                                        System.out.println("The 
update process seems to have been stuck for too long; let's switch to UoM! 
SHOULD NOT HAPPEN! (2)");
                                                }
-                                               maybeRequestMainJar();
+                                               maybeRequestExtJar();
                                        }
                                }, whenToTakeOverTheNormalUpdater - now);
                        }
                }
-
-               return true;
+               
        }

-       private void sendUOMRequestMain(final PeerNode source, boolean 
addOnFail) {
+       private void sendUOMRequest(final PeerNode source, boolean addOnFail, 
final boolean isExt) {
+               final String name = isExt ? "Main" : "Extra";
+               String lname = isExt ? "main" : "ext";
                if(logMINOR)
-                       Logger.minor(this, "sendUOMRequestMain(" + source + "," 
+ addOnFail + ")");
+                       Logger.minor(this, "sendUOMRequest"+name+"(" + source + 
"," + addOnFail + ")");
                if(!source.isConnected())
                        return;
+               final HashSet<PeerNode> sendingJar = isExt ? nodesSendingExtJar 
: nodesSendingMainJar;
+               final HashSet<PeerNode> askedSendJar = isExt ? 
nodesAskedSendExtJar : nodesAskedSendMainJar;
                synchronized(this) {
-                       long offeredVersion = source.getMainJarOfferedVersion();
-                       if(offeredVersion < updateManager.newMainJarVersion()) {
+                       long offeredVersion = isExt ? 
source.getExtJarOfferedVersion() : source.getMainJarOfferedVersion();
+                       long updateVersion = isExt ? 
updateManager.newExtJarVersion() : updateManager.newMainJarVersion();
+                       if(offeredVersion < updateVersion) {
                                if(offeredVersion <= 0)
-                                       Logger.error(this, "Not sending UOM 
request to " + source + " because it hasn't offered anything!");
+                                       Logger.error(this, "Not sending UOM 
"+lname+" request to " + source + " because it hasn't offered anything!");
                                else
                                        if(logMINOR)
-                                               Logger.minor(this, "Not sending 
UOM request to " + source + " because we already have its offered version " + 
offeredVersion);
+                                               Logger.minor(this, "Not sending 
UOM "+lname+" request to " + source + " because we already have its offered 
version " + offeredVersion);
                                return;
                        }
-                       if(updateManager.getMainVersion() >= offeredVersion) {
+                       int curVersion = isExt ? updateManager.getExtVersion() 
: updateManager.getMainVersion();
+                       if(curVersion >= offeredVersion) {
                                if(logMINOR)
-                                       Logger.minor(this, "Not fetching from " 
+ source + " because current jar version " + updateManager.getMainVersion() + " 
is more recent than " + source.getMainJarOfferedVersion());
+                                       Logger.minor(this, "Not fetching from " 
+ source + " because current "+lname+" jar version " + curVersion + " is more 
recent than " + offeredVersion);
                                return;
                        }
-                       if(nodesAskedSendMainJar.contains(source)) {
+                       if(askedSendJar.contains(source)) {
                                if(logMINOR)
-                                       Logger.minor(this, "Recently asked node 
" + source + " so not re-asking yet.");
+                                       Logger.minor(this, "Recently asked node 
" + source + " ("+lname+") so not re-asking yet.");
                                return;
                        }
-                       if(addOnFail && nodesAskedSendMainJar.size() + 
nodesSendingMainJar.size() >= MAX_NODES_SENDING_MAIN_JAR) {
-                               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).");
+                       if(addOnFail && askedSendJar.size() + sendingJar.size() 
>= MAX_NODES_SENDING_JAR) {
+                               HashSet<PeerNode> offeredJar = isExt ? 
nodesOfferedExtJar : nodesOfferedMainJar;
+                               if(offeredJar.add(source))
+                                       System.err.println("Offered "+lname+" 
jar by " + source.userToString() + " (already fetching from " + 
sendingJar.size() + "), but will use this offer if our current fetches fail).");
                                return;
                        } else {
-                               if(nodesSendingMainJar.contains(source)) {
+                               if(sendingJar.contains(source)) {
                                        if(logMINOR)
-                                               Logger.minor(this, "Not 
fetching main jar from " + source.userToString() + " because already fetching 
from that node");
+                                               Logger.minor(this, "Not 
fetching "+lname+" jar from " + source.userToString() + " because already 
fetching from that node");
                                        return;
                                }
-                               nodesAskedSendMainJar.add(source);
+                               sendingJar.add(source);
                        }
                }

-               Message msg = 
DMT.createUOMRequestMain(updateManager.node.random.nextLong());
+               Message msg = isExt ? 
DMT.createUOMRequestExtra(updateManager.node.random.nextLong()) :
+                       
DMT.createUOMRequestMain(updateManager.node.random.nextLong());

                try {
-                       System.err.println("Fetching main jar from " + 
source.userToString());
+                       System.err.println("Fetching "+lname+" jar from " + 
source.userToString());
                        source.sendAsync(msg, new AsyncMessageCallback() {

                                public void acknowledged() {
@@ -333,19 +425,25 @@
                                }

                                public void disconnected() {
-                                       Logger.normal(this, "Disconnected from 
" + source.userToString() + " after sending UOMRequestMain");
+                                       Logger.normal(this, "Disconnected from 
" + source.userToString() + " after sending UOMRequest"+name);
                                        
synchronized(UpdateOverMandatoryManager.this) {
-                                               
nodesAskedSendMainJar.remove(source);
+                                               sendingJar.remove(source);
                                        }
-                                       maybeRequestMainJar();
+                                       if(isExt)
+                                               maybeRequestExtJar();
+                                       else
+                                               maybeRequestMainJar();
                                }

                                public void fatalError() {
-                                       Logger.normal(this, "Fatal error from " 
+ source.userToString() + " after sending UOMRequestMain");
+                                       Logger.normal(this, "Fatal error from " 
+ source.userToString() + " after sending UOMRequest"+name);
                                        
synchronized(UpdateOverMandatoryManager.this) {
-                                               
nodesAskedSendMainJar.remove(source);
+                                               askedSendJar.remove(source);
                                        }
-                                       maybeRequestMainJar();
+                                       if(isExt)
+                                               maybeRequestExtJar();
+                                       else
+                                               maybeRequestMainJar();
                                }

                                public void sent() {
@@ -354,27 +452,33 @@

                                                public void run() {
                                                        
synchronized(UpdateOverMandatoryManager.this) {
-                                                               
if(!nodesAskedSendMainJar.contains(source))
+                                                               
if(!askedSendJar.contains(source))
                                                                        return;
-                                                               
nodesAskedSendMainJar.remove(source); // free up a slot
+                                                               
askedSendJar.remove(source); // free up a slot
                                                        }
-                                                       maybeRequestMainJar();
+                                                       if(isExt)
+                                                               
maybeRequestExtJar();
+                                                       else
+                                                               
maybeRequestMainJar();
                                                }
                                        }, REQUEST_MAIN_JAR_TIMEOUT);
                                }
                        }, updateManager.ctr);
                } catch(NotConnectedException e) {
                        synchronized(this) {
-                               nodesAskedSendMainJar.remove(source);
+                               askedSendJar.remove(source);
                        }
-                       maybeRequestMainJar();
+                       if(isExt)
+                               maybeRequestExtJar();
+                       else
+                               maybeRequestMainJar();
                }
        }

        protected void maybeRequestMainJar() {
                PeerNode[] offers;
                synchronized(this) {
-                       if(nodesAskedSendMainJar.size() + 
nodesSendingMainJar.size() >= MAX_NODES_SENDING_MAIN_JAR)
+                       if(nodesAskedSendMainJar.size() + 
nodesSendingMainJar.size() >= MAX_NODES_SENDING_JAR)
                                return;
                        if(nodesOfferedMainJar.isEmpty())
                                return;
@@ -384,17 +488,41 @@
                        if(!offers[i].isConnected())
                                continue;
                        synchronized(this) {
-                               if(nodesAskedSendMainJar.size() + 
nodesSendingMainJar.size() >= MAX_NODES_SENDING_MAIN_JAR)
+                               if(nodesAskedSendMainJar.size() + 
nodesSendingMainJar.size() >= MAX_NODES_SENDING_JAR)
                                        return;
                                if(nodesSendingMainJar.contains(offers[i]))
                                        continue;
                                if(nodesAskedSendMainJar.contains(offers[i]))
                                        continue;
                        }
-                       sendUOMRequestMain(offers[i], false);
+                       sendUOMRequest(offers[i], false, false);
                }
        }

+       protected void maybeRequestExtJar() {
+               PeerNode[] offers;
+               synchronized(this) {
+                       if(nodesAskedSendExtJar.size() + 
nodesSendingExtJar.size() >= MAX_NODES_SENDING_JAR)
+                               return;
+                       if(nodesOfferedExtJar.isEmpty())
+                               return;
+                       offers = nodesOfferedExtJar.toArray(new 
PeerNode[nodesOfferedExtJar.size()]);
+               }
+               for(int i = 0; i < offers.length; i++) {
+                       if(!offers[i].isConnected())
+                               continue;
+                       synchronized(this) {
+                               if(nodesAskedSendExtJar.size() + 
nodesSendingExtJar.size() >= MAX_NODES_SENDING_JAR)
+                                       return;
+                               if(nodesSendingExtJar.contains(offers[i]))
+                                       continue;
+                               if(nodesAskedSendExtJar.contains(offers[i]))
+                                       continue;
+                       }
+                       sendUOMRequest(offers[i], false, true);
+               }
+       }
+
        private void alertUser() {
                synchronized(this) {
                        if(alert != null)
@@ -822,7 +950,7 @@
                                        
updateManager.revocationChecker.onFailure(e, state, cleanedBlobFile);
                                        temp.delete();

-                                       
insertBlob(updateManager.revocationChecker.getBlobFile());
+                                       
insertBlob(updateManager.revocationChecker.getBlobFile(), "revocation");

                                } else {
                                        Logger.error(this, "Failed to fetch 
revocation certificate from blob from " + source.userToString());
@@ -853,7 +981,7 @@
                                System.err.println("Got revocation certificate 
from " + source.userToString());
                                
updateManager.revocationChecker.onSuccess(result, state, cleanedBlobFile);
                                temp.delete();
-                               
insertBlob(updateManager.revocationChecker.getBlobFile());
+                               
insertBlob(updateManager.revocationChecker.getBlobFile(), "revocation");
                        }

                        public void onSuccess(BaseClientPutter state, 
ObjectContainer container) {
@@ -876,7 +1004,7 @@

        }

-       protected void insertBlob(final File blob) {
+       protected void insertBlob(final File blob, final String type) {
                ClientCallback callback = new ClientCallback() {

                        public void onFailure(FetchException e, ClientGetter 
state, ObjectContainer container) {
@@ -884,7 +1012,7 @@
                        }

                        public void onFailure(InsertException e, 
BaseClientPutter state, ObjectContainer container) {
-                               Logger.error(this, "Failed to insert revocation 
key binary blob: " + e, e);
+                               Logger.error(this, "Failed to insert "+type+" 
binary blob: " + e, e);
                        }

                        public void onFetchable(BaseClientPutter state, 
ObjectContainer container) {
@@ -905,7 +1033,7 @@

                        public void onSuccess(BaseClientPutter state, 
ObjectContainer container) {
                                // All done. Cool.
-                               Logger.normal(this, "Inserted binary blob for 
revocation key");
+                               Logger.normal(this, "Inserted "+type+" binary 
blob");
                        }
                };
                FileBucket bucket = new FileBucket(blob, true, false, false, 
false, false);
@@ -917,7 +1045,7 @@
                try {
                        
updateManager.node.clientCore.clientContext.start(putter, false);
                } catch(InsertException e1) {
-                       Logger.error(this, "Failed to start insert of 
revocation key binary blob: " + e1, e1);
+                       Logger.error(this, "Failed to start insert of "+type+" 
binary blob: " + e1, e1);
                }
        }

@@ -934,14 +1062,16 @@
                updateManager.node.clientCore.alerts.unregister(alert);
        }

-       public boolean handleRequestMain(Message m, final PeerNode source) {
+       public boolean handleRequestJar(Message m, final PeerNode source, 
boolean isExt) {
                // Do we have the data?

-               int version = updateManager.newMainJarVersion();
-               File data = updateManager.getMainBlob(version);
+               int version = isExt ? updateManager.newExtJarVersion() : 
updateManager.newMainJarVersion();
+               File data = isExt ? updateManager.getExtBlob(version) : 
updateManager.getMainBlob(version);
+               
+               final String name = isExt ? "ext" : "main";

                if(data == null) {
-                       Logger.normal(this, "Peer " + source + " asked us for 
the blob file for the revocation key for the main jar but we don't have it!");
+                       Logger.normal(this, "Peer " + source + " asked us for 
the blob file for the "+name+" jar but we don't have it!");
                        // Probably a race condition on reconnect, hopefully 
we'll be asked again
                        return true;
                }
@@ -952,7 +1082,7 @@
                try {
                        raf = new RandomAccessFileWrapper(data, "r");
                } catch(FileNotFoundException e) {
-                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the main jar, we have downloaded it but don't have the file 
even though we did have it when we checked!: " + e, e);
+                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the "+name+" jar, we have downloaded it but don't have the 
file even though we did have it when we checked!: " + e, e);
                        return true;
                }

@@ -963,7 +1093,7 @@
                        prb = new 
PartiallyReceivedBulk(updateManager.node.getUSM(), length,
                                Node.PACKET_SIZE, raf, true);
                } catch(IOException e) {
-                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the main jar, we have downloaded it but we can't determine 
the file size: " + e, e);
+                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the "+name+" jar, we have downloaded it but we can't 
determine the file size: " + e, e);
                        return true;
                }

@@ -971,7 +1101,7 @@
                try {
                        bt = new BulkTransmitter(prb, source, uid, false, 
updateManager.ctr);
                } catch(DisconnectedException e) {
-                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the main jar, then disconnected: " + e, e);
+                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the "+name+" jar, then disconnected: " + e, e);
                        return true;
                }

@@ -979,13 +1109,15 @@

                        public void run() {
                                if(!bt.send())
-                                       Logger.error(this, "Failed to send main 
jar blob to " + source.userToString() + " : " + bt.getCancelReason());
+                                       Logger.error(this, "Failed to send 
"+name+" jar blob to " + source.userToString() + " : " + bt.getCancelReason());
                                else
-                                       Logger.normal(this, "Sent main jar blob 
to " + source.userToString());
+                                       Logger.normal(this, "Sent "+name+" jar 
blob to " + source.userToString());
                        }
                };

-               Message msg = DMT.createUOMSendingMain(uid, length, 
updateManager.updateURI.toString(), version);
+               Message msg =
+                       isExt ? DMT.createUOMSendingExtra(uid, length, 
updateManager.extURI.toString(), version) :
+                               DMT.createUOMSendingMain(uid, length, 
updateManager.updateURI.toString(), version);

                try {
                        source.sendAsync(msg, new AsyncMessageCallback() {
@@ -995,17 +1127,17 @@
                                                Logger.minor(this, "Sending 
data...");
                                        // Send the data

-                                       updateManager.node.executor.execute(r, 
"Main jar send for " + uid + " to " + source.userToString());
+                                       updateManager.node.executor.execute(r, 
name+" jar send for " + uid + " to " + source.userToString());
                                }

                                public void disconnected() {
                                        // Argh
-                                       Logger.error(this, "Peer " + source + " 
asked us for the blob file for the main jar, then disconnected when we tried to 
send the UOMSendingMain");
+                                       Logger.error(this, "Peer " + source + " 
asked us for the blob file for the "+name+" jar, then disconnected when we 
tried to send the UOMSendingMain");
                                }

                                public void fatalError() {
                                        // Argh
-                                       Logger.error(this, "Peer " + source + " 
asked us for the blob file for the main jar, then got a fatal error when we 
tried to send the UOMSendingMain");
+                                       Logger.error(this, "Peer " + source + " 
asked us for the blob file for the "+name+" jar, then got a fatal error when we 
tried to send the UOMSendingMain");
                                }

                                public void sent() {
@@ -1019,13 +1151,15 @@
                                }
                        }, updateManager.ctr);
                } catch(NotConnectedException e) {
-                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the main jar, then disconnected when we tried to send the 
UOMSendingMain: " + e, e);
+                       Logger.error(this, "Peer " + source + " asked us for 
the blob file for the "+name+" jar, then disconnected when we tried to send the 
UOMSendingExt: " + e, e);
                        return true;
                }

                return true;
+               
+               
        }
-
+       
        public boolean handleSendingMain(Message m, final PeerNode source) {
                final long uid = m.getLong(DMT.UID);
                final long length = m.getLong(DMT.FILE_LENGTH);
@@ -1137,6 +1271,117 @@
                return true;
        }

+       public boolean handleSendingExt(Message m, final PeerNode source) {
+               final long uid = m.getLong(DMT.UID);
+               final long length = m.getLong(DMT.FILE_LENGTH);
+               String key = m.getString(DMT.EXTRA_JAR_KEY);
+               final int version = m.getInt(DMT.EXTRA_JAR_VERSION);
+               final FreenetURI jarURI;
+               try {
+                       jarURI = new 
FreenetURI(key).setSuggestedEdition(version);
+               } catch(MalformedURLException e) {
+                       Logger.error(this, "Failed receiving ext jar " + 
version + " because URI not parsable: " + e + " for " + key, e);
+                       System.err.println("Failed receiving ext jar " + 
version + " because URI not parsable: " + e + " for " + key);
+                       e.printStackTrace();
+                       cancelSend(source, uid);
+                       synchronized(this) {
+                               this.nodesAskedSendExtJar.remove(source);
+                       }
+                       return true;
+               }
+
+               
if(!jarURI.equals(updateManager.extURI.setSuggestedEdition(version))) {
+                       System.err.println("Node sending us a ext jar update (" 
+ version + ") from the wrong URI:\n" +
+                               "Node: " + source.userToString() + "\n" +
+                               "Our   URI: " + updateManager.extURI + "\n" +
+                               "Their URI: " + jarURI);
+                       cancelSend(source, uid);
+                       synchronized(this) {
+                               this.nodesAskedSendExtJar.remove(source);
+                       }
+                       return true;
+               }
+
+               if(updateManager.isBlown()) {
+                       if(logMINOR)
+                               Logger.minor(this, "Key blown, so not receiving 
main jar from " + source + "(" + uid + ")");
+                       cancelSend(source, uid);
+                       synchronized(this) {
+                               this.nodesAskedSendExtJar.remove(source);
+                       }
+                       return true;
+               }
+
+               if(length > NodeUpdateManager.MAX_MAIN_JAR_LENGTH) {
+                       System.err.println("Node " + source.userToString() + " 
offered us a ext jar (" + version + ") " + SizeUtil.formatSize(length) + " 
long. This is unacceptably long so we have refused the transfer.");
+                       Logger.error(this, "Node " + source.userToString() + " 
offered us a ext jar (" + version + ") " + SizeUtil.formatSize(length) + " 
long. This is unacceptably long so we have refused the transfer.");
+                       // If the transfer fails, we don't try again.
+                       cancelSend(source, uid);
+                       synchronized(this) {
+                               this.nodesAskedSendExtJar.remove(source);
+                       }
+                       return true;
+               }
+
+               // Okay, we can receive it
+
+               final File temp;
+
+               try {
+                       temp = File.createTempFile("ext-", ".fblob.tmp", 
updateManager.node.clientCore.getPersistentTempDir());
+                       temp.deleteOnExit();
+               } catch(IOException e) {
+                       System.err.println("Cannot save new ext jar to disk and 
therefore cannot fetch it from our peer!: " + e);
+                       e.printStackTrace();
+                       cancelSend(source, uid);
+                       synchronized(this) {
+                               this.nodesAskedSendExtJar.remove(source);
+                       }
+                       return true;
+               }
+
+               RandomAccessFileWrapper raf;
+               try {
+                       raf = new RandomAccessFileWrapper(temp, "rw");
+               } catch(FileNotFoundException e) {
+                       Logger.error(this, "Peer " + source + " sending us a 
ext jar binary blob, but we lost the temp file " + temp + " : " + e, e);
+                       synchronized(this) {
+                               this.nodesAskedSendExtJar.remove(source);
+                       }
+                       return true;
+               }
+
+               PartiallyReceivedBulk prb = new 
PartiallyReceivedBulk(updateManager.node.getUSM(), length,
+                       Node.PACKET_SIZE, raf, false);
+
+               final BulkReceiver br = new BulkReceiver(prb, source, uid, 
updateManager.ctr);
+
+               updateManager.node.executor.execute(new Runnable() {
+
+                       public void run() {
+                               try {
+                                       
synchronized(UpdateOverMandatoryManager.class) {
+                                               
nodesAskedSendExtJar.remove(source);
+                                               nodesSendingExtJar.add(source);
+                                       }
+                                       if(br.receive())
+                                               // Success!
+                                               processExtJarBlob(temp, source, 
version, jarURI);
+                                       else {
+                                               Logger.error(this, "Failed to 
transfer ext jar " + version + " from " + source);
+                                               System.err.println("Failed to 
transfer ext jar " + version + " from " + source);
+                                       }
+                               } finally {
+                                       
synchronized(UpdateOverMandatoryManager.class) {
+                                               
nodesSendingExtJar.remove(source);
+                                       }
+                               }
+                       }
+               }, "Ext jar (" + version + ") receive for " + uid + " from " + 
source.userToString());
+
+               return true;
+       }
+
        protected void processMainJarBlob(final File temp, final PeerNode 
source, final int version, FreenetURI uri) {
                SimpleBlockSet blocks = new SimpleBlockSet();

@@ -1238,7 +1483,7 @@
                                }
                                mainUpdater.onSuccess(result, state, 
cleanedBlobFile, version);
                                temp.delete();
-                               insertBlob(mainUpdater.getBlobFile(version));
+                               insertBlob(mainUpdater.getBlobFile(version), 
"main jar");
                        }

                        public void onSuccess(BaseClientPutter state, 
ObjectContainer container) {
@@ -1259,6 +1504,128 @@

        }

+       protected void processExtJarBlob(final File temp, final PeerNode 
source, final int version, FreenetURI uri) {
+               SimpleBlockSet blocks = new SimpleBlockSet();
+
+               DataInputStream dis = null;
+               try {
+                       dis = new DataInputStream(new BufferedInputStream(new 
FileInputStream(temp)));
+                       BinaryBlob.readBinaryBlob(dis, blocks, true);
+               } catch(FileNotFoundException e) {
+                       Logger.error(this, "Somebody deleted " + temp + " ? We 
lost the ext jar (" + version + ") from " + source.userToString() + "!");
+                       System.err.println("Somebody deleted " + temp + " ? We 
lost the ext jar (" + version + ") from " + source.userToString() + "!");
+                       return;
+               } catch(IOException e) {
+                       Logger.error(this, "Could not read ext jar (" + version 
+ ") from temp file " + temp + " from node " + source.userToString() + " !");
+                       System.err.println("Could not read ext jar (" + version 
+ ") from temp file " + temp + " from node " + source.userToString() + " !");
+                       // FIXME will be kept until exit for debugging purposes
+                       return;
+               } catch(BinaryBlobFormatException e) {
+                       Logger.error(this, "Peer " + source.userToString() + " 
sent us an invalid ext jar (" + version + ")!: " + e, e);
+                       System.err.println("Peer " + source.userToString() + " 
sent us an invalid ext jar (" + version + ")!: " + e);
+                       e.printStackTrace();
+                       // FIXME will be kept until exit for debugging purposes
+                       return;
+               } finally {
+                       if(dis != null)
+                               try {
+                                       dis.close();
+                               } catch(IOException e) {
+                                       // Ignore
+                               }
+               }
+
+               // Fetch the jar from the datastore plus the binary blob
+
+               FetchContext tempContext = 
updateManager.node.clientCore.makeClient((short) 0, true).getFetchContext();
+               tempContext.localRequestOnly = true;
+               tempContext.blocks = blocks;
+
+               File f;
+               FileBucket b = null;
+               try {
+                       f = File.createTempFile("ext-", ".fblob.tmp", 
updateManager.node.clientCore.getPersistentTempDir());
+                       b = new FileBucket(f, false, false, true, true, true);
+               } catch(IOException e) {
+                       Logger.error(this, "Cannot share ext jar from " + 
source.userToString() + " with our peers because cannot write the cleaned 
version to disk: " + e, e);
+                       System.err.println("Cannot share ext jar from " + 
source.userToString() + " with our peers because cannot write the cleaned 
version to disk: " + e);
+                       e.printStackTrace();
+                       b = null;
+                       f = null;
+               }
+               final FileBucket cleanedBlob = b;
+               final File cleanedBlobFile = f;
+
+               ClientCallback myCallback = new ClientCallback() {
+
+                       public void onFailure(FetchException e, ClientGetter 
state, ObjectContainer container) {
+                               if(e.mode == FetchException.CANCELLED) {
+                                       // Eh?
+                                       Logger.error(this, "Cancelled fetch 
from store/blob of ext jar (" + version + ") from " + source.userToString());
+                                       System.err.println("Cancelled fetch 
from store/blob of ext jar (" + version + ") from " + source.userToString() + " 
to " + temp + " - please report to developers");
+                               // Probably best to keep files around for now.
+                               } else if(e.isFatal()) {
+                                       // Bogus as inserted. Ignore.
+                                       temp.delete();
+                                       Logger.error(this, "Failed to fetch ext 
jar " + version + " from " + source.userToString() + " : fatal error (update 
was probably inserted badly): " + e, e);
+                                       System.err.println("Failed to fetch ext 
jar " + version + " from " + source.userToString() + " : fatal error (update 
was probably inserted badly): " + e);
+                               } else {
+                                       Logger.error(this, "Failed to fetch ext 
jar " + version + " from blob from " + source.userToString());
+                                       System.err.println("Failed to fetch ext 
jar " + version + " from blob from " + source.userToString());
+                               }
+                       }
+
+                       public void onFailure(InsertException e, 
BaseClientPutter state, ObjectContainer container) {
+                               // Ignore, not possible
+                       }
+
+                       public void onFetchable(BaseClientPutter state, 
ObjectContainer container) {
+                               // Irrelevant
+                       }
+
+                       public void onGeneratedURI(FreenetURI uri, 
BaseClientPutter state, ObjectContainer container) {
+                               // Ignore, not possible
+                       }
+
+                       public void onMajorProgress(ObjectContainer container) {
+                               // Ignore
+                       }
+
+                       public void onSuccess(FetchResult result, ClientGetter 
state, ObjectContainer container) {
+                               System.err.println("Got ext jar version " + 
version + " from " + source.userToString());
+                               if(result.size() == 0) {
+                                       System.err.println("Ignoring because 0 
bytes long");
+                                       return;
+                               }
+
+                               NodeUpdater extUpdater = 
updateManager.extUpdater;
+                               if(extUpdater == null) {
+                                       System.err.println("Not updating 
because ext updater is disabled!");
+                                       return;
+                               }
+                               extUpdater.onSuccess(result, state, 
cleanedBlobFile, version);
+                               temp.delete();
+                               insertBlob(extUpdater.getBlobFile(version), 
"ext jar");
+                       }
+
+                       public void onSuccess(BaseClientPutter state, 
ObjectContainer container) {
+                               // Ignore, not possible
+                       }
+               };
+
+               ClientGetter cg = new ClientGetter(myCallback,
+                               
updateManager.node.clientCore.requestStarters.chkFetchScheduler,
+                               
updateManager.node.clientCore.requestStarters.sskFetchScheduler,
+                               uri, tempContext, (short) 0, this, null, 
cleanedBlob);
+
+                       try {
+                               
updateManager.node.clientCore.clientContext.start(cg);
+                       } catch(FetchException e1) {
+                               myCallback.onFailure(e1, cg, null);
+                       }
+
+       }
+
        protected boolean removeOldTempFiles() {
                File oldTempFilesPeerDir = 
updateManager.node.clientCore.getPersistentTempDir();
                if(!oldTempFilesPeerDir.exists())

Modified: branches/db4o/freenet/src/freenet/pluginmanager/PluginManager.java
===================================================================
--- branches/db4o/freenet/src/freenet/pluginmanager/PluginManager.java  
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/pluginmanager/PluginManager.java  
2008-11-29 20:45:18 UTC (rev 23990)
@@ -214,9 +214,12 @@
                        // not a freenet key
                }

-               if(new File(pluginname).exists()) {
-                       startPluginFile(pluginname, store);
-                       return;
+               File[] roots = File.listRoots();
+               for(File f : roots) {
+                       if(pluginname.startsWith(f.getName()) && new 
File(pluginname).exists()) {
+                               startPluginFile(pluginname, store);
+                               return;
+                       }
                }

                startPluginURL(pluginname, store);

Modified: 
branches/db4o/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java
===================================================================
--- 
branches/db4o/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java  
    2008-11-29 20:39:52 UTC (rev 23989)
+++ 
branches/db4o/freenet/src/freenet/store/saltedhash/SaltedHashFreenetStore.java  
    2008-11-29 20:45:18 UTC (rev 23990)
@@ -742,6 +742,17 @@
                        final long newHdLen = (headerBlockLength + 
dataBlockLength + hdPadding) * storeFileSize;

                        if (preallocate) {
+                               /*
+                                * Fill the store file with random data. This 
won't be compressed, unlike filling it with zeros.
+                                * So the disk space usage of the node will not 
change (apart from temp files).
+                                * 
+                                * Note that MersenneTwister is *not* 
cryptographically secure, in fact from 2.4KB of output you
+                                * can predict the rest of the stream! This is 
okay because an attacker knows which blocks are
+                                * occupied anyway; it is essential to label 
them to get good data retention on resizing etc.
+                                * 
+                                * On my test system (phenom 2.2GHz), this does 
approx 80MB/sec. If I reseed every 2kB from an
+                                * AES CTR, which is pointless as I just 
explained, it does 40MB/sec.
+                                */
                                byte[] b = new byte[4096];
                                ByteBuffer bf = ByteBuffer.wrap(b); 



Property changes on: 
branches/db4o/freenet/src/freenet/support/BinaryBloomFilter.java
___________________________________________________________________
Modified: svn:mergeinfo
   - /trunk/freenet/src/freenet/support/BinaryBloomFilter.java:22002-23920
   + /trunk/freenet/src/freenet/support/BinaryBloomFilter.java:22002-23968


Property changes on: branches/db4o/freenet/src/freenet/support/BloomFilter.java
___________________________________________________________________
Modified: svn:mergeinfo
   - /trunk/freenet/src/freenet/support/BloomFilter.java:22002-23920
   + /trunk/freenet/src/freenet/support/BloomFilter.java:22002-23968


Property changes on: 
branches/db4o/freenet/src/freenet/support/CountingBloomFilter.java
___________________________________________________________________
Modified: svn:mergeinfo
   - /trunk/freenet/src/freenet/support/CountingBloomFilter.java:22002-23920
   + /trunk/freenet/src/freenet/support/CountingBloomFilter.java:22002-23968

Modified: branches/db4o/freenet/src/freenet/support/TransferThread.java
===================================================================
--- branches/db4o/freenet/src/freenet/support/TransferThread.java       
2008-11-29 20:39:52 UTC (rev 23989)
+++ branches/db4o/freenet/src/freenet/support/TransferThread.java       
2008-11-29 20:45:18 UTC (rev 23990)
@@ -11,9 +11,11 @@
 import freenet.client.async.ClientGetter;
 import freenet.keys.FreenetURI;
 import freenet.node.Node;
+import freenet.node.PrioRunnable;
+import freenet.support.io.NativeThread;
 import freenet.support.io.TempBucketFactory;

-public abstract class TransferThread implements Runnable {
+public abstract class TransferThread implements PrioRunnable {

        private final String mName;
        protected final Node mNode;
@@ -37,6 +39,9 @@
        protected void start() {
                mNode.executor.execute(this, mName);
        }
+       
+       /** Specify the priority of this thread. Priorities to return can be 
found in class NativeThread. */
+       public abstract int getPriority();

        public void run() {
                isRunning = true;


Property changes on: 
branches/db4o/freenet/test/freenet/support/io/MockInputStream.java
___________________________________________________________________
Modified: svn:mergeinfo
   - /trunk/freenet/test/freenet/support/io/MockInputStream.java:22002-23920
   + /trunk/freenet/test/freenet/support/io/MockInputStream.java:22002-23968


Reply via email to