Author: toad
Date: 2006-11-10 15:50:03 +0000 (Fri, 10 Nov 2006)
New Revision: 10855

Modified:
   trunk/freenet/src/freenet/client/FetchException.java
   trunk/freenet/src/freenet/client/async/SingleFileFetcher.java
   trunk/freenet/src/freenet/keys/FreenetURI.java
Log:
Implement TOO_MANY_PATH_COMPONENTS.

Modified: trunk/freenet/src/freenet/client/FetchException.java
===================================================================
--- trunk/freenet/src/freenet/client/FetchException.java        2006-11-09 
22:57:43 UTC (rev 10854)
+++ trunk/freenet/src/freenet/client/FetchException.java        2006-11-10 
15:50:03 UTC (rev 10855)
@@ -63,6 +63,19 @@
                        Logger.minor(this, 
"FetchException("+getMessage(mode)+")", this);
        }

+       public FetchException(int m, long expectedSize, boolean finalizedSize, 
String expectedMimeType, FreenetURI uri) {
+               super(getMessage(m));
+               extraMessage = null;
+               this.finalizedSizeAndMimeType = finalizedSize;
+               mode = m;
+               errorCodes = null;
+               newURI = uri;
+               this.expectedSize = expectedSize;
+               this.expectedMimeType = expectedMimeType;
+               if(Logger.shouldLog(Logger.MINOR, this)) 
+                       Logger.minor(this, 
"FetchException("+getMessage(mode)+")", this);
+       }
+       
        public FetchException(MetadataParseException e) {
                super(getMessage(INVALID_METADATA)+": "+e.getMessage());
                extraMessage = e.getMessage();
@@ -143,6 +156,17 @@
                        Logger.minor(this, 
"FetchException("+getMessage(mode)+") -> "+newURI, this);
        }

