Author: toad
Date: 2006-01-14 01:51:07 +0000 (Sat, 14 Jan 2006)
New Revision: 7853

Added:
   trunk/freenet/src/freenet/clients/
   trunk/freenet/src/freenet/clients/http/
   trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
   trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java
   trunk/freenet/src/freenet/clients/http/Toadlet.java
   trunk/freenet/src/freenet/clients/http/ToadletContainer.java
   trunk/freenet/src/freenet/clients/http/ToadletContext.java
   trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java
   trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
   trunk/freenet/src/freenet/clients/http/TrivialToadlet.java
   trunk/freenet/src/freenet/support/HTMLDecoder.java
   trunk/freenet/src/freenet/support/HTMLEncoder.java
   trunk/freenet/src/freenet/support/LimitedEnumeration.java
   trunk/freenet/src/freenet/support/MultiValueTable.java
   trunk/freenet/src/freenet/support/URLDecoder.java
   trunk/freenet/src/freenet/support/URLEncodedFormatException.java
   trunk/freenet/src/freenet/support/URLEncoder.java
   trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
   trunk/freenet/src/freenet/support/io/TooLongException.java
Modified:
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/Version.java
Log:
349: Working fproxy! Of course it doesn't filter yet, and the GUI isn't very 
good.

