Author: toad
Date: 2006-05-20 01:54:17 +0000 (Sat, 20 May 2006)
New Revision: 8793

Modified:
   trunk/freenet/src/freenet/client/ArchiveContext.java
   trunk/freenet/src/freenet/client/ArchiveKey.java
   trunk/freenet/src/freenet/client/ArchiveManager.java
   trunk/freenet/src/freenet/client/ArchiveStoreContext.java
   trunk/freenet/src/freenet/client/ClientMetadata.java
   trunk/freenet/src/freenet/client/FetcherContext.java
   trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
   trunk/freenet/src/freenet/client/Metadata.java
   trunk/freenet/src/freenet/client/RealArchiveStoreItem.java
   trunk/freenet/src/freenet/client/async/ClientGetter.java
   trunk/freenet/src/freenet/client/async/ClientPutter.java
   trunk/freenet/src/freenet/client/async/ManifestElement.java
   trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java
   trunk/freenet/src/freenet/client/async/SingleFileFetcher.java
   trunk/freenet/src/freenet/client/async/SingleFileInserter.java
   trunk/freenet/src/freenet/client/async/SplitFileInserter.java
   trunk/freenet/src/freenet/keys/FreenetURI.java
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/Version.java
   trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
   trunk/freenet/src/freenet/support/LRUHashtable.java
Log:
731: Basic zip manifest support.

