Author: dbkr
Date: 2006-03-30 21:28:57 +0000 (Thu, 30 Mar 2006)
New Revision: 8375

Modified:
   trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java
   trunk/freenet/src/freenet/clients/http/ToadletContext.java
   trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
   trunk/freenet/src/freenet/node/Version.java
   trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java
Log:
598: Support for uploading node references in files.


Modified: trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java       
2006-03-30 21:24:04 UTC (rev 8374)
+++ trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java       
2006-03-30 21:28:57 UTC (rev 8375)
@@ -73,7 +73,7 @@

                buf.append("<div class=\"infobox\">\n");
                buf.append("<h2>My Connections</h2>\n");
-               buf.append("<form action\".\" method=\"post\">\n");
+               buf.append("<form action=\".\" method=\"post\" 
enctype=\"multipart/form-data\">\n");
                buf.append("<table class=\"darknet_connections\">\n");
                
buf.append("<tr><th>Status</th><th>Name</th><th>Address</th><th>Version</th><th>Location</th><th>Backoff</th><th>Backoff
 length</th><th></th></tr>\n");

@@ -145,7 +145,7 @@
                buf.append("</div>");

                // new connection box
-               buf.append("<form action=\".\" method=\"post\">\n");
+               buf.append("<form action=\".\" method=\"post\" 
enctype=\"multipart/form-data\">\n");
                buf.append("<div class=\"infobox\">\n");
                buf.append("<h2>\n");
                buf.append("Connect to another node\n");
@@ -156,6 +156,9 @@
                buf.append("or URL:\n");
                buf.append("<input type=\"text\" name=\"url\" />\n");
                buf.append("<br />\n");
+               buf.append("or file:\n");
+               buf.append("<input type=\"file\" name=\"reffile\" />\n");
+               buf.append("<br />\n");
                buf.append("<input type=\"submit\" name=\"connect\" 
value=\"Connect\" />\n");
                buf.append("</div>\n");
                buf.append("</form>\n");
@@ -170,22 +173,19 @@
                        this.writeReply(ctx, 400, "text/plain", "Too big", "Too 
much data, darknet toadlet limited to 1MB");
                        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;
-               }
+               request = new HTTPRequest(uri, data, ctx);

-               if (request.getParam("connect").length() > 0) {
+               if (request.isPartSet("connect")) {
                        // connect to a new node
-                       String urltext = request.getParam("url");
+                       String urltext = request.getPartAsString("url", 100);
                        urltext = urltext.trim();
-                       String reftext = request.getParam("ref");
+                       String reftext = request.getPartAsString("ref", 2000);
                        reftext = reftext.trim();
+                       if (reftext.length() < 200) {
+                               reftext = request.getPartAsString("reffile", 
2000);
+                       }
+                       reftext.trim();

                        String ref = new String("");

@@ -204,7 +204,7 @@
                                        this.sendErrorPage(ctx, 200, "Failed to 
add node", "Unable to retrieve node reference from "+urltext+".");
                                }
                        } else if (reftext.length() > 0) {
-                               // read directly from post data
+                               // read from post data or file upload
                                // this slightly scary looking regexp chops any 
extra characters off the beginning or ends of lines and removes extra line 
breaks
                                ref = 
reftext.replaceAll(".*?((?:[\\w,\\.]+\\=[^\r\n]+)|(?:End)).*(?:\\r?\\n)*", 
"$1\n");
                                if (ref.endsWith("\n")) {
@@ -212,8 +212,11 @@
                                }
                        } else {
                                this.sendErrorPage(ctx, 200, "Failed to add 
node", "Could not detect either a node reference or a URL. Please <a 
href=\".\">Try again</a>.");
+                               request.freeParts();
                                return;
                        }
+                       
+                       request.freeParts();
                        // we have a node reference in ref
                        SimpleFieldSet fs;

@@ -236,12 +239,12 @@
                        if(!this.node.addDarknetConnection(pn)) {
                                this.sendErrorPage(ctx, 200, "Failed to add 
node", "We already have the given reference. Return to the connections page <a 
href=\".\">here</a>.");
                        }
-               } else if (request.isParameterSet("disconnect")) {
+               } else if (request.isPartSet("disconnect")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

                        PeerNode[] peerNodes = node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
-                               if 
(request.isParameterSet("delete_node_"+peerNodes[i].hashCode())) {
+                               if 
(request.isPartSet("delete_node_"+peerNodes[i].hashCode())) {
                                        
this.node.removeDarknetConnection(peerNodes[i]);
                                }
                        }

Modified: trunk/freenet/src/freenet/clients/http/ToadletContext.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContext.java  2006-03-30 
21:24:04 UTC (rev 8374)
+++ trunk/freenet/src/freenet/clients/http/ToadletContext.java  2006-03-30 
21:28:57 UTC (rev 8375)
@@ -38,5 +38,7 @@
        PageMaker getPageMaker();

        BucketFactory getBucketFactory();
+       
+       MultiValueTable getHeaders();
 }


Modified: trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java      
2006-03-30 21:24:04 UTC (rev 8374)
+++ trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java      
2006-03-30 21:28:57 UTC (rev 8375)
@@ -89,6 +89,10 @@
        public PageMaker getPageMaker() {
                return pagemaker;
        }
+       
+       public MultiValueTable getHeaders() {
+               return headers;
+       }

        static void sendReplyHeaders(OutputStream sockOutputStream, int 
replyCode, String replyDescription, MultiValueTable mvt, String mimeType, long 
contentLength) throws IOException {
                // Construct headers

Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-03-30 21:24:04 UTC (rev 
8374)
+++ trunk/freenet/src/freenet/node/Version.java 2006-03-30 21:28:57 UTC (rev 
8375)
@@ -20,7 +20,7 @@
        public static final String protocolVersion = "1.0";

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

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

Modified: trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java
===================================================================
--- trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java    2006-03-30 
21:24:04 UTC (rev 8374)
+++ trunk/freenet/src/freenet/pluginmanager/HTTPRequest.java    2006-03-30 
21:28:57 UTC (rev 8375)
@@ -7,11 +7,22 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Iterator;
 import java.util.StringTokenizer;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.IOException;

 import freenet.support.Logger;
 import freenet.support.URLDecoder;
 import freenet.support.URLEncodedFormatException;
+import freenet.support.Bucket;
+import freenet.support.BucketFactory;
+import freenet.support.BucketTools;
+import freenet.support.MultiValueTable;
+import freenet.support.io.LineReadingInputStream;
+import freenet.clients.http.ToadletContext;

 /**
  * Used for passing all HTTP request information to the FredPlugin that handles
@@ -35,6 +46,23 @@
         * the original URI as given to the constructor
         */
        private URI uri;
+       
+       /**
+        * The headers sent by the client
+        */
+       private MultiValueTable headers;
+       
+       /**
+        * The data sent in the connection
+        */
+       private Bucket data;
+       
+       /**
+        * A hasmap of buckets that we use to store all the parts for a 
multipart/form-data request
+        */
+       private HashMap parts;
+       
+       private final BucketFactory bucketfactory;

        /**
         * Create a new HTTPRequest for the given URI and parse its request
@@ -46,6 +74,9 @@
        public HTTPRequest(URI uri) {
                this.uri = uri;
                this.parseRequestParameters(uri.getRawQuery(), true);
+               this.data = null;
+               this.parts = null;
+               this.bucketfactory = null;
        }

        /**
@@ -56,7 +87,9 @@
         * @throws URISyntaxException if the URI is invalid
         */
        public HTTPRequest(String path, String encodedQueryString) throws 
URISyntaxException {
-
+               this.data = null;
+               this.parts = null;
+               this.bucketfactory = null;
                if (encodedQueryString!=null && encodedQueryString.length()>0) {
                        this.uri = new URI(path+"?"+encodedQueryString);
                } else {
@@ -64,6 +97,29 @@
                }
                this.parseRequestParameters(uri.getRawQuery(), true);
        }
+       
+       /**
+        * Creates a new HTTPRequest for the given URI and data (for 
multipart/form-data)
+        * 
+        * @param uri The URI being requested
+        * @param h Client headers
+        * @param d The data
+        * @param ctx The toadlet context (for headers and bucket factory)
+        * @throws URISyntaxException if the URI is invalid
+        */
+       public HTTPRequest(URI uri, Bucket d, ToadletContext ctx) {
+               this.headers = ctx.getHeaders();
+               this.parseRequestParameters(uri.getRawQuery(), true);
+               this.data = d;
+               this.parts = new HashMap();
+               this.bucketfactory = ctx.getBucketFactory();
+               try {
+                       this.parseMultiPartData();
+               } catch (IOException ioe) {
+                       
+               }
+       }
+       

        /**
         * The path of this request, where the part of the path the specified 
the
@@ -329,5 +385,146 @@


        // TODO: add similar methods for multiple long, boolean etc.
-
+       
+       
+       private void parseMultiPartData() throws IOException {
+               String ctype = (String) this.headers.get("content-type");
+               if (ctype == null) return;
+               String[] ctypeparts = ctype.split(";");
+               if 
(!ctypeparts[0].trim().equalsIgnoreCase("multipart/form-data") || 
ctypeparts.length < 2) {
+                       return;
+               }
+               
+               String boundary = null;
+               for (int i = 0; i < ctypeparts.length; i++) {
+                       String[] subparts = ctypeparts[i].split("=");
+                       if (subparts.length == 2 && 
subparts[0].trim().equalsIgnoreCase("boundary")) {
+                               boundary = subparts[1];
+                       }
+               }
+               
+               if (boundary == null || boundary.length() == 0) return;
+               if (boundary.charAt(0) == '"') boundary = boundary.substring(1);
+               if (boundary.charAt(boundary.length() - 1) == '"')
+                       boundary = boundary.substring(0, boundary.length() - 1);
+               
+               boundary = "--"+boundary;
+               
+               InputStream is = this.data.getInputStream();
+               LineReadingInputStream lis = new LineReadingInputStream(is);
+               
+               String line;
+               line = lis.readLine(100, 100);
+               while (is.available() > 0 && !line.equals(boundary)) {
+                       line = lis.readLine(100, 100);
+               }
+               
+               boundary  = "\r\n"+boundary;
+               
+               Bucket filedata = null;
+               String name = null;
+               
+               while(is.available() > 0) {
+                       name = null;
+                       // chomp headers
+                       while( (line = lis.readLine(100, 100)) != null) {
+                               if (line.length() == 0) break;
+                               
+                               String[] lineparts = line.split(":");
+                               if (lineparts == null) continue;
+                               String hdrname = lineparts[0].trim();
+                               
+                               if (lineparts.length < 2) continue;
+                               String[] valueparts = lineparts[1].split(";");
+                               
+                               for (int i = 0; i < valueparts.length; i++) {
+                                       String[] subparts = 
valueparts[i].split("=");
+                                       if (subparts.length != 2) {
+                                               continue;
+                                       }
+                                       if 
(hdrname.equalsIgnoreCase("Content-Disposition")) {
+                                               if 
(subparts[0].trim().equalsIgnoreCase("name")) {
+                                                       name = 
subparts[1].trim();
+                                                       if (name.charAt(0) == 
'"') name = name.substring(1);
+                                                       if 
(name.charAt(name.length() - 1) == '"')
+                                                               name = 
name.substring(0, name.length() - 1);
+                                               }
+                                       }
+                               }
+                       }
+                       
+                       if (name == null) continue;
+                       
+                       // we should be at the data now. Start reading it in, 
checking for the
+                       // boundary string
+                       
+                       // we can only give an upper bound for the size of the 
bucket
+                       filedata = 
this.bucketfactory.makeBucket(is.available());
+                       OutputStream bucketos = filedata.getOutputStream();
+                       // buffer characters that match the boundary so far
+                       byte[] buf = new byte[boundary.length()];
+                       byte[] bbound = boundary.getBytes();
+                       int offset = 0;
+                       while (is.available() > 0 && !boundary.equals(new 
String(buf))) {
+                               byte b = (byte)is.read();
+                               
+                               if (b == bbound[offset]) {
+                                       buf[offset] = b;
+                                       offset++;
+                               } else if (b != bbound[offset] && offset > 0) {
+                                       // empty the buffer out
+                                       bucketos.write(buf, 0, offset);
+                                       bucketos.write(b);
+                                       offset = 0;
+                               } else {
+                                       bucketos.write(b);
+                               }
+                       }
+                       
+                       bucketos.close();
+                       
+                       this.parts.put(name, filedata);
+               }
+               
+               if (filedata != null) {
+                       this.bucketfactory.freeBucket(filedata);
+               }
+       }
+       
+       public Bucket getPart(String name) {
+               return (Bucket)this.parts.get(name);
+       }
+       
+       public boolean isPartSet(String name) {
+               return this.parts.containsKey(name);
+       }
+       
+       public String getPartAsString(String name, int maxlength) {
+               Bucket part = (Bucket)this.parts.get(name);
+               
+               if (part.size() > maxlength) return new String("");
+               
+               try {
+                       InputStream is = part.getInputStream();
+                       DataInputStream dis = new DataInputStream(is);
+                       byte[] buf = new byte[is.available()];
+                       dis.readFully(buf);
+                       is.close();
+                       return new String(buf);
+               } catch (IOException ioe) {
+                       
+               }
+               return new String("");
+       }
+       
+       public void freeParts() {
+               if (this.parts == null) return;
+               Iterator i = this.parts.keySet().iterator();
+               
+               while (i.hasNext()) {
+                       String key = (String) i.next();
+                       Bucket b = (Bucket)this.parts.get(key);
+                       b.free();
+               }
+       }
 }


Reply via email to