Added: trunk/freenet/src/freenet/clients/http/FproxyToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/FproxyToadlet.java   2006-01-13 
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/FproxyToadlet.java   2006-01-14 
01:51:07 UTC (rev 7853)
@@ -0,0 +1,62 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URI;
+
+import freenet.client.FetchException;
+import freenet.client.FetchResult;
+import freenet.client.HighLevelSimpleClient;
+import freenet.keys.FreenetURI;
+import freenet.support.Bucket;
+import freenet.support.HTMLEncoder;
+import freenet.support.Logger;
+
+public class FproxyToadlet extends Toadlet {
+
+       public FproxyToadlet(HighLevelSimpleClient client) {
+               super(client);
+       }
+
+       void handleGet(URI uri, ToadletContext ctx)
+                       throws ToadletContextClosedException, IOException {
+               String ks = uri.toString();
+               if(ks.startsWith("/"))
+                       ks = ks.substring(1);
+               FreenetURI key;
+               try {
+                       key = new FreenetURI(ks);
+               } catch (MalformedURLException e) {
+                       this.writeReply(ctx, 400, "text/html", "Invalid key", 
"<html><head><title>Invalid key</title></head><body>Expected a freenet key, but 
got "+HTMLEncoder.encode(ks)+"</body></html>");
+                       return;
+               }
+               try {
+                       FetchResult result = fetch(key);
+                       writeReply(ctx, 200, result.getMimeType(), "OK", 
result.asBucket());
+               } catch (FetchException e) {
+                       String msg = e.getMessage();
+                       this.writeReply(ctx, 500 /* close enough - FIXME 
probably should depend on status code */,
+                                       "text/html", msg, 
"<html><head><title>"+msg+"</title></head><body>Error: 
"+HTMLEncoder.encode(msg)+"</body></html>");
+               } catch (Throwable t) {
+                       Logger.error(this, "Caught "+t, t);
+                       String msg = "<html><head><title>Internal 
Error</title></head><body><h1>Internal Error: please report</h1><pre>";
+                       StringWriter sw = new StringWriter();
+                       PrintWriter pw = new PrintWriter(sw);
+                       t.printStackTrace(pw);
+                       pw.flush();
+                       msg = msg + sw.toString() + "</pre></body></html>";
+                       this.writeReply(ctx, 500, "text/html", "Internal 
Error", msg);
+               }
+       }
+
+       void handlePut(URI uri, Bucket data, ToadletContext ctx)
+                       throws ToadletContextClosedException, IOException {
+               String notSupported = "<html><head><title>Not 
supported</title></head><body>"+
+               "Operation not supported</body>";
+               // FIXME should be 405? Need to let toadlets indicate what is 
allowed maybe in a callback?
+               this.writeReply(ctx, 200, "text/html", "OK", notSupported);
+       }
+
+}

Added: trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java     
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/SimpleToadletServer.java     
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,113 @@
+package freenet.clients.http;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import freenet.support.FileLoggerHook;
+import freenet.support.Logger;
+
+public class SimpleToadletServer implements ToadletContainer, Runnable {
+
+       public class ToadletElement {
+               public ToadletElement(Toadlet t2, String urlPrefix) {
+                       t = t2;
+                       prefix = urlPrefix;
+               }
+               Toadlet t;
+               String prefix;
+       }
+
+       final int port;
+       private final ServerSocket sock;
+       private final LinkedList toadlets;
+       
+       public SimpleToadletServer(int i) throws IOException {
+               this.port = i;
+               this.sock = new ServerSocket(port, 0, 
InetAddress.getByName("127.0.0.1"));
+               toadlets = new LinkedList();
+               Thread t = new Thread(this, "SimpleToadletServer");
+               t.setDaemon(true);
+               t.start();
+       }
+
+       public void register(Toadlet t, String urlPrefix, boolean atFront) {
+               ToadletElement te = new ToadletElement(t, urlPrefix);
+               if(atFront) toadlets.addFirst(te);
+               else toadlets.addLast(te);
+               t.container = this;
+       }
+
+       public Toadlet findToadlet(URI uri) {
+               Iterator i = toadlets.iterator();
+               while(i.hasNext()) {
+                       ToadletElement te = (ToadletElement) i.next();
+                       
+                       if(uri.getPath().startsWith(te.prefix))
+                               return te.t;
+               }
+               return null;
+       }
+       
+       public static void main(String[] args) throws IOException {
+        File logDir = new File("logs-toadlettest");
+        logDir.mkdir();
+        FileLoggerHook logger = new FileLoggerHook(true, new File(logDir, 
"test-1111").getAbsolutePath(), 
+                       "d (c, t, p): m", "MMM dd, yyyy HH:mm:ss:SSS", 
Logger.MINOR, false, true, 
+                       1024*1024*1024 /* 1GB of old compressed logfiles */);
+        logger.setInterval("5MINUTES");
+        Logger.setupChain();
+        Logger.globalSetThreshold(Logger.MINOR);
+        Logger.globalAddHook(logger);
+        logger.start();
+               SimpleToadletServer server = new SimpleToadletServer(1111);
+               server.register(new TrivialToadlet(null), "", true);
+               System.out.println("Bound to port 1111.");
+               while(true) {
+                       try {
+                               Thread.sleep(100000);
+                       } catch (InterruptedException e) {
+                               // Ignore
+                       }
+               }
+       }
+
+       public void run() {
+               while(true) {
+                       try {
+                               Socket conn = sock.accept();
+                               Logger.minor(this, "Accepted connection");
+                               SocketHandler sh = new SocketHandler(conn);
+                       } catch (IOException e) {
+                               Logger.minor(this, "Got IOException accepting 
conn: "+e, e);
+                               // Ignore
+                               continue;
+                       }
+               }
+       }
+       
+       public class SocketHandler implements Runnable {
+
+               Socket sock;
+               
+               public SocketHandler(Socket conn) {
+                       this.sock = conn;
+                       Thread t = new Thread(this);
+                       t.setDaemon(true);
+                       t.start();
+               }
+
+               public void run() {
+                       Logger.minor(this, "Handling connection");
+                       ToadletContextImpl.handle(sock, 
SimpleToadletServer.this);
+                       Logger.minor(this, "Handled connection");
+               }
+
+       }
+
+}

Added: trunk/freenet/src/freenet/clients/http/Toadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-01-13 18:09:01 UTC 
(rev 7852)
+++ trunk/freenet/src/freenet/clients/http/Toadlet.java 2006-01-14 01:51:07 UTC 
(rev 7853)
@@ -0,0 +1,98 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.net.URI;
+
+import freenet.client.FetchException;
+import freenet.client.FetchResult;
+import freenet.client.HighLevelSimpleClient;
+import freenet.client.InsertBlock;
+import freenet.client.InserterException;
+import freenet.keys.FreenetURI;
+import freenet.support.Bucket;
+
+/**
+ * Replacement for servlets. Just an easy to use HTTP interface, which is
+ * compatible with continuations (eventually). You must extend this class
+ * and provide the abstract methods. Apologies but we can't do it as an
+ * interface and still have continuation compatibility; we can only 
+ * suspend in a member function at this level in most implementations.
+ * 
+ * When we eventually implement continuations, these will require very
+ * little thread overhead: We can suspend while running a freenet
+ * request, and only grab a thread when we are either doing I/O or doing
+ * computation in the derived class. We can suspend while doing I/O too;
+ * on systems with NIO, we use that, on systems without it, we just run
+ * the fetch on another (or this) thread. With no need to change any
+ * APIs, and no danger of exploding memory use (unlike the traditional
+ * NIO servlets approach).
+ */
+public abstract class Toadlet {
+
+       protected Toadlet(HighLevelSimpleClient client) {
+               this.client = client;
+       }
+       
+       private final HighLevelSimpleClient client;
+       ToadletContainer container;
+       
+       /**
+        * Handle a GET request.
+        * Must be implemented by the client.
+        * @param uri The URI (relative to this client's document root) to
+        * be fetched.
+        * @throws IOException 
+        * @throws ToadletContextClosedException 
+        */
+       abstract void handleGet(URI uri, ToadletContext ctx) throws 
ToadletContextClosedException, IOException;
+
+       /**
+        * Likewise for a PUT request.
+        */
+       abstract void handlePut(URI uri, Bucket data, ToadletContext ctx) 
throws ToadletContextClosedException, IOException;
+       
+       /**
+        * Client calls from the above messages to run a freenet request.
+        * This method may block (or suspend).
+        */
+       FetchResult fetch(FreenetURI uri) throws FetchException {
+               // For now, just run it blocking.
+               return client.fetch(uri);
+       }
+
+       FreenetURI insert(InsertBlock insert, boolean getCHKOnly) throws 
InserterException {
+               // For now, just run it blocking.
+               return client.insert(insert, getCHKOnly);
+       }
+
+       /**
+        * Client calls to write a reply to the HTTP requestor.
+        */
+       void writeReply(ToadletContext ctx, int code, String mimeType, String 
desc, byte[] data, int offset, int length) throws 
ToadletContextClosedException, IOException {
+               ctx.sendReplyHeaders(code, desc, null, mimeType, length);
+               ctx.writeData(data, offset, length);
+       }
+
+       /**
+        * Client calls to write a reply to the HTTP requestor.
+        */
+       void writeReply(ToadletContext ctx, int code, String mimeType, String 
desc, Bucket data) throws ToadletContextClosedException, IOException {
+               ctx.sendReplyHeaders(code, desc, null, mimeType, data.size());
+               ctx.writeData(data);
+       }
+
+       void writeReply(ToadletContext ctx, int code, String mimeType, String 
desc, String reply) throws ToadletContextClosedException, IOException {
+               byte[] buf = reply.getBytes("ISO-8859-1");
+               ctx.sendReplyHeaders(code, desc, null, mimeType, buf.length);
+               ctx.writeData(buf, 0, buf.length);
+       }
+       
+       /**
+        * Get the client impl. DO NOT call the blocking methods on it!!
+        * Just use it for configuration etc.
+        */
+       protected HighLevelSimpleClient getClientImpl() {
+               return client;
+       }
+       
+}

Added: trunk/freenet/src/freenet/clients/http/ToadletContainer.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContainer.java        
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContainer.java        
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,19 @@
+package freenet.clients.http;
+
+import java.net.URI;
+
+/** Interface for toadlet containers. Toadlets should register here. */
+public interface ToadletContainer {
+       
+       /** Register a Toadlet. All requests whose URL starts with the given
+        * prefix will be passed to this toadlet.
+        * @param atFront If true, add to front of list (where is checked 
first),
+        * else add to back of list (where is checked last).
+        */
+       public void register(Toadlet t, String urlPrefix, boolean atFront);
+
+       /**
+        * Find a Toadlet by URI.
+        */
+       public Toadlet findToadlet(URI uri);
+}

Added: trunk/freenet/src/freenet/clients/http/ToadletContext.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContext.java  2006-01-13 
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContext.java  2006-01-14 
01:51:07 UTC (rev 7853)
@@ -0,0 +1,34 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+
+import freenet.support.Bucket;
+import freenet.support.MultiValueTable;
+
+/**
+ * Object represents context for a single request. Is used as a token,
+ * when the Toadlet wants to e.g. write a reply.
+ */
+public interface ToadletContext {
+
+       /**
+        * Write reply headers.
+        * @param code HTTP code.
+        * @param desc HTTP code description.
+        * @param mvt Any extra headers.
+        * @param mimeType The MIME type of the reply.
+        * @param length The length of the reply.
+        */
+       void sendReplyHeaders(int code, String desc, MultiValueTable mvt, 
String mimeType, long length) throws ToadletContextClosedException, IOException;
+
+       /**
+        * Write data. Note you must send reply headers first.
+        */
+       void writeData(byte[] data, int offset, int length) throws 
ToadletContextClosedException, IOException;
+
+       /**
+        * Write data from a bucket. You must send reply headers first.
+        */
+       void writeData(Bucket data) throws ToadletContextClosedException, 
IOException;
+
+}

Added: trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java   
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContextClosedException.java   
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,5 @@
+package freenet.clients.http;
+
+public class ToadletContextClosedException extends Exception {
+
+}

Added: trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java      
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/ToadletContextImpl.java      
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,240 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+
+import freenet.support.Bucket;
+import freenet.support.BucketTools;
+import freenet.support.Logger;
+import freenet.support.MultiValueTable;
+import freenet.support.io.LineReadingInputStream;
+import freenet.support.io.TooLongException;
+
+/**
+ * ToadletContext implementation, including all the icky HTTP parsing etc.
+ * An actual ToadletContext object represents a request, after we have parsed 
the 
+ * headers. It provides methods to send replies.
+ * @author root
+ *
+ */
+public class ToadletContextImpl implements ToadletContext {
+
+       private final Socket sock;
+       private final MultiValueTable headers;
+       private final OutputStream sockOutputStream;
+       /** Is the context closed? If so, don't allow any more writes. This is 
because there
+        * may be later requests.
+        */
+       private boolean closed;
+       
+       public ToadletContextImpl(Socket sock, MultiValueTable headers) throws 
IOException {
+               this.sock = sock;
+               this.headers = headers;
+               this.closed = false;
+               sockOutputStream = sock.getOutputStream();
+       }
+
+       private void close() {
+               closed = true;
+       }
+
+       private void sendMethodNotAllowed(String method, boolean 
shouldDisconnect) throws ToadletContextClosedException, IOException {
+               if(closed) throw new ToadletContextClosedException();
+               MultiValueTable mvt = new MultiValueTable();
+               mvt.put("Allow", "GET, PUT");
+               sendError(sockOutputStream, 405, "Method not allowed", 
shouldDisconnect, mvt);
+       }
+
+       private static void sendError(OutputStream os, int code, String 
message, boolean shouldDisconnect, MultiValueTable mvt) throws IOException {
+               sendError(os, code, message, 
"<html><head><title>"+message+"</title></head><body><h1>"+message+"</h1></body>",
 shouldDisconnect, mvt);
+       }
+       
+       private static void sendError(OutputStream os, int code, String 
message, String htmlMessage, boolean disconnect, MultiValueTable mvt) throws 
IOException {
+               if(mvt == null) mvt = new MultiValueTable();
+               if(disconnect)
+                       mvt.put("Connection", "close");
+               byte[] messageBytes = htmlMessage.getBytes("ISO-8859-1");
+               sendReplyHeaders(os, code, message, mvt, "text/html", 
messageBytes.length);
+               os.write(messageBytes);
+       }
+       
+       private void sendNoToadletError(boolean shouldDisconnect) throws 
ToadletContextClosedException, IOException {
+               if(closed) throw new ToadletContextClosedException();
+               sendError(sockOutputStream, 404, "Service not found", 
shouldDisconnect, null);
+       }
+       
+       private static void sendURIParseError(OutputStream os, boolean 
shouldDisconnect) throws IOException {
+               sendError(os, 400, "URI parse error", shouldDisconnect, null);
+       }
+
+       public void sendReplyHeaders(int replyCode, String replyDescription, 
MultiValueTable mvt, String mimeType, long contentLength) throws 
ToadletContextClosedException, IOException {
+               if(closed) throw new ToadletContextClosedException();
+               sendReplyHeaders(sockOutputStream, replyCode, replyDescription, 
mvt, mimeType, contentLength);
+       }
+
+       static void sendReplyHeaders(OutputStream sockOutputStream, int 
replyCode, String replyDescription, MultiValueTable mvt, String mimeType, long 
contentLength) throws IOException {
+               // Construct headers
+               if(mvt == null)
+                       mvt = new MultiValueTable();
+               if(mimeType != null)
+                       mvt.put("content-type", mimeType);
+               if(contentLength >= 0)
+                       mvt.put("content-length", Long.toString(contentLength));
+               StringBuffer buf = new StringBuffer(1024);
+               buf.append("HTTP/1.1 ");
+               buf.append(replyCode);
+               buf.append(' ');
+               buf.append(replyDescription);
+               buf.append("\r\n");
+               for(Enumeration e = mvt.keys();e.hasMoreElements();) {
+                       String key = (String) e.nextElement();
+                       Object[] list = mvt.getArray(key);
+                       for(int i=0;i<list.length;i++) {
+                               String val = (String) list[i];
+                               buf.append(key);
+                               buf.append(": ");
+                               buf.append(val);
+                               buf.append("\r\n");
+                       }
+               }
+               buf.append("\r\n");
+               sockOutputStream.write(buf.toString().getBytes("US-ASCII"));
+       }
+       
+       /**
+        * Handle an incoming connection. Blocking, obviously.
+        */
+       public static void handle(Socket sock, ToadletContainer container) {
+               try {
+                       InputStream is = sock.getInputStream();
+                       
+                       LineReadingInputStream lis = new 
LineReadingInputStream(is);
+
+                       while(true) {
+                               
+                               String firstLine = lis.readLine(32768, 128);
+                               
+                               Logger.minor(ToadletContextImpl.class, "first 
line: "+firstLine);
+                               
+                               String[] split = firstLine.split(" ");
+                               
+                               if(split.length != 3)
+                                       throw new ParseException("Could not 
parse request line (split.length="+split.length+"): "+firstLine);
+                               
+                               if(!split[2].startsWith("HTTP/1."))
+                                       throw new ParseException("Unrecognized 
protocol "+split[2]);
+                               
+                               URI uri;
+                               try {
+                                       uri = new URI(split[1]);
+                               } catch (URISyntaxException e) {
+                                       
sendURIParseError(sock.getOutputStream(), true);
+                                       return;
+                               }
+                               
+                               String method = split[0];
+                               
+                               MultiValueTable headers = new MultiValueTable();
+                               
+                               while(true) {
+                                       String line = lis.readLine(32768, 128);
+                                       
System.out.println("Length="+line.length()+": "+line);
+                                       if(line.length() == 0) break;
+                                       int index = line.indexOf(':');
+                                       String before = line.substring(0, 
index);
+                                       String after = line.substring(index+1);
+                                       after = after.trim();
+                                       headers.put(before, after);
+                               }
+                               
+                               // Handle it.
+                               
+                               Toadlet t = container.findToadlet(uri);
+                               
+                               ToadletContextImpl ctx = new 
ToadletContextImpl(sock, headers);
+                               
+                               boolean shouldDisconnect = 
shouldDisconnectAfterHandled(split[2].equals("HTTP/1.0"), headers);
+                               
+                               if(t == null)
+                                       
ctx.sendNoToadletError(shouldDisconnect);
+                               
+                               if(method.equals("GET")) {
+                                       
+                                       t.handleGet(uri, ctx);
+                                       ctx.close();
+                                       
+                               } else if(method.equals("POST")) {
+                                       
+                                       Logger.error(ToadletContextImpl.class, 
"POST not supported");
+                                       ctx.sendMethodNotAllowed(method, 
shouldDisconnect);
+                                       ctx.close();
+                                       
+                               } else {
+                                       ctx.sendMethodNotAllowed(method, 
shouldDisconnect);
+                                       ctx.close();
+                               }
+                               
+                               if(shouldDisconnect) {
+                                       sock.close();
+                                       return;
+                               }
+                       }
+                       
+               } catch (ParseException e) {
+                       try {
+                               sendError(sock.getOutputStream(), 400, "Parse 
error: "+e.getMessage(), true, null);
+                       } catch (IOException e1) {
+                               // Ignore
+                       }
+               } catch (TooLongException e) {
+                       try {
+                               sendError(sock.getOutputStream(), 400, "Line 
too long parsing headers", true, null);
+                       } catch (IOException e1) {
+                               // Ignore
+                       }
+               } catch (IOException e) {
+                       return;
+               } catch (ToadletContextClosedException e) {
+                       Logger.error(ToadletContextImpl.class, 
"ToadletContextClosedException while handling connection!");
+                       return;
+               }
+       }
+
+       private static boolean shouldDisconnectAfterHandled(boolean isHTTP10, 
MultiValueTable headers) {
+               String connection = (String) headers.get("connection");
+               if(connection != null) {
+                       if(connection.equalsIgnoreCase("close"))
+                               return true;
+                       
+                       if(connection.equalsIgnoreCase("keep-alive"))
+                               return false;
+               }
+               if(!isHTTP10) return true;
+               // WTF?
+               return true;
+       }
+       
+       static class ParseException extends Exception {
+
+               ParseException(String string) {
+                       super(string);
+               }
+
+       }
+
+       public void writeData(byte[] data, int offset, int length) throws 
ToadletContextClosedException, IOException {
+               if(closed) throw new ToadletContextClosedException();
+               sockOutputStream.write(data, offset, length);
+       }
+
+       public void writeData(Bucket data) throws 
ToadletContextClosedException, IOException {
+               if(closed) throw new ToadletContextClosedException();
+               BucketTools.copyTo(data, sockOutputStream, Long.MAX_VALUE);
+       }
+
+}

Added: trunk/freenet/src/freenet/clients/http/TrivialToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/TrivialToadlet.java  2006-01-13 
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/clients/http/TrivialToadlet.java  2006-01-14 
01:51:07 UTC (rev 7853)
@@ -0,0 +1,32 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.net.URI;
+
+import freenet.client.HighLevelSimpleClient;
+import freenet.support.Bucket;
+import freenet.support.HTMLEncoder;
+
+public class TrivialToadlet extends Toadlet {
+
+       TrivialToadlet(HighLevelSimpleClient client) {
+               super(client);
+       }
+
+       void handleGet(URI uri, ToadletContext ctx) throws 
ToadletContextClosedException, IOException {
+               String fetched = uri.toString();
+               String encFetched = HTMLEncoder.encode(fetched);
+               String reply = "<html><head><title>You requested "+encFetched+
+                       "</title></head><body>You fetched <a 
href=\""+encFetched+"\">"+
+                       encFetched+"</a>.</body></html>";
+               this.writeReply(ctx, 200, "text/html", "OK", reply);
+       }
+
+       void handlePut(URI uri, Bucket data, ToadletContext ctx) throws 
ToadletContextClosedException, IOException {
+               String notSupported = "<html><head><title>Not 
supported</title></head><body>"+
+                       "Operation not supported</body>";
+               // This really should be 405, but then we'd have to put an 
Allow header in.
+               this.writeReply(ctx, 200, "text/html", "OK", notSupported);
+       }
+
+}

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2006-01-13 18:09:01 UTC (rev 
7852)
+++ trunk/freenet/src/freenet/node/Node.java    2006-01-14 01:51:07 UTC (rev 
7853)
@@ -26,6 +26,8 @@
 import freenet.client.ArchiveManager;
 import freenet.client.HighLevelSimpleClient;
 import freenet.client.HighLevelSimpleClientImpl;
+import freenet.clients.http.FproxyToadlet;
+import freenet.clients.http.SimpleToadletServer;
 import freenet.crypt.DSAPublicKey;
 import freenet.crypt.DiffieHellman;
 import freenet.crypt.RandomSource;
@@ -328,6 +330,12 @@
         Thread t = new Thread(new MemoryChecker(), "Memory checker");
         t.setPriority(Thread.MAX_PRIORITY);
         t.start();
+        SimpleToadletServer server = new SimpleToadletServer(port+2001);
+        FproxyToadlet fproxy = new 
FproxyToadlet(n.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS, 
(short)0));
+        server.register(fproxy, "/", false);
+        System.out.println("Starting fproxy on port "+(port+2001));
+        //server.register(fproxy, "/SSK@", false);
+        //server.register(fproxy, "/KSK@", false);
     }

     // FIXME - the whole overrideIP thing is a hack to avoid config

Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2006-01-13 18:09:01 UTC (rev 
7852)
+++ trunk/freenet/src/freenet/node/Version.java 2006-01-14 01:51:07 UTC (rev 
7853)
@@ -20,7 +20,7 @@
        public static final String protocolVersion = "1.0";

        /** The build number of the current revision */
-       public static final int buildNumber = 348;
+       public static final int buildNumber = 349;

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

Added: trunk/freenet/src/freenet/support/HTMLDecoder.java
===================================================================
--- trunk/freenet/src/freenet/support/HTMLDecoder.java  2006-01-13 18:09:01 UTC 
(rev 7852)
+++ trunk/freenet/src/freenet/support/HTMLDecoder.java  2006-01-14 01:51:07 UTC 
(rev 7853)
@@ -0,0 +1,411 @@
+package freenet.support;
+
+import java.util.HashMap;
+
+/**
+ * Description: Utility for converting character references e.g.: &lt; &gt;
+ * &quot; &#229; &#1048; &#x6C34;
+ * 
+ * @author Yves Lempereur (avian)
+ */
+public class HTMLDecoder {
+
+       private static final HashMap charTable;
+
+       public static String decode(String s) {
+               String t;
+               Character ch;
+               int tmpPos, i;
+
+               int maxPos = s.length();
+               StringBuffer sb = new StringBuffer(maxPos);
+               int curPos = 0;
+               while (curPos < maxPos) {
+                       char c = s.charAt(curPos++);
+                       if (c == '&') {
+                               tmpPos = curPos;
+                               if (tmpPos < maxPos) {
+                                       char d = s.charAt(tmpPos++);
+                                       if (d == '#') {
+                                               if (tmpPos < maxPos) {
+                                                       d = s.charAt(tmpPos++);
+                                                       if (d == 'x' || d == 
'X') {
+                                                               if (tmpPos < 
maxPos) {
+                                                                       d = 
s.charAt(tmpPos++);
+                                                                       if 
(isHexDigit(d)) {
+                                                                               
while (tmpPos < maxPos) {
+                                                                               
        d = s.charAt(tmpPos++);
+                                                                               
        if (!isHexDigit(d)) {
+                                                                               
                if (d == ';') {
+                                                                               
                        t =
+                                                                               
                                s.substring(
+                                                                               
                                        curPos + 2,
+                                                                               
                                        tmpPos - 1);
+                                                                               
                        try {
+                                                                               
                                i =
+                                                                               
                                        Integer.parseInt(
+                                                                               
                                                t,
+                                                                               
                                                16);
+                                                                               
                                if (i >= 0
+                                                                               
                                        && i < 65536) {
+                                                                               
                                        c = (char) i;
+                                                                               
                                        curPos = tmpPos;
+                                                                               
                                }
+                                                                               
                        } catch (NumberFormatException e) {
+                                                                               
                        }
+                                                                               
                }
+                                                                               
                break;
+                                                                               
        }
+                                                                               
}
+                                                                       }
+                                                               }
+                                                       } else if (isDigit(d)) {
+                                                               while (tmpPos < 
maxPos) {
+                                                                       d = 
s.charAt(tmpPos++);
+                                                                       if 
(!isDigit(d)) {
+                                                                               
if (d == ';') {
+                                                                               
        t =
+                                                                               
                s.substring(
+                                                                               
                        curPos + 1,
+                                                                               
                        tmpPos - 1);
+                                                                               
        try {
+                                                                               
                i = Integer.parseInt(t);
+                                                                               
                if (i >= 0 && i < 65536) {
+                                                                               
                        c = (char) i;
+                                                                               
                        curPos = tmpPos;
+                                                                               
                }
+                                                                               
        } catch (NumberFormatException e) {
+                                                                               
        }
+                                                                               
}
+                                                                               
break;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       } else if (isLetter(d)) {
+                                               while (tmpPos < maxPos) {
+                                                       d = s.charAt(tmpPos++);
+                                                       if 
(!isLetterOrDigit(d)) {
+                                                               if (d == ';') {
+                                                                       t = 
s.substring(curPos, tmpPos - 1);
+                                                                       ch = 
(Character) charTable.get(t);
+                                                                       if (ch 
!= null) {
+                                                                               
c = ch.charValue();
+                                                                               
curPos = tmpPos;
+                                                                       }
+                                                               }
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       sb.append(c);
+               }
+               return sb.toString();
+       }
+
+       private static boolean isLetterOrDigit(char c) {
+               return isLetter(c) || isDigit(c);
+       }
+
+       private static boolean isHexDigit(char c) {
+               return isHexLetter(c) || isDigit(c);
+       }
+
+       private static boolean isLetter(char c) {
+               return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+       }
+
+       private static boolean isHexLetter(char c) {
+               return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+       }
+
+       private static boolean isDigit(char c) {
+               return c >= '0' && c <= '9';
+       }
+
+       public static String compact(String s) {
+               int maxPos = s.length();
+               StringBuffer sb = new StringBuffer(maxPos);
+               int curPos = 0;
+               while (curPos < maxPos) {
+                       char c = s.charAt(curPos++);
+                       if (isWhitespace(c)) {
+                               while (curPos < maxPos && 
isWhitespace(s.charAt(curPos))) {
+                                       curPos++;
+                }
+                               c = '\u0020';
+                       }
+                       sb.append(c);
+               }
+               return sb.toString();
+       }
+
+       // HTML is very particular about what constitutes white space.
+       public static boolean isWhitespace(char ch) {
+               return ch == '\u0020'
+                       || ch == '\r'
+                       || ch == '\n'
+                       || ch == '\u0009'
+                       || ch == '\u000c'
+                       || ch == '\u200b';
+       }
+
+       static {
+               charTable = new HashMap();
+               charTable.put("quot", new Character((char) 34));
+               charTable.put("amp", new Character((char) 38));
+               charTable.put("apos", new Character((char) 39));
+               charTable.put("lt", new Character((char) 60));
+               charTable.put("gt", new Character((char) 62));
+               charTable.put("nbsp", new Character((char) 160));
+               charTable.put("iexcl", new Character((char) 161));
+               charTable.put("cent", new Character((char) 162));
+               charTable.put("pound", new Character((char) 163));
+               charTable.put("curren", new Character((char) 164));
+               charTable.put("yen", new Character((char) 165));
+               charTable.put("brvbar", new Character((char) 166));
+               charTable.put("sect", new Character((char) 167));
+               charTable.put("uml", new Character((char) 168));
+               charTable.put("copy", new Character((char) 169));
+               charTable.put("ordf", new Character((char) 170));
+               charTable.put("laquo", new Character((char) 171));
+               charTable.put("not", new Character((char) 172));
+               charTable.put("shy", new Character((char) 173));
+               charTable.put("reg", new Character((char) 174));
+               charTable.put("macr", new Character((char) 175));
+               charTable.put("deg", new Character((char) 176));
+               charTable.put("plusmn", new Character((char) 177));
+               charTable.put("sup2", new Character((char) 178));
+               charTable.put("sup3", new Character((char) 179));
+               charTable.put("acute", new Character((char) 180));
+               charTable.put("micro", new Character((char) 181));
+               charTable.put("para", new Character((char) 182));
+               charTable.put("middot", new Character((char) 183));
+               charTable.put("cedil", new Character((char) 184));
+               charTable.put("sup1", new Character((char) 185));
+               charTable.put("ordm", new Character((char) 186));
+               charTable.put("raquo", new Character((char) 187));
+               charTable.put("frac14", new Character((char) 188));
+               charTable.put("frac12", new Character((char) 189));
+               charTable.put("frac34", new Character((char) 190));
+               charTable.put("iquest", new Character((char) 191));
+               charTable.put("Agrave", new Character((char) 192));
+               charTable.put("Aacute", new Character((char) 193));
+               charTable.put("Acirc", new Character((char) 194));
+               charTable.put("Atilde", new Character((char) 195));
+               charTable.put("Auml", new Character((char) 196));
+               charTable.put("Aring", new Character((char) 197));
+               charTable.put("AElig", new Character((char) 198));
+               charTable.put("Ccedil", new Character((char) 199));
+               charTable.put("Egrave", new Character((char) 200));
+               charTable.put("Eacute", new Character((char) 201));
+               charTable.put("Ecirc", new Character((char) 202));
+               charTable.put("Euml", new Character((char) 203));
+               charTable.put("Igrave", new Character((char) 204));
+               charTable.put("Iacute", new Character((char) 205));
+               charTable.put("Icirc", new Character((char) 206));
+               charTable.put("Iuml", new Character((char) 207));
+               charTable.put("ETH", new Character((char) 208));
+               charTable.put("Ntilde", new Character((char) 209));
+               charTable.put("Ograve", new Character((char) 210));
+               charTable.put("Oacute", new Character((char) 211));
+               charTable.put("Ocirc", new Character((char) 212));
+               charTable.put("Otilde", new Character((char) 213));
+               charTable.put("Ouml", new Character((char) 214));
+               charTable.put("times", new Character((char) 215));
+               charTable.put("Oslash", new Character((char) 216));
+               charTable.put("Ugrave", new Character((char) 217));
+               charTable.put("Uacute", new Character((char) 218));
+               charTable.put("Ucirc", new Character((char) 219));
+               charTable.put("Uuml", new Character((char) 220));
+               charTable.put("Yacute", new Character((char) 221));
+               charTable.put("THORN", new Character((char) 222));
+               charTable.put("szlig", new Character((char) 223));
+               charTable.put("agrave", new Character((char) 224));
+               charTable.put("aacute", new Character((char) 225));
+               charTable.put("acirc", new Character((char) 226));
+               charTable.put("atilde", new Character((char) 227));
+               charTable.put("auml", new Character((char) 228));
+               charTable.put("aring", new Character((char) 229));
+               charTable.put("aelig", new Character((char) 230));
+               charTable.put("ccedil", new Character((char) 231));
+               charTable.put("egrave", new Character((char) 232));
+               charTable.put("eacute", new Character((char) 233));
+               charTable.put("ecirc", new Character((char) 234));
+               charTable.put("euml", new Character((char) 235));
+               charTable.put("igrave", new Character((char) 236));
+               charTable.put("iacute", new Character((char) 237));
+               charTable.put("icirc", new Character((char) 238));
+               charTable.put("iuml", new Character((char) 239));
+               charTable.put("eth", new Character((char) 240));
+               charTable.put("ntilde", new Character((char) 241));
+               charTable.put("ograve", new Character((char) 242));
+               charTable.put("oacute", new Character((char) 243));
+               charTable.put("ocirc", new Character((char) 244));
+               charTable.put("otilde", new Character((char) 245));
+               charTable.put("ouml", new Character((char) 246));
+               charTable.put("divide", new Character((char) 247));
+               charTable.put("oslash", new Character((char) 248));
+               charTable.put("ugrave", new Character((char) 249));
+               charTable.put("uacute", new Character((char) 250));
+               charTable.put("ucirc", new Character((char) 251));
+               charTable.put("uuml", new Character((char) 252));
+               charTable.put("yacute", new Character((char) 253));
+               charTable.put("thorn", new Character((char) 254));
+               charTable.put("yuml", new Character((char) 255));
+               charTable.put("OElig", new Character((char) 338));
+               charTable.put("oelig", new Character((char) 339));
+               charTable.put("Scaron", new Character((char) 352));
+               charTable.put("scaron", new Character((char) 353));
+               charTable.put("Yuml", new Character((char) 376));
+               charTable.put("fnof", new Character((char) 402));
+               charTable.put("circ", new Character((char) 710));
+               charTable.put("tilde", new Character((char) 732));
+               charTable.put("Alpha", new Character((char) 913));
+               charTable.put("Beta", new Character((char) 914));
+               charTable.put("Gamma", new Character((char) 915));
+               charTable.put("Delta", new Character((char) 916));
+               charTable.put("Epsilon", new Character((char) 917));
+               charTable.put("Zeta", new Character((char) 918));
+               charTable.put("Eta", new Character((char) 919));
+               charTable.put("Theta", new Character((char) 920));
+               charTable.put("Iota", new Character((char) 921));
+               charTable.put("Kappa", new Character((char) 922));
+               charTable.put("Lambda", new Character((char) 923));
+               charTable.put("Mu", new Character((char) 924));
+               charTable.put("Nu", new Character((char) 925));
+               charTable.put("Xi", new Character((char) 926));
+               charTable.put("Omicron", new Character((char) 927));
+               charTable.put("Pi", new Character((char) 928));
+               charTable.put("Rho", new Character((char) 929));
+               charTable.put("Sigma", new Character((char) 931));
+               charTable.put("Tau", new Character((char) 932));
+               charTable.put("Upsilon", new Character((char) 933));
+               charTable.put("Phi", new Character((char) 934));
+               charTable.put("Chi", new Character((char) 935));
+               charTable.put("Psi", new Character((char) 936));
+               charTable.put("Omega", new Character((char) 937));
+               charTable.put("alpha", new Character((char) 945));
+               charTable.put("beta", new Character((char) 946));
+               charTable.put("gamma", new Character((char) 947));
+               charTable.put("delta", new Character((char) 948));
+               charTable.put("epsilon", new Character((char) 949));
+               charTable.put("zeta", new Character((char) 950));
+               charTable.put("eta", new Character((char) 951));
+               charTable.put("theta", new Character((char) 952));
+               charTable.put("iota", new Character((char) 953));
+               charTable.put("kappa", new Character((char) 954));
+               charTable.put("lambda", new Character((char) 955));
+               charTable.put("mu", new Character((char) 956));
+               charTable.put("nu", new Character((char) 957));
+               charTable.put("xi", new Character((char) 958));
+               charTable.put("omicron", new Character((char) 959));
+               charTable.put("pi", new Character((char) 960));
+               charTable.put("rho", new Character((char) 961));
+               charTable.put("sigmaf", new Character((char) 962));
+               charTable.put("sigma", new Character((char) 963));
+               charTable.put("tau", new Character((char) 964));
+               charTable.put("upsilon", new Character((char) 965));
+               charTable.put("phi", new Character((char) 966));
+               charTable.put("chi", new Character((char) 967));
+               charTable.put("psi", new Character((char) 968));
+               charTable.put("omega", new Character((char) 969));
+               charTable.put("thetasym", new Character((char) 977));
+               charTable.put("upsih", new Character((char) 978));
+               charTable.put("piv", new Character((char) 982));
+               charTable.put("ensp", new Character((char) 8194));
+               charTable.put("emsp", new Character((char) 8195));
+               charTable.put("thinsp", new Character((char) 8201));
+               charTable.put("zwnj", new Character((char) 8204));
+               charTable.put("zwj", new Character((char) 8205));
+               charTable.put("lrm", new Character((char) 8206));
+               charTable.put("rlm", new Character((char) 8207));
+               charTable.put("ndash", new Character((char) 8211));
+               charTable.put("mdash", new Character((char) 8212));
+               charTable.put("lsquo", new Character((char) 8216));
+               charTable.put("rsquo", new Character((char) 8217));
+               charTable.put("sbquo", new Character((char) 8218));
+               charTable.put("ldquo", new Character((char) 8220));
+               charTable.put("rdquo", new Character((char) 8221));
+               charTable.put("bdquo", new Character((char) 8222));
+               charTable.put("dagger", new Character((char) 8224));
+               charTable.put("Dagger", new Character((char) 8225));
+               charTable.put("bull", new Character((char) 8226));
+               charTable.put("hellip", new Character((char) 8230));
+               charTable.put("permil", new Character((char) 8240));
+               charTable.put("prime", new Character((char) 8242));
+               charTable.put("Prime", new Character((char) 8243));
+               charTable.put("lsaquo", new Character((char) 8249));
+               charTable.put("rsaquo", new Character((char) 8250));
+               charTable.put("oline", new Character((char) 8254));
+               charTable.put("frasl", new Character((char) 8260));
+               charTable.put("euro", new Character((char) 8364));
+               charTable.put("image", new Character((char) 8465));
+               charTable.put("weierp", new Character((char) 8472));
+               charTable.put("real", new Character((char) 8476));
+               charTable.put("trade", new Character((char) 8482));
+               charTable.put("alefsym", new Character((char) 8501));
+               charTable.put("larr", new Character((char) 8592));
+               charTable.put("uarr", new Character((char) 8593));
+               charTable.put("rarr", new Character((char) 8594));
+               charTable.put("darr", new Character((char) 8595));
+               charTable.put("harr", new Character((char) 8596));
+               charTable.put("crarr", new Character((char) 8629));
+               charTable.put("lArr", new Character((char) 8656));
+               charTable.put("uArr", new Character((char) 8657));
+               charTable.put("rArr", new Character((char) 8658));
+               charTable.put("dArr", new Character((char) 8659));
+               charTable.put("hArr", new Character((char) 8660));
+               charTable.put("forall", new Character((char) 8704));
+               charTable.put("part", new Character((char) 8706));
+               charTable.put("exist", new Character((char) 8707));
+               charTable.put("empty", new Character((char) 8709));
+               charTable.put("nabla", new Character((char) 8711));
+               charTable.put("isin", new Character((char) 8712));
+               charTable.put("notin", new Character((char) 8713));
+               charTable.put("ni", new Character((char) 8715));
+               charTable.put("prod", new Character((char) 8719));
+               charTable.put("sum", new Character((char) 8721));
+               charTable.put("minus", new Character((char) 8722));
+               charTable.put("lowast", new Character((char) 8727));
+               charTable.put("radic", new Character((char) 8730));
+               charTable.put("prop", new Character((char) 8733));
+               charTable.put("infin", new Character((char) 8734));
+               charTable.put("ang", new Character((char) 8736));
+               charTable.put("and", new Character((char) 8743));
+               charTable.put("or", new Character((char) 8744));
+               charTable.put("cap", new Character((char) 8745));
+               charTable.put("cup", new Character((char) 8746));
+               charTable.put("int", new Character((char) 8747));
+               charTable.put("there4", new Character((char) 8756));
+               charTable.put("sim", new Character((char) 8764));
+               charTable.put("cong", new Character((char) 8773));
+               charTable.put("asymp", new Character((char) 8776));
+               charTable.put("ne", new Character((char) 8800));
+               charTable.put("equiv", new Character((char) 8801));
+               charTable.put("le", new Character((char) 8804));
+               charTable.put("ge", new Character((char) 8805));
+               charTable.put("sub", new Character((char) 8834));
+               charTable.put("sup", new Character((char) 8835));
+               charTable.put("nsub", new Character((char) 8836));
+               charTable.put("sube", new Character((char) 8838));
+               charTable.put("supe", new Character((char) 8839));
+               charTable.put("oplus", new Character((char) 8853));
+               charTable.put("otimes", new Character((char) 8855));
+               charTable.put("perp", new Character((char) 8869));
+               charTable.put("sdot", new Character((char) 8901));
+               charTable.put("lceil", new Character((char) 8968));
+               charTable.put("rceil", new Character((char) 8969));
+               charTable.put("lfloor", new Character((char) 8970));
+               charTable.put("rfloor", new Character((char) 8971));
+               charTable.put("lang", new Character((char) 9001));
+               charTable.put("rang", new Character((char) 9002));
+               charTable.put("loz", new Character((char) 9674));
+               charTable.put("spades", new Character((char) 9824));
+               charTable.put("clubs", new Character((char) 9827));
+               charTable.put("hearts", new Character((char) 9829));
+               charTable.put("diams", new Character((char) 9830));
+       }
+}

Added: trunk/freenet/src/freenet/support/HTMLEncoder.java
===================================================================
--- trunk/freenet/src/freenet/support/HTMLEncoder.java  2006-01-13 18:09:01 UTC 
(rev 7852)
+++ trunk/freenet/src/freenet/support/HTMLEncoder.java  2006-01-14 01:51:07 UTC 
(rev 7853)
@@ -0,0 +1,39 @@
+package freenet.support;
+
+/**
+ * Originally from com.websiteasp.ox pasckage.
+ * 
+ * Author: Yves Lempereur
+ */
+public class HTMLEncoder {
+
+       public static String encode(String s) {
+               int n = s.length();
+               StringBuffer sb = new StringBuffer(n);
+               for (int i = 0; i < n; i++) {
+                       char c = s.charAt(i);
+                       switch (c) {
+                               case '"' :
+                                       sb.append("&quot;");
+                                       break;
+                               case '&' :
+                                       sb.append("&amp;");
+                                       break;
+                                       //                              case 
'\'':
+                                       //                                      
sb.append("&apos;");
+                                       //                                      
break;
+                               case '<' :
+                                       sb.append("&lt;");
+                                       break;
+                               case '>' :
+                                       sb.append("&gt;");
+                                       break;
+                               default :
+                                       sb.append(c);
+                                       break;
+                       }
+               }
+               return sb.toString();
+       }
+
+}

Added: trunk/freenet/src/freenet/support/LimitedEnumeration.java
===================================================================
--- trunk/freenet/src/freenet/support/LimitedEnumeration.java   2006-01-13 
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/LimitedEnumeration.java   2006-01-14 
01:51:07 UTC (rev 7853)
@@ -0,0 +1,34 @@
+package freenet.support;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/** We kept remaking this everywhere so wtf.
+  * @author tavin
+  */
+public final class LimitedEnumeration implements Enumeration {
+
+    private Object next;
+    
+    public LimitedEnumeration() {
+        next = null;
+    }
+
+    public LimitedEnumeration(Object loner) {
+        next = loner;
+    }
+        
+    public final boolean hasMoreElements() {
+        return next != null;
+    }
+    
+    public final Object nextElement() {
+        if (next == null) throw new NoSuchElementException();
+        try {
+            return next;
+        }
+        finally {
+            next = null;
+        }
+    }
+}

Added: trunk/freenet/src/freenet/support/MultiValueTable.java
===================================================================
--- trunk/freenet/src/freenet/support/MultiValueTable.java      2006-01-13 
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/MultiValueTable.java      2006-01-14 
01:51:07 UTC (rev 7853)
@@ -0,0 +1,158 @@
+package freenet.support;
+import java.util.*;
+/**
+ * A hashtable that can store several values for each entry.
+ *
+ * @author oskar
+ */
+
+public class MultiValueTable {
+
+    private Hashtable table;
+    private int ies;
+
+    public MultiValueTable() {
+        this(16, 3);
+    }
+
+    public MultiValueTable(int initialSize) {
+        this(initialSize, 3);
+    }
+
+    public MultiValueTable(int initialSize, int initialEntrySize) {
+        table = new Hashtable(initialSize);
+        ies = initialEntrySize;
+    }
+
+    public void put(Object key, Object value) {
+        synchronized (table) {
+            Vector v = (Vector) table.get(key);
+            if (v == null) {
+                v = new Vector(ies);
+                table.put(key, v);
+            }
+            v.addElement(value);
+        }
+    }
+
+    /**
+     * Returns the first element for this key.
+     */
+    public Object get(Object key) {
+        synchronized (table) {
+            Vector v = (Vector) table.get(key);
+            return (v == null ?
+                    null :
+                    v.firstElement());
+        }
+    }
+
+    public boolean containsKey(Object key) {
+        return table.containsKey(key);
+    }
+
+    public boolean containsElement(Object key, Object value) {
+        synchronized (table) {
+            Vector v = (Vector) table.get(key);
+            return v != null && v.contains(value);
+        }
+    }
+
+    /**
+     * Users will have to handle synchronizing.
+     */
+    public Enumeration getAll(Object key) {
+        Vector v = (Vector) table.get(key);
+        return (v == null ?
+                new LimitedEnumeration(null) :
+                v.elements());
+    }
+    
+    public int countAll(Object key) {
+       Vector v = (Vector)table.get(key);
+       if(v != null) 
+               return v.size();
+        else
+               return 0;
+    }
+    
+    public Object getSync(Object key) {
+       return table.get(key);
+    }
+    
+    public Object[] getArray(Object key) {
+        synchronized (table) {
+            Vector v = (Vector) table.get(key);
+            if (v == null)
+                return null;
+            else {
+                Object[] r = new Object[v.size()];
+                v.copyInto(r);
+                return r;
+            }
+        }
+    }
+
+    public void remove(Object key) {
+        table.remove(key);
+    }
+
+    public boolean isEmpty() {
+        return table.isEmpty();
+    }
+
+    public void clear() {
+        table.clear();
+    }
+
+    public boolean removeElement(Object key, Object value) {
+        synchronized (table) {
+            Vector v = (Vector) table.get(key);
+            if (v == null)
+                return false;
+            else {
+                boolean b = v.removeElement(value);
+                if (v.isEmpty())
+                    table.remove(key);
+                return b;
+            }
+        }
+    }
+
+    public Enumeration keys() {
+        return table.keys();
+    }
+
+    public Enumeration elements() {
+        if (table.isEmpty())
+            return new LimitedEnumeration(null);
+        else 
+            return new MultiValueEnumeration();
+    }
+
+    private class MultiValueEnumeration implements Enumeration {
+        private Enumeration current;
+        private Enumeration global;
+        public MultiValueEnumeration() {
+            global = table.elements();
+            current = ((Vector) global.nextElement()).elements();
+            step();
+        }
+
+        public final void step() {
+            while (!current.hasMoreElements() && global.hasMoreElements())
+                current = ((Vector) global.nextElement()).elements();
+        }
+
+        public final boolean hasMoreElements() {
+            return global.hasMoreElements(); // || current.hasMoreElements();
+        }
+        
+        public final Object nextElement() {
+            Object o = current.nextElement();
+            step();
+            return o;
+        }
+    }
+
+}

Added: trunk/freenet/src/freenet/support/URLDecoder.java
===================================================================
--- trunk/freenet/src/freenet/support/URLDecoder.java   2006-01-13 18:09:01 UTC 
(rev 7852)
+++ trunk/freenet/src/freenet/support/URLDecoder.java   2006-01-14 01:51:07 UTC 
(rev 7853)
@@ -0,0 +1,97 @@
+package freenet.support;
+
+/*
+  This code is part of the Java Adaptive Network Client by Ian Clarke. 
+  It is distributed under the GNU Public Licence (GPL) version 2.  See
+  http://www.gnu.org/ for further details of the GPL.
+*/
+
+
+/**
+ * The class contains a utility method for converting a 
+ * <code>String</code> out of a MIME format called 
+ * "<code>x-www-form-urlencoded</code>" format. 
+ * <p>
+ * To convert a <code>String</code>, each character is examined in turn:
+ * <ul>
+ * <li>The ASCII characters '<code>a</code>' through '<code>z</code>', 
+ *     '<code>A</code>' through '<code>Z</code>', and '<code>0</code>' 
+ *     through '<code>9</code>' remain the same. 
+ * <li>The plus sign '<code>+</code>' is converted into a 
+ *     space character '<code>&nbsp;</code>'. 
+ * <li>The percent sign '<code>%</code>' must be followed by a 
+ *     two-digit hexadecimal number, and is converted into the
+ *     corresponding 8-bit character.
+ * <li>The following "safe" characters [RFC 1738] are passed as is,
+ *     if they appear:
+ *     <code>$ - _ . + ! * ' ( ) ,</code>
+ * <li>Anything else encountered, though strictly speaking illegal, 
+ *     is passed as is.
+ * </ul>
+ *
+ * @author <a href="http://www.doc.ic.ac.uk/~twh1/";>Theodore Hong</a>
+ **/
+
+public class URLDecoder
+{
+    // test harness
+    public static void main(String[] args) throws URLEncodedFormatException {
+       for (int i = 0; i < args.length; i++) {
+           System.out.println(args[i] + " -> " + decode(args[i]));
+       }
+    }
+
+    /**
+     * Characters which will be passed unaltered.
+     **/
+    private static final String safeCharList = "$-_.+!*'(),";
+
+    /**
+     * Translates a string out of x-www-form-urlencoded format.
+     *
+     * @param s String to be translated.
+     * @return the translated String.
+     *
+     **/
+    public static String decode(String s) throws URLEncodedFormatException {
+       if (s.length()==0) return "";
+       int len = s.length();
+       StringBuffer buf = new StringBuffer();
+
+       for (int i = 0; i < len; i++) {
+           char c = s.charAt(i);
+           if (Character.isLetterOrDigit(c))
+               buf.append(c);
+           else if (c == '+')
+               buf.append(' ');
+           else if (safeCharList.indexOf(c) != -1)
+               buf.append(c);
+           else if (c == '%') {
+               if (i >= len - 2) {
+                   throw new URLEncodedFormatException(s);
+               } else {
+                   char[] hexChars = new char[2];
+
+                   hexChars[0] = s.charAt(++i);
+                   hexChars[1] = s.charAt(++i);
+
+                   String hexval = new String(hexChars);
+                   try {
+                        long read = Fields.hexToLong(hexval);
+                        if (read == 0)
+                            throw new URLEncodedFormatException("Can't encode"
+                                                                + " 00");
+                       buf.append(new Character((char) read));
+                   }
+                   catch (NumberFormatException nfe) {
+                       throw new URLEncodedFormatException(s);
+                   }
+               }
+           }
+           else
+               buf.append(c);
+               // throw new URLEncodedFormatException(s);
+       }
+       return buf.toString();
+    }
+}

Added: trunk/freenet/src/freenet/support/URLEncodedFormatException.java
===================================================================
--- trunk/freenet/src/freenet/support/URLEncodedFormatException.java    
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/URLEncodedFormatException.java    
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,18 @@
+package freenet.support;
+
+/*
+  This code is part of the Java Adaptive Network Client by Ian Clarke. 
+  It is distributed under the GNU Public Licence (GPL) version 2.  See
+  http://www.gnu.org/ for further details of the GPL.
+*/
+
+
+/**
+ * Thrown when trying to decode a string which is not in 
+ * "<code>x-www-form-urlencoded</code>" format.
+ **/
+
+public class URLEncodedFormatException extends Exception {
+    URLEncodedFormatException () {}
+    URLEncodedFormatException (String s) { super(s); }
+}

Added: trunk/freenet/src/freenet/support/URLEncoder.java
===================================================================
--- trunk/freenet/src/freenet/support/URLEncoder.java   2006-01-13 18:09:01 UTC 
(rev 7852)
+++ trunk/freenet/src/freenet/support/URLEncoder.java   2006-01-14 01:51:07 UTC 
(rev 7853)
@@ -0,0 +1,39 @@
+package freenet.support;
+
+public class URLEncoder {
+  // Moved here from fproxy by amphibian
+  final static String safeURLCharacters = 
"@*-./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
+
+  /**
+   * Encode a string for inclusion in HTML tags
+   *
+   * @param  URL  String to encode
+   * @return      HTML-safe version of string
+   */
+  public final static String encode(String URL) {
+    StringBuffer enc = new StringBuffer(URL.length());
+    for (int i = 0; i < URL.length(); ++i) {
+      char c = URL.charAt(i);
+      if (safeURLCharacters.indexOf(c) >= 0) {
+        enc.append(c);
+      } else {
+        // Too harsh.
+        // if (c < 0 || c > 255)
+        //    throw new RuntimeException("illegal code "+c+" of char 
'"+URL.charAt(i)+"'");
+        // else
+
+        // Just keep lsb like:
+        // http://java.sun.com/j2se/1.3/docs/api/java/net/URLEncoder.html
+        c = (char) (c & '\u00ff');
+        if (c < 16) {
+          enc.append("%0");
+        } else {
+          enc.append("%");
+        }
+        enc.append(Integer.toHexString(c));
+      }
+    }
+    return enc.toString();
+  }
+
+}

Added: trunk/freenet/src/freenet/support/io/LineReadingInputStream.java
===================================================================
--- trunk/freenet/src/freenet/support/io/LineReadingInputStream.java    
2006-01-13 18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/io/LineReadingInputStream.java    
2006-01-14 01:51:07 UTC (rev 7853)
@@ -0,0 +1,39 @@
+package freenet.support.io;
+
+import java.io.EOFException;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A FilterInputStream which provides readLine().
+ */
+public class LineReadingInputStream extends FilterInputStream {
+
+       public LineReadingInputStream(InputStream in) {
+               super(in);
+       }
+
+       /**
+        * Read a line of US-ASCII. Used for e.g. HTTP.
+        */
+       public String readLine(int maxLength, int bufferSize) throws 
IOException {
+               StringBuffer sb = new StringBuffer(bufferSize);
+               while(true) {
+                       int x = read();
+                       if(x == -1) throw new EOFException();
+                       char c = (char) x;
+                       if(c == '\n') {
+                               if(sb.length() > 0) {
+                                       if(sb.charAt(sb.length()-1) == '\r')
+                                               sb.setLength(sb.length()-1);
+                               }
+                               return sb.toString();
+                       }
+                       sb.append(c);
+                       if(sb.length() >= maxLength)
+                               throw new TooLongException();
+               }
+       }
+       
+}

Added: trunk/freenet/src/freenet/support/io/TooLongException.java
===================================================================
--- trunk/freenet/src/freenet/support/io/TooLongException.java  2006-01-13 
18:09:01 UTC (rev 7852)
+++ trunk/freenet/src/freenet/support/io/TooLongException.java  2006-01-14 
01:51:07 UTC (rev 7853)
@@ -0,0 +1,8 @@
+package freenet.support.io;
+
+import java.io.IOException;
+
+/** Exception thrown by a LineReadingInputStream when a line is too long. */
+public class TooLongException extends IOException {
+
+}
\ No newline at end of file


Reply via email to