Author: toad
Date: 2006-05-05 17:30:56 +0000 (Fri, 05 May 2006)
New Revision: 8607

Modified:
   trunk/freenet/src/freenet/client/DefaultMIMETypes.java
   trunk/freenet/src/freenet/client/HighLevelSimpleClient.java
   trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
   trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
   trunk/freenet/src/freenet/clients/http/QueueToadlet.java
   trunk/freenet/src/freenet/clients/http/Toadlet.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/ClientGet.java
   trunk/freenet/src/freenet/node/fcp/ClientRequest.java
   trunk/freenet/src/freenet/node/fcp/FCPServer.java
   trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java
Log:
674: Large-file support in fproxy.
Query the user on a large file download in fproxy.
They can either continue with the download, or queue it to be downloaded to 
disk.

Modified: trunk/freenet/src/freenet/client/DefaultMIMETypes.java
===================================================================
--- trunk/freenet/src/freenet/client/DefaultMIMETypes.java      2006-05-05 
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/client/DefaultMIMETypes.java      2006-05-05 
17:30:56 UTC (rev 8607)
@@ -116,6 +116,8 @@
         * (y=0; while read x; do echo "$x" | 
         * sed -n "s/^\([^ ]*\)$/addMIMEType\($y, \"\1\"\);/p;s/^\([^ (),]\+\) 
\(.*\)$/addMIMEType\($y, \"\1\", \"\2\"\);/p;"; y=$((y+1)); done)
         */
+
+       // FIXME should we support aliases?

        static {
                addMIMEType((short)0, "application/activemessage");
@@ -750,4 +752,11 @@
                if(typeNumber < 0) return null;
                return (String) primaryExtensionByMimeNumber.get(new 
Short(typeNumber));
        }
+       
+       public static boolean isValidExt(String expectedMimeType, String 
oldExt) {
+               Short s = (Short) mimeTypesByExtension.get(oldExt);
+               if(s == null) return false;
+               String type = byNumber(s.shortValue());
+               return type.equals(expectedMimeType);
+       }
 }

Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClient.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClient.java 2006-05-05 
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClient.java 2006-05-05 
17:30:56 UTC (rev 8607)
@@ -24,6 +24,11 @@
        public FetchResult fetch(FreenetURI uri) throws FetchException;

        /**
+        * Blocking fetch of a URI with a configurable max-size.
+        */
+       public FetchResult fetch(FreenetURI uri, long maxSize) throws 
FetchException;
+       
+       /**
         * Blocking insert.
         * @throws InserterException If there is an error inserting the data
         */
@@ -47,4 +52,5 @@
         * Add a ClientEventListener.
         */
        public void addGlobalHook(ClientEventListener listener);
+
 }

Modified: trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java
===================================================================
--- trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2006-05-05 17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/client/HighLevelSimpleClientImpl.java     
2006-05-05 17:30:56 UTC (rev 8607)
@@ -99,6 +99,15 @@
                return fw.waitForCompletion();
        }

+       public FetchResult fetch(FreenetURI uri, long overrideMaxSize) throws 
FetchException {
+               if(uri == null) throw new NullPointerException();
+               FetcherContext context = getFetcherContext(overrideMaxSize);
+               FetchWaiter fw = new FetchWaiter();
+               ClientGetter get = new ClientGetter(fw, node.chkFetchScheduler, 
node.sskFetchScheduler, uri, context, priorityClass, this, null);
+               get.start();
+               return fw.waitForCompletion();
+       }
+
        public FreenetURI insert(InsertBlock insert, boolean getCHKOnly) throws 
InserterException {
                return insert(insert, getCHKOnly, false);
        }
