[PoC PATCH JGIT 1/2] Freenet Freenet Client Protocol (FCP) 2.0 Client

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Daniel Cheng (aka SDiZ) <j16sdiz+freenet@xxxxxxxxx>
---
 .../jgit/transport/fcpv2/FCPConnection.java        |  427 ++++++++++++++++++++
 1 files changed, 427 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java
new file mode 100644
index 0000000..ce2ca90
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/fcpv2/FCPConnection.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2008, CHENG Yuk-Pong, Daniel <j16sdiz+freenet@xxxxxxxxx>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ *   copyright notice, this list of conditions and the following
+ *   disclaimer in the documentation and/or other materials provided
+ *   with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ *   names of its contributors may be used to endorse or promote
+ *   products derived from this software without specific prior
+ *   written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.spearce.jgit.transport.fcpv2;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.spearce.jgit.lib.ProgressMonitor;
+
+/**
+ * Freenet Client Protocol 2.0 Connection
+ * 
+ * A simplified interface to Freenet 0.7 node using FCPv2. This is not a
+ * full-blown interface to Freenet node, but rather some "quick and dirty" hack
+ * just enough to upload/download simple file from the network.
+ * <p>
+ * See <a href="http://wiki.freenetproject.org/FreenetFCPSpec2Point0";>Freenet
+ * Client Protocol 2.0 Specification</a> for detail on this protocol.
+ */
+public class FCPConnection {
+	/** Default FCP port */
+	public static final int DEFAULT_FCP_PORT = 9481;
+
+	protected InetAddress addr;
+
+	protected int port;
+
+	protected Socket socket;
+
+	protected InputStream is;
+
+	protected OutputStream os;
+
+	/**
+	 * Create a new FCP Connection to default host and port (
+	 * <code>localhost:9481</code>)
+	 * 
+	 * @throws UnknownHostException
+	 *             if no IP address for the <code>localhost</code> could be
+	 *             found.
+	 */
+	public FCPConnection() throws UnknownHostException {
+		this(InetAddress.getAllByName("127.0.0.1")[0], DEFAULT_FCP_PORT);
+	}
+
+	/**
+	 * Create a new FCP Connection to the specified IP address and port
+	 * 
+	 * @param address
+	 *            the node IP address
+	 * @param port
+	 *            the port number
+	 */
+	public FCPConnection(InetAddress address, int port) {
+		this.addr = address;
+		this.port = port;
+	}
+
+	/**
+	 * Connect to the node
+	 * 
+	 * @throws IOException
+	 *             if any I/O error occurred
+	 */
+	public void connect() throws IOException {
+		socket = new Socket(addr, port);
+
+		is = new BufferedInputStream(socket.getInputStream());
+		os = new BufferedOutputStream(socket.getOutputStream());
+	}
+
+	/**
+	 * Hello Message
+	 * 
+	 * Send handshake <code>ClientHello</code> message to node. Block until
+	 * <code>NodeHello</code> is received.
+	 * 
+	 * @param clientName
+	 *            Client name, must be unique in the freenet node
+	 * 
+	 * @throws IOException
+	 *             if any I/O error occurred
+	 */
+	public void hello(String clientName) throws IOException {
+		FCPMessage msg = new FCPMessage();
+		msg.messageType = "ClientHello";
+		msg.put("ExpectedVersion", "2.0");
+		msg.put("Name", clientName);
+
+		msg.writeTo(os);
+
+		while (true) {
+			FCPMessage reply = FCPMessage.parse(is);
+			if ("NodeHello".equals(reply.messageType))
+				return;
+			if ("ProtocolError".equals(reply.messageType))
+				throw new IOException("Protocol error");
+		}
+	}
+
+	/**
+	 * Simple FCP Put
+	 * 
+	 * This method block until the data are fetchable or <code>PutFailed</code>
+	 * received.
+	 * 
+	 * @param freenetURI
+	 *            the freenet URI
+	 * @param data
+	 *            the data to be put
+	 * @param monitor
+	 *            progress monitor to post write completion to during the
+	 *            stream's close method.
+	 * @param monitorTask
+	 *            task name to display during the close method.
+	 * @return <code>true</code> iff <code>PutFetchable</code>,
+	 *         <code>PutSuccessful</code> or <code>PutFetchable</code> with
+	 *         insert collision (data already inserted)
+	 * @throws IOException
+	 *             if any I/O error occurred
+	 */
+	public boolean simplePut(String freenetURI, byte[] data,
+			ProgressMonitor monitor, String monitorTask) throws IOException {
+		System.err.println(freenetURI);
+
+		FCPMessage msg = new FCPMessage();
+		msg.messageType = "ClientPut";
+		msg.put("URI", freenetURI);
+		msg.put("Identifier", freenetURI);
+		msg.put("Verbosity", "1");
+		msg.put("Global", "false");
+		msg.put("UploadFrom", "direct");
+		msg.put("DataLength", "" + data.length);
+		msg.extraData = data;
+
+		msg.writeTo(os);
+
+		int totalBlocks = -1;
+		int completedBlocks = 0;
+
+		try {
+			while (true) {
+				FCPMessage reply = FCPMessage.parse(is);
+
+				if ("ProtocolError".equals(reply.messageType))
+					throw new IOException("Protocol error");
+				if (!freenetURI.equals(reply.get("Identifier"))) // identifier don't match, ignore it
+					continue;
+				if ("IdentifierCollision".equals(reply.messageType))
+					throw new IOException("IdentifierCollision");
+
+				if ("PutFailed".equals(reply.messageType)) {
+					if ("9".equals(reply.get("Code"))) // collision, data already inserted
+						return true;
+					return false;
+				}
+
+				if ("SimpleProgress".equals(reply.messageType)) {
+					if (totalBlocks == -1) {
+						totalBlocks = Integer.parseInt(reply.get("Total"));
+						monitor.beginTask(monitorTask, totalBlocks);
+					}
+					int tmp = Integer.parseInt(reply.get("Succeeded"));
+					if (tmp < totalBlocks)
+						monitor.update(tmp - completedBlocks);
+					completedBlocks = tmp;
+				}
+
+				if ("PutSuccessful".equals(reply.messageType))
+					return true;
+				if ("PutFetchable".equals(reply.messageType))
+					return true;
+			}
+		} finally {
+			if (totalBlocks == -1)
+				monitor.beginTask(monitorTask, 1);
+			monitor.endTask();
+		}
+	}
+
+	/**
+	 * Simple FCP Get
+	 * 
+	 * @param freenetURI
+	 *            the freenet URI
+	 * @return the data
+	 * @throws IOException
+	 *             if any I/O error occurred
+	 * @throws FileNotFoundException
+	 *             if data not found
+	 */
+	public byte[] simpleGet(String freenetURI) throws IOException,
+			FileNotFoundException {
+		FCPMessage msg = new FCPMessage();
+		msg.messageType = "ClientGet";
+		msg.put("URI", freenetURI);
+		msg.put("Identifier", freenetURI);
+		msg.put("Verbosity", "1");
+		msg.put("MaxSize", "2147483647");
+		msg.put("Global", "false");
+		msg.put("ClientToken", freenetURI);
+		msg.put("ReturnType", "direct");
+
+		msg.writeTo(os);
+
+		while (true) {
+			FCPMessage reply = FCPMessage.parse(is);
+			if ("ProtocolError".equals(reply.messageType))
+				throw new IOException("Protocol error");
+			if (!freenetURI.equals(reply.get("Identifier"))) // identifier don't match, ignore it
+				continue;
+			if ("IdentifierCollision".equals(reply.messageType))
+				throw new IOException("IdentifierCollision");
+
+			if ("GetFailed".equals(reply.messageType)) {
+				String redirectURI = reply.get("RedirectURI");
+				if (redirectURI != null)
+					return simpleGet(redirectURI);
+
+				System.err.println(" -> nf");
+				throw new FileNotFoundException();
+			}
+
+			if ("AllData".equals(reply.messageType))
+				return reply.extraData;
+		}
+	}
+
+	/**
+	 * Generate SSK Key Pair
+	 * 
+	 * @return the generated SSK key pair. <code>key[0]</code> is the public
+	 *         key, <code>key[1]</code> is the private key.
+	 * @throws IOException
+	 *             if any I/O error occurred
+	 */
+	public String[] generateSSK() throws IOException {
+		FCPMessage msg = new FCPMessage();
+		msg.messageType = "GenerateSSK";
+		msg.put("Identifier", "GenerateSSK");
+
+		msg.writeTo(os);
+
+		while (true) {
+			FCPMessage reply = FCPMessage.parse(is);
+			if ("ProtocolError".equals(reply.messageType))
+				throw new IOException("Protocol error");
+
+			if ("SSKKeypair".equals(reply.messageType)) {
+				String[] keys = new String[2];
+				keys[0] = reply.get("RequestURI");
+				keys[1] = reply.get("InsertURI");
+				return keys;
+			}
+		}
+	}
+
+	/**
+	 * Close the connection
+	 * 
+	 * @throws IOException
+	 *             if any I/O error occurred
+	 */
+	public void close() throws IOException {
+		os.close();
+		is.close();
+		socket.close();
+	}
+
+	private static class FCPMessage {
+		private String messageType;
+
+		private Map<String, String> parameter = new HashMap<String, String>();
+
+		byte[] extraData;
+
+		FCPMessage() {
+			// default constructor
+		}
+
+		private static FCPMessage parse(InputStream in) throws IOException {
+			FCPMessage ret = new FCPMessage();
+			String line = readLine(in);
+			ret.messageType = line;
+
+			while (true) {
+				line = readLine(in);
+
+				if (line == null)
+					throw new IOException("Malformed FCP message");
+				if ("EndMessage".equals(line))
+					break;
+				if ("Data".equals(line)) {
+					String strLen = ret.parameter.get("DataLength");
+					if (strLen == null)
+						throw new IOException("DataLength not found");
+					int len;
+					try {
+						len = Integer.parseInt(strLen);
+					} catch (NumberFormatException e) {
+						throw new IOException("DataLength malformed");
+					}
+					ret.extraData = readData(in, len);
+					break;
+				}
+
+				String[] v = line.split("=", 2);
+				if (v.length != 2)
+					throw new IOException("No '=' found in: " + line);
+				ret.parameter.put(v[0], v[1]);
+			}
+
+			return ret;
+		}
+
+		void put(String key, String value) {
+			parameter.put(key, value);
+		}
+
+		String get(String key) {
+			return parameter.get(key);
+		}
+
+		void writeTo(OutputStream os) throws IOException {
+			os.write(messageType.getBytes("UTF-8"));
+			os.write('\n');
+
+			for (Map.Entry<String, String> e : parameter.entrySet()) {
+				String l = e.getKey() + '=' + e.getValue();
+				os.write(l.getBytes("UTF-8"));
+				os.write('\n');
+			}
+
+			os.write("EndMessage".getBytes("UTF-8"));
+			os.write('\n');
+
+			if (extraData != null)
+				os.write(extraData);
+			os.flush();
+		}
+	}
+
+	private static String readLine(InputStream in) throws IOException {
+		byte[] buf = new byte[256];
+		int offset = 0;
+
+		while (true) {
+			int b = in.read();
+			if (b == -1)
+				return null;
+			if (b == '\n') {
+				if (offset == 0)
+					continue; // skip empty line
+				break;
+			}
+
+			if (offset == buf.length) {
+				if (offset >= 4096)
+					throw new IOException("line too long");
+
+				byte[] buf2 = new byte[buf.length * 2];
+				System.arraycopy(buf, 0, buf2, 0, buf.length);
+				buf = buf2;
+			}
+
+			buf[offset++] = (byte) b;
+		}
+
+		return new String(buf, 0, offset, "UTF-8");
+	}
+
+	private static byte[] readData(InputStream in, int len) throws IOException {
+		byte[] buf = new byte[len];
+		int read = 0;
+		while (read < len) {
+			int r = in.read(buf, read, len - read);
+			if (r == -1)
+				throw new IOException("Not enough data");
+			read += r;
+		}
+		return buf;
+	}
+}
-- 
1.6.2.rc2

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux