From: "Daniel Cheng (aka SDiZ)" <j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: devl-RdDMkVZAZeuJnvDnx1genB2eb7JE58TQ@public.gmane.org,
git-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: "Daniel Cheng \(aka SDiZ\)"
<j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Subject: [PoC PATCH JGIT 1/2] Freenet Freenet Client Protocol (FCP) 2.0 Client
Date: Mon, 9 Mar 2009 11:36:04 +0800 [thread overview]
Message-ID: <1236569765-8882-2-git-send-email-j16sdiz+freenet@gmail.com> (raw)
In-Reply-To: <1236569765-8882-1-git-send-email-j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Daniel Cheng (aka SDiZ) <j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
.../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-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * 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
next prev parent reply other threads:[~2009-03-09 3:37 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-03-09 3:36 [PoC PATCH JGIT 0/2] Proof of concept code for Git Freenet transport Daniel Cheng (aka SDiZ)
[not found] ` <1236569765-8882-1-git-send-email-j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2009-03-09 3:36 ` Daniel Cheng (aka SDiZ) [this message]
[not found] ` <1236569765-8882-2-git-send-email-j16sdiz+freenet-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2009-03-09 3:36 ` [PoC PATCH JGIT 2/2] PoC " Daniel Cheng (aka SDiZ)
2009-03-09 5:58 ` [PoC PATCH JGIT 0/2] Proof of concept code for Git " Daniel Cheng
2009-03-09 9:58 ` Johannes Schindelin
2009-03-09 14:59 ` Shawn O. Pearce
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1236569765-8882-2-git-send-email-j16sdiz+freenet@gmail.com \
--to=j16sdiz+freenet-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=devl-RdDMkVZAZeuJnvDnx1genB2eb7JE58TQ@public.gmane.org \
--cc=git-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).