From 90f3a1c8e0c85f19c82130e49d7a605cb9e55286 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 8 Apr 2008 08:39:53 +0000 Subject: [PATCH] current state git-svn-id: http://trooper/svn/projects/jSite/trunk@651 c3eda9e8-030b-0410-8277-bc7414b0a119 --- src/net/pterodactylus/jsite/gui/MainWindow.java | 31 ++- src/net/pterodactylus/jsite/gui/ProjectPanel.java | 62 ++++- .../pterodactylus/jsite/gui/SwingInterface.java | 61 ++++- src/net/pterodactylus/jsite/i18n/jSite.properties | 12 + src/net/pterodactylus/util/fcp/FcpConnection.java | 286 +++++++++++++++++++++ .../util/fcp/FcpConnectionHandler.java | 120 +++++++++ src/net/pterodactylus/util/fcp/FcpException.java | 45 ++++ src/net/pterodactylus/util/fcp/FcpKeyPair.java | 67 +++++ src/net/pterodactylus/util/fcp/FcpListener.java | 44 ++++ src/net/pterodactylus/util/fcp/FcpMessage.java | 100 +++++++ .../util/fcp/FcpProtocolException.java | 128 +++++++++ src/net/pterodactylus/util/fcp/FcpTest.java | 82 ++++++ .../util/fcp/client/FcpHighLevelClient.java | 61 +++++ .../util/fcp/client/FcpNodeInformation.java | 143 +++++++++++ .../util/fcp/client/NodeHelloCallback.java | 32 +++ 15 files changed, 1260 insertions(+), 14 deletions(-) create mode 100644 src/net/pterodactylus/util/fcp/FcpConnection.java create mode 100644 src/net/pterodactylus/util/fcp/FcpConnectionHandler.java create mode 100644 src/net/pterodactylus/util/fcp/FcpException.java create mode 100644 src/net/pterodactylus/util/fcp/FcpKeyPair.java create mode 100644 src/net/pterodactylus/util/fcp/FcpListener.java create mode 100644 src/net/pterodactylus/util/fcp/FcpMessage.java create mode 100644 src/net/pterodactylus/util/fcp/FcpProtocolException.java create mode 100644 src/net/pterodactylus/util/fcp/FcpTest.java create mode 100644 src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java create mode 100644 src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java create mode 100644 src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java diff --git a/src/net/pterodactylus/jsite/gui/MainWindow.java b/src/net/pterodactylus/jsite/gui/MainWindow.java index 19982e9..ed2b1db 100644 --- a/src/net/pterodactylus/jsite/gui/MainWindow.java +++ b/src/net/pterodactylus/jsite/gui/MainWindow.java @@ -36,6 +36,7 @@ import javax.swing.JToolBar; 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; @@ -72,6 +73,9 @@ public class MainWindow extends JFrame implements I18nable { /** 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 @@ -105,6 +109,19 @@ public class MainWindow extends JFrame implements I18nable { statusBar.setText(text); } + /** + * {@inheritDoc} + */ + @Override + public Container getContentPane() { + return contentPane; + } + + + public Project getSelectedProject() { + return null; + } + // // PRIVATE METHODS // @@ -187,11 +204,11 @@ public class MainWindow extends JFrame implements I18nable { 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()); @@ -203,14 +220,6 @@ public class MainWindow extends JFrame implements I18nable { // getContentPane().add(lowerPanel, BorderLayout.CENTER); } - /** - * {@inheritDoc} - */ - @Override - public Container getContentPane() { - return contentPane; - } - // // INTERFACE I18nable // diff --git a/src/net/pterodactylus/jsite/gui/ProjectPanel.java b/src/net/pterodactylus/jsite/gui/ProjectPanel.java index c16f2a5..dd27016 100644 --- a/src/net/pterodactylus/jsite/gui/ProjectPanel.java +++ b/src/net/pterodactylus/jsite/gui/ProjectPanel.java @@ -20,10 +20,15 @@ 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. @@ -33,13 +38,35 @@ import javax.swing.JPanel; */ 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 @@ -50,7 +77,38 @@ public class ProjectPanel extends JPanel { */ 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; } } diff --git a/src/net/pterodactylus/jsite/gui/SwingInterface.java b/src/net/pterodactylus/jsite/gui/SwingInterface.java index 79b799d..d00ebe1 100644 --- a/src/net/pterodactylus/jsite/gui/SwingInterface.java +++ b/src/net/pterodactylus/jsite/gui/SwingInterface.java @@ -34,6 +34,7 @@ import javax.swing.JOptionPane; 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; @@ -85,6 +86,12 @@ public class SwingInterface implements CoreListener { /** 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; @@ -226,6 +233,24 @@ public class SwingInterface implements CoreListener { 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 // @@ -252,7 +277,6 @@ public class SwingInterface implements CoreListener { /* 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() + "”!"); @@ -399,6 +423,26 @@ public class SwingInterface implements CoreListener { 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(); + } + }; } /** @@ -488,6 +532,21 @@ public class SwingInterface implements CoreListener { * 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() { } // diff --git a/src/net/pterodactylus/jsite/i18n/jSite.properties b/src/net/pterodactylus/jsite/i18n/jSite.properties index fd71773..1a79960 100644 --- a/src/net/pterodactylus/jsite/i18n/jSite.properties +++ b/src/net/pterodactylus/jsite/i18n/jSite.properties @@ -107,6 +107,18 @@ mainWindow.button.addProject.accelerator: Ctrl-VK_A 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 # diff --git a/src/net/pterodactylus/util/fcp/FcpConnection.java b/src/net/pterodactylus/util/fcp/FcpConnection.java new file mode 100644 index 0000000..1b4d249 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpConnection.java @@ -0,0 +1,286 @@ +/* + * 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 fcpListeners = new ArrayList(); + + 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 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 null if the peer is + * unknown + * @throws IOException + * @throws FcpException + */ + public Map 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> 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> peers = new ArrayList>(); + while (true) { + FcpMessage returnMessage = waitForMessage("Peer", "EndListPeers"); + if (returnMessage.getName().equals("EndListPeers")) { + break; + } + peers.add(returnMessage.getFields()); + } + return peers; + } + + public List> sendListPeerNotes(String nodeIdentifier) throws IOException, FcpException { + FcpMessage listPeerNotesMessage = new FcpMessage("ListPeerNotes"); + listPeerNotesMessage.setField("NodeIdentifier", nodeIdentifier); + sendMessage(listPeerNotesMessage); + List> peerNotes = new ArrayList>(); + 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 wantedIdentifiers = new HashMap(); + 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 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; + } + } + } + +} diff --git a/src/net/pterodactylus/util/fcp/FcpConnectionHandler.java b/src/net/pterodactylus/util/fcp/FcpConnectionHandler.java new file mode 100644 index 0000000..2693c1f --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpConnectionHandler.java @@ -0,0 +1,120 @@ +/* + * 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(); + } + +} diff --git a/src/net/pterodactylus/util/fcp/FcpException.java b/src/net/pterodactylus/util/fcp/FcpException.java new file mode 100644 index 0000000..8a998fe --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpException.java @@ -0,0 +1,45 @@ +/* + * 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); + } + +} diff --git a/src/net/pterodactylus/util/fcp/FcpKeyPair.java b/src/net/pterodactylus/util/fcp/FcpKeyPair.java new file mode 100644 index 0000000..993ded2 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpKeyPair.java @@ -0,0 +1,67 @@ +/* + * 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; + } + +} diff --git a/src/net/pterodactylus/util/fcp/FcpListener.java b/src/net/pterodactylus/util/fcp/FcpListener.java new file mode 100644 index 0000000..e3d6d00 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpListener.java @@ -0,0 +1,44 @@ +/* + * 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 nodeProperties); + public void fcpCloseConnectionDuplicateName(FcpConnection fcpConnection); + + +} diff --git a/src/net/pterodactylus/util/fcp/FcpMessage.java b/src/net/pterodactylus/util/fcp/FcpMessage.java new file mode 100644 index 0000000..6ff4ec6 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpMessage.java @@ -0,0 +1,100 @@ +/* + * 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 { + + private static final String LINEFEED = "\r\n"; + + private final String name; + private final Map fields = new HashMap(); + 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 getFields() { + return Collections.unmodifiableMap(fields); + } + + /** + * {@inheritDoc} + */ + public Iterator 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 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")); + } + +} diff --git a/src/net/pterodactylus/util/fcp/FcpProtocolException.java b/src/net/pterodactylus/util/fcp/FcpProtocolException.java new file mode 100644 index 0000000..795798f --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpProtocolException.java @@ -0,0 +1,128 @@ +/* + * 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; + } + +} diff --git a/src/net/pterodactylus/util/fcp/FcpTest.java b/src/net/pterodactylus/util/fcp/FcpTest.java new file mode 100644 index 0000000..cedce1d --- /dev/null +++ b/src/net/pterodactylus/util/fcp/FcpTest.java @@ -0,0 +1,82 @@ +/* + * 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> peers = fcpConnection.sendListPeers(true, true); + for (Map peer: peers) { + for (Map.Entry entry: peer.entrySet()) { + System.out.println(entry.getKey() + ": " + entry.getValue()); + } + } + } + + public void testListPeerNotes() throws FcpException, IOException { + List> peers = fcpConnection.sendListPeerNotes("AY6rGAGjayoyQD2CkvQibf1Ct3mh6iwqyntzNpfRsiM"); + for (Map peer: peers) { + for (Map.Entry 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()); + } + +} diff --git a/src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java b/src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java new file mode 100644 index 0000000..8fd85b5 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java @@ -0,0 +1,61 @@ +/* + * 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"); + } + } + +} diff --git a/src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java b/src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java new file mode 100644 index 0000000..5163446 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java @@ -0,0 +1,143 @@ +/* + * 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; + } + +} diff --git a/src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java b/src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java new file mode 100644 index 0000000..6586b03 --- /dev/null +++ b/src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java @@ -0,0 +1,32 @@ +/* + * 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); + +} -- 2.7.4