import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.Map.Entry;
+import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName;
+import net.pterodactylus.util.fcp.message.NodeHello;
import net.pterodactylus.util.io.Closer;
/**
- * TODO
+ * An FCP connection to a Freenet node.
*
* @author David ‘Bombe’ Roden <bombe@freenetproject.org>
* @version $Id$
*/
public class FcpConnection {
+ /** The default port for FCP v2. */
public static final int DEFAULT_PORT = 9481;
- private final Object messageWaitSync = new Object();
- private FcpMessage receivedMessage = null;
-
+ /** The list of FCP listeners. */
private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
+ /** The address of the node. */
private final InetAddress address;
+
+ /** The port number of the node’s FCP port. */
private final int port;
- private final String clientName;
+ /** The remote socket. */
private Socket remoteSocket;
+
+ /** The input stream from the node. */
private InputStream remoteInputStream;
+
+ /** The output stream to the node. */
private OutputStream remoteOutputStream;
+
+ /** The connection handler. */
private FcpConnectionHandler connectionHandler;
- private boolean connected;
- public FcpConnection(String host, String clientName) throws UnknownHostException {
- this(host, DEFAULT_PORT, clientName);
+ /**
+ * Creates a new FCP connection to the Freenet node running on the given
+ * host, listening on the default port.
+ *
+ * @param host
+ * The hostname of the Freenet node
+ * @throws UnknownHostException
+ * if <code>host</code> can not be resolved
+ */
+ public FcpConnection(String host) throws UnknownHostException {
+ this(host, DEFAULT_PORT);
}
- public FcpConnection(String host, int port, String clientName) throws UnknownHostException {
- this(InetAddress.getByName(host), port, clientName);
+ /**
+ * Creates a new FCP connection to the Freenet node running on the given
+ * host, listening on the given port.
+ *
+ * @param host
+ * The hostname of the Freenet node
+ * @param port
+ * The port number of the node’s FCP port
+ * @throws UnknownHostException
+ * if <code>host</code> can not be resolved
+ */
+ public FcpConnection(String host, int port) throws UnknownHostException {
+ this(InetAddress.getByName(host), port);
}
- public FcpConnection(InetAddress address, String clientName) {
- this(address, DEFAULT_PORT, clientName);
+ /**
+ * Creates a new FCP connection to the Freenet node running at the given
+ * address, listening on the default port.
+ *
+ * @param address
+ * The address of the Freenet node
+ */
+ public FcpConnection(InetAddress address) {
+ this(address, DEFAULT_PORT);
}
- public FcpConnection(InetAddress address, int port, String clientName) {
+ /**
+ * Creates a new FCP connection to the Freenet node running at the given
+ * address, listening on the given port.
+ *
+ * @param address
+ * The address of the Freenet node
+ * @param port
+ * The port number of the node’s FCP port
+ */
+ public FcpConnection(InetAddress address, int port) {
this.address = address;
this.port = port;
- this.clientName = clientName;
}
//
}
/**
+ * Notifies listeners that a “NodeHello” message was received.
+ *
+ * @see FcpListener#receivedNodeHello(FcpConnection, NodeHello)
+ * @param nodeHello
+ * The “NodeHello” message
+ */
+ private void fireReceivedNodeHello(NodeHello nodeHello) {
+ for (FcpListener fcpListener: fcpListeners) {
+ fcpListener.receivedNodeHello(this, nodeHello);
+ }
+ }
+
+ /**
+ * Notifies listeners that a “CloseConnectionDuplicateClientName” message
+ * was received.
+ *
+ * @see FcpListener#receivedCloseConnectionDuplicateClientName(FcpConnection,
+ * CloseConnectionDuplicateClientName)
+ * @param closeConnectionDuplicateClientName
+ * The “CloseConnectionDuplicateClientName” message
+ */
+ private void fireReceivedCloseConnectionDuplicateClientName(CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
+ for (FcpListener fcpListener: fcpListeners) {
+ fcpListener.receivedCloseConnectionDuplicateClientName(this, closeConnectionDuplicateClientName);
+ }
+ }
+
+ /**
* Notifies all registered listeners that a message has been received.
*
* @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
remoteSocket = new Socket(address, port);
remoteInputStream = remoteSocket.getInputStream();
remoteOutputStream = remoteSocket.getOutputStream();
- connected = true;
new Thread(connectionHandler = new FcpConnectionHandler(this, remoteInputStream)).start();
}
if (connectionHandler == null) {
return;
}
- connected = false;
Closer.close(remoteSocket);
connectionHandler.stop();
connectionHandler = null;
}
- public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
- System.out.println("sending message: " + fcpMessage.getName());
- fcpMessage.write(remoteOutputStream);
- }
-
/**
- * Sends a “ListPeer” command to the node and returns the properties of the
- * peer.
+ * Sends the given FCP message.
*
- * @param nodeIdentifier
- * The name (except for OpenNet nodes), the identity or the
- * node’s “address:port” pair
- * @return The properties of the peer, or <code>null</code> if the peer is
- * unknown
+ * @param fcpMessage
+ * The FCP message to send
* @throws IOException
- * @throws FcpException
+ * if an I/O error occurs
*/
- public Map<String, String> sendListPeer(String nodeIdentifier) throws IOException, FcpException {
- FcpMessage listPeerMessage = new FcpMessage("ListPeer");
- listPeerMessage.setField("NodeIdentifier", nodeIdentifier);
- sendMessage(listPeerMessage);
- FcpMessage returnMessage = waitForMessage("Peer", "UnknownNodeIdentifier");
- if (returnMessage.getName().equals("Peer")) {
- return returnMessage.getFields();
- }
- return null;
- }
-
- void handleMessage(FcpMessage fcpMessage) {
- fireMessageReceived(fcpMessage);
- }
-
- public List<Map<String, String>> sendListPeers(boolean withMetadata, boolean withVolatile) throws IOException, FcpException {
- FcpMessage listPeersMessage = new FcpMessage("ListPeers");
- listPeersMessage.setField("WithMetadata", String.valueOf(withMetadata));
- listPeersMessage.setField("WithVolatile", String.valueOf(withVolatile));
- sendMessage(listPeersMessage);
- List<Map<String, String>> peers = new ArrayList<Map<String, String>>();
- while (true) {
- FcpMessage returnMessage = waitForMessage("Peer", "EndListPeers");
- if (returnMessage.getName().equals("EndListPeers")) {
- break;
- }
- peers.add(returnMessage.getFields());
- }
- return peers;
- }
-
- public List<Map<String, String>> sendListPeerNotes(String nodeIdentifier) throws IOException, FcpException {
- FcpMessage listPeerNotesMessage = new FcpMessage("ListPeerNotes");
- listPeerNotesMessage.setField("NodeIdentifier", nodeIdentifier);
- sendMessage(listPeerNotesMessage);
- List<Map<String, String>> peerNotes = new ArrayList<Map<String, String>>();
- while (true) {
- FcpMessage returnMessage = waitForMessage("PeerNote", "EndListPeerNotes");
- if (returnMessage.getName().equals("EndListPeerNotes")) {
- break;
- }
- peerNotes.add(returnMessage.getFields());
- }
- return peerNotes;
- }
-
- public void sendTestDDARequest(String directory, boolean wantReadDirectory, boolean wantWriteDirectory) throws IOException, FcpException {
- FcpMessage testDDARequestMessage = new FcpMessage("TestDDARequest");
- testDDARequestMessage.setField("Directory", directory);
- testDDARequestMessage.setField("WantReadDirectory", String.valueOf(wantReadDirectory));
- testDDARequestMessage.setField("WantWriteDirectory", String.valueOf(wantWriteDirectory));
- sendMessage(testDDARequestMessage);
- }
-
- public FcpKeyPair generateSSK() throws IOException, FcpException {
- FcpMessage generateSSKMessage = new FcpMessage("GenerateSSK");
- String identifier = hashCode() + String.valueOf(System.currentTimeMillis());
- generateSSKMessage.setField("Identifier", identifier);
- sendMessage(generateSSKMessage);
- FcpMessage returnMessage = waitForMessage("SSKKeypair(Identifier=" + identifier + ")");
- String publicKey = returnMessage.getField("RequestURI");
- String privateKey = returnMessage.getField("InsertURI");
- return new FcpKeyPair(publicKey, privateKey);
+ public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
+ System.out.println("sending message: " + fcpMessage.getName());
+ fcpMessage.write(remoteOutputStream);
}
//
- // PRIVATE METHODS
+ // PACKAGE-PRIVATE METHODS
//
- public FcpMessage waitForMessage(String... messageNames) throws FcpException {
- FcpMessage oldMessage = null;
- synchronized (messageWaitSync) {
- while (true) {
- while (receivedMessage == oldMessage) {
- System.out.println("waiting for receivedMessage");
- try {
- messageWaitSync.wait();
- } catch (InterruptedException ie1) {
- }
- }
- System.out.println("got message: " + receivedMessage.getName());
- String receivedMessageName = receivedMessage.getName();
- if ("ProtocolError".equals(receivedMessageName)) {
- int code = Integer.valueOf(receivedMessage.getField("Code"));
- boolean fatal = Boolean.valueOf(receivedMessage.getField("Fatal"));
- boolean global = Boolean.valueOf(receivedMessage.getField("Global"));
- String codeDescription = receivedMessage.getField("CodeDescription");
- String extraDescription = receivedMessage.getField("ExtraDescription");
- String identifier = receivedMessage.getField("Identifier");
- FcpProtocolException fcpProtocolException = new FcpProtocolException(code, fatal, global);
- fcpProtocolException.setCodeDescription(codeDescription);
- fcpProtocolException.setExtraDescription(extraDescription);
- fcpProtocolException.setIdentifier(identifier);
- throw fcpProtocolException;
- }
- for (String messageName: messageNames) {
- int firstBracket = messageName.indexOf('(');
- Map<String, String> wantedIdentifiers = new HashMap<String, String>();
- if (firstBracket > -1) {
- StringTokenizer identifierTokens = new StringTokenizer(messageName.substring(firstBracket), "()");
- while (identifierTokens.hasMoreTokens()) {
- String identifierToken = identifierTokens.nextToken();
- int equalSign = identifierToken.indexOf('=');
- if (equalSign > -1) {
- wantedIdentifiers.put(identifierToken.substring(0, equalSign), identifierToken.substring(equalSign + 1));
- }
- }
- messageName = messageName.substring(0, firstBracket);
- }
- if (receivedMessageName.equals(messageName)) {
- boolean found = true;
- for (Entry<String, String> wantedIdentifier: wantedIdentifiers.entrySet()) {
- System.out.println("key: " + wantedIdentifier.getKey() + ", value: " + wantedIdentifier.getValue() + ", msg: " + receivedMessage.getField(wantedIdentifier.getKey()));
- if (!wantedIdentifier.getValue().equals(receivedMessage.getField(wantedIdentifier.getKey()))) {
- found = false;
- break;
- }
- }
- if (found) {
- System.out.println("message found");
- FcpMessage foundMessage = receivedMessage;
- receivedMessage = null;
- messageWaitSync.notifyAll();
- return foundMessage;
- }
- }
- }
- oldMessage = receivedMessage;
- }
+ /**
+ * Handles the given message, notifying listeners. This message should only
+ * be called by {@link FcpConnectionHandler}.
+ *
+ * @param fcpMessage
+ * The received message
+ */
+ void handleMessage(FcpMessage fcpMessage) {
+ String messageName = fcpMessage.getName();
+ if ("NodeHello".equals(messageName)) {
+ fireReceivedNodeHello(new NodeHello(fcpMessage));
+ } else if ("CloseConnectionDuplicateClientName".equals(messageName)) {
+ fireReceivedCloseConnectionDuplicateClientName(new CloseConnectionDuplicateClientName(fcpMessage));
+ } else {
+ fireMessageReceived(fcpMessage);
}
}
import java.util.EventListener;
+import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName;
+import net.pterodactylus.util.fcp.message.NodeHello;
+
/**
* Interface for objects that want to be notified on certain FCP events.
*
public interface FcpListener extends EventListener {
/**
- * Notifies listeners that a message has been received.
+ * Notifies listeners that a “NodeHello” message was received.
+ *
+ * @param fcpConnection
+ * The connection that received the message
+ * @param nodeHello
+ * The “NodeHello” message
+ */
+ public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello);
+
+ /**
+ * Notifies all listeners that a “CloseConnectionDuplicateClientName”
+ * message was received.
+ *
+ * @param fcpConnection
+ * The connection that received the message
+ * @param closeConnectionDuplicateClientName
+ * The “CloseConnectionDuplicateClientName” message
+ */
+ public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName);
+
+ /**
+ * 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!
*
* @param fcpConnection
* The connection that received the message
public String getName() {
return name;
}
-
+
public void setField(String field, String value) {
+ if ((field == null) || (value == null)) {
+ throw new NullPointerException(((field == null) ? "field " : "value ") + "must not be null");
+ }
fields.put(field, value);
}
-
+
public String getField(String field) {
return fields.get(field);
}
-
+
public Map<String, String> getFields() {
return Collections.unmodifiableMap(fields);
}
-
+
/**
* {@inheritDoc}
*/
public Iterator<String> iterator() {
return fields.keySet().iterator();
}
-
+
public void setDataInputStream(InputStream dataInputStream) {
this.dataInputStream = dataInputStream;
}
--- /dev/null
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Helper class with utility methods for the FCP protocol.
+ *
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class FcpUtils {
+
+ /** Counter for unique identifiers. */
+ private static AtomicLong counter = new AtomicLong();
+
+ /**
+ * Returns a unique identifier.
+ *
+ * @return A unique identifier
+ */
+ public static String getUniqueIdentifier() {
+ return new StringBuilder().append(System.currentTimeMillis()).append('-').append(counter.getAndIncrement()).toString();
+ }
+
+}
+++ /dev/null
-/*
- * jSite2 - FcpHighLevelClient.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.util.fcp.client;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import net.pterodactylus.util.fcp.FcpConnection;
-import net.pterodactylus.util.fcp.FcpException;
-import net.pterodactylus.util.fcp.FcpListener;
-import net.pterodactylus.util.fcp.FcpMessage;
-
-/**
- * High-level client for FCP connections.
- *
- * @author David Roden <droden@gmail.com>
- * @version $Id$
- */
-public class FcpHighLevelClient implements FcpListener {
-
- /** The FCP connection of this client. */
- private final FcpConnection fcpConnection;
-
- /** Storage for incoming messages. */
- private final List<FcpMessage> incomingMessages = Collections.synchronizedList(new ArrayList<FcpMessage>());
-
- /** Registered handlers for incoming messages. */
- private final List<Callback<?>> incomingMessageCallbacks = Collections.synchronizedList(new ArrayList<Callback<?>>());
-
- /**
- * Creates a new high-level client that operates on the given FCP
- * connection.
- *
- * @param fcpConnection
- * The FCP connection to operate on
- */
- public FcpHighLevelClient(FcpConnection fcpConnection) {
- this.fcpConnection = fcpConnection;
- fcpConnection.addFcpListener(this);
- }
-
- //
- // ACTIONS
- //
-
- /**
- * Stops the high-level client.
- */
- public void stop() {
- fcpConnection.removeFcpListener(this);
- }
-
- public Map<String, String> login(String clientName, final NodeHelloCallback nodeHelloCallback) throws FcpException, IOException {
- FcpMessage clientHelloMessage = new FcpMessage("ClientHello");
- clientHelloMessage.setField("Name", clientName);
- clientHelloMessage.setField("ExpectedVersion", "2.0");
- Callback<Map<String, String>> callback = new Callback<Map<String, String>>() {
- /**
- * {@inheritDoc}
- */
- @SuppressWarnings("synthetic-access")
- public CallbackContainer<Map<String, String>> consumeMessage(FcpMessage fcpMessage) throws FcpException {
- String messageName = fcpMessage.getName();
- if ("CloseConnectionDuplicateClientName".equals(messageName)) {
- incomingMessages.remove(0);
- if (nodeHelloCallback == null) {
- throw new FcpException("duplicate client name");
- }
- nodeHelloCallback.closeConnectionDuplicateClientName();
- }
- if ("NodeHello".equals(messageName)) {
- incomingMessages.remove(0);
- return new CallbackContainer<Map<String, String>>(true, fcpMessage.getFields());
- }
- return new CallbackContainer<Map<String, String>>(false, null);
- }
- };
- synchronized (incomingMessages) {
- synchronized (incomingMessageCallbacks) {
- fcpConnection.sendMessage(clientHelloMessage);
- if (nodeHelloCallback != null) {
- incomingMessageCallbacks.add(callback);
- return null;
- }
- }
- return processMessages(callback);
- }
- }
-
- //
- // PRIVATE METHODS
- //
-
- private <T> T processMessages(Callback<T> callback) throws FcpException {
- while (true) {
- FcpMessage fcpMessage;
- synchronized (incomingMessages) {
- while (incomingMessages.isEmpty()) {
- try {
- incomingMessages.wait();
- } catch (InterruptedException ie1) {
- }
- }
- fcpMessage = incomingMessages.get(0);
- }
- CallbackContainer<T> callbackContainer = callback.consumeMessage(fcpMessage);
- if (callbackContainer.isConsumed()) {
- incomingMessages.remove(0);
- return callbackContainer.getContent();
- }
- }
- }
-
- //
- // INTERFACE FcpListener
- //
-
- /**
- * {@inheritDoc}
- */
- public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) {
- if (this.fcpConnection != fcpConnection) {
- return;
- }
- synchronized (incomingMessages) {
- incomingMessages.add(fcpMessage);
- incomingMessages.notifyAll();
- }
- synchronized (incomingMessageCallbacks) {
- for (Callback<?> callback: incomingMessageCallbacks) {
- try {
- CallbackContainer<?> callbackContainer = callback.consumeMessage(fcpMessage);
- } catch (FcpException e) {
- }
-
- }
- }
- }
-
- public static interface Callback<T> {
-
- /**
- * TODO
- *
- * @param fcpMessage
- * @return
- */
- public CallbackContainer<T> consumeMessage(FcpMessage fcpMessage) throws FcpException;
-
- }
-
- public static class CallbackContainer<T> {
-
- private final boolean consumed;
- private final T content;
-
- public CallbackContainer(boolean consumed, T content) {
- this.consumed = consumed;
- this.content = content;
- }
-
- /**
- * TODO
- *
- * @return
- */
- public boolean isConsumed() {
- return consumed;
- }
-
- /**
- * TODO
- *
- * @return
- */
- public T getContent() {
- return content;
- }
-
- }
-
-}
+++ /dev/null
-/*
- * jSite2 - FcpNodeInformation.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.util.fcp.client;
-
-/**
- * Container for node information. This information is transmitted to the client
- * in the “NodeHello” response.
- *
- * @author David Roden <droden@gmail.com>
- * @version $Id$
- */
-public class FcpNodeInformation {
-
- /** The “FCPVersion” field. */
- private final String fcpVersion;
-
- /** The “Version” field. */
- private final String version;
-
- /** The “Node” field. */
- private final String node;
-
- /** The “Testnet” field. */
- private final boolean testnet;
-
- /** The “CompressionCodecs” field. */
- private final int compressionCodecs;
-
- /** The “ConnectionIdentifier” field. */
- private final String connectionIdentifier;
-
- /** The “NodeLanguage” field. */
- private final String nodeLanguage;
-
- /**
- * Creates a new node information object.
- *
- * @param fcpVersion
- * The “FCPVersion” field
- * @param version
- * The “Version” field
- * @param node
- * The “Node” field
- * @param testnet
- * The “Testnet” field
- * @param compressionCodecs
- * The “CompressionCodecs” field
- * @param connectionIdentifier
- * The “ConnectionIdentifier” field
- * @param nodeLanguage
- * The “NodeLanguage” field
- */
- public FcpNodeInformation(String fcpVersion, String version, String node, boolean testnet, int compressionCodecs, String connectionIdentifier, String nodeLanguage) {
- this.fcpVersion = fcpVersion;
- this.version = version;
- this.node = node;
- this.testnet = testnet;
- this.compressionCodecs = compressionCodecs;
- this.connectionIdentifier = connectionIdentifier;
- this.nodeLanguage = nodeLanguage;
- }
-
- /**
- * Returns the “FCPVersion” field.
- *
- * @return The “FCPVersion” field
- */
- public String getFcpVersion() {
- return fcpVersion;
- }
-
- /**
- * Returns the “Version” field.
- *
- * @return The “Version” field
- */
- public String getVersion() {
- return version;
- }
-
- /**
- * Returns the “Node” field.
- *
- * @return The “Node” field
- */
- public String getNode() {
- return node;
- }
-
- /**
- * Returns the “Testnet” field.
- *
- * @return The “Testnet” field
- */
- public boolean isTestnet() {
- return testnet;
- }
-
- /**
- * Returns the “CompressionCodecs” field.
- *
- * @return The “CompressionCodecs” field
- */
- public int getCompressionCodecs() {
- return compressionCodecs;
- }
-
- /**
- * Returns the “ConnectionIdentifier” field.
- *
- * @return The “ConnectionIdentifier” field
- */
- public String getConnectionIdentifier() {
- return connectionIdentifier;
- }
-
- /**
- * Returns the “NodeLanguage” field.
- *
- * @return The “NodeLanguage” field
- */
- public String getNodeLanguage() {
- return nodeLanguage;
- }
-
-}
+++ /dev/null
-/*
- * jSite2 - NodeHelloCallback.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.util.fcp.client;
-
-
-/**
- * TODO
- * @author David Roden <droden@gmail.com>
- * @version $Id$
- */
-public interface NodeHelloCallback {
-
- public void nodeHello(FcpNodeInformation fcpNodeInformation);
- public void closeConnectionDuplicateClientName();
-
-}
--- /dev/null
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * A basic message abstraction that wraps a received FCP message.
+ *
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class BaseMessage {
+
+ /** The received message, wrapped here. */
+ private final FcpMessage receivedMessage;
+
+ /**
+ * Creates a new base message that wraps the given message.
+ *
+ * @param receivedMessage
+ * The FCP message that was received
+ */
+ public BaseMessage(FcpMessage receivedMessage) {
+ this.receivedMessage = receivedMessage;
+ }
+
+ /**
+ * Returns the name of the message.
+ *
+ * @return The name of the message
+ */
+ public String getName() {
+ return receivedMessage.getName();
+ }
+
+ /**
+ * Returns the content of the field.
+ *
+ * @param field
+ * The name of the field
+ * @return The content of the field, or <code>null</code> if there is no
+ * such field
+ */
+ public String getField(String field) {
+ return receivedMessage.getField(field);
+ }
+
+}
--- /dev/null
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpConnection;
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * A “ClientHello” message that <i>must</i> be sent to the node first thing
+ * after calling {@link FcpConnection#connect()}.
+ *
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class ClientHello extends FcpMessage {
+
+ /**
+ * Creates a new “ClientHello” message with the given client name. The
+ * client name has to be unique to the node otherwise you will get a
+ * {@link CloseConnectionDuplicateClientName} response from the node!
+ *
+ * @param clientName
+ * The unique client name
+ */
+ public ClientHello(String clientName) {
+ this(clientName, "2.0");
+ }
+
+ /**
+ * Creates a new “ClientHello” message with the given client name. The
+ * client name has to be unique to the node otherwise you will get a
+ * {@link CloseConnectionDuplicateClientName} response from the node!
+ *
+ * The expected FCP version is currently ignored by the node.
+ *
+ * @param clientName
+ * The unique client name
+ * @param expectedVersion
+ * The FCP version that the node is expected to talk
+ */
+ public ClientHello(String clientName, String expectedVersion) {
+ super("ClientHello");
+ setField("Name", clientName);
+ setField("ExpectedVersion", expectedVersion);
+ }
+
+}
--- /dev/null
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * A “CloseConnectionDuplicateClientName” message.
+ *
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class CloseConnectionDuplicateClientName extends BaseMessage {
+
+ /**
+ * Creates a new CloseConnectionDuplicateClientName message that wraps the
+ * given message.
+ *
+ * @param receivedMessage
+ * The received message
+ */
+ public CloseConnectionDuplicateClientName(FcpMessage receivedMessage) {
+ super(receivedMessage);
+ }
+
+}
--- /dev/null
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+import net.pterodactylus.util.fcp.FcpUtils;
+
+/**
+ * A “GenerateSSK” message. This message tells the node to generate a new SSK
+ * key pair.
+ *
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class GenerateSSK extends FcpMessage {
+
+ /**
+ * Creates a new “GenerateSSK” message.
+ */
+ public GenerateSSK() {
+ this(FcpUtils.getUniqueIdentifier());
+ }
+
+ /**
+ * Creates a new “GenerateSSK” message with the given client identifier.
+ *
+ * @param clientIdentifier
+ * The client identifier
+ */
+ public GenerateSSK(String clientIdentifier) {
+ super("GenerateSSK");
+ setField("Identifier", clientIdentifier);
+ }
+
+}
--- /dev/null
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * Some convenience methods for parsing a “NodeHello” message from the node.
+ *
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class NodeHello extends BaseMessage {
+
+ /**
+ * @param receivedMessage
+ * The received FCP message
+ */
+ public NodeHello(FcpMessage receivedMessage) {
+ super(receivedMessage);
+ }
+
+ /**
+ * Returns the build of the node. This may not be a number but also a string
+ * like “@custom@” in case you built the node yourself.
+ *
+ * @return The build of the node
+ */
+ public String getBuild() {
+ return getField("Build");
+ }
+
+ /**
+ * Returns the build number of the node. This may not be a number but also a
+ * string like “@custom@” in case you built the node yourself.
+ *
+ * @return The build number of the node, or <code>-1</code> if the build
+ * number could not be determined
+ */
+ public int getBuildNumber() {
+ String build = getBuild();
+ try {
+ return Integer.valueOf(build);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the number of compression codecs.
+ *
+ * @return The number of compression codecs
+ */
+ public String getCompressionCodecs() {
+ return getField("CompressionCodecs");
+ }
+
+ /**
+ * Returns the number of compression codecs.
+ *
+ * @return The number of compression codecs, or <code>-1</code> if the
+ * number of compression codecs could not be determined
+ */
+ public int getCompressionCodecsNumber() {
+ String compressionCodecs = getCompressionCodecs();
+ try {
+ return Integer.valueOf(compressionCodecs);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the unique connection identifier.
+ *
+ * @return The connection identifier
+ */
+ public String getConnectionIdentifier() {
+ return getField("ConnectionIdentifier");
+ }
+
+ /**
+ * Returns the build of the external library file.
+ *
+ * @return The build of the external library file
+ */
+ public String getExtBuild() {
+ return getField("ExtBuild");
+ }
+
+ /**
+ * Returns the build number of the external library file.
+ *
+ * @return The build number of the external library file, or <code>-1</code>
+ * if the build number could not be determined
+ */
+ public int getExtBuildNumber() {
+ String extBuild = getExtBuild();
+ try {
+ return Integer.valueOf(extBuild);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the revision of the external library file.
+ *
+ * @return The revision of the external library file
+ */
+ public String getExtRevision() {
+ return getField("ExtRevision");
+ }
+
+ /**
+ * Returns the revision number of the external library file.
+ *
+ * @return The revision number of the external library file, or
+ * <code>-1</code> if the revision number could not be determined
+ */
+ public int getExtRevisionNumber() {
+ String extRevision = getExtRevision();
+ try {
+ return Integer.valueOf(extRevision);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the FCP version the node speaks.
+ *
+ * @return The FCP version the node speaks
+ */
+ public String getFCPVersion() {
+ return getField("FCPVersion");
+ }
+
+ /**
+ * Returns the make of the node, e.g. “Fred” (freenet reference
+ * implementation).
+ *
+ * @return The make of the node
+ */
+ public String getNode() {
+ return getField("Node");
+ }
+
+ /**
+ * Returns the language of the node as 2-letter code, e.g. “en” or “de”.
+ *
+ * @return The language of the node
+ */
+ public String getNodeLanguage() {
+ return getField("NodeLanguage");
+ }
+
+ /**
+ * Returns the revision of the node.
+ *
+ * @return The revision of the node
+ */
+ public String getRevision() {
+ return getField("Revision");
+ }
+
+ /**
+ * Returns the revision number of the node.
+ *
+ * @return The revision number of the node, or <code>-1</code> if the
+ * revision number coult not be determined
+ */
+ public int getRevisionNumber() {
+ String revision = getRevision();
+ try {
+ return Integer.valueOf(revision);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ return -1;
+ }
+
+ /**
+ * Returns whether the node is currently is testnet mode.
+ *
+ * @return <code>true</code> if the node is currently in testnet mode,
+ * <code>false</code> otherwise
+ */
+ public boolean getTestnet() {
+ return Boolean.valueOf(getField("Testnet"));
+ }
+
+ /**
+ * Returns the version of the node.
+ *
+ * @return The version of the node
+ */
+ public String getVersion() {
+ return getField("Version");
+ }
+
+}
--- /dev/null
+/**
+ * Package that holds all the message types that are used in the communication with a Freenet Node.
+ */
+package net.pterodactylus.util.fcp.message;
\ No newline at end of file