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();
+ }
+ }
}