add ListPeer and ListPeers commands and replies
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 9 Apr 2008 11:25:13 +0000 (11:25 +0000)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 9 Apr 2008 11:25:13 +0000 (11:25 +0000)
git-svn-id: http://trooper/svn/projects/jSite/trunk@660 c3eda9e8-030b-0410-8277-bc7414b0a119

src/net/pterodactylus/util/fcp/FcpAdapter.java
src/net/pterodactylus/util/fcp/FcpConnection.java
src/net/pterodactylus/util/fcp/FcpListener.java
src/net/pterodactylus/util/fcp/FcpUtils.java
src/net/pterodactylus/util/fcp/message/EndListPeers.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/ListPeer.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/ListPeers.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/Peer.java [new file with mode: 0644]

index 65ab77b..d867b91 100644 (file)
@@ -4,7 +4,9 @@
 package net.pterodactylus.util.fcp;
 
 import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName;
+import net.pterodactylus.util.fcp.message.EndListPeers;
 import net.pterodactylus.util.fcp.message.NodeHello;
+import net.pterodactylus.util.fcp.message.Peer;
 import net.pterodactylus.util.fcp.message.SSKKeypair;
 
 /**
@@ -40,6 +42,22 @@ public class FcpAdapter implements FcpListener {
        }
 
        /**
+        * @see net.pterodactylus.util.fcp.FcpListener#receivedPeer(net.pterodactylus.util.fcp.FcpConnection,
+        *      net.pterodactylus.util.fcp.message.Peer)
+        */
+       public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
+               /* empty. */
+       }
+
+       /**
+        * @see net.pterodactylus.util.fcp.FcpListener#receivedEndListPeers(net.pterodactylus.util.fcp.FcpConnection,
+        *      net.pterodactylus.util.fcp.message.EndListPeers)
+        */
+       public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
+               /* empty. */
+       }
+
+       /**
         * @see net.pterodactylus.util.fcp.FcpListener#receivedMessage(net.pterodactylus.util.fcp.FcpConnection,
         *      net.pterodactylus.util.fcp.FcpMessage)
         */
index 846e135..6d4ca97 100644 (file)
@@ -29,7 +29,9 @@ import java.util.ArrayList;
 import java.util.List;
 
 import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName;
+import net.pterodactylus.util.fcp.message.EndListPeers;
 import net.pterodactylus.util.fcp.message.NodeHello;
+import net.pterodactylus.util.fcp.message.Peer;
 import net.pterodactylus.util.fcp.message.SSKKeypair;
 import net.pterodactylus.util.io.Closer;
 