@@ -139,8 +148,18 @@
        }

        public FetcherContext getFetcherContext() {
+               return getFetcherContext(-1);
+       }
+       
+       public FetcherContext getFetcherContext(long overrideMaxSize) {
+               long maxLength = curMaxLength;
+               long maxTempLength = curMaxTempLength;
+               if(overrideMaxSize >= 0) {
+                       maxLength = overrideMaxSize;
+                       maxTempLength = overrideMaxSize;
+               }
                return                  
-                       new FetcherContext(curMaxLength, curMaxTempLength, 
curMaxMetadataLength, 
+                       new FetcherContext(maxLength, maxTempLength, 
curMaxMetadataLength, 
                                MAX_RECURSION, MAX_ARCHIVE_RESTARTS, 
DONT_ENTER_IMPLICIT_ARCHIVES, 
                                SPLITFILE_THREADS, SPLITFILE_BLOCK_RETRIES, 
NON_SPLITFILE_RETRIES,
                                FETCH_SPLITFILES, FOLLOW_REDIRECTS, 
LOCAL_REQUESTS_ONLY,

Modified: trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/FproxyToadlet.java   2006-05-05 
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/clients/http/FproxyToadlet.java   2006-05-05 
17:30:56 UTC (rev 8607)
@@ -10,6 +10,7 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;

+import freenet.client.DefaultMIMETypes;
 import freenet.client.FetchException;
 import freenet.client.FetchResult;
 import freenet.client.HighLevelSimpleClient;
@@ -23,11 +24,13 @@
 import freenet.node.RequestStarter;
 import freenet.pluginmanager.HTTPRequest;
 import freenet.pluginmanager.PproxyToadlet;
+import freenet.support.Base64;
 import freenet.support.Bucket;
 import freenet.support.HTMLEncoder;
 import freenet.support.HexUtil;
 import freenet.support.Logger;
 import freenet.support.MultiValueTable;
+import freenet.support.SizeUtil;
 import freenet.support.URLEncoder;

 public class FproxyToadlet extends Toadlet {
@@ -36,10 +39,14 @@

        // ?force= links become invalid after 2 hours.
        long FORCE_GRAIN_INTERVAL = 60*60*1000;
+       /** Maximum size for transparent pass-through, should be a config 
option */
+       static final long MAX_LENGTH = 2*1024*1024; // 2MB

        public FproxyToadlet(HighLevelSimpleClient client, byte[] random) {
                super(client);
                this.random = random;
+               client.setMaxLength(MAX_LENGTH);
+               client.setMaxIntermediateLength(MAX_LENGTH);
        }

        public String supportedMethods() {
@@ -112,6 +119,8 @@
                if(ks.startsWith("/"))
                        ks = ks.substring(1);

+               long maxSize = httprequest.getLongParam("max-size", MAX_LENGTH);
+               
                FreenetURI key;
                try {
                        key = new FreenetURI(ks);
@@ -121,7 +130,7 @@
                }
                try {
                        Logger.minor(this, "Fproxy fetching "+key);
-                       FetchResult result = fetch(key);
+                       FetchResult result = fetch(key, maxSize);

                        // Now, is it safe?

@@ -181,6 +190,67 @@
                                this.writePermanentRedirect(ctx, "Not enough 
meta-strings", "/" + URLEncoder.encode(key.toString(false)) + "/");
                        } else if(e.newURI != null) {
                                this.writePermanentRedirect(ctx, msg, 
"/"+e.newURI.toString());
+                       } else if(e.mode == FetchException.TOO_BIG) {
+                               StringBuffer buf = new StringBuffer();
+                               ctx.getPageMaker().makeHead(buf, "Large file");
+                               buf.append("<table border=\"0\">\n");
+                               String fnam = getFilename(e, key, 
e.getExpectedMimeType());
+                               buf.append("<tr><td><b>Filename</b></td>");
+                               buf.append("<a 
href=\"/"+URLEncoder.encode(key.toString(false))+"\">");
+                               buf.append(fnam);
+                               buf.append("</a>");
+                               buf.append("</td></tr>\n");
+                               boolean finalized = e.finalizedSize();
+                               if(e.expectedSize > 0) {
+                                       buf.append("<tr><td><b>");
+                                       if(!finalized)
+                                               buf.append("Expected size (may 
change)");
+                                       else
+                                               buf.append("Size");
+                                       buf.append("</b></td><td>");
+                                       
buf.append(SizeUtil.formatSize(e.expectedSize));
+                                       buf.append("</td></tr>\n");
+                               }
+                               String mime = e.getExpectedMimeType();
+                               if(mime != null) {
+                                       buf.append("<tr><td><b>");
+                                       if(!finalized)
+                                               buf.append("Expected MIME 
type");
+                                       else
+                                               buf.append("MIME type");
+                                       buf.append("</b></td><td>");
+                                       buf.append(mime);
+                                       buf.append(" bytes </td></tr>\n");
+                               }
+                               // FIXME filename
+                               buf.append("</table>\n");
+                               buf.append("<br>This is a large file, so it has 
not been streamed direct" +
+                                               " to your browser, because 
Freenet cannot send any data to the " +
+                                               "browser until it has the whole 
file, and this may take some time, " +
+                                               "and also for resource usage 
reasons.\n");
+                               buf.append("<br>What would you like to do with 
it?:<ul>");
+                               buf.append("<li>");
+                               buf.append("<form method=\"get\" 
action=\"/"+key.toString(false)+"\">");
+                               buf.append("<input type=\"hidden\" 
name=\"max-size\" value=\""+e.expectedSize+"\">");
+                               buf.append("<input type=\"submit\" 
name=\"fetch\" value=\"Fetch anyway\">");
+                               buf.append("</form></li>");
+                               buf.append("<li><form method=\"post\" 
action=\"/queue/\">");
+                               buf.append("<input type=\"hidden\" name=\"key\" 
value=\""+key.toString(false)+"\">");
+                               buf.append("<input type=\"hidden\" 
name=\"return-type\" value=\"disk\">");
+                               buf.append("<input type=\"hidden\" 
name=\"persistence\" value=\"forever\">");
+                               if(mime != null)
+                                       buf.append("<input type=\"hidden\" 
name=\"type\" value=\""+URLEncoder.encode(mime)+"\">");
+                               buf.append("<input type=\"submit\" 
name=\"download\" value=\"Download to disk in background\">");
+                               buf.append("</form></li>");
+                               // FIXME add a queue-a-download option.
+//                             buf.append("<li>Save it to disk at </li>");
+                               // FIXME add return-to-referring-page
+                               //buf.append("<li>Return to the referring page: 
");
+                               buf.append("<li>Return to the fproxy home page: 
<a href=\"/\">here</a>");
+                               buf.append("</ul>");
+                               ctx.getPageMaker().makeTail(buf);
+                               writeReply(ctx, 200, "text/html", "OK", 
buf.toString());
+                               // FIXME provide option to queue write to disk.
                        } else {
                                if(e.errorCodes != null)
                                        extra = 
"<pre>"+e.errorCodes.toVerboseString()+"</pre>";
@@ -254,4 +324,56 @@

                fproxyConfig.finishedInitialization();
        }
+       
+       /**
+        * Get expected filename for a file.
+        * @param e The FetchException.
+        * @param uri The original URI.
+        * @param expectedMimeType The expected MIME type.
+        */
+       private String getFilename(FetchException e, FreenetURI uri, String 
expectedMimeType) {
+               String s = getFilename(e, uri);
+               int dotIdx = s.indexOf('.');
+               String ext = DefaultMIMETypes.getExtension(expectedMimeType);
+               if(ext == null)
+                       ext = "bin";
+               if(dotIdx == -1 && expectedMimeType != null) {
+                       s += "." + ext;
+                       return s;
+               }
+               if(dotIdx != -1) {
+                       String oldExt = s.substring(dotIdx+1);
+                       if(DefaultMIMETypes.isValidExt(expectedMimeType, 
oldExt))
+                               return s;
+                       return s + "." + ext;
+               }
+               return s + "." + ext;
+       }
+       
+       private String getFilename(FetchException e, FreenetURI uri) {
+               String fnam = sanitize(uri.getDocName());
+               if(fnam != null && fnam.length() > 0) return fnam;
+               String[] meta = uri.getAllMetaStrings();
+               for(int i=meta.length-1;i>=0;i++) {
+                       String s = meta[i];
+                       if(s.length() == 0) continue;
+                       fnam = sanitize(s);
+                       if(s != null && s.length() > 0) return fnam;
+               }
+               return Base64.encode(uri.getRoutingKey());
+       }
+       
+       private String sanitize(String s) {
+               if(s == null) return null;
+               StringBuffer sb = new StringBuffer(s.length());
+               for(int i=0;i<s.length();i++) {
+                       char c = s.charAt(i);
+                       if(Character.isLetterOrDigit(c) ||
+                                       c == ' ' || c == '.' || c == '-' || c 
== '_' ||
+                                       c == '+' || c == ',')
+                               sb.append(c);
+               }
+               return sb.toString();
+       }
+       
 }

Modified: trunk/freenet/src/freenet/clients/http/QueueToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/QueueToadlet.java    2006-05-05 
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/clients/http/QueueToadlet.java    2006-05-05 
17:30:56 UTC (rev 8607)
@@ -2,8 +2,8 @@

 import java.io.File;
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.text.NumberFormat;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -12,15 +12,12 @@
 import freenet.keys.FreenetURI;
 import freenet.node.fcp.ClientGet;
 import freenet.node.fcp.ClientPut;
-import freenet.node.fcp.ClientPutBase;
 import freenet.node.fcp.ClientPutDir;
 import freenet.node.fcp.ClientRequest;
 import freenet.node.fcp.FCPServer;
 import freenet.node.fcp.MessageInvalidException;
 import freenet.pluginmanager.HTTPRequest;
 import freenet.support.Bucket;
-import freenet.support.BucketTools;
-import freenet.support.Fields;
 import freenet.support.HTMLEncoder;
 import freenet.support.Logger;
 import freenet.support.SizeUtil;
@@ -41,27 +38,46 @@
                        this.writeReply(ctx, 400, "text/plain", "Too big", 
"Data exceeds 1MB limit");
                        return;
                }
-               byte[] d = BucketTools.toByteArray(data);
-               String s = new String(d, "us-ascii");
-               HTTPRequest request;
-               try {
-                       request = new HTTPRequest("/", s);
-               } catch (URISyntaxException e) {
-                       Logger.error(this, "Impossible: "+e, e);
-                       return;
-               }
+               HTTPRequest request = new HTTPRequest(uri, data, ctx);

                if(request.isParameterSet("remove_request") && 
request.getParam("remove_request").length() > 0) {
                        String identifier = request.getParam("identifier");
+                       Logger.minor(this, "Removing "+identifier);
                        try {
                                fcp.removeGlobalRequest(identifier);
                        } catch (MessageInvalidException e) {
-                               this.sendErrorPage(ctx, 200, "OK", "Failed to 
remove "+HTMLEncoder.encode(identifier)+" : 
"+HTMLEncoder.encode(e.getMessage()));
+                               this.sendErrorPage(ctx, 200, "Failed to remove 
request", "Failed to remove "+HTMLEncoder.encode(identifier)+" : 
"+HTMLEncoder.encode(e.getMessage()));
                        }
                }
+               if(request.isParameterSet("download")) {
+                       // Queue a download
+                       if(!request.isParameterSet("key")) {
+                               writeError("No key specified to download", "No 
key specified to download");
+                               return;
+                       }
+                       String expectedMIMEType = null;
+                       if(request.isParameterSet("type")) {
+                               expectedMIMEType = request.getParam("type");
+                       }
+                       FreenetURI fetchURI;
+                       try {
+                               fetchURI = new 
FreenetURI(request.getParam("key"));
+                       } catch (MalformedURLException e) {
+                               writeError("Invalid URI to download", "Invalid 
URI to download");
+                               return;
+                       }
+                       String persistence = request.getParam("persistence");
+                       String returnType = request.getParam("return-type");
+                       fcp.makePersistentGlobalRequest(fetchURI, 
expectedMIMEType, persistence, returnType);
+               }
                this.handleGet(uri, ctx);
        }

+       private void writeError(String string, String string2) {
+               // TODO Auto-generated method stub
+               
+       }
+
        public void handleGet(URI uri, ToadletContext ctx) 
        throws ToadletContextClosedException, IOException, RedirectException {

@@ -396,7 +412,7 @@
                buf.append("<td>");
                buf.append("<form action=\"/queue/\" method=\"post\">");
                buf.append("<input type=\"hidden\" name=\"identifier\" 
value=\"");
-               buf.append(URLEncoder.encode(p.getIdentifier()));
+               buf.append(HTMLEncoder.encode(p.getIdentifier()));
                buf.append("\"><input type=\"submit\" name=\"remove_request\" 
value=\"Delete\">");
                buf.append("</form>\n");
                buf.append("</td>\n");

Modified: trunk/freenet/src/freenet/clients/http/Toadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-05-05 17:21:30 UTC 
(rev 8606)
+++ trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-05-05 17:30:56 UTC 
(rev 8607)
@@ -107,10 +107,11 @@
        /**
         * Client calls from the above messages to run a freenet request.
         * This method may block (or suspend).
+        * @param maxSize Maximum length of returned content.
         */
-       FetchResult fetch(FreenetURI uri) throws FetchException {
+       FetchResult fetch(FreenetURI uri, long maxSize) throws FetchException {
                // For now, just run it blocking.
-               return client.fetch(uri);
+               return client.fetch(uri, maxSize);
        }

        FreenetURI insert(InsertBlock insert, boolean getCHKOnly) throws 
InserterException {

Modified: trunk/freenet/src/freenet/keys/FreenetURI.java
===================================================================
--- trunk/freenet/src/freenet/keys/FreenetURI.java      2006-05-05 17:21:30 UTC 
(rev 8606)
+++ trunk/freenet/src/freenet/keys/FreenetURI.java      2006-05-05 17:30:56 UTC 
(rev 8607)
@@ -602,4 +602,57 @@
                else throw new IllegalArgumentException("Not a USK requesting 
suggested edition");
        }

+       public String getPreferredFilename() {
+               Logger.minor(this, "Getting preferred filename for "+this);
+               Vector names = new Vector();
+               if(keyType.equals("KSK") || keyType.equals("SSK")) {
+                       Logger.minor(this, "Adding docName: "+docName);
+                       names.add(docName);
+               }
+               if(metaStr != null)
+                       for(int i=0;i<metaStr.length;i++) {
+                               Logger.minor(this, "Adding metaString "+i+": 
"+metaStr[i]);
+                               names.add(metaStr[i]);
+                       }
+               StringBuffer out = new StringBuffer();
+               for(int i=0;i<names.size();i++) {
+                       String s = (String) names.get(i);
+                       Logger.minor(this, "name "+i+" = "+s);
+                       s = sanitize(s);
+                       Logger.minor(this, "Sanitized name "+i+" = "+s);
+                       if(s.length() > 0) {
+                               if(out.length() > 0)
+                                       out.append("-");
+                               out.append(s);
+                       }
+               }
+               Logger.minor(this, "out = "+out.toString());
+               if(out.length() == 0) {
+                       if(routingKey != null) {
+                               Logger.minor(this, "Returning base64 encoded 
routing key");
+                               return Base64.encode(routingKey);
+                       }
+                       return "unknown";
+               }
+               return out.toString();
+       }
+
+       private String sanitize(String s) {
+               StringBuffer sb = new StringBuffer(s.length());
+               for(int i=0;i<s.length();i++) {
+                       char c = s.charAt(i);
+                       if(c == '/' || c == '\\' || c == '%' || c == '>' || c 
== '<' || c == ':' || c == '\'' || c == '\"')
+                               continue;
+                       if(Character.isDigit(c))
+                               sb.append(c);
+                       else if(Character.isLetter(c))
+                               sb.append(c);
+                       else if(Character.isWhitespace(c))
+                               sb.append(' ');
+                       else if(c == '-' || c == '_' || c == '.')
+                               sb.append(c);
+               }
+               return sb.toString();
+       }
+
 }

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2006-05-05 17:21:30 UTC (rev 
8606)
+++ trunk/freenet/src/freenet/node/Node.java    2006-05-05 17:30:56 UTC (rev 
8607)
@@ -2292,4 +2292,8 @@
        public void removeDarknetConnection(PeerNode pn) {
                peers.disconnect(pn);
        }
+
+       public File getDownloadDir() {
+               return downloadDir;
+       }
 }

Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-05-05 17:21:30 UTC (rev 
8606)
+++ trunk/freenet/src/freenet/node/Version.java 2006-05-05 17:30:56 UTC (rev 
8607)
@@ -20,7 +20,7 @@
        public static final String protocolVersion = "1.0";

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

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

Modified: trunk/freenet/src/freenet/node/fcp/ClientGet.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientGet.java   2006-05-05 17:21:30 UTC 
(rev 8606)
+++ trunk/freenet/src/freenet/node/fcp/ClientGet.java   2006-05-05 17:30:56 UTC 
(rev 8607)
@@ -61,6 +61,59 @@
        /** Last progress message. Not persistent - FIXME this will be made 
persistent
         * when we have proper persistence at the ClientGetter level. */
        private SimpleProgressMessage progressPending;
+
+       /**
+        * Create one for a global-queued request not made by FCP.
+        * @throws IdentifierCollisionException 
+        */
+       public ClientGet(FCPClient globalClient, FreenetURI uri, boolean 
dsOnly, boolean ignoreDS, 
+                       int maxSplitfileRetries, int maxNonSplitfileRetries, 
long maxOutputLength, 
+                       short returnType, boolean persistRebootOnly, String 
identifier, int verbosity, short prioClass,
+                       File returnFilename, File returnTempFilename) throws 
IdentifierCollisionException {
+               super(uri, identifier, verbosity, null, globalClient, 
prioClass, 
+                               (persistRebootOnly ? 
ClientRequest.PERSIST_REBOOT : ClientRequest.PERSIST_FOREVER),
+                                               null, true);
+               fctx = new FetcherContext(client.defaultFetchContext, 
FetcherContext.IDENTICAL_MASK, false);
+               fctx.eventProducer.addEventListener(this);
+               fctx.localRequestOnly = dsOnly;
+               fctx.ignoreStore = ignoreDS;
+               fctx.maxNonSplitfileRetries = maxNonSplitfileRetries;
+               fctx.maxSplitfileBlockRetries = maxSplitfileRetries;
+               fctx.maxOutputLength = maxOutputLength;
+               fctx.maxTempLength = maxOutputLength;
+               Bucket ret = null;
+               this.returnType = returnType;
+               if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
+                       this.targetFile = returnFilename;
+                       this.tempFile = returnTempFilename;
+                       ret = new FileBucket(returnTempFilename, false, false, 
false, false);
+               } else if(returnType == ClientGetMessage.RETURN_TYPE_NONE) {
+                       targetFile = null;
+                       tempFile = null;
+                       ret = new NullBucket();
+               } else {
+                       targetFile = null;
+                       tempFile = null;
+                       try {
+                               ret = 
client.server.node.persistentTempBucketFactory.makeEncryptedBucket();
+                       } catch (IOException e) {
+                               onFailure(new 
FetchException(FetchException.BUCKET_ERROR), null);
+                               getter = null;
+                               returnBucket = null;
+                               return;
+                       }
+               }
+               returnBucket = ret;
+               if(persistenceType != PERSIST_CONNECTION)
+                       try {
+                               client.register(this);
+                       } catch (IdentifierCollisionException e) {
+                               ret.free();
+                               throw e;
+                       }
+               getter = new ClientGetter(this, client.node.chkFetchScheduler, 
client.node.sskFetchScheduler, uri, fctx, priorityClass, client, returnBucket);
+       }
+                       

        public ClientGet(FCPConnectionHandler handler, ClientGetMessage 
message) throws IdentifierCollisionException {
                super(message.uri, message.identifier, message.verbosity, 
handler, message.priorityClass,
@@ -102,7 +155,12 @@
                }
                returnBucket = ret;
                if(persistenceType != PERSIST_CONNECTION)
-                       client.register(this);
+                       try {
+                               client.register(this);
+                       } catch (IdentifierCollisionException e) {
+                               ret.free();
+                               throw e;
+                       }
                getter = new ClientGetter(this, client.node.chkFetchScheduler, 
client.node.sskFetchScheduler, uri, fctx, priorityClass, client, returnBucket);
                if(persistenceType != PERSIST_CONNECTION && handler != null)
                        sendPendingMessages(handler.outputHandler, true, true);

Modified: trunk/freenet/src/freenet/node/fcp/ClientRequest.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ClientRequest.java       2006-05-05 
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/node/fcp/ClientRequest.java       2006-05-05 
17:30:56 UTC (rev 8607)
@@ -39,6 +39,23 @@
        protected String clientToken;
        /** Is the request on the global queue? */
        protected final boolean global;
+
+       public ClientRequest(FreenetURI uri2, String identifier2, int 
verbosity2, FCPConnectionHandler handler, 
+                       FCPClient client, short priorityClass2, short 
persistenceType2, String clientToken2, boolean global) {
+               this.uri = uri2;
+               this.identifier = identifier2;
+               this.verbosity = verbosity2;
+               this.finished = false;
+               this.priorityClass = priorityClass2;
+               this.persistenceType = persistenceType2;
+               this.clientToken = clientToken2;
+               this.global = global;
+               if(persistenceType == PERSIST_CONNECTION)
+                       this.origHandler = handler;
+               else
+                       origHandler = null;
+               this.client = client;
+       }

        public ClientRequest(FreenetURI uri2, String identifier2, int 
verbosity2, FCPConnectionHandler handler, 
                        short priorityClass2, short persistenceType2, String 
clientToken2, boolean global) {

Modified: trunk/freenet/src/freenet/node/fcp/FCPServer.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/FCPServer.java   2006-05-05 17:21:30 UTC 
(rev 8606)
+++ trunk/freenet/src/freenet/node/fcp/FCPServer.java   2006-05-05 17:30:56 UTC 
(rev 8607)
@@ -18,6 +18,8 @@
 import java.util.Vector;
 import java.util.WeakHashMap;

+import freenet.client.ClientMetadata;
+import freenet.client.DefaultMIMETypes;
 import freenet.client.FetcherContext;
 import freenet.client.HighLevelSimpleClient;
 import freenet.client.InserterContext;
@@ -29,7 +31,10 @@
 import freenet.config.StringCallback;
 import freenet.config.SubConfig;
 import freenet.io.NetworkInterface;
+import freenet.keys.FreenetURI;
 import freenet.node.Node;
+import freenet.node.RequestStarter;
+import freenet.support.Base64;
 import freenet.support.Logger;

 /**
@@ -57,6 +62,8 @@
        private long persistenceInterval;
        final FetcherContext defaultFetchContext;
        public InserterContext defaultInsertContext;
+       public static final int QUEUE_MAX_RETRIES = -1;
+       public static final long QUEUE_MAX_DATA_SIZE = Long.MAX_VALUE;

        private void startPersister() {
                Thread t = new Thread(persister = new FCPServerPersister(), 
"FCP request persistence handler");
@@ -543,4 +550,92 @@
                globalClient.removeByIdentifier(identifier, true);
        }

+       /**
+        * Create a persistent globally-queued request for a file.
+        * @param fetchURI The file to fetch.
+        * @param persistence The persistence type.
+        * @param returnType The return type.
+        */
+       public void makePersistentGlobalRequest(FreenetURI fetchURI, String 
expectedMimeType, String persistenceTypeString, String returnTypeString) {
+               boolean persistence = 
persistenceTypeString.equalsIgnoreCase("reboot");
+               short returnType = 
ClientGetMessage.parseReturnType(returnTypeString);
+               File returnFilename = null, returnTempFilename = null;
+               if(returnType == ClientGetMessage.RETURN_TYPE_DISK) {
+                       returnFilename = makeReturnFilename(fetchURI, 
expectedMimeType);
+                       returnTempFilename = 
makeTempReturnFilename(returnFilename);
+               }
+//             public ClientGet(FCPClient globalClient, FreenetURI uri, 
boolean dsOnly, boolean ignoreDS, 
+//                             int maxSplitfileRetries, int 
maxNonSplitfileRetries, long maxOutputLength, 
+//                             short returnType, boolean persistRebootOnly, 
String identifier, int verbosity, short prioClass,
+//                             File returnFilename, File returnTempFilename) 
throws IdentifierCollisionException {
+               
+               try {
+                       innerMakePersistentGlobalRequest(fetchURI, persistence, 
returnType, "FProxy:"+fetchURI.getPreferredFilename(), returnFilename, 
returnTempFilename);
+                       return;
+               } catch (IdentifierCollisionException ee) {
+                       try {
+                               innerMakePersistentGlobalRequest(fetchURI, 
persistence, returnType, "FProxy:"+fetchURI.getDocName(), returnFilename, 
returnTempFilename);
+                               return;
+                       } catch (IdentifierCollisionException e) {
+                               try {
+                                       
innerMakePersistentGlobalRequest(fetchURI, persistence, returnType, 
"FProxy:"+fetchURI.toString(false), returnFilename, returnTempFilename);
+                                       return;
+                               } catch (IdentifierCollisionException e1) {
+                                       // FIXME maybe use DateFormat
+                                       try {
+                                               
innerMakePersistentGlobalRequest(fetchURI, persistence, returnType, "FProxy 
("+System.currentTimeMillis()+")", returnFilename, returnTempFilename);
+                                               return;
+                                       } catch (IdentifierCollisionException 
e2) {
+                                               while(true) {
+                                                       byte[] buf = new 
byte[8];
+                                                       try {
+                                                               
node.random.nextBytes(buf);
+                                                               String id = 
"FProxy:"+Base64.encode(buf);
+                                                               
innerMakePersistentGlobalRequest(fetchURI, persistence, returnType, id, 
returnFilename, returnTempFilename);
+                                                               return;
+                                                       } catch 
(IdentifierCollisionException e3) {};
+                                               }
+                                       }
+                               }
+                       }
+                       
+               }
+       }
+
+       private File makeTempReturnFilename(File returnFilename) {
+               return new File(returnFilename.toString() + ".freenet-tmp");
+       }
+
+       private File makeReturnFilename(FreenetURI uri, String 
expectedMimeType) {
+               String ext;
+               if(expectedMimeType != null && expectedMimeType.length() > 0 &&
+                               
!expectedMimeType.equals(DefaultMIMETypes.DEFAULT_MIME_TYPE)) {
+                       ext = DefaultMIMETypes.getExtension(expectedMimeType);
+               } else ext = null;
+               String extAdd = (ext == null ? "" : "." + ext);
+               String preferred = uri.getPreferredFilename();
+               File f = new File(node.getDownloadDir(), preferred + extAdd);
+               File f1 = new File(node.getDownloadDir(), preferred + 
".freenet-tmp");
+               int x = 0;
+               while(f.exists() || f1.exists()) {
+                       f = new File(node.getDownloadDir(), preferred + "-" + x 
+ extAdd);
+                       f1 = new File(node.getDownloadDir(), preferred + "-" + 
x + extAdd + ".freenet-tmp");
+               }
+               return f;
+       }
+
+       private void innerMakePersistentGlobalRequest(FreenetURI fetchURI, 
boolean persistRebootOnly, short returnType, String id, File returnFilename, 
+                       File returnTempFilename) throws 
IdentifierCollisionException {
+               ClientGet cg = 
+                       new ClientGet(globalClient, fetchURI, 
defaultFetchContext.localRequestOnly, 
+                                       defaultFetchContext.ignoreStore, 
QUEUE_MAX_RETRIES, QUEUE_MAX_RETRIES,
+                                       QUEUE_MAX_DATA_SIZE, returnType, 
persistRebootOnly, id, Integer.MAX_VALUE,
+                                       
RequestStarter.BULK_SPLITFILE_PRIORITY_CLASS, returnFilename, 
returnTempFilename);
+               // Register before starting, because it may complete 
immediately, and if it does,
+               // we may end up with it not being removable because it wasn't 
registered!
+               if(cg.isPersistentForever())
+                       forceStorePersistentRequests();
+               cg.start();
+       }
+
 }

Modified: trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java
===================================================================
--- trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java    2006-05-05 
17:21:30 UTC (rev 8606)
+++ trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java    2006-05-05 
17:30:56 UTC (rev 8607)
@@ -17,6 +17,7 @@
 import freenet.clients.http.ToadletContext;
 import freenet.support.Bucket;
 import freenet.support.BucketFactory;
+import freenet.support.BucketTools;
 import freenet.support.Logger;
 import freenet.support.MultiValueTable;
 import freenet.support.URLDecoder;
@@ -98,7 +99,9 @@
        }

        /**
-        * Creates a new HTTPRequest for the given URI and data (for 
multipart/form-data)
+        * Creates a new HTTPRequest for the given URI and data.
+        * multipart/form-data will be split into Part's, but
+        * application/x-www-form-urlencoded will be split into Param's.
         * 
         * @param uri The URI being requested
         * @param h Client headers
@@ -386,10 +389,23 @@
        // TODO: add similar methods for multiple long, boolean etc.


+       /**
+        * Parse submitted data from a bucket.
+        * Note that if this is application/x-www-form-urlencoded, it will come 
out as
+        * params, whereas if it is multipart/form-data it will be separated 
into buckets.
+        */
        private void parseMultiPartData() throws IOException {
                String ctype = (String) this.headers.get("content-type");
                if (ctype == null) return;
                String[] ctypeparts = ctype.split(";");
+               
if(ctypeparts[0].equalsIgnoreCase("application/x-www-form-urlencoded")) {
+                       // Completely different encoding, but easy to handle
+                       if(data.size() > 1024*1024)
+                               throw new IOException("Too big");
+                       byte[] buf = BucketTools.toByteArray(data);
+                       String s = new String(buf, "us-ascii");
+                       parseRequestParameters(s, true);
+               }
                if 
(!ctypeparts[0].trim().equalsIgnoreCase("multipart/form-data") || 
ctypeparts.length < 2) {
                        return;
                }
@@ -527,4 +543,16 @@
                        b.free();
                }
        }
+
+       public long getLongParam(String name, long defaultValue) {
+               if (!this.isParameterSet(name)) {
+                       return defaultValue;
+               }
+               String value = this.getParameterValue(name);
+               try {
+                       return Long.parseLong(value);
+               } catch (NumberFormatException e) {
+                       return defaultValue;
+               }
+       }
 }


Reply via email to