Modified: trunk/freenet/src/freenet/client/ArchiveContext.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveContext.java        2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/ArchiveContext.java        2006-05-20 
01:54:17 UTC (rev 8793)
@@ -12,15 +12,17 @@
 public class ArchiveContext {

        HashSet soFar = new HashSet();
-       int maxArchiveLevels;
+       final int maxArchiveLevels;

+       public ArchiveContext(int max) {
+               this.maxArchiveLevels = max;
+       }
+       
        /**
         * Check for a loop.
         * The URI provided is expected to be a reasonably unique identifier 
for the archive.
         */
        public synchronized void doLoopDetection(FreenetURI key) throws 
ArchiveFailureException {
-               if(!soFar.add(key))
-                       throw new ArchiveFailureException("Archive loop 
detected");
                if(soFar.size() > maxArchiveLevels)
                        throw new 
ArchiveFailureException(ArchiveFailureException.TOO_MANY_LEVELS);
        }

Modified: trunk/freenet/src/freenet/client/ArchiveKey.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveKey.java    2006-05-19 22:36:32 UTC 
(rev 8792)
+++ trunk/freenet/src/freenet/client/ArchiveKey.java    2006-05-20 01:54:17 UTC 
(rev 8793)
@@ -22,4 +22,8 @@
        public int hashCode() {
                return key.hashCode() ^ filename.hashCode();
        }
+       
+       public String toString() {
+               return key+":"+filename;
+       }
 }
\ No newline at end of file

Modified: trunk/freenet/src/freenet/client/ArchiveManager.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveManager.java        2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/ArchiveManager.java        2006-05-20 
01:54:17 UTC (rev 8793)
@@ -70,6 +70,7 @@

        /** Add an ArchiveHandler by key */
        private synchronized void putCached(FreenetURI key, ArchiveHandler zip) 
{
+               Logger.minor(this, "Put cached AH for "+key+" : "+zip);
                archiveHandlers.push(key, zip);
                while(archiveHandlers.size() > maxArchiveHandlers)
                        archiveHandlers.popKey(); // dump it
@@ -77,7 +78,9 @@

        /** Get an ArchiveHandler by key */
        public ArchiveHandler getCached(FreenetURI key) {
+               Logger.minor(this, "Get cached AH for "+key);
                ArchiveHandler handler = (ArchiveHandler) 
archiveHandlers.get(key);
+               if(handler == null) return null;
                archiveHandlers.push(key, handler);
                return handler;
        }
@@ -126,6 +129,7 @@
         * @throws ArchiveFailureException 
         */
        public synchronized Bucket getCached(FreenetURI key, String filename) 
throws ArchiveFailureException {
+               Logger.minor(this, "Fetch cached: "+key+" "+filename);
                ArchiveKey k = new ArchiveKey(key, filename);
                ArchiveStoreItem asi = (ArchiveStoreItem) storedData.get(k);
                if(asi == null) return null;
@@ -155,6 +159,9 @@
         * changed.
         */
        public void extractToCache(FreenetURI key, short archiveType, Bucket 
data, ArchiveContext archiveContext, ArchiveStoreContext ctx) throws 
ArchiveFailureException, ArchiveRestartException {
+               
+               Logger.minor(this, "Extracting "+key);
+               
                ctx.removeAllCachedItems(); // flush cache anyway
                long expectedSize = ctx.getLastSize();
                long archiveSize = data.size();
@@ -188,14 +195,16 @@
                        zis = new ZipInputStream(data.getInputStream());

                        // MINOR: Assumes the first entry in the zip is a 
directory. 
-                       ZipEntry entry =  zis.getNextEntry();
+                       ZipEntry entry;

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

-outer:         while(entry != null) {
+outer:         while(true) {
                                entry = zis.getNextEntry();
+                               if(entry == null) break;
+                               if(entry.isDirectory()) continue;
                                String name = entry.getName();
                                if(names.contains(name)) {
                                        Logger.error(this, "Duplicate key 
"+name+" in archive "+key);
@@ -351,6 +360,7 @@
         */
        private void addErrorElement(ArchiveStoreContext ctx, FreenetURI key, 
String name, String error) {
                ErrorArchiveStoreItem element = new ErrorArchiveStoreItem(ctx, 
key, name, error);
+               Logger.minor(this, "Adding error element: "+element+" for 
"+key+" "+name);
                synchronized(storedData) {
                        storedData.push(element.key, element);
                }
@@ -358,10 +368,10 @@

        /**
         * Add a store element.
-        * @return True if this was the metadata element.
         */
        private void addStoreElement(ArchiveStoreContext ctx, FreenetURI key, 
String name, TempStoreElement temp) {
                RealArchiveStoreItem element = new RealArchiveStoreItem(this, 
ctx, key, name, temp);
+               Logger.minor(this, "Adding store element: "+element+" ( "+key+" 
"+name+" )");
                synchronized(storedData) {
                        storedData.push(element.key, element);
                        trimStoredData();

Modified: trunk/freenet/src/freenet/client/ArchiveStoreContext.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveStoreContext.java   2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/ArchiveStoreContext.java   2006-05-20 
01:54:17 UTC (rev 8793)
@@ -3,6 +3,7 @@
 import freenet.keys.FreenetURI;
 import freenet.support.Bucket;
 import freenet.support.DoublyLinkedListImpl;
+import freenet.support.Logger;

 /**
  * Tracks all files currently in the cache from a given key.
@@ -43,8 +44,7 @@
        }

        /**
-        * Fetch a file in an archive. Will check the cache first, then fetch 
the archive if
-        * necessary.
+        * Fetch a file in an archive.
         */
        public Bucket get(String internalName, ArchiveContext archiveContext, 
ClientMetadata dm, int recursionLevel, 
                        boolean dontEnterImplicitArchives) throws 
ArchiveFailureException, ArchiveRestartException, MetadataParseException, 
FetchException {
@@ -55,6 +55,7 @@
                Bucket data;

                // Fetch from cache
+               Logger.minor(this, "Checking cache: "+key+" "+internalName);
                if((data = manager.getCached(key, internalName)) != null) {
                        return data;
                }

Modified: trunk/freenet/src/freenet/client/ClientMetadata.java
===================================================================
--- trunk/freenet/src/freenet/client/ClientMetadata.java        2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/ClientMetadata.java        2006-05-20 
01:54:17 UTC (rev 8793)
@@ -3,7 +3,7 @@
 /**
  * Stores the metadata that the client might actually be interested in.
  */
-public class ClientMetadata {
+public class ClientMetadata implements Cloneable {

        /** The document MIME type */
        private String mimeType;
@@ -37,4 +37,12 @@
        public boolean isTrivial() {
                return (mimeType == null || mimeType.equals(""));
        }
+       
+       public Object clone() {
+               try {
+                       return super.clone();
+               } catch (CloneNotSupportedException e) {
+                       throw new Error(e);
+               }
+       }
 }

Modified: trunk/freenet/src/freenet/client/FetcherContext.java
===================================================================
--- trunk/freenet/src/freenet/client/FetcherContext.java        2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/FetcherContext.java        2006-05-20 
01:54:17 UTC (rev 8793)
@@ -22,6 +22,7 @@
        public USKManager uskManager;
        public int maxRecursionLevel;
        public int maxArchiveRestarts;
+       public int maxArchiveLevels;
        public boolean dontEnterImplicitArchives;
        public int maxSplitfileThreads;
        public int maxSplitfileBlockRetries;
@@ -44,7 +45,7 @@
        public boolean returnZIPManifests;

        public FetcherContext(long curMaxLength, 
-                       long curMaxTempLength, int maxMetadataSize, int 
maxRecursionLevel, int maxArchiveRestarts,
+                       long curMaxTempLength, int maxMetadataSize, int 
maxRecursionLevel, int maxArchiveRestarts, int maxArchiveLevels,
                        boolean dontEnterImplicitArchives, int 
maxSplitfileThreads,
                        int maxSplitfileBlockRetries, int 
maxNonSplitfileRetries,
                        boolean allowSplitfiles, boolean followRedirects, 
boolean localRequestOnly,
@@ -59,6 +60,7 @@
                this.bucketFactory = bucketFactory;
                this.maxRecursionLevel = maxRecursionLevel;
                this.maxArchiveRestarts = maxArchiveRestarts;
+               this.maxArchiveLevels = maxArchiveLevels;
                this.dontEnterImplicitArchives = dontEnterImplicitArchives;
                this.random = random;
                this.maxSplitfileThreads = maxSplitfileThreads;
@@ -88,6 +90,7 @@
                        this.bucketFactory = ctx.bucketFactory;
                        this.maxRecursionLevel = ctx.maxRecursionLevel;
                        this.maxArchiveRestarts = ctx.maxArchiveRestarts;
+                       this.maxArchiveLevels = ctx.maxArchiveLevels;
                        this.dontEnterImplicitArchives = 
ctx.dontEnterImplicitArchives;
                        this.random = ctx.random;
                        this.maxSplitfileThreads = ctx.maxSplitfileThreads;
@@ -109,6 +112,7 @@
                        this.bucketFactory = ctx.bucketFactory;
                        this.maxRecursionLevel = 1;
                        this.maxArchiveRestarts = 0;
+                       this.maxArchiveLevels = ctx.maxArchiveLevels;
                        this.dontEnterImplicitArchives = true;
                        this.random = ctx.random;
                        this.maxSplitfileThreads = 0;
@@ -130,6 +134,7 @@
                        this.bucketFactory = ctx.bucketFactory;
                        this.maxRecursionLevel = ctx.maxRecursionLevel;
                        this.maxArchiveRestarts = ctx.maxArchiveRestarts;
+                       this.maxArchiveLevels = ctx.maxArchiveLevels;
                        this.dontEnterImplicitArchives = 
ctx.dontEnterImplicitArchives;
                        this.random = ctx.random;
                        this.maxSplitfileThreads = ctx.maxSplitfileThreads;
@@ -151,6 +156,7 @@
                        this.bucketFactory = ctx.bucketFactory;
                        this.maxRecursionLevel = ctx.maxRecursionLevel;
                        this.maxArchiveRestarts = ctx.maxArchiveRestarts;
+                       this.maxArchiveLevels = ctx.maxArchiveLevels;
                        this.dontEnterImplicitArchives = 
ctx.dontEnterImplicitArchives;
                        this.random = ctx.random;
                        this.maxSplitfileThreads = ctx.maxSplitfileThreads;
@@ -172,6 +178,7 @@
                        this.bucketFactory = ctx.bucketFactory;
                        this.maxRecursionLevel = ctx.maxRecursionLevel;
                        this.maxArchiveRestarts = ctx.maxArchiveRestarts;
+                       this.maxArchiveLevels = ctx.maxArchiveLevels;
                        this.dontEnterImplicitArchives = 
ctx.dontEnterImplicitArchives;
                        this.random = ctx.random;
                        this.maxSplitfileThreads = ctx.maxSplitfileThreads;

Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2006-05-19 22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2006-05-20 01:54:17 UTC (rev 8793)
@@ -35,6 +35,7 @@
        private final boolean cacheLocalRequests;
        static final int MAX_RECURSION = 10;
        static final int MAX_ARCHIVE_RESTARTS = 2;
+       static final int MAX_ARCHIVE_LEVELS = 4;
        static final boolean DONT_ENTER_IMPLICIT_ARCHIVES = true;
        /** Number of threads used by a splitfile fetch */
        static final int SPLITFILE_THREADS = 20;
@@ -163,7 +164,7 @@
                }
                return                  
                        new FetcherContext(maxLength, maxTempLength, 
curMaxMetadataLength, 
-                               MAX_RECURSION, MAX_ARCHIVE_RESTARTS, 
DONT_ENTER_IMPLICIT_ARCHIVES, 
+                               MAX_RECURSION, MAX_ARCHIVE_RESTARTS, 
MAX_ARCHIVE_LEVELS, DONT_ENTER_IMPLICIT_ARCHIVES, 
                                SPLITFILE_THREADS, SPLITFILE_BLOCK_RETRIES, 
NON_SPLITFILE_RETRIES,
                                FETCH_SPLITFILES, FOLLOW_REDIRECTS, 
LOCAL_REQUESTS_ONLY,
                                MAX_SPLITFILE_BLOCKS_PER_SEGMENT, 
MAX_SPLITFILE_CHECK_BLOCKS_PER_SEGMENT,

Modified: trunk/freenet/src/freenet/client/Metadata.java
===================================================================
--- trunk/freenet/src/freenet/client/Metadata.java      2006-05-19 22:36:32 UTC 
(rev 8792)
+++ trunk/freenet/src/freenet/client/Metadata.java      2006-05-20 01:54:17 UTC 
(rev 8793)
@@ -40,8 +40,8 @@
        public static final byte SIMPLE_REDIRECT = 0;
        static final byte MULTI_LEVEL_METADATA = 1;
        static final byte SIMPLE_MANIFEST = 2;
-       static final byte ZIP_MANIFEST = 3;
-       static final byte ZIP_INTERNAL_REDIRECT = 4;
+       public static final byte ZIP_MANIFEST = 3;
+       public static final byte ZIP_INTERNAL_REDIRECT = 4;

        // 2 bytes of flags
        /** Is a splitfile */
@@ -107,7 +107,6 @@
        FreenetURI[] splitfileCheckKeys;

        // Manifests
-       int manifestEntryCount;
        /** Manifest entries by name */
        HashMap manifestEntries;

@@ -148,7 +147,7 @@
                Metadata m;
                try {
                        DataInputStream dis = new DataInputStream(is);
-                       m = new Metadata(dis, false, data.size());
+                       m = new Metadata(dis, data.size());
                } finally {
                        is.close();
                }
@@ -158,12 +157,12 @@
        /** Parse some metadata from a byte[]. 
         * @throws IOException If the data is incomplete, or something wierd 
happens. */
        private Metadata(byte[] data) throws IOException {
-               this(new DataInputStream(new ByteArrayInputStream(data)), 
false, data.length);
+               this(new DataInputStream(new ByteArrayInputStream(data)), 
data.length);
        }

        /** Parse some metadata from a DataInputStream
         * @throws IOException If an I/O error occurs, or the data is 
incomplete. */
-       public Metadata(DataInputStream dis, boolean 
acceptZipInternalRedirects, long length) throws IOException, 
MetadataParseException {
+       public Metadata(DataInputStream dis, long length) throws IOException, 
MetadataParseException {
                long magic = dis.readLong();
                if(magic != FREENET_METADATA_MAGIC)
                        throw new MetadataParseException("Invalid magic 
"+magic);
@@ -171,13 +170,13 @@
                if(version != 0)
                        throw new MetadataParseException("Unsupported version 
"+version);
                documentType = dis.readByte();
-               if(documentType < 0 || documentType > 5 || 
-                               (documentType == ZIP_INTERNAL_REDIRECT && 
!acceptZipInternalRedirects))
+               if(documentType < 0 || documentType > 5)
                        throw new MetadataParseException("Unsupported document 
type: "+documentType);
+               Logger.minor(this, "Document type: "+documentType);

                boolean compressed = false;
                if(documentType == SIMPLE_REDIRECT || documentType == 
MULTI_LEVEL_METADATA
-                               || documentType == ZIP_MANIFEST) {
+                               || documentType == ZIP_MANIFEST || documentType 
== ZIP_INTERNAL_REDIRECT) {
                        short flags = dis.readShort();
                        splitfile = (flags & FLAGS_SPLITFILE) == 
FLAGS_SPLITFILE;
                        dbr = (flags & FLAGS_DBR) == FLAGS_DBR;
@@ -190,12 +189,14 @@
                }

                if(documentType == ZIP_MANIFEST) {
+                       Logger.minor(this, "Zip manifest");
                        archiveType = dis.readShort();
                        if(archiveType != ARCHIVE_ZIP)
                                throw new MetadataParseException("Unrecognized 
archive type "+archiveType);
                }

                if(splitfile) {
+                       Logger.minor(this, "Splitfile");
                        dataLength = dis.readLong();
                        if(dataLength < -1)
                                throw new MetadataParseException("Invalid real 
content length "+dataLength);
@@ -261,7 +262,7 @@

                clientMetadata = new ClientMetadata(mimeType);

-               if((!splitfile) && documentType == SIMPLE_REDIRECT || 
documentType == ZIP_MANIFEST) {
+               if((!splitfile) && (documentType == SIMPLE_REDIRECT || 
documentType == ZIP_MANIFEST)) {
                        simpleRedirectKey = readKey(dis);
                } else if(splitfile) {
                        splitfileAlgorithm = dis.readShort();
@@ -304,7 +305,7 @@
                }

                if(documentType == SIMPLE_MANIFEST) {
-                       manifestEntryCount = dis.readInt();
+                       int manifestEntryCount = dis.readInt();
                        if(manifestEntryCount < 0)
                                throw new MetadataParseException("Invalid 
manifest entry count: "+manifestEntryCount);

@@ -317,6 +318,7 @@
                                byte[] buf = new byte[nameLength];
                                dis.readFully(buf);
                                String name = new String(buf, "UTF-8");
+                               Logger.minor(this, "Entry "+i+" name "+name);
                                short len = dis.readShort();
                                if(len < 0)
                                        throw new 
MetadataParseException("Invalid manifest entry size: "+len);
@@ -335,8 +337,11 @@

                if(documentType == ZIP_INTERNAL_REDIRECT) {
                        int len = dis.readShort();
+                       Logger.minor(this, "Reading zip internal redirect 
length "+len);
                        byte[] buf = new byte[len];
+                       dis.readFully(buf);
                        nameInArchive = new String(buf, "UTF-8");
+                       Logger.minor(this, "Zip internal redirect: 
"+nameInArchive+" ("+len+")");
                }
        }

@@ -378,8 +383,6 @@
                        } else throw new IllegalArgumentException("Not String 
nor HashMap: "+o);
                        manifestEntries.put(key, target);
                }
-               manifestEntryCount = count;
-               
        }

        /**
@@ -432,7 +435,6 @@
                                manifestEntries.put(key, subMap);
                        }
                }
-               manifestEntryCount = count;
        }

        /**
@@ -457,13 +459,12 @@
                        Metadata target;
                        if(o instanceof String) {
                                // Zip internal redirect
-                               target = new Metadata(ZIP_INTERNAL_REDIRECT, 
key);
+                               target = new Metadata(ZIP_INTERNAL_REDIRECT, 
key, new ClientMetadata(DefaultMIMETypes.guessMIMEType(key)));
                        } else if(o instanceof HashMap) {
                                target = new Metadata((HashMap)o);
                        } else throw new IllegalArgumentException("Not String 
nor HashMap: "+o);
                        manifestEntries.put(key, target);
                }
-               manifestEntryCount = count;
        }

        /**
@@ -488,13 +489,12 @@
                        Metadata target;
                        if(o instanceof String) {
                                // Zip internal redirect
-                               target = new Metadata(ZIP_INTERNAL_REDIRECT, 
key);
+                               target = new Metadata(ZIP_INTERNAL_REDIRECT, 
key, new ClientMetadata(DefaultMIMETypes.guessMIMEType(key)));
                        } else if(o instanceof HashMap) {
                                target = new Metadata((HashMap)o);
                        } else throw new IllegalArgumentException("Not String 
nor HashMap: "+o);
                        manifestEntries.put(key, target);
                }
-               manifestEntryCount = count;
        }

        /**
@@ -504,12 +504,12 @@
         * @param arg The argument; in the case of ZIP_INTERNAL_REDIRECT, the 
filename in
         * the archive to read from.
         */
-       public Metadata(byte docType, String arg) {
+       public Metadata(byte docType, String arg, ClientMetadata cm) {
                if(docType == ZIP_INTERNAL_REDIRECT) {
                        documentType = docType;
                        // Determine MIME type
-                       setMIMEType(DefaultMIMETypes.guessMIMEType(arg));
-                       clientMetadata = new ClientMetadata(mimeType);
+                       this.clientMetadata = cm;
+                       this.setMIMEType(cm.getMIMEType());
                        nameInArchive = arg;
                } else
                        throw new IllegalArgumentException();
@@ -522,7 +522,7 @@
         * @param cm The client metadata, if any.
         */
        public Metadata(byte docType, FreenetURI uri, ClientMetadata cm) {
-               if(docType == SIMPLE_REDIRECT) {
+               if(docType == SIMPLE_REDIRECT || docType == ZIP_MANIFEST) {
                        documentType = docType;
                        clientMetadata = cm;
                        if(cm != null && !cm.isTrivial()) {
@@ -538,11 +538,14 @@
                        throw new IllegalArgumentException();
        }

-       public Metadata(short algo, FreenetURI[] dataURIs, FreenetURI[] 
checkURIs, int segmentSize, int checkSegmentSize, ClientMetadata cm, long 
dataLength, short compressionAlgo, boolean isMetadata) {
+       public Metadata(short algo, FreenetURI[] dataURIs, FreenetURI[] 
checkURIs, int segmentSize, int checkSegmentSize, ClientMetadata cm, long 
dataLength, short compressionAlgo, boolean isMetadata, boolean 
insertAsArchiveManifest) {
                if(isMetadata)
                        documentType = MULTI_LEVEL_METADATA;
-               else
-                       documentType = SIMPLE_REDIRECT;
+               else {
+                       if(insertAsArchiveManifest)
+                               documentType = ZIP_MANIFEST;
+                       else documentType = SIMPLE_REDIRECT;
+               }
                splitfile = true;
                splitfileAlgorithm = algo;
                this.dataLength = dataLength;
@@ -725,7 +728,7 @@
                dos.writeShort(0); // version
                dos.writeByte(documentType);
                if(documentType == SIMPLE_REDIRECT || documentType == 
MULTI_LEVEL_METADATA
-                               || documentType == ZIP_MANIFEST) {
+                               || documentType == ZIP_MANIFEST || documentType 
== ZIP_INTERNAL_REDIRECT) {
                        short flags = 0;
                        if(splitfile) flags |= FLAGS_SPLITFILE;
                        if(dbr) flags |= FLAGS_DBR;
@@ -772,7 +775,7 @@
                if(extraMetadata)
                        throw new UnsupportedOperationException("No extra 
metadata support yet");

-               if((!splitfile) && documentType == SIMPLE_REDIRECT || 
documentType == ZIP_MANIFEST) {
+               if((!splitfile) && (documentType == SIMPLE_REDIRECT || 
documentType == ZIP_MANIFEST)) {
                        writeKey(dos, simpleRedirectKey);
                } else if(splitfile) {
                        dos.writeShort(splitfileAlgorithm);
@@ -791,7 +794,7 @@
                }

                if(documentType == SIMPLE_MANIFEST) {
-                       dos.writeInt(manifestEntryCount);
+                       dos.writeInt(manifestEntries.size());
                        boolean kill = false;
                        LinkedList unresolvedMetadata = null;
                        for(Iterator 
i=manifestEntries.keySet().iterator();i.hasNext();) {
@@ -891,4 +894,8 @@
                byte[] buf = writeToByteArray();
                return BucketTools.makeImmutableBucket(bf, buf);
        }
+
+       public boolean isResolved() {
+               return resolvedURI != null;
+       }
 }

Modified: trunk/freenet/src/freenet/client/RealArchiveStoreItem.java
===================================================================
--- trunk/freenet/src/freenet/client/RealArchiveStoreItem.java  2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/RealArchiveStoreItem.java  2006-05-20 
01:54:17 UTC (rev 8793)
@@ -31,6 +31,7 @@
                this.underBucket = temp.underBucket;
                underBucket.dontDeleteOnFinalize();
                underBucket.setReadOnly();
+               this.myFilename = underBucket.getFile();
                this.manager.cachedData += spaceUsed();
        }


Modified: trunk/freenet/src/freenet/client/async/ClientGetter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ClientGetter.java    2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/async/ClientGetter.java    2006-05-20 
01:54:17 UTC (rev 8793)
@@ -48,7 +48,7 @@
                this.uri = uri;
                this.ctx = ctx;
                this.finished = false;
-               this.actx = new ArchiveContext();
+               this.actx = new ArchiveContext(ctx.maxArchiveLevels);
                archiveRestarts = 0;
        }


Modified: trunk/freenet/src/freenet/client/async/ClientPutter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ClientPutter.java    2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/async/ClientPutter.java    2006-05-20 
01:54:17 UTC (rev 8793)
@@ -54,7 +54,7 @@
        public void start() throws InserterException {
                try {
                        currentState =
-                               new SingleFileInserter(this, this, new 
InsertBlock(data, cm, targetURI), isMetadata, ctx, false, getCHKOnly, false, 
null);
+                               new SingleFileInserter(this, this, new 
InsertBlock(data, cm, targetURI), isMetadata, ctx, false, getCHKOnly, false, 
null, false);
                        ((SingleFileInserter)currentState).start();
                } catch (InserterException e) {
                        finished = true;

Modified: trunk/freenet/src/freenet/client/async/ManifestElement.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ManifestElement.java 2006-05-19 
22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/async/ManifestElement.java 2006-05-20 
01:54:17 UTC (rev 8793)
@@ -11,6 +11,8 @@
        /** Filename */
        final String name;

+       final String fullName;
+       
        /** Data to be inserted. Can be null, if the insert has completed. */
        final Bucket data;

@@ -23,16 +25,27 @@
        /** Redirect target */
        final FreenetURI targetURI;

+       public ManifestElement(String name, String fullName, Bucket data, 
String mimeOverride, long size) {
+               this.name = name;
+               this.fullName = fullName;
+               this.data = data;
+               this.mimeOverride = mimeOverride;
+               this.dataSize = size;
+               this.targetURI = null;
+       }
+       
        public ManifestElement(String name, Bucket data, String mimeOverride, 
long size) {
                this.name = name;
+               this.fullName = name;
                this.data = data;
                this.mimeOverride = mimeOverride;
                this.dataSize = size;
                this.targetURI = null;
        }

-       public ManifestElement(ManifestElement me, String fullName) {
-               this.name = fullName;
+       public ManifestElement(ManifestElement me, String newName) {
+               this.name = newName;
+               this.fullName = me.fullName;
                this.data = me.data;
                this.mimeOverride = me.mimeOverride;
                this.dataSize = me.dataSize;
@@ -41,6 +54,7 @@

        public ManifestElement(String name, FreenetURI targetURI, String 
mimeOverride) {
                this.name = name;
+               this.fullName = name;
                this.data = null;
                this.mimeOverride = mimeOverride;
                this.dataSize = -1;

Modified: trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java    
2006-05-19 22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/async/SimpleManifestPutter.java    
2006-05-20 01:54:17 UTC (rev 8793)
@@ -1,10 +1,14 @@
 package freenet.client.async;

 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;

 import freenet.client.ClientMetadata;
 import freenet.client.DefaultMIMETypes;
@@ -29,10 +33,11 @@
                        super(SimpleManifestPutter.this.getPriorityClass(), 
SimpleManifestPutter.this.chkScheduler, SimpleManifestPutter.this.sskScheduler, 
SimpleManifestPutter.this.client);
                        this.name = name;
                        this.cm = cm;
+                       this.data = data;
                        InsertBlock block = 
                                new InsertBlock(data, cm, 
FreenetURI.EMPTY_CHK_URI);
                        this.origSFI =
-                               new SingleFileInserter(this, this, block, 
false, ctx, false, getCHKOnly, true, null);
+                               new SingleFileInserter(this, this, block, 
false, ctx, false, getCHKOnly, true, null, false);
                        currentState = origSFI;
                        metadata = null;
                }
@@ -41,19 +46,33 @@
                        super(SimpleManifestPutter.this.getPriorityClass(), 
SimpleManifestPutter.this.chkScheduler, SimpleManifestPutter.this.sskScheduler, 
SimpleManifestPutter.this.client);
                        this.name = name;
                        this.cm = cm;
+                       this.data = null;
                        Metadata m = new Metadata(Metadata.SIMPLE_REDIRECT, 
target, cm);
-                       cm = null;
                        metadata = m;
                        origSFI = null;
                        currentState = null;
                }

+               protected PutHandler(String name, String targetInZip, 
ClientMetadata cm, Bucket data) {
+                       super(SimpleManifestPutter.this.getPriorityClass(), 
SimpleManifestPutter.this.chkScheduler, SimpleManifestPutter.this.sskScheduler, 
SimpleManifestPutter.this.client);
+                       this.name = name;
+                       this.cm = cm;
+                       this.data = data;
+                       this.targetInZip = targetInZip;
+                       Metadata m = new 
Metadata(Metadata.ZIP_INTERNAL_REDIRECT, targetInZip, cm);
+                       metadata = m;
+                       origSFI = null;
+                       currentState = null;
+               }
+               
                private SingleFileInserter origSFI;
                private ClientPutState currentState;
                private ClientMetadata cm;
                private final String name;
                private Metadata metadata;
                private boolean finished;
+               private String targetInZip;
+               private final Bucket data;

                public void start() throws InserterException {
                        if(origSFI == null && metadata != null) return;
@@ -174,8 +193,10 @@
        private long totalSize;
        private boolean metadataBlockSetFinalized;
        private Metadata baseMetadata;
+       private boolean hasResolvedBase = false;
        private final static String[] defaultDefaultNames =
                new String[] { "index.html", "index.htm", "default.html", 
"default.htm" };
+       private LinkedList elementsToPutInZip;

        public SimpleManifestPutter(ClientCallback cb, ClientRequestScheduler 
chkSched,
                        ClientRequestScheduler sskSched, HashMap 
manifestElements, short prioClass, FreenetURI target, 
@@ -191,9 +212,16 @@
                putHandlersWaitingForMetadata = new HashSet();
                waitingForBlockSets = new HashSet();
                metadataPuttersByMetadata = new HashMap();
+               elementsToPutInZip = new LinkedList();
                makePutHandlers(manifestElements, putHandlersByName);
+               checkZips();
        }

+       private void checkZips() {
+               // If there are too few files in the zip, then insert them 
directly instead.
+               // FIXME do something.
+       }
+
        public void start() throws InserterException {
                PutHandler[] running;
                synchronized(this) {
@@ -235,16 +263,27 @@
                                        ph = new PutHandler(name, 
element.targetURI, cm);
                                        // Just a placeholder, don't actually 
run it
                                } else {
-                                       try {
-                                               ph = new PutHandler(name, data, 
cm, getCHKOnly);
-                                       } catch (InserterException e) {
-                                               cancelAndFinish();
-                                               throw e;
+                                       // Decide whether to put it in the ZIP.
+                                       // FIXME support multiple ZIPs and size 
limits.
+                                       // FIXME support better heuristics.
+                                       if(data.size() <= 65536) { // totally 
dumb heuristic!
+                                               // Put it in the zip.
+                                               ph = new PutHandler(name, 
element.fullName, cm, data);
+                                               elementsToPutInZip.addLast(ph);
+                                               numberOfFiles++;
+                                               totalSize += data.size();
+                                       } else {
+                                               try {
+                                                       ph = new 
PutHandler(name, data, cm, getCHKOnly);
+                                               } catch (InserterException e) {
+                                                       cancelAndFinish();
+                                                       throw e;
+                                               }
+                                               runningPutHandlers.add(ph);
+                                               
putHandlersWaitingForMetadata.add(ph);
+                                               numberOfFiles++;
+                                               totalSize += data.size();
                                        }
-                                       runningPutHandlers.add(ph);
-                                       putHandlersWaitingForMetadata.add(ph);
-                                       numberOfFiles++;
-                                       totalSize += data.size();
                                }
                                putHandlersByName.put(name, ph);
                        }
@@ -283,12 +322,17 @@
                baseMetadata =
                        
Metadata.mkRedirectionManifestWithMetadata(namesToByteArrays);
                resolveAndStartBase();
+               
        }
+
+       private void startMetadataInsert() {
+               resolveAndStartBase();
+       }

        private void resolveAndStartBase() {
                Bucket bucket = null;
                synchronized(this) {
-                       
if(this.metadataPuttersByMetadata.containsKey(baseMetadata)) return;
+                       if(hasResolvedBase) return;
                }
                while(true) {
                        try {
@@ -309,11 +353,64 @@
                        }
                }
                if(bucket == null) return;
-               InsertBlock block =
-                       new InsertBlock(bucket, null, targetURI);
+               synchronized(this) {
+                       if(hasResolvedBase) return;
+                       hasResolvedBase = true;
+               }
+               InsertBlock block;
+               boolean isMetadata = true;
+               boolean insertAsArchiveManifest = false;
+               if(!(elementsToPutInZip.isEmpty())) {
+                       // There is a zip to insert.
+                       // We want to include the metadata.
+                       // We have the metadata, fortunately enough, because 
everything has been resolve()d.
+                       // So all we need to do is create the actual ZIP.
+                       try {
+                               
+                               // FIXME support formats other than .zip.
+                               // Only the *decoding* is generic at present.
+                               
+                               Bucket zipBucket = ctx.bf.makeBucket(-1);
+                               OutputStream os = zipBucket.getOutputStream();
+                               ZipOutputStream zos = new ZipOutputStream(os);
+                               ZipEntry ze;
+                               
+                               for(Iterator 
i=elementsToPutInZip.iterator();i.hasNext();) {
+                                       PutHandler ph = (PutHandler) i.next();
+                                       ze = new ZipEntry(ph.targetInZip);
+                                       ze.setTime(0);
+                                       zos.putNextEntry(ze);
+                                       BucketTools.copyTo(ph.data, zos, 
ph.data.size());
+                               }
+                               
+                               // Add .metadata - after the rest.
+                               ze = new ZipEntry(".metadata");
+                               ze.setTime(0); // -1 = now, 0 = 1970.
+                               zos.putNextEntry(ze);
+                               BucketTools.copyTo(bucket, zos, bucket.size());
+                               
+                               zos.closeEntry();
+                               // Both finish() and close() are necessary.
+                               zos.finish();
+                               zos.close();
+                               
+                               // Now we have to insert the ZIP.
+                               
+                               // Can we just insert it, and not bother with a 
redirect to it?
+                               // Thereby exploiting implicit manifest 
support, which will pick up on .metadata??
+                               // We ought to be able to !!
+                               block = new InsertBlock(zipBucket, new 
ClientMetadata("application/zip"), targetURI);
+                               isMetadata = false;
+                               insertAsArchiveManifest = true;
+                       } catch (IOException e) {
+                               fail(new 
InserterException(InserterException.BUCKET_ERROR, e, null));
+                               return;
+                       }
+               } else
+                       block = new InsertBlock(bucket, null, targetURI);
                try {
                        SingleFileInserter metadataInserter = 
-                               new SingleFileInserter(this, this, block, true, 
ctx, false, getCHKOnly, false, baseMetadata);
+                               new SingleFileInserter(this, this, block, 
isMetadata, ctx, false, getCHKOnly, false, baseMetadata, 
insertAsArchiveManifest);
                        Logger.minor(this, "Inserting main metadata: 
"+metadataInserter);
                        this.metadataPuttersByMetadata.put(baseMetadata, 
metadataInserter);
                        metadataInserter.start();
@@ -322,18 +419,22 @@
                }
        }

-       private void resolve(MetadataUnresolvedException e) throws 
InserterException, IOException {
+       private boolean resolve(MetadataUnresolvedException e) throws 
InserterException, IOException {
                Metadata[] metas = e.mustResolve;
+               boolean mustWait = false;
                for(int i=0;i<metas.length;i++) {
                        Metadata m = metas[i];
+                       if(!m.isResolved())
+                               mustWait = true;
                        synchronized(this) {
                                if(metadataPuttersByMetadata.containsKey(m)) 
continue;
                        }
                        try {
                                Bucket b = m.toBucket(ctx.bf);
+                               
                                InsertBlock ib = new InsertBlock(b, null, 
FreenetURI.EMPTY_CHK_URI);
                                SingleFileInserter metadataInserter = 
-                                       new SingleFileInserter(this, this, ib, 
true, ctx, false, getCHKOnly, false, m);
+                                       new SingleFileInserter(this, this, ib, 
true, ctx, false, getCHKOnly, false, m, false);
                                Logger.minor(this, "Inserting subsidiary 
metadata: "+metadataInserter+" for "+m);
                                synchronized(this) {
                                        this.metadataPuttersByMetadata.put(m, 
metadataInserter);
@@ -343,6 +444,7 @@
                                resolve(e1);
                        }
                }
+               return mustWait;
        }

        private void namesToByteArrays(HashMap putHandlersByName, HashMap 
namesToByteArrays) {

Modified: trunk/freenet/src/freenet/client/async/SingleFileFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SingleFileFetcher.java       
2006-05-19 22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/async/SingleFileFetcher.java       
2006-05-20 01:54:17 UTC (rev 8793)
@@ -36,6 +36,7 @@
        final GetCompletionCallback rcb;
        final ClientMetadata clientMetadata;
        private Metadata metadata;
+       private Metadata archiveMetadata;
        final ArchiveContext actx;
        /** Archive handler. We can only have one archive handler at a time. */
        private ArchiveStoreContext ah;
@@ -90,7 +91,7 @@
                this.dontTellClientGet = fetcher.dontTellClientGet;
                this.actx = fetcher.actx;
                this.ah = fetcher.ah;
-               this.clientMetadata = fetcher.clientMetadata;
+               this.clientMetadata = (ClientMetadata) 
fetcher.clientMetadata.clone();
                this.metadata = newMeta;
                this.metaStrings = fetcher.metaStrings;
                this.rcb = callback;
@@ -186,12 +187,14 @@
        private void handleMetadata() throws FetchException, 
MetadataParseException, ArchiveFailureException, ArchiveRestartException {
                while(true) {
                        if(metadata.isSimpleManifest()) {
+                               Logger.minor(this, "Is simple manifest");
                                String name;
                                if(metaStrings.isEmpty())
                                        throw new 
FetchException(FetchException.NOT_ENOUGH_METASTRINGS);
                                else
                                        name = (String) 
metaStrings.removeFirst();
                                // Since metadata is a document, we just 
replace metadata here
+                               Logger.minor(this, "Next meta-string: "+name);
                                if(name == null) {
                                        metadata = 
metadata.getDefaultDocument();
                                        if(metadata == null)
@@ -204,6 +207,7 @@
                                }
                                continue; // loop
                        } else if(metadata.isArchiveManifest()) {
+                               Logger.minor(this, "Is archive manifest");
                                if(metaStrings.isEmpty() && 
ctx.returnZIPManifests) {
                                        // Just return the archive, whole.
                                        metadata.setSimpleRedirect();
@@ -213,6 +217,7 @@
                                // Then parse it.
                                // Then we may need to fetch something from 
inside the archive.
                                ah = (ArchiveStoreContext) 
ctx.archiveManager.makeHandler(thisKey, metadata.getArchiveType(), false);
+                               archiveMetadata = metadata;
                                // ah is set. This means we are currently 
handling an archive.
                                Bucket metadataBucket;
                                metadataBucket = ah.getMetadata(actx, null, 
recursionLevel+1, true);
@@ -224,19 +229,21 @@
                                                throw new 
FetchException(FetchException.BUCKET_ERROR, e);
                                        }
                                } else {
-                                       fetchArchive(false); // will result in 
this function being called again
+                                       fetchArchive(false, archiveMetadata); 
// will result in this function being called again
                                        return;
                                }
                                continue;
                        } else if(metadata.isArchiveInternalRedirect()) {
-                               
clientMetadata.mergeNoOverwrite(metadata.getClientMetadata()); // even 
splitfiles can have mime types!
+                               Logger.minor(this, "Is archive-internal 
redirect");
+                               
clientMetadata.mergeNoOverwrite(metadata.getClientMetadata());
                                // Fetch it from the archive
                                if(ah == null)
                                        throw new 
FetchException(FetchException.UNKNOWN_METADATA, "Archive redirect not in an 
archive");
-                               if(metaStrings.isEmpty())
-                                       throw new 
FetchException(FetchException.NOT_ENOUGH_METASTRINGS);
-                               Bucket dataBucket = ah.get((String) 
metaStrings.removeFirst(), actx, null, recursionLevel+1, true);
+                               String filename = metadata.getZIPInternalName();
+                               Logger.minor(this, "Fetching "+filename);
+                               Bucket dataBucket = ah.get(filename, actx, 
null, recursionLevel+1, true);
                                if(dataBucket != null) {
+                                       Logger.minor(this, "Returning data");
                                        // The client may free it, which is 
bad, or it may hang on to it for so long that it gets
                                        // freed by us, which is also bad.
                                        // So copy it.
@@ -256,14 +263,16 @@
                                        onSuccess(new 
FetchResult(this.clientMetadata, out));
                                        return;
                                } else {
+                                       Logger.minor(this, "Fetching archive");
                                        // Metadata cannot contain pointers to 
files which don't exist.
                                        // We enforce this in ArchiveHandler.
                                        // Therefore, the archive needs to be 
fetched.
-                                       fetchArchive(true);
+                                       fetchArchive(true, archiveMetadata);
                                        // Will call back into this function 
when it has been fetched.
                                        return;
                                }
                        } else if(metadata.isMultiLevelMetadata()) {
+                               Logger.minor(this, "Is multi-level metadata");
                                // Fetch on a second SingleFileFetcher, like 
with archives.
                                Metadata newMeta = (Metadata) metadata.clone();
                                newMeta.setSimpleRedirect();
@@ -271,6 +280,7 @@
                                f.handleMetadata();
                                return;
                        } else if(metadata.isSingleFileRedirect()) {
+                               Logger.minor(this, "Is single-file redirect");
                                
clientMetadata.mergeNoOverwrite(metadata.getClientMetadata()); // even 
splitfiles can have mime types!
                                // FIXME implement implicit archive support

@@ -356,14 +366,14 @@
                decompressors.addLast(codec);
        }

-       private void fetchArchive(boolean forData) throws FetchException, 
MetadataParseException, ArchiveFailureException, ArchiveRestartException {
+       private void fetchArchive(boolean forData, Metadata meta) throws 
FetchException, MetadataParseException, ArchiveFailureException, 
ArchiveRestartException {
                // Fetch the archive
                // How?
                // Spawn a separate SingleFileFetcher,
                // which fetches the archive, then calls
                // our Callback, which unpacks the archive, then
                // reschedules us.
-               Metadata newMeta = (Metadata) metadata.clone();
+               Metadata newMeta = (Metadata) meta.clone();
                newMeta.setSimpleRedirect();
                SingleFileFetcher f;
                f = new SingleFileFetcher(this, newMeta, new 
ArchiveFetcherCallback(forData), new FetcherContext(ctx, 
FetcherContext.SET_RETURN_ARCHIVES, true));

Modified: trunk/freenet/src/freenet/client/async/SingleFileInserter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SingleFileInserter.java      
2006-05-19 22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/async/SingleFileInserter.java      
2006-05-20 01:54:17 UTC (rev 8793)
@@ -39,6 +39,7 @@
        final boolean metadata;
        final PutCompletionCallback cb;
        final boolean getCHKOnly;
+       final boolean insertAsArchiveManifest;
        /** If true, we are not the top level request, and should not
         * update our parent to point to us as current put-stage. */
        private boolean cancelled = false;
@@ -54,11 +55,12 @@
         * @param dontCompress
         * @param getCHKOnly
         * @param reportMetadataOnly If true, don't insert the metadata, just 
report it.
+        * @param insertAsArchiveManifest If true, insert the metadata as an 
archive manifest.
         * @throws InserterException
         */
        SingleFileInserter(BaseClientPutter parent, PutCompletionCallback cb, 
InsertBlock block, 
                        boolean metadata, InserterContext ctx, boolean 
dontCompress, 
-                       boolean getCHKOnly, boolean reportMetadataOnly, Object 
token) throws InserterException {
+                       boolean getCHKOnly, boolean reportMetadataOnly, Object 
token, boolean insertAsArchiveManifest) throws InserterException {
                this.reportMetadataOnly = reportMetadataOnly;
                this.token = token;
                this.parent = parent;
@@ -67,6 +69,7 @@
                this.metadata = metadata;
                this.cb = cb;
                this.getCHKOnly = getCHKOnly;
+               this.insertAsArchiveManifest = insertAsArchiveManifest;
        }

        public void start() throws InserterException {
@@ -164,7 +167,7 @@
                if(block.getData().size() > Integer.MAX_VALUE)
                        throw new 
InserterException(InserterException.INTERNAL_ERROR, "2GB+ should not encode to 
one block!", null);

-               if((block.clientMetadata == null || 
block.clientMetadata.isTrivial())) {
+               if((block.clientMetadata == null || 
block.clientMetadata.isTrivial()) && !insertAsArchiveManifest) {
                        if(data.size() < blockSize) {
                                // Just insert it
                                ClientPutState bi =
@@ -179,7 +182,7 @@
                        // Insert single block, then insert pointer to it
                        if(reportMetadataOnly) {
                                SingleBlockInserter dataPutter = new 
SingleBlockInserter(parent, data, codecNumber, FreenetURI.EMPTY_CHK_URI, ctx, 
cb, metadata, (int)origSize, -1, getCHKOnly, true, true, token);
-                               Metadata meta = new 
Metadata(Metadata.SIMPLE_REDIRECT, dataPutter.getURI(), block.clientMetadata);
+                               Metadata meta = new 
Metadata(insertAsArchiveManifest ? Metadata.ZIP_MANIFEST : 
Metadata.SIMPLE_REDIRECT, dataPutter.getURI(), block.clientMetadata);
                                cb.onMetadata(meta, this);
                                cb.onTransition(this, dataPutter);
                                dataPutter.schedule();
@@ -188,7 +191,7 @@
                                MultiPutCompletionCallback mcb = 
                                        new MultiPutCompletionCallback(cb, 
parent, token);
                                SingleBlockInserter dataPutter = new 
SingleBlockInserter(parent, data, codecNumber, FreenetURI.EMPTY_CHK_URI, ctx, 
mcb, metadata, (int)origSize, -1, getCHKOnly, true, false, token);
-                               Metadata meta = new 
Metadata(Metadata.SIMPLE_REDIRECT, dataPutter.getURI(), block.clientMetadata);
+                               Metadata meta = new 
Metadata(insertAsArchiveManifest ? Metadata.ZIP_MANIFEST : 
Metadata.SIMPLE_REDIRECT, dataPutter.getURI(), block.clientMetadata);
                                Bucket metadataBucket;
                                try {
                                        metadataBucket = 
BucketTools.makeImmutableBucket(ctx.bf, meta.writeToByteArray());
@@ -217,12 +220,12 @@
                // insert it. Then when the splitinserter has finished, and the
                // metadata insert has finished too, tell the master callback.
                if(reportMetadataOnly) {
-                       SplitFileInserter sfi = new SplitFileInserter(parent, 
cb, data, bestCodec, block.clientMetadata, ctx, getCHKOnly, metadata, token);
+                       SplitFileInserter sfi = new SplitFileInserter(parent, 
cb, data, bestCodec, block.clientMetadata, ctx, getCHKOnly, metadata, token, 
insertAsArchiveManifest);
                        cb.onTransition(this, sfi);
                        sfi.start();
                } else {
                        SplitHandler sh = new SplitHandler();
-                       SplitFileInserter sfi = new SplitFileInserter(parent, 
sh, data, bestCodec, block.clientMetadata, ctx, getCHKOnly, metadata, token);
+                       SplitFileInserter sfi = new SplitFileInserter(parent, 
sh, data, bestCodec, block.clientMetadata, ctx, getCHKOnly, metadata, token, 
insertAsArchiveManifest);
                        sh.sfi = sfi;
                        cb.onTransition(this, sh);
                        sfi.start();
@@ -325,7 +328,7 @@
                                        }
                                        InsertBlock newBlock = new 
InsertBlock(metadataBucket, null, block.desiredURI);
                                        try {
-                                               metadataPutter = new 
SingleFileInserter(parent, this, newBlock, true, ctx, false, getCHKOnly, false, 
token);
+                                               metadataPutter = new 
SingleFileInserter(parent, this, newBlock, true, ctx, false, getCHKOnly, false, 
token, false);
                                                Logger.minor(this, "Putting 
metadata on "+metadataPutter);
                                        } catch (InserterException e) {
                                                cb.onFailure(e, this);

Modified: trunk/freenet/src/freenet/client/async/SplitFileInserter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SplitFileInserter.java       
2006-05-19 22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/client/async/SplitFileInserter.java       
2006-05-20 01:54:17 UTC (rev 8793)
@@ -35,9 +35,11 @@
        final boolean isMetadata;
        private boolean finished;
        public final Object token;
+       final boolean insertAsArchiveManifest;

-       public SplitFileInserter(BaseClientPutter put, PutCompletionCallback 
cb, Bucket data, Compressor bestCodec, ClientMetadata clientMetadata, 
InserterContext ctx, boolean getCHKOnly, boolean isMetadata, Object token) 
throws InserterException {
+       public SplitFileInserter(BaseClientPutter put, PutCompletionCallback 
cb, Bucket data, Compressor bestCodec, ClientMetadata clientMetadata, 
InserterContext ctx, boolean getCHKOnly, boolean isMetadata, Object token, 
boolean insertAsArchiveManifest) throws InserterException {
                this.parent = put;
+               this.insertAsArchiveManifest = insertAsArchiveManifest;
                this.token = token;
                this.finished = false;
                this.isMetadata = isMetadata;
@@ -149,7 +151,7 @@

                        if(!missingURIs) {
                                // Create Metadata
-                               m = new Metadata(splitfileAlgorithm, dataURIs, 
checkURIs, segmentSize, checkSegmentSize, cm, dataLength, compressionCodec, 
isMetadata);
+                               m = new Metadata(splitfileAlgorithm, dataURIs, 
checkURIs, segmentSize, checkSegmentSize, cm, dataLength, compressionCodec, 
isMetadata, insertAsArchiveManifest);
                        }
                        haveSentMetadata = true;
                }

Modified: trunk/freenet/src/freenet/keys/FreenetURI.java
===================================================================
--- trunk/freenet/src/freenet/keys/FreenetURI.java      2006-05-19 22:36:32 UTC 
(rev 8792)
+++ trunk/freenet/src/freenet/keys/FreenetURI.java      2006-05-20 01:54:17 UTC 
(rev 8793)
@@ -12,6 +12,7 @@
 import java.util.Vector;

 import freenet.support.Base64;
+import freenet.support.Fields;
 import freenet.support.HexUtil;
 import freenet.support.IllegalBase64Exception;
 import freenet.support.Logger;
@@ -63,6 +64,29 @@
        private final String[] metaStr;
        private final byte[] routingKey, cryptoKey, extra;
        private final long suggestedEdition; // for USKs
+       private boolean hasHashCode;
+       private int hashCode;
+       
+       public int hashCode() {
+               if(hasHashCode) return hashCode;
+               int x = keyType.hashCode();
+               if(docName != null) x ^= docName.hashCode();
+               if(metaStr != null) {
+                       for(int i=0;i<metaStr.length;i++)
+                               x ^= metaStr[i].hashCode();
+               }
+               if(routingKey != null)
+                       x ^= Fields.hashCode(routingKey);
+               if(cryptoKey != null)
+                       x ^= Fields.hashCode(cryptoKey);
+               if(extra != null)
+                       x ^= Fields.hashCode(extra);
+               if(keyType.equals("USK"))
+                       x ^= suggestedEdition;
+               hashCode = x;
+               hasHashCode = true;
+               return x;
+       }

        public boolean equals(Object o) {
                if(!(o instanceof FreenetURI))

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2006-05-19 22:36:32 UTC (rev 
8792)
+++ trunk/freenet/src/freenet/node/Node.java    2006-05-20 01:54:17 UTC (rev 
8793)
@@ -85,7 +85,6 @@
 import freenet.keys.NodeSSK;
 import freenet.keys.SSKBlock;
 import freenet.keys.SSKVerifyException;
-import freenet.node.TextModeClientInterfaceServer.TMCIBindtoCallback;
 import freenet.node.fcp.FCPServer;
 import freenet.pluginmanager.PluginManager;
 import freenet.store.BerkeleyDBFreenetStore;
@@ -547,7 +546,7 @@
     // Client stuff that needs to be configged - FIXME
     static final int MAX_ARCHIVE_HANDLERS = 200; // don't take up much RAM... 
FIXME
     static final long MAX_CACHED_ARCHIVE_DATA = 32*1024*1024; // make a fixed 
fraction of the store by default? FIXME
-    static final long MAX_ARCHIVE_SIZE = 1024*1024; // ??? FIXME
+    static final long MAX_ARCHIVE_SIZE = 2*1024*1024; // ??? FIXME
     static final long MAX_ARCHIVED_FILE_SIZE = 1024*1024; // arbitrary... FIXME
     static final int MAX_CACHED_ELEMENTS = 1024; // equally arbitrary! FIXME 
hopefully we can cache many of these though


Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-05-19 22:36:32 UTC (rev 
8792)
+++ trunk/freenet/src/freenet/node/Version.java 2006-05-20 01:54:17 UTC (rev 
8793)
@@ -18,7 +18,7 @@
        public static final String protocolVersion = "1.0";

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

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

Modified: trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java     
2006-05-19 22:36:32 UTC (rev 8792)
+++ trunk/freenet/src/freenet/node/fcp/ClientPutDiskDirMessage.java     
2006-05-20 01:54:17 UTC (rev 8793)
@@ -47,7 +47,7 @@
                        throws MessageInvalidException {
                // Create a directory listing of Buckets of data, mapped to 
ManifestElement's.
                // Directories are sub-HashMap's.
-               HashMap buckets = makeBucketsByName(dirname);
+               HashMap buckets = makeBucketsByName(dirname, "");
                handler.startClientPutDir(this, buckets);
        }

@@ -56,7 +56,7 @@
      * and its subdirs.
      * @throws MessageInvalidException 
      */
-    private HashMap makeBucketsByName(File thisdir) throws 
MessageInvalidException {
+    private HashMap makeBucketsByName(File thisdir, String prefix) throws 
MessageInvalidException {

        Logger.minor(this, "Listing directory: "+thisdir);

@@ -74,9 +74,9 @@

                                FileBucket bucket = new FileBucket(f, true, 
false, false, false);

-                               ret.put(f.getName(), new 
ManifestElement(f.getName(), bucket, null, f.length()));
+                               ret.put(f.getName(), new 
ManifestElement(f.getName(), prefix + f.getName(), bucket, null, f.length()));
                        } else if(filelist[i].isDirectory()) {
-                               HashMap subdir = makeBucketsByName(new 
File(thisdir, filelist[i].getName()));
+                               HashMap subdir = makeBucketsByName(new 
File(thisdir, filelist[i].getName()), prefix + filelist[i].getName() + "/" );
                                ret.put(filelist[i].getName(), subdir);
                        } else if(!allowUnreadableFiles) {
                                throw new 
MessageInvalidException(ProtocolErrorMessage.FILE_NOT_FOUND, "Not directory and 
not file: "+filelist[i], identifier);

Modified: trunk/freenet/src/freenet/support/LRUHashtable.java
===================================================================
--- trunk/freenet/src/freenet/support/LRUHashtable.java 2006-05-19 22:36:32 UTC 
(rev 8792)
+++ trunk/freenet/src/freenet/support/LRUHashtable.java 2006-05-20 01:54:17 UTC 
(rev 8793)
@@ -30,7 +30,7 @@
                insert.value = value;
             list.remove(insert);
         }
-        Logger.minor(this, "Pushed "+insert);
+        Logger.minor(this, "Pushed "+insert+" ( "+key+" "+value+" )");

         list.unshift(insert);
     } 


Reply via email to