@@ -184,6 +186,30 @@ public class FcpConnection {
        }
 
        /**
+        * Notifies listeners that a “Peer” message was received.
+        * 
+        * @param peer
+        *            The “Peer” message
+        */
+       private void fireReceivedPeer(Peer peer) {
+               for (FcpListener fcpListener: fcpListeners) {
+                       fcpListener.receivedPeer(this, peer);
+               }
+       }
+
+       /**
+        * Notifies all listeners that an “EndListPeers” message was received.
+        * 
+        * @param endListPeers
+        *            The “EndListPeers” message
+        */
+       private void fireReceivedEndListPeers(EndListPeers endListPeers) {
+               for (FcpListener fcpListener: fcpListeners) {
+                       fcpListener.receivedEndListPeers(this, endListPeers);
+               }
+       }
+
+       /**
         * Notifies all registered listeners that a message has been received.
         * 
         * @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
@@ -257,7 +283,11 @@ public class FcpConnection {
         */
        void handleMessage(FcpMessage fcpMessage) {
                String messageName = fcpMessage.getName();
-               if ("SSKKeypair".equals(messageName)) {
+               if ("Peer".equals(messageName)) {
+                       fireReceivedPeer(new Peer(fcpMessage));
+               } else if ("EndListPeers".equals(messageName)) {
+                       fireReceivedEndListPeers(new EndListPeers(fcpMessage));
+               } else if ("SSKKeypair".equals(messageName)) {
                        fireReceivedSSKKeypair(new SSKKeypair(fcpMessage));
                } else if ("NodeHello".equals(messageName)) {
                        fireReceivedNodeHello(new NodeHello(fcpMessage));
index a983f88..2c72280 100644 (file)
@@ -22,7 +22,9 @@ package net.pterodactylus.util.fcp;
 import java.util.EventListener;
 
 import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName;
+import net.pterodactylus.util.fcp.message.EndListPeers;
 import net.pterodactylus.util.fcp.message.NodeHello;
+import net.pterodactylus.util.fcp.message.Peer;
 import net.pterodactylus.util.fcp.message.SSKKeypair;
 
 /**
@@ -65,6 +67,26 @@ public interface FcpListener extends EventListener {
        public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair);
 
        /**
+        * Notifies all listeners that a “Peer” message was received.
+        * 
+        * @param fcpConnection
+        *            The connection that received the message
+        * @param peer
+        *            The “Peer” message
+        */
+       public void receivedPeer(FcpConnection fcpConnection, Peer peer);
+
+       /**
+        * Notifies a listener that an “EndListPeers” message was received.
+        * 
+        * @param fcpConnection
+        *            The connection that recevied the message
+        * @param endListPeers
+        *            The “EndListPeers” message
+        */
+       public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers);
+
+       /**
         * Notifies listeners that a message has been received. This method is only
         * called if {@link FcpConnection#handleMessage(FcpMessage)} does not
         * recognize the message. Should that ever happen, please file a bug report!
index 9892610..a492f2e 100644 (file)
@@ -3,6 +3,7 @@
  */
 package net.pterodactylus.util.fcp;
 
+import java.util.StringTokenizer;
 import java.util.concurrent.atomic.AtomicLong;
 
 /**
@@ -25,4 +26,24 @@ public class FcpUtils {
                return new StringBuilder().append(System.currentTimeMillis()).append('-').append(counter.getAndIncrement()).toString();
        }
 
+       /**
+        * Parses an integer field, separated by ‘;’ and returns the parsed values.
+        * 
+        * @param field
+        *            The field to parse
+        * @return An array with the parsed values
+        * @throws NumberFormatException
+        *             if a value can not be converted to a number
+        */
+       public static int[] parseMultiIntegerField(String field) throws NumberFormatException {
+               StringTokenizer fieldTokens = new StringTokenizer(field, ";");
+               int[] result = new int[fieldTokens.countTokens()];
+               int counter = 0;
+               while (fieldTokens.hasMoreTokens()) {
+                       String fieldToken = fieldTokens.nextToken();
+                       result[counter++] = Integer.valueOf(fieldToken);
+               }
+               return result;
+       }
+
 }
diff --git a/src/net/pterodactylus/util/fcp/message/EndListPeers.java b/src/net/pterodactylus/util/fcp/message/EndListPeers.java
new file mode 100644 (file)
index 0000000..2fac3fb
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * This message marks the end of a list of “Peer” replies.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class EndListPeers extends BaseMessage {
+
+       /**
+        * Creates a new “EndListPeers” message that wraps the received message.
+        * 
+        * @param receivedMessage
+        *            The message that was received
+        */
+       public EndListPeers(FcpMessage receivedMessage) {
+               super(receivedMessage);
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/ListPeer.java b/src/net/pterodactylus/util/fcp/message/ListPeer.java
new file mode 100644 (file)
index 0000000..a09a868
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * The “ListPeer” request asks the node about the details of a given peer.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class ListPeer extends FcpMessage {
+
+       /**
+        * Creates a new “ListPeer” request that returns information about the node
+        * specified by <code>nodeIdentifier</code>. <code>nodeIdentifier</code>
+        * can be of several formats: The node’s name, its identity, or its IP
+        * address and port (connection with a ‘:’).
+        * 
+        * @param nodeIdentifier
+        *            The identifier of the node to get details about
+        */
+       public ListPeer(String nodeIdentifier) {
+               super("ListPeer");
+               setField("NodeIdentifier", nodeIdentifier);
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/ListPeers.java b/src/net/pterodactylus/util/fcp/message/ListPeers.java
new file mode 100644 (file)
index 0000000..c585868
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * The “ListPeer” requests asks the node for a list of all peers it has.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class ListPeers extends FcpMessage {
+
+       /**
+        * Creates a new “ListPeers” request that only includes basic data of the
+        * peers.
+        */
+       public ListPeers() {
+               this(false, false);
+       }
+
+       /**
+        * Creates a new “ListPeers” request that includes wanted data of the peers.
+        * 
+        * @param withMetadata
+        *            If <code>true</code> metadata of the peers is included in
+        *            the reply
+        * @param withVolatile
+        *            if <code>true</code> volatile data of the peers is included
+        *            in the reply
+        */
+       public ListPeers(boolean withMetadata, boolean withVolatile) {
+               super("ListPeers");
+               setField("WithMetadata", String.valueOf(withMetadata));
+               setField("WithVolatile", String.valueOf(withVolatile));
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/Peer.java b/src/net/pterodactylus/util/fcp/message/Peer.java
new file mode 100644 (file)
index 0000000..9a20378
--- /dev/null
@@ -0,0 +1,372 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import java.security.interfaces.DSAParams;
+import java.util.StringTokenizer;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+import net.pterodactylus.util.fcp.FcpUtils;
+
+/**
+ * The “Peer” reply by the node contains information about a peer.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class Peer extends BaseMessage {
+
+       /**
+        * Creates a new “Peer” reply from the received message.
+        * 
+        * @param receivedMessage
+        *            The received message
+        */
+       public Peer(FcpMessage receivedMessage) {
+               super(receivedMessage);
+       }
+
+       /**
+        * Returns the “physical.udp” line from the message. It contains all IP
+        * addresses and port numbers of the peer.
+        * 
+        * @return The IP addresses and port numbers of the peer
+        */
+       public String getPhysicalUDP() {
+               return getField("physical.udp");
+       }
+
+       /**
+        * Returns whether the listed peer is an opennet peer.
+        * 
+        * @return <code>true</code> if the peer is an opennet peer,
+        *         <code>false</code> if the peer is a darknet peer
+        */
+       public boolean isOpennet() {
+               return Boolean.valueOf(getField("opennet"));
+       }
+
+       /**
+        * Returns the “y” part of the peer’s public DSA key.
+        * 
+        * @return The public DSA key
+        */
+       public String getDSAPublicKey() {
+               return getField("dsaPubKey.y");
+       }
+
+       /**
+        * Returns the DSA group of the peer.
+        * 
+        * @return The DSA group of the peer
+        */
+       public DSAGroup getDSAGroup() {
+               return new DSAGroup(getField("dsaGroup.g"), getField("dsaGroup.p"), getField("dsaGroup.q"));
+       }
+
+       /**
+        * Returns the last good version of the peer, i.e. the oldest version the
+        * peer will connect to.
+        * 
+        * @return The last good version of the peer
+        */
+       public Version getLastGoodVersion() {
+               return new Version(getField("lastGoodVersion"));
+       }
+
+       /**
+        * Returns the ARK of the peer.
+        * 
+        * @return The ARK of the peer
+        */
+       public ARK getARK() {
+               return new ARK(getField("ark.pubURI"), getField("ark.number"));
+       }
+
+       /**
+        * Returns the identity of the peer.
+        * 
+        * @return The identity of the peer
+        */
+       public String getIdentity() {
+               return getField("identity");
+       }
+
+       /**
+        * Returns the name of the peer. If the peer is not a darknet peer it will
+        * have no name.
+        * 
+        * @return The name of the peer, or <code>null</code> if the peer is an
+        *         opennet peer
+        */
+       public String getMyName() {
+               return getField("myName");
+       }
+
+       /**
+        * Returns the location of the peer.
+        * 
+        * @return The location of the peer
+        * @throws NumberFormatException
+        *             if the field can not be parsed
+        */
+       public double getLocation() throws NumberFormatException {
+               return Double.valueOf(getField("location"));
+       }
+
+       /**
+        * Returns whether the peer is a testnet node.
+        * 
+        * @return <code>true</code> if the peer is a testnet node,
+        *         <code>false</code> otherwise
+        */
+       public boolean isTestnet() {
+               return Boolean.valueOf("testnet");
+       }
+
+       /**
+        * Returns the version of the peer.
+        * 
+        * @return The version of the peer
+        */
+       public Version getVersion() {
+               return new Version(getField("version"));
+       }
+
+       /**
+        * Returns the negotiation types the peer supports.
+        * 
+        * @return The supported negotiation types
+        */
+       public int[] getNegotiationTypes() {
+               return FcpUtils.parseMultiIntegerField(getField("auth.negTypes"));
+       }
+
+       /**
+        * Container for the “lastGoodVersion” field.
+        * 
+        * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+        * @version $Id$
+        */
+       public static class Version {
+
+               /** The name of the node implementation. */
+               private final String nodeName;
+
+               /** The tree version of the node. */
+               private final String treeVersion;
+
+               /** The protocol version of the node. */
+               private final String protocolVersion;
+
+               /** The build number of the node. */
+               private final int buildNumber;
+
+               /**
+                * Creates a new Version from the given string. The string consists of
+                * the four required fields node name, tree version, protocol version,
+                * and build number, separated by a comma.
+                * 
+                * @param version
+                *            The version string
+                * @throws NullPointerException
+                *             if <code>version</code> is <code>null</code>
+                * @throws IllegalArgumentException
+                *             if <code>version</code> is not in the right format
+                */
+               public Version(String version) {
+                       if (version == null) {
+                               throw new NullPointerException("version must not be null");
+                       }
+                       StringTokenizer versionTokens = new StringTokenizer(version, ",");
+                       if (versionTokens.countTokens() != 4) {
+                               throw new IllegalArgumentException("version must consist of four fields");
+                       }
+                       this.nodeName = versionTokens.nextToken();
+                       this.treeVersion = versionTokens.nextToken();
+                       this.protocolVersion = versionTokens.nextToken();
+                       try {
+                               this.buildNumber = Integer.valueOf(versionTokens.nextToken());
+                       } catch (NumberFormatException nfe1) {
+                               throw new IllegalArgumentException("last part of version must be numeric", nfe1);
+                       }
+               }
+
+               /**
+                * Creates a new Version from the given parts.
+                * 
+                * @param nodeName
+                *            The name of the node implementation
+                * @param treeVersion
+                *            The tree version
+                * @param protocolVersion
+                *            The protocol version
+                * @param buildNumber
+                *            The build number of the node
+                */
+               public Version(String nodeName, String treeVersion, String protocolVersion, int buildNumber) {
+                       this.nodeName = nodeName;
+                       this.treeVersion = treeVersion;
+                       this.protocolVersion = protocolVersion;
+                       this.buildNumber = buildNumber;
+               }
+
+               /**
+                * Returns the name of the node implementation.
+                * 
+                * @return The node name
+                */
+               public String getNodeName() {
+                       return nodeName;
+               }
+
+               /**
+                * The tree version of the node.
+                * 
+                * @return The tree version of the node
+                */
+               public String getTreeVersion() {
+                       return treeVersion;
+               }
+
+               /**
+                * The protocol version of the node
+                * 
+                * @return The protocol version of the node
+                */
+               public String getProtocolVersion() {
+                       return protocolVersion;
+               }
+
+               /**
+                * The build number of the node.
+                * 
+                * @return The build number of the node
+                */
+               public int getBuildNumber() {
+                       return buildNumber;
+               }
+
+       }
+
+       /**
+        * Container for ARKs (address resolution keys).
+        * 
+        * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+        * @version $Id$
+        */
+       public static class ARK {
+
+               /** The public URI of the ARK. */
+               private final String publicURI;
+
+               /** The number of the ARK. */
+               private final int number;
+
+               /**
+                * Creates a new ARK with the given URI and number.
+                * 
+                * @param publicURI
+                *            The public URI of the ARK
+                * @param number
+                *            The number of the ARK
+                */
+               public ARK(String publicURI, String number) {
+                       if ((publicURI == null) || (number == null)) {
+                               throw new NullPointerException(((publicURI == null) ? "publicURI" : "number") + " must not be null");
+                       }
+                       this.publicURI = publicURI;
+                       try {
+                               this.number = Integer.valueOf(number);
+                       } catch (NumberFormatException nfe1) {
+                               throw new IllegalArgumentException("number must be numeric", nfe1);
+                       }
+               }
+
+               /**
+                * Returns the public URI of the ARK.
+                * 
+                * @return The public URI of the ARK
+                */
+               public String getPublicURI() {
+                       return publicURI;
+               }
+
+               /**
+                * Returns the number of the ARK.
+                * 
+                * @return The number of the ARK
+                */
+               public int getNumber() {
+                       return number;
+               }
+
+       }
+
+       /**
+        * Container for the DSA group of a peer. A DSA group consists of a base
+        * (called “g”), a prime (called “p”) and a subprime (called “q”).
+        * 
+        * @see DSAParams
+        * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+        * @version $Id$
+        */
+       public static class DSAGroup {
+
+               /** The base of the DSA group. */
+               private final String base;
+
+               /** The prime of the DSA group. */
+               private final String prime;
+
+               /** The subprime of the DSA group. */
+               private final String subprime;
+
+               /**
+                * Creates a new DSA group with the given base (“g”), prime (“p”), and
+                * subprime (“q”).
+                * 
+                * @param base
+                *            The base of the DSA group
+                * @param prime
+                *            The prime of the DSA group
+                * @param subprime
+                *            The subprime of the DSA group
+                */
+               public DSAGroup(String base, String prime, String subprime) {
+                       this.base = base;
+                       this.prime = prime;
+                       this.subprime = subprime;
+               }
+
+               /**
+                * Returns the base (“g”) of the DSA group.
+                * 
+                * @return The base of the DSA group
+                */
+               public String getBase() {
+                       return base;
+               }
+
+               /**
+                * Returns the prime (“p”) of the DSA group.
+                * 
+                * @return The prime of the DSA group
+                */
+               public String getPrime() {
+                       return prime;
+               }
+
+               /**
+                * Returns the subprime (“q”) of the DSA group.
+                * 
+                * @return The subprime of the DSA group
+                */
+               public String getSubprime() {
+                       return subprime;
+               }
+
+       }
+
+}