+       public FetchException(int mode, String msg, FreenetURI uri) {
+               super(getMessage(mode)+": "+msg);
+               extraMessage = msg;
+               errorCodes = null;
+               this.mode = mode;
+               newURI = uri;
+               expectedSize = -1;
+               if(Logger.shouldLog(Logger.MINOR, this))
+                       Logger.minor(this, 
"FetchException("+getMessage(mode)+"): "+msg,this);
+       }
+
        public static String getShortMessage(int mode) {
                switch(mode) {
                case TOO_DEEP_ARCHIVE_RECURSION:

Modified: trunk/freenet/src/freenet/client/async/SingleFileFetcher.java
===================================================================
--- trunk/freenet/src/freenet/client/async/SingleFileFetcher.java       
2006-11-09 22:57:43 UTC (rev 10854)
+++ trunk/freenet/src/freenet/client/async/SingleFileFetcher.java       
2006-11-10 15:50:03 UTC (rev 10855)
@@ -35,8 +35,13 @@
 public class SingleFileFetcher extends BaseSingleFileFetcher implements 
ClientGetState {

        private static boolean logMINOR;
-       //final FreenetURI uri;
+       /** Original URI */
+       final FreenetURI uri;
+       /** Meta-strings. (Path elements that aren't part of a key type) */
        final LinkedList metaStrings;
+       /** Number of metaStrings which were added by redirects etc. They are 
added to the start, so this is decremented
+        * when we consume one. */
+       private int addedMetaStrings;
        final GetCompletionCallback rcb;
        final ClientMetadata clientMetadata;
        private Metadata metadata;
@@ -51,20 +56,22 @@
        private final boolean dontTellClientGet;
        private Object token;
        private final Bucket returnBucket;
+       /** If true, success/failure is immediately reported to the client, and 
therefore we can check TOO_MANY_PATH_COMPONENTS. */
+       private final boolean isFinal;

        /** Create a new SingleFileFetcher and register self.
         * Called when following a redirect, or direct from ClientGet.
-        * @param token
-        * @param dontTellClientGet
+        * FIXME: Many times where this is called internally we might be better 
off using a copy constructor? 
         */
        public SingleFileFetcher(BaseClientGetter get, GetCompletionCallback 
cb, ClientMetadata metadata,
-                       ClientKey key, LinkedList metaStrings, FetcherContext 
ctx,
+                       ClientKey key, LinkedList metaStrings, FreenetURI 
origURI, int addedMetaStrings, FetcherContext ctx,
                        ArchiveContext actx, int maxRetries, int recursionLevel,
                        boolean dontTellClientGet, Object token, boolean 
isEssential,
-                       Bucket returnBucket) throws FetchException {
+                       Bucket returnBucket, boolean isFinal) throws 
FetchException {
                super(key, maxRetries, ctx, get);
                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR) Logger.minor(this, "Creating SingleFileFetcher for 
"+key);
+               this.isFinal = isFinal;
                this.cancelled = false;
                this.returnBucket = returnBucket;
                this.dontTellClientGet = dontTellClientGet;
@@ -73,9 +80,11 @@
                //this.key = ClientKey.getBaseKey(uri);
                //metaStrings = uri.listMetaStrings();
                this.metaStrings = metaStrings;
+               this.addedMetaStrings = addedMetaStrings;
                this.rcb = cb;
                this.clientMetadata = metadata;
                thisKey = key.getURI();
+               this.uri = origURI;
                this.actx = actx;
                this.recursionLevel = recursionLevel + 1;
                if(recursionLevel > ctx.maxRecursionLevel)
@@ -94,18 +103,22 @@
                if(logMINOR) Logger.minor(this, "Creating SingleFileFetcher for 
"+fetcher.key);
                this.token = fetcher.token;
                this.returnBucket = null;
+               // We expect significant further processing in the parent
+               this.isFinal = false;
                this.dontTellClientGet = fetcher.dontTellClientGet;
                this.actx = fetcher.actx;
                this.ah = fetcher.ah;
                this.clientMetadata = (ClientMetadata) 
fetcher.clientMetadata.clone();
                this.metadata = newMeta;
                this.metaStrings = fetcher.metaStrings;
+               this.addedMetaStrings = fetcher.addedMetaStrings;
                this.rcb = callback;
                this.recursionLevel = fetcher.recursionLevel + 1;
                if(recursionLevel > ctx.maxRecursionLevel)
                        throw new 
FetchException(FetchException.TOO_MUCH_RECURSION);
                this.thisKey = fetcher.thisKey;
                this.decompressors = fetcher.decompressors;
+               this.uri = fetcher.uri;
        }

        // Process the completed data. May result in us going to a
@@ -184,8 +197,26 @@
                        }
                        result = new FetchResult(result, data);
                }
-               if(result.size() > ctx.maxOutputLength) {
+               if(!metaStrings.isEmpty()) {
+                       // Some meta-strings left
+                       if(addedMetaStrings > 0) {
+                               // Should this be an error?
+                               // It would be useful to be able to fetch the 
data ...
+                               // On the other hand such inserts could cause 
unpredictable results?
+                               // Would be useful to make a redirect to the 
key we actually fetched.
+                               rcb.onFailure(new 
FetchException(FetchException.INVALID_METADATA, "Invalid metadata: too many 
path components in redirects", thisKey), this);
+                       } else {
+                               // TOO_MANY_PATH_COMPONENTS
+                               // report to user
+                               FreenetURI tryURI = uri;
+                               tryURI = 
tryURI.dropLastMetaStrings(metaStrings.size());
+                               rcb.onFailure(new 
FetchException(FetchException.TOO_MANY_PATH_COMPONENTS, result.size(), (rcb == 
parent), result.getMimeType(), tryURI), this);
+                       }
+                       result.asBucket().free();
+                       return;
+               } else if(result.size() > ctx.maxOutputLength) {
                        rcb.onFailure(new 
FetchException(FetchException.TOO_BIG, result.size(), (rcb == parent), 
result.getMimeType()), this);
+                       result.asBucket().free();
                } else {
                        rcb.onSuccess(result, this);
                }
@@ -198,8 +229,7 @@
                                String name;
                                if(metaStrings.isEmpty())
                                        throw new 
FetchException(FetchException.NOT_ENOUGH_PATH_COMPONENTS);
-                               else
-                                       name = (String) 
metaStrings.removeFirst();
+                               else name = removeMetaString();
                                // Since metadata is a document, we just 
replace metadata here
                                if(logMINOR) Logger.minor(this, "Next 
meta-string: "+name);
                                if(name == null) {
@@ -295,11 +325,11 @@
                                // Just create a new SingleFileFetcher
                                // Which will then fetch the target URI, and 
call the rcd.success
                                // Hopefully!
-                               FreenetURI uri = metadata.getSingleTarget();
-                               if(logMINOR) Logger.minor(this, "Redirecting to 
"+uri);
+                               FreenetURI newURI = metadata.getSingleTarget();
+                               if(logMINOR) Logger.minor(this, "Redirecting to 
"+newURI);
                                ClientKey key;
                                try {
-                                       BaseClientKey k = 
BaseClientKey.getBaseKey(uri);
+                                       BaseClientKey k = 
BaseClientKey.getBaseKey(newURI);
                                        if(k instanceof ClientKey)
                                                key = (ClientKey) k;
                                        else
@@ -310,16 +340,17 @@
                                } catch (MalformedURLException e) {
                                        throw new 
FetchException(FetchException.INVALID_URI, e);
                                }
-                               LinkedList newMetaStrings = 
uri.listMetaStrings();
+                               LinkedList newMetaStrings = 
newURI.listMetaStrings();

                                // Move any new meta strings to beginning of 
our list of remaining meta strings
                                while(!newMetaStrings.isEmpty()) {
                                        Object o = newMetaStrings.removeLast();
                                        metaStrings.addFirst(o);
+                                       addedMetaStrings++;
                                }

                                // **FIXME** Is key in the call to 
SingleFileFetcher here supposed to be this.key or the same key used in the try 
block above?  MultiLevelMetadataCallback.onSuccess() below uses this.key, thus 
the question
-                               SingleFileFetcher f = new 
SingleFileFetcher(parent, rcb, clientMetadata, key, metaStrings, ctx, actx, 
maxRetries, recursionLevel, false, token, true, returnBucket);
+                               SingleFileFetcher f = new 
SingleFileFetcher(parent, rcb, clientMetadata, key, metaStrings, this.uri, 
addedMetaStrings, ctx, actx, maxRetries, recursionLevel, false, token, true, 
returnBucket, true);
                                if((key instanceof ClientCHK) && 
!((ClientCHK)key).isMetadata())
                                        rcb.onBlockSetFinished(this);
                                if(metadata.isCompressed()) {
@@ -343,6 +374,27 @@
                                        addDecompressor(codec);
                                }

+                               if(isFinal) {
+                                       if(!metaStrings.isEmpty()) {
+                                               // Some meta-strings left
+                                               if(addedMetaStrings > 0) {
+                                                       // Should this be an 
error?
+                                                       // It would be useful 
to be able to fetch the data ...
+                                                       // On the other hand 
such inserts could cause unpredictable results?
+                                                       // Would be useful to 
make a redirect to the key we actually fetched.
+                                                       rcb.onFailure(new 
FetchException(FetchException.INVALID_METADATA, "Invalid metadata: too many 
path components in redirects", thisKey), this);
+                                               } else {
+                                                       // 
TOO_MANY_PATH_COMPONENTS
+                                                       // report to user
+                                                       FreenetURI tryURI = uri;
+                                                       tryURI = 
tryURI.dropLastMetaStrings(metaStrings.size());
+                                                       rcb.onFailure(new 
FetchException(FetchException.TOO_MANY_PATH_COMPONENTS, 
metadata.uncompressedDataLength(), (rcb == parent), 
clientMetadata.getMIMEType(), tryURI), this);
+                                               }
+                                               return;
+                                       }
+                               } else
+                                       if(logMINOR) Logger.minor(this, "Not 
finished: rcb="+rcb+" for "+this); 
+                               
                                long len = metadata.dataLength();
                                if(metadata.uncompressedDataLength() > len)
                                        len = metadata.uncompressedDataLength();
@@ -350,9 +402,7 @@
                                if((len > ctx.maxOutputLength) ||
                                                (len > ctx.maxTempLength)) {

-                                       boolean finished = (rcb == parent);
-                                       
-                                       onFailure(new 
FetchException(FetchException.TOO_BIG, len, finished, 
clientMetadata.getMIMEType()));
+                                       onFailure(new 
FetchException(FetchException.TOO_BIG, len, isFinal && decompressors.isEmpty(), 
clientMetadata.getMIMEType()));
                                        return;
                                }

@@ -372,6 +422,12 @@
                }
        }

+       private String removeMetaString() {
+               String name = (String) metaStrings.removeFirst();
+               if(addedMetaStrings > 0) addedMetaStrings--;
+               return name;
+       }
+
        private void addDecompressor(Compressor codec) {
                decompressors.addLast(codec);
        }
@@ -444,9 +500,7 @@
                public void onSuccess(FetchResult result, ClientGetState state) 
{
                        try {
                                metadata = 
Metadata.construct(result.asBucket());
-                               SingleFileFetcher f = new 
SingleFileFetcher(parent, rcb, clientMetadata, key, metaStrings, ctx, actx, 
maxRetries, recursionLevel, dontTellClientGet, token, true, returnBucket);
-                               f.metadata = metadata;
-                               f.handleMetadata();
+                               handleMetadata();
                        } catch (MetadataParseException e) {
                                SingleFileFetcher.this.onFailure(new 
FetchException(e));
                                return;
@@ -552,7 +606,7 @@
        public static ClientGetState create(BaseClientGetter parent, 
GetCompletionCallback cb, ClientMetadata clientMetadata, FreenetURI uri, 
FetcherContext ctx, ArchiveContext actx, int maxRetries, int recursionLevel, 
boolean dontTellClientGet, Object token, boolean isEssential, Bucket 
returnBucket) throws MalformedURLException, FetchException {
                BaseClientKey key = BaseClientKey.getBaseKey(uri);
                if(key instanceof ClientKey)
-                       return new SingleFileFetcher(parent, cb, 
clientMetadata, (ClientKey)key, uri.listMetaStrings(), ctx, actx, maxRetries, 
recursionLevel, dontTellClientGet, token, isEssential, returnBucket);
+                       return new SingleFileFetcher(parent, cb, 
clientMetadata, (ClientKey)key, uri.listMetaStrings(), uri, 0, ctx, actx, 
maxRetries, recursionLevel, dontTellClientGet, token, isEssential, 
returnBucket, true);
                else {
                        return uskCreate(parent, cb, clientMetadata, (USK)key, 
uri.listMetaStrings(), ctx, actx, maxRetries, recursionLevel, 
dontTellClientGet, token, isEssential, returnBucket);
                }
@@ -569,8 +623,8 @@
                                // Want to update the latest known good iff the 
fetch succeeds.
                                SingleFileFetcher sf = 
                                        new SingleFileFetcher(parent, myCB, 
clientMetadata, usk.getSSK(usk.suggestedEdition),
-                                                       metaStrings, ctx, actx, 
maxRetries, recursionLevel, dontTellClientGet,
-                                                       token, false, 
returnBucket);
+                                                       metaStrings, 
usk.getURI().addMetaStrings(metaStrings), 0, ctx, actx, maxRetries, 
recursionLevel, dontTellClientGet,
+                                                       token, false, 
returnBucket, true);
                                // Background fetch
                                
ctx.uskManager.startTemporaryBackgroundFetcher(usk);
                                return sf;
@@ -623,9 +677,8 @@
                        ClientSSK key = usk.getSSK(l);
                        try {
                                if(l == usk.suggestedEdition) {
-                                       SingleFileFetcher sf = new 
SingleFileFetcher(parent, cb, clientMetadata, key, metaStrings,
-                                                       ctx, actx, maxRetries, 
recursionLevel+1, dontTellClientGet,
-                                                       token, false, 
returnBucket);
+                                       SingleFileFetcher sf = new 
SingleFileFetcher(parent, cb, clientMetadata, key, metaStrings, 
key.getURI().addMetaStrings(metaStrings),
+                                                       0, ctx, actx, 
maxRetries, recursionLevel+1, dontTellClientGet, token, false, returnBucket, 
true);
                                        sf.schedule();
                                } else {
                                        cb.onFailure(new 
FetchException(FetchException.PERMANENT_REDIRECT, 
newUSK.getURI().addMetaStrings(metaStrings)), null);

Modified: trunk/freenet/src/freenet/keys/FreenetURI.java
===================================================================
--- trunk/freenet/src/freenet/keys/FreenetURI.java      2006-11-09 22:57:43 UTC 
(rev 10854)
+++ trunk/freenet/src/freenet/keys/FreenetURI.java      2006-11-10 15:50:03 UTC 
(rev 10855)
@@ -412,6 +412,16 @@
                return setMetaString(newMetaStr);
        }

+       public FreenetURI dropLastMetaStrings(int i) {
+               String[] newMetaStr = null;
+               if ((metaStr != null) && (metaStr.length > 1)) {
+                       if(i > metaStr.length) i = metaStr.length;
+                       newMetaStr = new String[metaStr.length - i];
+                       System.arraycopy(metaStr, 0, newMetaStr, 0, 
newMetaStr.length);
+               }
+               return setMetaString(newMetaStr);
+       }
+
        /**
         * Returns a copy of this URI with the given string added as a new meta 
string.
         */


Reply via email to