import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
+import net.pterodactylus.jsite.core.Project;
import net.pterodactylus.jsite.i18n.I18n;
import net.pterodactylus.jsite.i18n.I18nable;
import net.pterodactylus.jsite.i18n.gui.I18nAction;
/** The about menu. */
private I18nMenu aboutMenu;
+
+ /** The tabbed project pane. */
+ private JTabbedPane projectPane;
/**
* Creates a new main window that redirects all actions to the given swing
statusBar.setText(text);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Container getContentPane() {
+ return contentPane;
+ }
+
+
+ public Project getSelectedProject() {
+ return null;
+ }
+
//
// PRIVATE METHODS
//
getContentPane().add(upperPanel, BorderLayout.PAGE_START);
contentPane.setBorder(new EmptyBorder(12, 12, 12, 12));
- JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
- upperPanel.add(tabbedPane, BorderLayout.CENTER);
+ projectPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
+ upperPanel.add(projectPane, BorderLayout.CENTER);
Box projectOverviewPanel = new Box(BoxLayout.PAGE_AXIS);
- tabbedPane.add(I18n.get("mainWindow.pane.overview.title"), projectOverviewPanel);
+ projectPane.add(I18n.get("mainWindow.pane.overview.title"), projectOverviewPanel);
projectOverviewPanel.setBorder(new EmptyBorder(12, 12, 12, 12));
projectOverviewPanel.add(Box.createVerticalGlue());
JButton addProjectButton = new JButton(swingInterface.getAddProjectAction());
// getContentPane().add(lowerPanel, BorderLayout.CENTER);
}
- /**
- * {@inheritDoc}
- */
- @Override
- public Container getContentPane() {
- return contentPane;
- }
-
//
// INTERFACE I18nable
//
package net.pterodactylus.jsite.gui;
import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
+import javax.swing.JButton;
import javax.swing.JPanel;
+import net.pterodactylus.jsite.core.Project;
+
/**
* A panel that contains all information about a project and lets the user edit
* the properties of the project.
*/
public class ProjectPanel extends JPanel {
+ /** The Swing interface. */
+ private final SwingInterface swingInterface;
+ private final Project project;
+
/**
* Creates a new project panel.
+ *
+ * @param swingInterface
+ * The Swing interface
+ * @param project The project to display
*/
- public ProjectPanel() {
+ public ProjectPanel(SwingInterface swingInterface, Project project) {
super(new BorderLayout(12, 12));
+ this.swingInterface = swingInterface;
+ this.project = project;
initComponents();
}
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the project that is displayed in this panel.
+ * @return The project of this panel
+ */
+ public Project getProject() {
+ return project;
+ }
//
// PRIVATE METHODS
*/
private void initComponents() {
setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
-
+
+ JPanel propertiesPanel = createPropertiesPanel();
+ add(propertiesPanel, BorderLayout.CENTER);
+
+ JPanel buttonPanel = createButtonPanel();
+ add(buttonPanel, BorderLayout.PAGE_END);
+ }
+
+ /**
+ * Creates the properties panel.
+ *
+ * @return The properties panel
+ */
+ private JPanel createPropertiesPanel() {
+ JPanel propertiesPanel = new JPanel(new GridBagLayout());
+
+ return propertiesPanel;
+ }
+
+ /**
+ * Creates the button panel.
+ *
+ * @return The button panel
+ */
+ private JPanel createButtonPanel() {
+ JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
+ buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
+
+ buttonPanel.add(new JButton(swingInterface.getDeleteProjectAction()));
+ buttonPanel.add(new JButton(swingInterface.getCloneProjectAction()));
+
+ return buttonPanel;
}
}
import net.pterodactylus.jsite.core.Core;
import net.pterodactylus.jsite.core.CoreListener;
import net.pterodactylus.jsite.core.Node;
+import net.pterodactylus.jsite.core.Project;
import net.pterodactylus.jsite.i18n.I18n;
import net.pterodactylus.jsite.i18n.gui.I18nAction;
import net.pterodactylus.util.io.Closer;
/** The “add project” action. */
private I18nAction addProjectAction;
+ /** The “clone project” action. */
+ private I18nAction cloneProjectAction;
+
+ /** The “delete project” action. */
+ private I18nAction deleteProjectAction;
+
/** The “about” dialog. */
private AboutDialog aboutDialog;
return addProjectAction;
}
+ /**
+ * Returns the “clone project” action.
+ *
+ * @return The “clone project” action
+ */
+ I18nAction getCloneProjectAction() {
+ return cloneProjectAction;
+ }
+
+ /**
+ * Returns the “delete project” action.
+ *
+ * @return The “delete project” action
+ */
+ I18nAction getDeleteProjectAction() {
+ return deleteProjectAction;
+ }
+
//
// ACTIONS
//
/* initialize default stuff. */
beautify = false;
/* now read config. */
- System.out.println("configDirectory: “" + configDirectory + "”");
File configFile = new File(configDirectory, "swing-interface.properties");
if (!configFile.exists() || !configFile.canRead() || !configFile.isFile()) {
System.err.println("could not find “" + configFile.getAbsolutePath() + "”!");
addProject();
}
};
+ cloneProjectAction = new I18nAction("mainWindow.button.cloneProject") {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent actionEvent) {
+ cloneProject();
+ }
+ };
+ deleteProjectAction = new I18nAction("mainWindow.button.deleteProject") {
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("synthetic-access")
+ public void actionPerformed(ActionEvent actionEvent) {
+ deleteProject();
+ }
+ };
}
/**
* Adds a project.
*/
private void addProject() {
+ Project project = new Project();
+ project.setName("New Project");
+ project.setDescription("");
+ }
+
+ /**
+ * Clones a project.
+ */
+ private void cloneProject() {
+ }
+
+ /**
+ * Deletes a project.
+ */
+ private void deleteProject() {
}
//
mainWindow.button.addProject.shortDescription: Add a project
mainWindow.button.addProject.longDescription: Adds a project
+mainWindow.button.cloneProject.name: Clone Project
+mainWindow.button.cloneProject.mnemonic: VK_L
+mainWindow.button.cloneProject.accelerator: Ctrl-VK_L
+mainWindow.button.cloneProject.shortDescription: Clones the selected project
+mainWindow.button.cloneProject.longDescription: Clones the selected project
+
+mainWindow.button.deleteProject.name: Delete Project
+mainWindow.button.deleteProject.mnemonic: VK_E
+mainWindow.button.deleteProject.accelerator: Ctrl-VK_R
+mainWindow.button.deleteProject.shortDescription: Deletes the selected project
+mainWindow.button.deleteProject.longDescription: Deletes the selected project
+
mainWindow.pane.overview.title: Project Overview
#
--- /dev/null
+/*
+ * jSite2 - FpcConnection.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;
+
+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.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.io.Closer;
+
+/**
+ * TODO
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public class FcpConnection {
+
+ public static final int DEFAULT_PORT = 9481;
+
+ private final Object messageWaitSync = new Object();
+ private FcpMessage receivedMessage = null;
+
+ private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
+
+ private final InetAddress address;
+ private final int port;
+ private final String clientName;
+
+ private Socket remoteSocket;
+ private InputStream remoteInputStream;
+ private OutputStream remoteOutputStream;
+ private boolean connected;
+
+ public FcpConnection(String host, String clientName) throws UnknownHostException {
+ this(host, DEFAULT_PORT, clientName);
+ }
+
+ public FcpConnection(String host, int port, String clientName) throws UnknownHostException {
+ this(InetAddress.getByName(host), port, clientName);
+ }
+
+ public FcpConnection(InetAddress address, String clientName) {
+ this(address, DEFAULT_PORT, clientName);
+ }
+
+ public FcpConnection(InetAddress address, int port, String clientName) {
+ this.address = address;
+ this.port = port;
+ this.clientName = clientName;
+ }
+
+ //
+ // LISTENER MANAGEMENT
+ //
+
+ public void addFcpListener(FcpListener fcpListener) {
+ fcpListeners.add(fcpListener);
+ }
+
+ public void removeFcpListener(FcpListener fcpListener) {
+ fcpListeners.remove(fcpListener);
+ }
+
+ private void fireNodeHello(Map<String, String> nodeProperties) {
+ for (FcpListener fcpListener: fcpListeners) {
+ fcpListener.fcpNodeHello(this, nodeProperties);
+ }
+ }
+
+ //
+ // ACTIONS
+ //
+
+ public synchronized void connect() throws FcpException, IOException {
+ System.out.println("connecting...");
+ remoteSocket = new Socket(address, port);
+ remoteInputStream = remoteSocket.getInputStream();
+ remoteOutputStream = remoteSocket.getOutputStream();
+ connected = true;
+ System.out.println("connected.");
+ new Thread(new FcpConnectionHandler(this, remoteInputStream)).start();
+ sendMessage(clientHelloMessage);
+ }
+
+ public synchronized void disconnect() {
+ connected = false;
+ Closer.close(remoteSocket);
+ }
+
+ /**
+ * Sends a “ListPeer” command to the node and returns the properties of the
+ * peer.
+ *
+ * @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
+ * @throws IOException
+ * @throws FcpException
+ */
+ 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;
+ }
+
+ 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);
+ }
+
+ //
+ // PACKAGE-PRIVATE METHODS
+ //
+
+ void handleMessage(FcpMessage fcpMessage) {
+ synchronized (messageWaitSync) {
+ while (receivedMessage != null) {
+ /* previous message has not yet been consumed */
+ System.out.println("waiting for message to be consumed...");
+ try {
+ messageWaitSync.wait();
+ } catch (InterruptedException ie1) {
+ }
+ }
+ /* TODO - check whether to send events here or later. */
+ if ("NodeHello".equals(fcpMessage.getName())) {
+ fireNodeHello(fcpMessage.getFields());
+ }
+ System.out.println("setting receivedMessage");
+ receivedMessage = fcpMessage;
+ messageWaitSync.notifyAll();
+ }
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
+ System.out.println("sending message: " + fcpMessage.getName());
+ fcpMessage.write(remoteOutputStream);
+ }
+
+ 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;
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * jSite2 - FcpConnectionHandler.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/**
+ * TODO
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public class FcpConnectionHandler implements Runnable {
+
+ private final FcpConnection fcpConnection;
+ private final InputStream remoteInputStream;
+
+ private boolean ignoreNextLinefeed;
+
+ public FcpConnectionHandler(FcpConnection fcpConnection, InputStream remoteInputStream) {
+ this.fcpConnection = fcpConnection;
+ this.remoteInputStream = remoteInputStream;
+ }
+
+ public void run() {
+ FcpMessage fcpMessage = null;
+ while (true) {
+ try {
+ String line = readLine();
+ System.out.println("read line: " + line);
+ if (line == null) {
+ break;
+ }
+ if (line.length() == 0) {
+ continue;
+ }
+ line = line.trim();
+ if (fcpMessage == null) {
+ fcpMessage = new FcpMessage(line);
+ continue;
+ }
+ if ("EndMessage".equals(line)) {
+ fcpConnection.handleMessage(fcpMessage);
+ fcpMessage = null;
+ }
+ int equalSign = line.indexOf('=');
+ if (equalSign == -1) {
+ /* something's fishy! */
+ continue;
+ }
+ String field = line.substring(0, equalSign);
+ String value = line.substring(equalSign + 1);
+ fcpMessage.setField(field, value);
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ /**
+ * Reads bytes from {@link #remoteInputStream} until ‘\r’ or ‘\n’ are
+ * encountered and decodes the read bytes using UTF-8.
+ *
+ * @return The decoded line
+ * @throws IOException
+ * if an I/O error occurs
+ */
+ private String readLine() throws IOException {
+ byte[] readBytes = new byte[512];
+ int readIndex = 0;
+ while (true) {
+ int nextByte = remoteInputStream.read();
+ if (nextByte == -1) {
+ if (readIndex == 0) {
+ return null;
+ }
+ break;
+ }
+ if (nextByte == 10) {
+ if (!ignoreNextLinefeed) {
+ break;
+ }
+ }
+ ignoreNextLinefeed = false;
+ if (nextByte == 13) {
+ ignoreNextLinefeed = true;
+ break;
+ }
+ if (readIndex == readBytes.length) {
+ /* recopy & enlarge array */
+ byte[] newReadBytes = new byte[readBytes.length * 2];
+ System.arraycopy(readBytes, 0, newReadBytes, 0, readBytes.length);
+ readBytes = newReadBytes;
+ }
+ readBytes[readIndex++] = (byte) nextByte;
+ }
+ ByteBuffer byteBuffer = ByteBuffer.wrap(readBytes, 0, readIndex);
+ return Charset.forName("UTF-8").decode(byteBuffer).toString();
+ }
+
+}
--- /dev/null
+/*
+ * jSite2 - FcpException.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;
+
+/**
+ * TODO
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public class FcpException extends Exception {
+
+ public FcpException() {
+ }
+
+ public FcpException(String message) {
+ super(message);
+ }
+
+ public FcpException(Throwable cause) {
+ super(cause);
+ }
+
+ public FcpException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+/*
+ * jSite2 - FcpKeyPair.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;
+
+/**
+ * Container for an SSK keypair.
+ *
+ * @author David Roden <droden@gmail.com>
+ * @version $Id$
+ */
+public class FcpKeyPair {
+
+ /** The public key. */
+ private final String publicKey;
+
+ /** The private key. */
+ private final String privateKey;
+
+ /**
+ * Creates a new keypair from the given keys.
+ *
+ * @param publicKey
+ * The public key
+ * @param privateKey
+ * The private key
+ */
+ public FcpKeyPair(String publicKey, String privateKey) {
+ this.publicKey = publicKey;
+ this.privateKey = privateKey;
+ }
+
+ /**
+ * Returns the public key of this keypair.
+ *
+ * @return The public key
+ */
+ public String getPublicKey() {
+ return publicKey;
+ }
+
+ /**
+ * Returns the private key of this keypair.
+ *
+ * @return The private key
+ */
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+}
--- /dev/null
+/*
+ * jSite2 - FpcListener.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;
+
+import java.util.EventListener;
+import java.util.Map;
+
+
+/**
+ * TODO
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public interface FcpListener extends EventListener {
+
+ public void fcpConnected(FcpConnection fcpConnection);
+ public void fcpDisconnected(FcpConnection fcpConnection);
+
+ //
+ // connection setup
+ //
+
+ public void fcpNodeHello(FcpConnection fcpConnection, Map<String, String> nodeProperties);
+ public void fcpCloseConnectionDuplicateName(FcpConnection fcpConnection);
+
+
+}
--- /dev/null
+/*
+ * jSite2 - FcpMessage.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import net.pterodactylus.util.io.StreamCopier;
+
+/**
+ * TODO
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public class FcpMessage implements Iterable<String> {
+
+ private static final String LINEFEED = "\r\n";
+
+ private final String name;
+ private final Map<String, String> fields = new HashMap<String, String>();
+ private InputStream dataInputStream;
+
+ public FcpMessage(String name) {
+ this(name, null);
+ }
+
+ public FcpMessage(String name, InputStream dataInputStream) {
+ this.name = name;
+ this.dataInputStream = dataInputStream;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setField(String field, String value) {
+ 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;
+ }
+
+ public void write(OutputStream outputStream) throws IOException {
+ writeLine(outputStream, name);
+ for (Entry<String, String> fieldEntry: fields.entrySet()) {
+ writeLine(outputStream, fieldEntry.getKey() + "=" + fieldEntry.getValue());
+ }
+ writeLine(outputStream, "EndMessage");
+ outputStream.flush();
+ if (dataInputStream != null) {
+ StreamCopier.copy(dataInputStream, outputStream);
+ }
+ outputStream.flush();
+ }
+
+ private void writeLine(OutputStream outputStream, String line) throws IOException {
+ outputStream.write((line + LINEFEED).getBytes("UTF-8"));
+ }
+
+}
--- /dev/null
+/*
+ * jSite2 - FcpProtocolException.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;
+
+/**
+ * TODO
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public class FcpProtocolException extends FcpException {
+
+ private final int code;
+ private final boolean fatal;
+ private final boolean global;
+
+ private String codeDescription;
+ private String extraDescription;
+ private String identifier;
+
+ public FcpProtocolException(int code, boolean fatal, boolean global) {
+ this.code = code;
+ this.fatal = fatal;
+ this.global = global;
+ }
+
+ /**
+ * TODO
+ *
+ * @return the code
+ */
+ int getCode() {
+ return code;
+ }
+
+ /**
+ * TODO
+ *
+ * @return the fatal
+ */
+ boolean isFatal() {
+ return fatal;
+ }
+
+ /**
+ * TODO
+ *
+ * @return the global
+ */
+ boolean isGlobal() {
+ return global;
+ }
+
+ /**
+ * TODO
+ *
+ * @return the codeDescription
+ */
+ String getCodeDescription() {
+ return codeDescription;
+ }
+
+ /**
+ * TODO
+ *
+ * @param codeDescription
+ * the codeDescription to set
+ */
+ void setCodeDescription(String codeDescription) {
+ this.codeDescription = codeDescription;
+ }
+
+ /**
+ * TODO
+ *
+ * @return the extraDescription
+ */
+ String getExtraDescription() {
+ return extraDescription;
+ }
+
+ /**
+ * TODO
+ *
+ * @param extraDescription
+ * the extraDescription to set
+ */
+ void setExtraDescription(String extraDescription) {
+ this.extraDescription = extraDescription;
+ }
+
+ /**
+ * TODO
+ *
+ * @return the identifier
+ */
+ String getIdentifier() {
+ return identifier;
+ }
+
+ /**
+ * TODO
+ *
+ * @param identifier
+ * the identifier to set
+ */
+ void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+}
--- /dev/null
+/*
+ * jSite2 - FcpTest.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;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+
+/**
+ * TODO
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ * @version $Id$
+ */
+public class FcpTest extends TestCase {
+
+ private FcpConnection fcpConnection;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void setUp() throws Exception {
+ fcpConnection = new FcpConnection("wing", "FcpTest");
+ fcpConnection.connect();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ fcpConnection.disconnect();
+ }
+
+ public void testFcpConnection() throws FcpException, IOException {
+ }
+
+ public void testListPeers() throws FcpException, IOException {
+ List<Map<String, String>> peers = fcpConnection.sendListPeers(true, true);
+ for (Map<String, String> peer: peers) {
+ for (Map.Entry<String, String> entry: peer.entrySet()) {
+ System.out.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+ }
+
+ public void testListPeerNotes() throws FcpException, IOException {
+ List<Map<String, String>> peers = fcpConnection.sendListPeerNotes("AY6rGAGjayoyQD2CkvQibf1Ct3mh6iwqyntzNpfRsiM");
+ for (Map<String, String> peer: peers) {
+ for (Map.Entry<String, String> entry: peer.entrySet()) {
+ System.out.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+ }
+
+ public void testGenerateSSK() throws IOException, FcpException {
+ FcpKeyPair keyPair = fcpConnection.generateSSK();
+ System.out.println("PrivateKey: " + keyPair.getPrivateKey());
+ System.out.println("PublicKey: " + keyPair.getPublicKey());
+ }
+
+}
--- /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 net.pterodactylus.util.fcp.FcpConnection;
+import net.pterodactylus.util.fcp.FcpException;
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * High-level client for FCP connections.
+ *
+ * @author David Roden <droden@gmail.com>
+ * @version $Id$
+ */
+public class FcpHighLevelClient {
+
+ /** The FCP connection of this client. */
+ private final FcpConnection fcpConnection;
+
+ /**
+ * 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;
+ }
+
+ public FcpNodeInformation login(String clientName, NodeHelloCallback nodeHelloCallback) throws FcpException, IOException {
+ FcpMessage clientHelloMessage = new FcpMessage("ClientHello");
+ clientHelloMessage.setField("Name", clientName);
+ clientHelloMessage.setField("ExpectedVersion", "2.0");
+ fcpConnection.sendMessage(clientHelloMessage);
+ FcpMessage nodeHelloMessage = fcpConnection.waitForMessage("NodeHello", "CloseConnectionDuplicateClientName");
+ if (nodeHelloMessage.getName().equals("CloseConnectionDuplicateClientName")) {
+ throw new FcpException("duplicate client name");
+ }
+ }
+
+}
--- /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);
+
+}