<version>0.1-SNAPSHOT</version>
<dependencies>
<dependency>
+ <groupId>net.pterodactylus</groupId>
+ <artifactId>utils</artifactId>
+ <version>0.10.1-SNAPSHOT</version>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
--- /dev/null
+/*
+ * Sone - L10nFilter.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import net.pterodactylus.util.template.Filter;
+import net.pterodactylus.util.template.TemplateContext;
+import freenet.l10n.BaseL10n;
+
+/**
+ * {@link Filter} implementation replaces {@link String} values with their
+ * translated equivalents.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class L10nFilter implements Filter {
+
+ /** The l10n handler. */
+ private final BaseL10n l10n;
+
+ /**
+ * Creates a new L10n filter.
+ *
+ * @param l10n
+ * The l10n handler
+ */
+ public L10nFilter(BaseL10n l10n) {
+ this.l10n = l10n;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String format(TemplateContext templateContext, Object data, Map<String, String> parameters) {
+ if (parameters.isEmpty()) {
+ return l10n.getString(String.valueOf(data));
+ }
+ List<Object> parameterValues = new ArrayList<Object>();
+ int parameterIndex = 0;
+ while (parameters.containsKey(String.valueOf(parameterIndex))) {
+ Object value = parameters.get(String.valueOf(parameterIndex));
+ if (((String) value).startsWith("=")) {
+ value = ((String) value).substring(1);
+ } else {
+ value = templateContext.get((String) value);
+ }
+ parameterValues.add(value);
+ ++parameterIndex;
+ }
+ return new MessageFormat(l10n.getString(String.valueOf(data)), new Locale(l10n.getSelectedLanguage().shortCode)).format(parameterValues.toArray());
+ }
+}
--- /dev/null
+/*
+ * Sone - SimpleFieldSetBuilder.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet;
+
+import net.pterodactylus.util.validation.Validation;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * Helper class to construct {@link SimpleFieldSet} objects in a single call.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class SimpleFieldSetBuilder {
+
+ /** The simple field set that is being constructed. */
+ private final SimpleFieldSet simpleFieldSet;
+
+ /**
+ * Creates a new simple field set builder using a new, empty simple field
+ * set.
+ */
+ public SimpleFieldSetBuilder() {
+ this(new SimpleFieldSet(true));
+ }
+
+ /**
+ * Creates a new simple field set builder that will return the given simple
+ * field set on {@link #get()}.
+ *
+ * @param simpleFieldSet
+ * The simple field set to build
+ */
+ public SimpleFieldSetBuilder(SimpleFieldSet simpleFieldSet) {
+ Validation.begin().isNotNull("Simple Field Set", simpleFieldSet).check();
+ this.simpleFieldSet = simpleFieldSet;
+ }
+
+ /**
+ * Returns the constructed simple field set.
+ *
+ * @return The construct simple field set
+ */
+ public SimpleFieldSet get() {
+ return simpleFieldSet;
+ }
+
+ /**
+ * Copies the given simple field set into the simple field set being built
+ * in this builder, overwriting all previously existing values.
+ *
+ * @param simpleFieldSet
+ * The simple field set to copy
+ * @return This simple field set builder
+ */
+ public SimpleFieldSetBuilder put(@SuppressWarnings("hiding") SimpleFieldSet simpleFieldSet) {
+ this.simpleFieldSet.putAllOverwrite(simpleFieldSet);
+ return this;
+ }
+
+ /**
+ * Stores the given value under the given key, overwriting any previous
+ * value.
+ *
+ * @param key
+ * The key of the value
+ * @param value
+ * The value to store
+ * @return This simple field set builder
+ */
+ public SimpleFieldSetBuilder put(String key, String value) {
+ simpleFieldSet.putOverwrite(key, value);
+ return this;
+ }
+
+ /**
+ * Stores the given value under the given key, overwriting any previous
+ * value.
+ *
+ * @param key
+ * The key of the value
+ * @param value
+ * The value to store
+ * @return This simple field set builder
+ */
+ public SimpleFieldSetBuilder put(String key, int value) {
+ simpleFieldSet.put(key, value);
+ return this;
+ }
+
+ /**
+ * Stores the given value under the given key, overwriting any previous
+ * value.
+ *
+ * @param key
+ * The key of the value
+ * @param value
+ * The value to store
+ * @return This simple field set builder
+ */
+ public SimpleFieldSetBuilder put(String key, long value) {
+ simpleFieldSet.put(key, value);
+ return this;
+ }
+
+}
--- /dev/null
+/*
+ * Sone - AbstractCommand.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.fcp;
+
+import freenet.node.FSParseException;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * Basic implementation of a {@link Command} with various helper methods to
+ * simplify processing of input parameters.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public abstract class AbstractCommand implements Command {
+
+ //
+ // PROTECTED METHODS
+ //
+
+ /**
+ * Returns a String value from the given simple field set.
+ *
+ * @param simpleFieldSet
+ * The simple field set to get the value from
+ * @param key
+ * The key of the value
+ * @return The String value
+ * @throws FcpException
+ * if there is no value for the given key in the simple field
+ * set, or the value can not be converted to a String
+ */
+ protected String getString(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+ try {
+ return simpleFieldSet.getString(key);
+ } catch (FSParseException fspe1) {
+ throw new FcpException("Could not get parameter “" + key + "” as String.", fspe1);
+ }
+ }
+
+ /**
+ * Returns an int value from the given simple field set.
+ *
+ * @param simpleFieldSet
+ * The simple field set to get the value from
+ * @param key
+ * The key of the value
+ * @return The int value
+ * @throws FcpException
+ * if there is no value for the given key in the simple field
+ * set, or the value can not be converted to an int
+ */
+ protected int getInt(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+ try {
+ return simpleFieldSet.getInt(key);
+ } catch (FSParseException fspe1) {
+ throw new FcpException("Could not get parameter “" + key + "” as int.", fspe1);
+ }
+ }
+
+ /**
+ * Returns an int value from the given simple field set, returning a default
+ * value if the value can not be found or converted.
+ *
+ * @param simpleFieldSet
+ * The simple field set to get the value from
+ * @param key
+ * The key of the value
+ * @param defaultValue
+ * The default value
+ * @return The int value
+ */
+ protected int getInt(SimpleFieldSet simpleFieldSet, String key, int defaultValue) {
+ return simpleFieldSet.getInt(key, defaultValue);
+ }
+
+ /**
+ * Returns a boolean value from the given simple field set.
+ *
+ * @param simpleFieldSet
+ * The simple field set to get the value from
+ * @param key
+ * The key of the value
+ * @return The boolean value
+ * @throws FcpException
+ * if there is no value for the given key in the simple field
+ * set, or the value can not be converted to a boolean
+ */
+ protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+ try {
+ return simpleFieldSet.getBoolean(key);
+ } catch (FSParseException fspe1) {
+ throw new FcpException("Could not get parameter “" + key + "” as boolean.", fspe1);
+ }
+ }
+
+ /**
+ * Returns a boolean value from the given simple field set, returning a
+ * default value if the value can not be found or converted.
+ *
+ * @param simpleFieldSet
+ * The simple field set to get the value from
+ * @param key
+ * The key of the value
+ * @param defaultValue
+ * The default value
+ * @return The boolean value
+ */
+ protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key, boolean defaultValue) {
+ return simpleFieldSet.getBoolean(key, defaultValue);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - Command.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.fcp;
+
+import net.pterodactylus.wotns.freenet.SimpleFieldSetBuilder;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implementation of an FCP interface for other clients or plugins to
+ * communicate with Sone.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface Command {
+
+ /**
+ * Executes the command, returning a reply that will be sent back to the
+ * requesting plugin.
+ *
+ * @param parameters
+ * The parameters of the comand
+ * @param data
+ * The data of the command (may be {@code null})
+ * @param accessType
+ * The access type
+ * @return A reply to send back to the plugin
+ * @throws FcpException
+ * if an error processing the parameters occurs
+ */
+ public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException;
+
+ /**
+ * The access type of the request.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public static enum AccessType {
+
+ /** Access from another plugin. */
+ DIRECT,
+
+ /** Access via restricted FCP. */
+ RESTRICTED_FCP,
+
+ /** Access via FCP with full access. */
+ FULL_FCP,
+
+ }
+
+ /**
+ * Interface for command replies.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public static class Response {
+
+ /** The message name of the reponse. */
+ private final String messageName;
+
+ /** The reply parameters. */
+ private final SimpleFieldSet replyParameters;
+
+ /** The reply data, may be {@code null}. */
+ private final byte[] data;
+
+ /** The data bucket, may be {@code null}. */
+ private final Bucket bucket;
+
+ /**
+ * Creates a new reply with the given parameters.
+ *
+ * @param messageName
+ * The message name
+ * @param replyParameters
+ * The reply parameters
+ */
+ public Response(String messageName, SimpleFieldSet replyParameters) {
+ this(messageName, replyParameters, null, null);
+ }
+
+ /**
+ * Creates a new reply with the given parameters.
+ *
+ * @param messageName
+ * The message name
+ * @param replyParameters
+ * The reply parameters
+ * @param data
+ * The data of the reply (may be {@code null})
+ */
+ public Response(String messageName, SimpleFieldSet replyParameters, byte[] data) {
+ this(messageName, replyParameters, data, null);
+ }
+
+ /**
+ * Creates a new reply with the given parameters.
+ *
+ * @param messageName
+ * The message name
+ * @param replyParameters
+ * The reply parameters
+ * @param bucket
+ * The bucket of the reply (may be {@code null})
+ */
+ public Response(String messageName, SimpleFieldSet replyParameters, Bucket bucket) {
+ this(messageName, replyParameters, null, bucket);
+ }
+
+ /**
+ * Creates a new reply with the given parameters.
+ *
+ * @param messageName
+ * The message name
+ * @param replyParameters
+ * The reply parameters
+ * @param data
+ * The data of the reply (may be {@code null})
+ * @param bucket
+ * The bucket of the reply (may be {@code null})
+ */
+ private Response(String messageName, SimpleFieldSet replyParameters, byte[] data, Bucket bucket) {
+ this.messageName = messageName;
+ this.replyParameters = replyParameters;
+ this.data = data;
+ this.bucket = bucket;
+ }
+
+ /**
+ * Returns the reply parameters.
+ *
+ * @return The reply parameters
+ */
+ public SimpleFieldSet getReplyParameters() {
+ return new SimpleFieldSetBuilder(replyParameters).put("Message", messageName).get();
+ }
+
+ /**
+ * Returns whether the reply has reply data.
+ *
+ * @see #getData()
+ * @return {@code true} if this reply has data, {@code false} otherwise
+ */
+ public boolean hasData() {
+ return data != null;
+ }
+
+ /**
+ * Returns the data of the reply.
+ *
+ * @return The data of the reply
+ */
+ public byte[] getData() {
+ return data;
+ }
+
+ /**
+ * Returns whether the reply has a data bucket.
+ *
+ * @see #getBucket()
+ * @return {@code true} if the reply has a data bucket, {@code false}
+ * otherwise
+ */
+ public boolean hasBucket() {
+ return bucket != null;
+ }
+
+ /**
+ * Returns the data bucket of the reply.
+ *
+ * @return The data bucket of the reply
+ */
+ public Bucket getBucket() {
+ return bucket;
+ }
+
+ }
+
+ /**
+ * Response implementation that can return an error message and an optional
+ * error code.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public class ErrorResponse extends Response {
+
+ /**
+ * Creates a new error response with the given message.
+ *
+ * @param message
+ * The error message
+ */
+ public ErrorResponse(String message) {
+ super("Error", new SimpleFieldSetBuilder().put("ErrorMessage", message).get());
+ }
+
+ /**
+ * Creates a new error response with the given code and message.
+ *
+ * @param code
+ * The error code
+ * @param message
+ * The error message
+ */
+ public ErrorResponse(int code, String message) {
+ super("Error", new SimpleFieldSetBuilder().put("ErrorMessage", message).put("ErrorCode", code).get());
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Sone - FcpException.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.fcp;
+
+/**
+ * Base exception for FCP communication.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FcpException extends Exception {
+
+ /**
+ * Creates a new FCP exception.
+ */
+ public FcpException() {
+ super();
+ }
+
+ /**
+ * Creates a new FCP exception.
+ *
+ * @param message
+ * The message of the exception
+ */
+ public FcpException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new FCP exception.
+ *
+ * @param cause
+ * The cause of the exception
+ */
+ public FcpException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new FCP exception.
+ *
+ * @param message
+ * The message of the exception
+ * @param cause
+ * The cause of the exception
+ */
+ public FcpException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - ConnectorListener.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.plugin;
+
+import java.util.EventListener;
+
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Interface for objects that want to be notified if a {@link PluginConnector}
+ * receives a reply from a plugin. As a connection listener is always
+ * {@link PluginConnector#addConnectorListener(String, String, ConnectorListener)
+ * added} for a specific plugin, it will always be notified for replies from the
+ * correct plugin (unless you register the same listener for multiple
+ * plugins—which you subsequently should not do).
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface ConnectorListener extends EventListener {
+
+ /**
+ * A reply was received from the plugin this connection listener was added
+ * for.
+ *
+ * @param pluginConnector
+ * The plugin connector that received the reply
+ * @param fields
+ * The fields of the reply
+ * @param data
+ * The data of the reply (may be null)
+ */
+ public void receivedReply(PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data);
+
+}
--- /dev/null
+/*
+ * Sone - ConnectorListenerManager.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.plugin;
+
+import net.pterodactylus.util.event.AbstractListenerManager;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Manages {@link ConnectorListener}s and fire events.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ConnectorListenerManager extends AbstractListenerManager<PluginConnector, ConnectorListener> {
+
+ /**
+ * Creates a new manager for {@link ConnectorListener}s.
+ *
+ * @param pluginConnector
+ * The plugin connector that is the source for all events
+ */
+ public ConnectorListenerManager(PluginConnector pluginConnector) {
+ super(pluginConnector);
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Notifies all registered listeners that a reply from the plugin was
+ * received.
+ *
+ * @param fields
+ * The fields of the reply
+ * @param data
+ * The data of the reply (may be null)
+ */
+ public void fireReceivedReply(SimpleFieldSet fields, Bucket data) {
+ for (ConnectorListener connectorListener : getListeners()) {
+ connectorListener.receivedReply(getSource(), fields, data);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PluginConnector.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.plugin;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import net.pterodactylus.util.collection.Pair;
+import freenet.pluginmanager.FredPluginTalker;
+import freenet.pluginmanager.PluginNotFoundException;
+import freenet.pluginmanager.PluginRespirator;
+import freenet.pluginmanager.PluginTalker;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Interface for talking to other plugins. Other plugins are identified by their
+ * name and a unique connection identifier.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PluginConnector implements FredPluginTalker {
+
+ /** The plugin respirator. */
+ private final PluginRespirator pluginRespirator;
+
+ /** Connector listener managers for all plugin connections. */
+ private final Map<Pair<String, String>, ConnectorListenerManager> connectorListenerManagers = Collections.synchronizedMap(new HashMap<Pair<String, String>, ConnectorListenerManager>());
+
+ /**
+ * Creates a new plugin connector.
+ *
+ * @param pluginRespirator
+ * The plugin respirator
+ */
+ public PluginConnector(PluginRespirator pluginRespirator) {
+ this.pluginRespirator = pluginRespirator;
+ }
+
+ //
+ // LISTENER MANAGEMENT
+ //
+
+ /**
+ * Adds a connection listener for the given plugin connection.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param connectorListener
+ * The listener to add
+ */
+ public void addConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) {
+ getConnectorListenerManager(pluginName, identifier).addListener(connectorListener);
+ }
+
+ /**
+ * Removes a connection listener for the given plugin connection.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param connectorListener
+ * The listener to remove
+ */
+ public void removeConnectorListener(String pluginName, String identifier, ConnectorListener connectorListener) {
+ getConnectorListenerManager(pluginName, identifier).removeListener(connectorListener);
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Sends a request to the given plugin.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param fields
+ * The fields of the message
+ * @throws PluginException
+ * if the plugin can not be found
+ */
+ public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields) throws PluginException {
+ sendRequest(pluginName, identifier, fields, null);
+ }
+
+ /**
+ * Sends a request to the given plugin.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param fields
+ * The fields of the message
+ * @param data
+ * The payload of the message (may be null)
+ * @throws PluginException
+ * if the plugin can not be found
+ */
+ public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields, Bucket data) throws PluginException {
+ getPluginTalker(pluginName, identifier).send(fields, data);
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Returns the connection listener manager for the given plugin connection,
+ * creating a new one if none does exist yet.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @return The connection listener manager
+ */
+ private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier) {
+ return getConnectorListenerManager(pluginName, identifier, true);
+ }
+
+ /**
+ * Returns the connection listener manager for the given plugin connection,
+ * optionally creating a new one if none does exist yet.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @param create
+ * {@code true} to create a new manager if there is none,
+ * {@code false} to return {@code null} in that case
+ * @return The connection listener manager, or {@code null} if none existed
+ * and {@code create} is {@code false}
+ */
+ private ConnectorListenerManager getConnectorListenerManager(String pluginName, String identifier, boolean create) {
+ ConnectorListenerManager connectorListenerManager = connectorListenerManagers.get(new Pair<String, String>(pluginName, identifier));
+ if (create && (connectorListenerManager == null)) {
+ connectorListenerManager = new ConnectorListenerManager(this);
+ connectorListenerManagers.put(new Pair<String, String>(pluginName, identifier), connectorListenerManager);
+ }
+ return connectorListenerManager;
+ }
+
+ /**
+ * Returns the plugin talker for the given plugin connection.
+ *
+ * @param pluginName
+ * The name of the plugin
+ * @param identifier
+ * The identifier of the connection
+ * @return The plugin talker
+ * @throws PluginException
+ * if the plugin can not be found
+ */
+ private PluginTalker getPluginTalker(String pluginName, String identifier) throws PluginException {
+ try {
+ return pluginRespirator.getPluginTalker(this, pluginName, identifier);
+ } catch (PluginNotFoundException pnfe1) {
+ throw new PluginException(pnfe1);
+ }
+ }
+
+ //
+ // INTERFACE FredPluginTalker
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onReply(String pluginName, String identifier, SimpleFieldSet params, Bucket data) {
+ ConnectorListenerManager connectorListenerManager = getConnectorListenerManager(pluginName, identifier, false);
+ if (connectorListenerManager == null) {
+ /* we don’t care about events for this plugin. */
+ return;
+ }
+ connectorListenerManager.fireReceivedReply(params, data);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PluginException.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.plugin;
+
+import net.pterodactylus.wotns.freenet.wot.WebOfTrustException;
+
+/**
+ * Exception that signals an error when communicating with a plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PluginException extends WebOfTrustException {
+
+ /**
+ * Creates a new plugin exception.
+ */
+ public PluginException() {
+ super();
+ }
+
+ /**
+ * Creates a new plugin exception.
+ *
+ * @param message
+ * The message of the exception
+ */
+ public PluginException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new plugin exception.
+ *
+ * @param cause
+ * The cause of the exception
+ */
+ public PluginException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new plugin exception.
+ *
+ * @param message
+ * The message of the exception
+ * @param cause
+ * The cause of the exception
+ */
+ public PluginException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - DefaultIdentity.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.util.cache.CacheException;
+import net.pterodactylus.util.cache.CacheItem;
+import net.pterodactylus.util.cache.DefaultCacheItem;
+import net.pterodactylus.util.cache.MemoryCache;
+import net.pterodactylus.util.cache.ValueRetriever;
+import net.pterodactylus.util.cache.WritableCache;
+import net.pterodactylus.util.collection.TimedMap;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.wotns.freenet.plugin.PluginException;
+
+/**
+ * A Web of Trust identity.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DefaultIdentity implements Identity {
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(DefaultIdentity.class);
+
+ /** The web of trust connector. */
+ private final WebOfTrustConnector webOfTrustConnector;
+
+ /** The ID of the identity. */
+ private final String id;
+
+ /** The nickname of the identity. */
+ private final String nickname;
+
+ /** The request URI of the identity. */
+ private final String requestUri;
+
+ /** The contexts of the identity. */
+ private final Set<String> contexts = Collections.synchronizedSet(new HashSet<String>());
+
+ /** The properties of the identity. */
+ private final Map<String, String> properties = Collections.synchronizedMap(new HashMap<String, String>());
+
+ /** Cached trust. */
+ /* synchronize on itself. */
+ private final WritableCache<OwnIdentity, Trust> trustCache = new MemoryCache<OwnIdentity, Trust>(new ValueRetriever<OwnIdentity, Trust>() {
+
+ @Override
+ @SuppressWarnings("synthetic-access")
+ public CacheItem<Trust> retrieve(OwnIdentity ownIdentity) throws CacheException {
+ try {
+ return new DefaultCacheItem<Trust>(webOfTrustConnector.getTrust(ownIdentity, DefaultIdentity.this));
+ } catch (PluginException pe1) {
+ throw new CacheException("Could not retrieve trust for OwnIdentity: " + ownIdentity, pe1);
+ }
+ }
+
+ }, new TimedMap<OwnIdentity, CacheItem<Trust>>(60 * 60 * 1000));
+
+ /**
+ * Creates a new identity.
+ *
+ * @param webOfTrustConnector
+ * The web of trust connector
+ * @param id
+ * The ID of the identity
+ * @param nickname
+ * The nickname of the identity
+ * @param requestUri
+ * The request URI of the identity
+ */
+ public DefaultIdentity(WebOfTrustConnector webOfTrustConnector, String id, String nickname, String requestUri) {
+ this.webOfTrustConnector = webOfTrustConnector;
+ this.id = id;
+ this.nickname = nickname;
+ this.requestUri = requestUri;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getNickname() {
+ return nickname;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getRequestUri() {
+ return requestUri;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Set<String> getContexts() {
+ return Collections.unmodifiableSet(contexts);
+ }
+
+ /**
+ * Sets the contexts of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param contexts
+ * The contexts to set
+ */
+ void setContextsPrivate(Set<String> contexts) {
+ this.contexts.clear();
+ this.contexts.addAll(contexts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean hasContext(String context) {
+ return contexts.contains(context);
+ }
+
+ /**
+ * Adds the given context to this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to add
+ */
+ void addContextPrivate(String context) {
+ contexts.add(context);
+ }
+
+ /**
+ * Removes the given context from this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to remove
+ */
+ public void removeContextPrivate(String context) {
+ contexts.remove(context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map<String, String> getProperties() {
+ synchronized (properties) {
+ return Collections.unmodifiableMap(properties);
+ }
+ }
+
+ /**
+ * Sets all properties of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param properties
+ * The new properties of this identity
+ */
+ void setPropertiesPrivate(Map<String, String> properties) {
+ synchronized (this.properties) {
+ this.properties.clear();
+ this.properties.putAll(properties);
+ }
+ }
+
+ /**
+ * Sets the property with the given name to the given value.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param name
+ * The name of the property
+ * @param value
+ * The value of the property
+ */
+ void setPropertyPrivate(String name, String value) {
+ synchronized (properties) {
+ properties.put(name, value);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getProperty(String name) {
+ synchronized (properties) {
+ return properties.get(name);
+ }
+ }
+
+ /**
+ * Removes the property with the given name.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param name
+ * The name of the property to remove
+ */
+ void removePropertyPrivate(String name) {
+ synchronized (properties) {
+ properties.remove(name);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Trust getTrust(OwnIdentity ownIdentity) {
+ try {
+ synchronized (trustCache) {
+ return trustCache.get(ownIdentity);
+ }
+ } catch (CacheException ce1) {
+ logger.log(Level.WARNING, "Could not get trust for OwnIdentity: " + ownIdentity, ce1);
+ return null;
+ }
+ }
+
+ /**
+ * Sets the trust received for this identity by the given own identity.
+ *
+ * @param ownIdentity
+ * The own identity that gives the trust
+ * @param trust
+ * The trust received for this identity
+ */
+ void setTrustPrivate(OwnIdentity ownIdentity, Trust trust) {
+ synchronized (trustCache) {
+ trustCache.put(ownIdentity, trust);
+ }
+ }
+
+ //
+ // OBJECT METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (!(object instanceof DefaultIdentity)) {
+ return false;
+ }
+ DefaultIdentity identity = (DefaultIdentity) object;
+ return identity.id.equals(id);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "[id=" + id + ",nickname=" + nickname + ",contexts=" + contexts + ",properties=" + properties + "]";
+ }
+
+}
--- /dev/null
+/*
+ * Sone - DefaultOwnIdentity.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import net.pterodactylus.util.validation.Validation;
+
+/**
+ * An own identity is an identity that the owner of the node has full control
+ * over.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity {
+
+ /** The identity manager. */
+ private final WebOfTrustConnector webOfTrustConnector;
+
+ /** The insert URI of the identity. */
+ private final String insertUri;
+
+ /**
+ * Creates a new own identity.
+ *
+ * @param webOfTrustConnector
+ * The identity manager
+ * @param id
+ * The ID of the identity
+ * @param nickname
+ * The nickname of the identity
+ * @param requestUri
+ * The request URI of the identity
+ * @param insertUri
+ * The insert URI of the identity
+ */
+ public DefaultOwnIdentity(WebOfTrustConnector webOfTrustConnector, String id, String nickname, String requestUri, String insertUri) {
+ super(webOfTrustConnector, id, nickname, requestUri);
+ this.webOfTrustConnector = webOfTrustConnector;
+ this.insertUri = insertUri;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getInsertUri() {
+ return insertUri;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addContext(String context) throws WebOfTrustException {
+ webOfTrustConnector.addContext(this, context);
+ addContextPrivate(context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeContext(String context) throws WebOfTrustException {
+ webOfTrustConnector.removeContext(this, context);
+ removeContextPrivate(context);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setContexts(Set<String> contexts) throws WebOfTrustException {
+ for (String context : getContexts()) {
+ if (!contexts.contains(context)) {
+ webOfTrustConnector.removeContext(this, context);
+ }
+ }
+ for (String context : contexts) {
+ if (!getContexts().contains(context)) {
+ webOfTrustConnector.addContext(this, context);
+ }
+ }
+ setContextsPrivate(contexts);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setProperty(String name, String value) throws WebOfTrustException {
+ webOfTrustConnector.setProperty(this, name, value);
+ setPropertyPrivate(name, value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeProperty(String name) throws WebOfTrustException {
+ webOfTrustConnector.removeProperty(this, name);
+ removePropertyPrivate(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setProperties(Map<String, String> properties) throws WebOfTrustException {
+ for (Entry<String, String> oldProperty : getProperties().entrySet()) {
+ if (!properties.containsKey(oldProperty.getKey())) {
+ webOfTrustConnector.removeProperty(this, oldProperty.getKey());
+ } else {
+ webOfTrustConnector.setProperty(this, oldProperty.getKey(), properties.get(oldProperty.getKey()));
+ }
+ }
+ for (Entry<String, String> newProperty : properties.entrySet()) {
+ if (!getProperties().containsKey(newProperty.getKey())) {
+ webOfTrustConnector.setProperty(this, newProperty.getKey(), newProperty.getValue());
+ }
+ }
+ setPropertiesPrivate(properties);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException {
+ Validation.begin().isNotNull("Trust Target", target).isNotNull("Trust Comment", comment).isLessOrEqual("Trust Value", trustValue, 100).isGreaterOrEqual("Trust Value", trustValue, -100).check();
+ webOfTrustConnector.setTrust(this, target, trustValue, comment);
+ if (target instanceof DefaultIdentity) {
+ ((DefaultIdentity) target).setTrustPrivate(this, new Trust(trustValue, trustValue, 0));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeTrust(Identity target) throws WebOfTrustException {
+ Validation.begin().isNotNull("Trust Target", target).check();
+ webOfTrustConnector.removeTrust(this, target);
+ if (target instanceof DefaultIdentity) {
+ ((DefaultIdentity) target).setTrustPrivate(this, new Trust(null, null, null));
+ }
+ }
+
+ //
+ // OBJECT METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ /* The hash of DefaultIdentity is fine. */
+ return super.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals(Object object) {
+ /* The ID of the superclass is still enough. */
+ return super.equals(object);
+ }
+
+}
--- /dev/null
+/*
+ * Sone - Identity.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Interface for web of trust identities, defining all functions that can be
+ * performed on an identity. The identity is the main entry point for identity
+ * management.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface Identity {
+
+ /**
+ * Returns the ID of the identity.
+ *
+ * @return The ID of the identity
+ */
+ public String getId();
+
+ /**
+ * Returns the nickname of the identity.
+ *
+ * @return The nickname of the identity
+ */
+ public String getNickname();
+
+ /**
+ * Returns the request URI of the identity.
+ *
+ * @return The request URI of the identity
+ */
+ public String getRequestUri();
+
+ /**
+ * Returns all contexts of this identity.
+ *
+ * @return All contexts of this identity
+ */
+ public Set<String> getContexts();
+
+ /**
+ * Returns whether this identity has the given context.
+ *
+ * @param context
+ * The context to check for
+ * @return {@code true} if this identity has the given context,
+ * {@code false} otherwise
+ */
+ public boolean hasContext(String context);
+
+ /**
+ * Returns all properties of this identity.
+ *
+ * @return All properties of this identity
+ */
+ public Map<String, String> getProperties();
+
+ /**
+ * Returns the value of the property with the given name.
+ *
+ * @param name
+ * The name of the property
+ * @return The value of the property
+ */
+ public String getProperty(String name);
+
+ /**
+ * Retrieves the trust that this identity receives from the given own
+ * identity. If this identity is not in the own identity’s trust tree, a
+ * {@link Trust} is returned that has all its elements set to {@code null}.
+ * If the trust can not be retrieved, {@code null} is returned.
+ *
+ * @param ownIdentity
+ * The own identity to get the trust for
+ * @return The trust assigned to this identity, or {@code null} if the trust
+ * could not be retrieved
+ */
+ public Trust getTrust(OwnIdentity ownIdentity);
+
+}
--- /dev/null
+/*
+ * Sone - IdentityListener.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import java.util.EventListener;
+
+/**
+ * Listener interface for {@link IdentityManager} events.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface IdentityListener extends EventListener {
+
+ /**
+ * Notifies a listener that an {@link OwnIdentity} that was not known on the
+ * previous check is available.
+ *
+ * @param ownIdentity
+ * The new own identity
+ */
+ public void ownIdentityAdded(OwnIdentity ownIdentity);
+
+ /**
+ * Notifies a listener that an {@link OwnIdentity} that was available during
+ * the last check has gone away.
+ *
+ * @param ownIdentity
+ * The disappeared own identity
+ */
+ public void ownIdentityRemoved(OwnIdentity ownIdentity);
+
+ /**
+ * Notifies a listener that a new identity was discovered.
+ *
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
+ * @param identity
+ * The new identity
+ */
+ public void identityAdded(OwnIdentity ownIdentity, Identity identity);
+
+ /**
+ * Notifies a listener that some properties of the identity have changed.
+ *
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
+ * @param identity
+ * The updated identity
+ */
+ public void identityUpdated(OwnIdentity ownIdentity, Identity identity);
+
+ /**
+ * Notifies a listener that an identity has gone away.
+ *
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
+ * @param identity
+ * The disappeared identity
+ */
+ public void identityRemoved(OwnIdentity ownIdentity, Identity identity);
+
+}
--- /dev/null
+/*
+ * Sone - IdentityListenerManager.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import net.pterodactylus.util.event.AbstractListenerManager;
+
+/**
+ * Manager for {@link IdentityListener}s.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class IdentityListenerManager extends AbstractListenerManager<IdentityManager, IdentityListener> {
+
+ /**
+ * Creates a new identity listener manager.
+ */
+ public IdentityListenerManager() {
+ super(null);
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Notifies all listeners that an {@link OwnIdentity} that was not known on
+ * the previous check is available.
+ *
+ * @see IdentityListener#ownIdentityAdded(OwnIdentity)
+ * @param ownIdentity
+ * The new own identity
+ */
+ public void fireOwnIdentityAdded(OwnIdentity ownIdentity) {
+ for (IdentityListener identityListener : getListeners()) {
+ identityListener.ownIdentityAdded(ownIdentity);
+ }
+ }
+
+ /**
+ * Notifies all listeners that an {@link OwnIdentity} that was available
+ * during the last check has gone away.
+ *
+ * @see IdentityListener#ownIdentityRemoved(OwnIdentity)
+ * @param ownIdentity
+ * The disappeared own identity
+ */
+ public void fireOwnIdentityRemoved(OwnIdentity ownIdentity) {
+ for (IdentityListener identityListener : getListeners()) {
+ identityListener.ownIdentityRemoved(ownIdentity);
+ }
+ }
+
+ /**
+ * Notifies all listeners that a new identity was discovered.
+ *
+ * @see IdentityListener#identityAdded(OwnIdentity, Identity)
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
+ * @param identity
+ * The new identity
+ */
+ public void fireIdentityAdded(OwnIdentity ownIdentity, Identity identity) {
+ for (IdentityListener identityListener : getListeners()) {
+ identityListener.identityAdded(ownIdentity, identity);
+ }
+ }
+
+ /**
+ * Notifies all listeners that some properties of the identity have changed.
+ *
+ * @see IdentityListener#identityUpdated(OwnIdentity, Identity)
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
+ * @param identity
+ * The updated identity
+ */
+ public void fireIdentityUpdated(OwnIdentity ownIdentity, Identity identity) {
+ for (IdentityListener identityListener : getListeners()) {
+ identityListener.identityUpdated(ownIdentity, identity);
+ }
+ }
+
+ /**
+ * Notifies all listeners that an identity has gone away.
+ *
+ * @see IdentityListener#identityRemoved(OwnIdentity, Identity)
+ * @param ownIdentity
+ * The own identity at the root of the trust tree
+ * @param identity
+ * The disappeared identity
+ */
+ public void fireIdentityRemoved(OwnIdentity ownIdentity, Identity identity) {
+ for (IdentityListener identityListener : getListeners()) {
+ identityListener.identityRemoved(ownIdentity, identity);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - IdentityManager.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.util.collection.SetBuilder;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.service.AbstractService;
+import net.pterodactylus.wotns.freenet.plugin.PluginException;
+
+/**
+ * The identity manager takes care of loading and storing identities, their
+ * contexts, and properties. It does so in a way that does not expose errors via
+ * exceptions but it only logs them and tries to return sensible defaults.
+ * <p>
+ * It is also responsible for polling identities from the Web of Trust plugin
+ * and notifying registered {@link IdentityListener}s when {@link Identity}s and
+ * {@link OwnIdentity}s are discovered or disappearing.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class IdentityManager extends AbstractService {
+
+ /** Object used for synchronization. */
+ private final Object syncObject = new Object() {
+ /* inner class for better lock names. */
+ };
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(IdentityManager.class);
+
+ /** The event manager. */
+ private final IdentityListenerManager identityListenerManager = new IdentityListenerManager();
+
+ /** The Web of Trust connector. */
+ private final WebOfTrustConnector webOfTrustConnector;
+
+ /** The context to filter for. */
+ private volatile String context;
+
+ /** The currently known own identities. */
+ /* synchronize access on syncObject. */
+ private Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
+
+ /** The currently trusted identities. */
+ private Map<OwnIdentity, Set<Identity>> currentTrustedIdentities = new HashMap<OwnIdentity, Set<Identity>>();
+
+ /**
+ * Creates a new identity manager.
+ *
+ * @param webOfTrustConnector
+ * The Web of Trust connector
+ */
+ public IdentityManager(WebOfTrustConnector webOfTrustConnector) {
+ super("Sone Identity Manager", false);
+ this.webOfTrustConnector = webOfTrustConnector;
+ }
+
+ //
+ // LISTENER MANAGEMENT
+ //
+
+ /**
+ * Adds a listener for identity events.
+ *
+ * @param identityListener
+ * The listener to add
+ */
+ public void addIdentityListener(IdentityListener identityListener) {
+ identityListenerManager.addListener(identityListener);
+ }
+
+ /**
+ * Removes a listener for identity events.
+ *
+ * @param identityListener
+ * The listener to remove
+ */
+ public void removeIdentityListener(IdentityListener identityListener) {
+ identityListenerManager.removeListener(identityListener);
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Sets the context to filter own identities and trusted identities for.
+ *
+ * @param context
+ * The context to filter for, or {@code null} to not filter
+ */
+ public void setContext(String context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns whether the Web of Trust plugin could be reached during the last
+ * try.
+ *
+ * @return {@code true} if the Web of Trust plugin is connected, {@code
+ * false} otherwise
+ */
+ public boolean isConnected() {
+ try {
+ webOfTrustConnector.ping();
+ return true;
+ } catch (PluginException pe1) {
+ /* not connected, ignore. */
+ return false;
+ }
+ }
+
+ /**
+ * Returns the own identity with the given ID.
+ *
+ * @param id
+ * The ID of the own identity
+ * @return The own identity, or {@code null} if there is no such identity
+ */
+ public OwnIdentity getOwnIdentity(String id) {
+ Set<OwnIdentity> allOwnIdentities = getAllOwnIdentities();
+ for (OwnIdentity ownIdentity : allOwnIdentities) {
+ if (ownIdentity.getId().equals(id)) {
+ return ownIdentity;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns all own identities.
+ *
+ * @return All own identities
+ */
+ public Set<OwnIdentity> getAllOwnIdentities() {
+ try {
+ Set<OwnIdentity> ownIdentities = webOfTrustConnector.loadAllOwnIdentities();
+ Map<String, OwnIdentity> newOwnIdentities = new HashMap<String, OwnIdentity>();
+ for (OwnIdentity ownIdentity : ownIdentities) {
+ newOwnIdentities.put(ownIdentity.getId(), ownIdentity);
+ }
+ checkOwnIdentities(newOwnIdentities);
+ return ownIdentities;
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "Could not load all own identities!", wote1);
+ return Collections.emptySet();
+ }
+ }
+
+ public Set<Identity> getTrustedIdentities(OwnIdentity ownIdentity) {
+ SetBuilder<Identity> identities = new SetBuilder<Identity>();
+ if ((context == null) || ownIdentity.getContexts().contains(context)) {
+ identities.add(ownIdentity);
+ }
+ synchronized (syncObject) {
+ identities.addAll(currentTrustedIdentities.get(ownIdentity));
+ }
+ return identities.get();
+ }
+
+ //
+ // SERVICE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void serviceRun() {
+ Map<OwnIdentity, Map<String, Identity>> oldIdentities = Collections.emptyMap();
+ while (!shouldStop()) {
+ Map<OwnIdentity, Map<String, Identity>> currentIdentities = new HashMap<OwnIdentity, Map<String, Identity>>();
+ @SuppressWarnings("hiding")
+ Map<String, OwnIdentity> currentOwnIdentities = new HashMap<String, OwnIdentity>();
+
+ Set<OwnIdentity> ownIdentities = null;
+ boolean identitiesLoaded = false;
+ try {
+ /* get all identities with the wanted context from WoT. */
+ ownIdentities = webOfTrustConnector.loadAllOwnIdentities();
+
+ /* load trusted identities. */
+ for (OwnIdentity ownIdentity : ownIdentities) {
+ if ((context != null) && !ownIdentity.hasContext(context)) {
+ continue;
+ }
+ currentOwnIdentities.put(ownIdentity.getId(), ownIdentity);
+
+ Set<Identity> trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context);
+ Map<String, Identity> identities = new HashMap<String, Identity>();
+ currentIdentities.put(ownIdentity, identities);
+ for (Identity identity : trustedIdentities) {
+ identities.put(identity.getId(), identity);
+ }
+ }
+ identitiesLoaded = true;
+ } catch (WebOfTrustException wote1) {
+ logger.log(Level.WARNING, "WoT has disappeared!", wote1);
+ }
+
+ if (identitiesLoaded) {
+
+ /* check for changes. */
+ checkOwnIdentities(currentOwnIdentities);
+
+ /* now check for changes in remote identities. */
+ for (OwnIdentity ownIdentity : currentOwnIdentities.values()) {
+
+ /* find new identities. */
+ for (Identity currentIdentity : currentIdentities.get(ownIdentity).values()) {
+ if (!oldIdentities.containsKey(ownIdentity) || !oldIdentities.get(ownIdentity).containsKey(currentIdentity.getId())) {
+ identityListenerManager.fireIdentityAdded(ownIdentity, currentIdentity);
+ }
+ }
+
+ /* find removed identities. */
+ if (oldIdentities.containsKey(ownIdentity)) {
+ for (Identity oldIdentity : oldIdentities.get(ownIdentity).values()) {
+ if (!currentIdentities.get(ownIdentity).containsKey(oldIdentity.getId())) {
+ identityListenerManager.fireIdentityRemoved(ownIdentity, oldIdentity);
+ }
+ }
+
+ /* check for changes in the contexts. */
+ for (Identity oldIdentity : oldIdentities.get(ownIdentity).values()) {
+ if (!currentIdentities.get(ownIdentity).containsKey(oldIdentity.getId())) {
+ continue;
+ }
+ Identity newIdentity = currentIdentities.get(ownIdentity).get(oldIdentity.getId());
+ Set<String> oldContexts = oldIdentity.getContexts();
+ Set<String> newContexts = newIdentity.getContexts();
+ if (oldContexts.size() != newContexts.size()) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ continue;
+ }
+ for (String oldContext : oldContexts) {
+ if (!newContexts.contains(oldContext)) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ break;
+ }
+ }
+ }
+
+ /* check for changes in the properties. */
+ for (Identity oldIdentity : oldIdentities.get(ownIdentity).values()) {
+ if (!currentIdentities.get(ownIdentity).containsKey(oldIdentity.getId())) {
+ continue;
+ }
+ Identity newIdentity = currentIdentities.get(ownIdentity).get(oldIdentity.getId());
+ Map<String, String> oldProperties = oldIdentity.getProperties();
+ Map<String, String> newProperties = newIdentity.getProperties();
+ if (oldProperties.size() != newProperties.size()) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ continue;
+ }
+ for (Entry<String, String> oldProperty : oldProperties.entrySet()) {
+ if (!newProperties.containsKey(oldProperty.getKey()) || !newProperties.get(oldProperty.getKey()).equals(oldProperty.getValue())) {
+ identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* remember the current set of identities. */
+ oldIdentities = currentIdentities;
+ synchronized (syncObject) {
+ currentTrustedIdentities.clear();
+ for (Entry<OwnIdentity, Map<String, Identity>> entry : currentIdentities.entrySet()) {
+ Set<Identity> identities = new HashSet<Identity>();
+ currentTrustedIdentities.put(entry.getKey(), identities);
+ for (Identity identity : entry.getValue().values()) {
+ identities.add(identity);
+ }
+ }
+ }
+ }
+
+ /* wait a minute before checking again. */
+ sleep(60 * 1000);
+ }
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ /**
+ * Checks the given new list of own identities for added or removed own
+ * identities, as compared to {@link #currentOwnIdentities}.
+ *
+ * @param newOwnIdentities
+ * The new own identities
+ */
+ private void checkOwnIdentities(Map<String, OwnIdentity> newOwnIdentities) {
+ synchronized (syncObject) {
+
+ /* find removed own identities: */
+ for (OwnIdentity oldOwnIdentity : currentOwnIdentities.values()) {
+ if (!newOwnIdentities.containsKey(oldOwnIdentity.getId())) {
+ identityListenerManager.fireOwnIdentityRemoved(oldOwnIdentity);
+ }
+ }
+
+ /* find added own identities. */
+ for (OwnIdentity currentOwnIdentity : newOwnIdentities.values()) {
+ if (!currentOwnIdentities.containsKey(currentOwnIdentity.getId())) {
+ identityListenerManager.fireOwnIdentityAdded(currentOwnIdentity);
+ }
+ }
+
+ currentOwnIdentities.clear();
+ currentOwnIdentities.putAll(newOwnIdentities);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - OwnIdentity.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Defines a local identity, an own identity.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface OwnIdentity extends Identity {
+
+ /**
+ * Returns the insert URI of the identity.
+ *
+ * @return The insert URI of the identity
+ */
+ public String getInsertUri();
+
+ /**
+ * Adds the given context to this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to add
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void addContext(String context) throws WebOfTrustException;
+
+ /**
+ * Sets all contexts of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param contexts
+ * All contexts of the identity
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void setContexts(Set<String> contexts) throws WebOfTrustException;
+
+ /**
+ * Removes the given context from this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param context
+ * The context to remove
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void removeContext(String context) throws WebOfTrustException;
+
+ /**
+ * Sets the property with the given name to the given value.
+ *
+ * @param name
+ * The name of the property
+ * @param value
+ * The value of the property
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void setProperty(String name, String value) throws WebOfTrustException;
+
+ /**
+ * Sets all properties of this identity.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param properties
+ * The new properties of this identity
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void setProperties(Map<String, String> properties) throws WebOfTrustException;
+
+ /**
+ * Removes the property with the given name.
+ * <p>
+ * This method is only called by the {@link IdentityManager}.
+ *
+ * @param name
+ * The name of the property to remove
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void removeProperty(String name) throws WebOfTrustException;
+
+ /**
+ * Sets the trust for the given target identity.
+ *
+ * @param target
+ * The target to set the trust for
+ * @param trustValue
+ * The new trust value (from {@code -100} or {@code 100})
+ * @param comment
+ * The comment for the trust assignment
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void setTrust(Identity target, int trustValue, String comment) throws WebOfTrustException;
+
+ /**
+ * Removes any trust assignment for the given target identity.
+ *
+ * @param target
+ * The targe to remove the trust assignment for
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void removeTrust(Identity target) throws WebOfTrustException;
+
+}
--- /dev/null
+/*
+ * Sone - Trust.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+/**
+ * Container class for trust in the web of trust.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Trust {
+
+ /** Explicitely assigned trust. */
+ private final Integer explicit;
+
+ /** Implicitely calculated trust. */
+ private final Integer implicit;
+
+ /** The distance from the owner of the trust tree. */
+ private final Integer distance;
+
+ /**
+ * Creates a new trust container.
+ *
+ * @param explicit
+ * The explicit trust
+ * @param implicit
+ * The implicit trust
+ * @param distance
+ * The distance
+ */
+ public Trust(Integer explicit, Integer implicit, Integer distance) {
+ this.explicit = explicit;
+ this.implicit = implicit;
+ this.distance = distance;
+ }
+
+ /**
+ * Returns the trust explicitely assigned to an identity.
+ *
+ * @return The explicitely assigned trust, or {@code null} if the identity
+ * is not in the own identity’s trust tree
+ */
+ public Integer getExplicit() {
+ return explicit;
+ }
+
+ /**
+ * Returns the implicitely assigned trust, or the calculated trust.
+ *
+ * @return The calculated trust, or {@code null} if the identity is not in
+ * the own identity’s trust tree
+ */
+ public Integer getImplicit() {
+ return implicit;
+ }
+
+ /**
+ * Returns the distance of the trusted identity from the trusting identity.
+ *
+ * @return The distance from the own identity, or {@code null} if the
+ * identity is not in the own identity’s trust tree
+ */
+ public Integer getDistance() {
+ return distance;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return getClass().getName() + "[explicit=" + explicit + ",implicit=" + implicit + ",distance=" + distance + "]";
+ }
+
+}
--- /dev/null
+/*
+ * Sone - WebOfTrustConnector.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.number.Numbers;
+import net.pterodactylus.wotns.freenet.plugin.ConnectorListener;
+import net.pterodactylus.wotns.freenet.plugin.PluginConnector;
+import net.pterodactylus.wotns.freenet.plugin.PluginException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Connector for the Web of Trust plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class WebOfTrustConnector implements ConnectorListener {
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(WebOfTrustConnector.class);
+
+ /** The name of the WoT plugin. */
+ private static final String WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust";
+
+ /** A random connection identifier. */
+ private static final String PLUGIN_CONNECTION_IDENTIFIER = "WoTNS-WoT-Connector-" + Math.abs(Math.random());
+
+ /** The current reply. */
+ private Reply reply;
+
+ /** The plugin connector. */
+ private final PluginConnector pluginConnector;
+
+ /**
+ * Creates a new Web of Trust connector that uses the given plugin
+ * connector.
+ *
+ * @param pluginConnector
+ * The plugin connector
+ */
+ public WebOfTrustConnector(PluginConnector pluginConnector) {
+ this.pluginConnector = pluginConnector;
+ pluginConnector.addConnectorListener(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, this);
+ }
+
+ //
+ // ACTIONS
+ //
+
+ public void stop() {
+ pluginConnector.removeConnectorListener(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, this);
+ }
+
+ /**
+ * Loads all own identities from the Web of Trust plugin.
+ *
+ * @return All own identity
+ * @throws WebOfTrustException
+ * if the own identities can not be loaded
+ */
+ public Set<OwnIdentity> loadAllOwnIdentities() throws WebOfTrustException {
+ @SuppressWarnings("hiding")
+ Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get());
+ SimpleFieldSet fields = reply.getFields();
+ int ownIdentityCounter = -1;
+ Set<OwnIdentity> ownIdentities = new HashSet<OwnIdentity>();
+ while (true) {
+ String id = fields.get("Identity" + ++ownIdentityCounter);
+ if (id == null) {
+ break;
+ }
+ String requestUri = fields.get("RequestURI" + ownIdentityCounter);
+ String insertUri = fields.get("InsertURI" + ownIdentityCounter);
+ String nickname = fields.get("Nickname" + ownIdentityCounter);
+ DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(this, id, nickname, requestUri, insertUri);
+ ownIdentity.setContextsPrivate(parseContexts("Contexts" + ownIdentityCounter + ".", fields));
+ ownIdentity.setPropertiesPrivate(parseProperties("Properties" + ownIdentityCounter + ".", fields));
+ ownIdentities.add(ownIdentity);
+ }
+ return ownIdentities;
+ }
+
+ /**
+ * Loads all identities that the given identities trusts with a score of
+ * more than 0.
+ *
+ * @param ownIdentity
+ * The own identity
+ * @return All trusted identities
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity) throws PluginException {
+ return loadTrustedIdentities(ownIdentity, null);
+ }
+
+ /**
+ * Loads all identities that the given identities trusts with a score of
+ * more than 0 and the (optional) given context.
+ *
+ * @param ownIdentity
+ * The own identity
+ * @param context
+ * The context to filter, or {@code null}
+ * @return All trusted identities
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity, String context) throws PluginException {
+ @SuppressWarnings("hiding")
+ Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("TreeOwner", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).get());
+ SimpleFieldSet fields = reply.getFields();
+ Set<Identity> identities = new HashSet<Identity>();
+ int identityCounter = -1;
+ while (true) {
+ String id = fields.get("Identity" + ++identityCounter);
+ if (id == null) {
+ break;
+ }
+ String nickname = fields.get("Nickname" + identityCounter);
+ String requestUri = fields.get("RequestURI" + identityCounter);
+ DefaultIdentity identity = new DefaultIdentity(this, id, nickname, requestUri);
+ identity.setContextsPrivate(parseContexts("Contexts" + identityCounter + ".", fields));
+ identity.setPropertiesPrivate(parseProperties("Properties" + identityCounter + ".", fields));
+ identity.setTrustPrivate(ownIdentity, new Trust(Numbers.safeParseInteger(fields.get("Trust" + identityCounter)), Numbers.safeParseInteger(fields.get("Score" + identityCounter)), Numbers.safeParseInteger(fields.get("Rank" + identityCounter))));
+ identities.add(identity);
+ }
+ return identities;
+ }
+
+ /**
+ * Adds the given context to the given identity.
+ *
+ * @param ownIdentity
+ * The identity to add the context to
+ * @param context
+ * The context to add
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public void addContext(OwnIdentity ownIdentity, String context) throws PluginException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "AddContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
+ }
+
+ /**
+ * Removes the given context from the given identity.
+ *
+ * @param ownIdentity
+ * The identity to remove the context from
+ * @param context
+ * The context to remove
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public void removeContext(OwnIdentity ownIdentity, String context) throws PluginException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
+ }
+
+ /**
+ * Returns the value of the property with the given name.
+ *
+ * @param identity
+ * The identity whose properties to check
+ * @param name
+ * The name of the property to return
+ * @return The value of the property, or {@code null} if there is no value
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public String getProperty(Identity identity, String name) throws PluginException {
+ @SuppressWarnings("hiding")
+ Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetProperty").put("Identity", identity.getId()).put("Property", name).get());
+ return reply.getFields().get("Property");
+ }
+
+ /**
+ * Sets the property with the given name to the given value.
+ *
+ * @param ownIdentity
+ * The identity to set the property on
+ * @param name
+ * The name of the property to set
+ * @param value
+ * The value to set
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public void setProperty(OwnIdentity ownIdentity, String name, String value) throws PluginException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "SetProperty").put("Identity", ownIdentity.getId()).put("Property", name).put("Value", value).get());
+ }
+
+ /**
+ * Removes the property with the given name.
+ *
+ * @param ownIdentity
+ * The identity to remove the property from
+ * @param name
+ * The name of the property to remove
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public void removeProperty(OwnIdentity ownIdentity, String name) throws PluginException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveProperty").put("Identity", ownIdentity.getId()).put("Property", name).get());
+ }
+
+ /**
+ * Returns the trust for the given identity assigned to it by the given own
+ * identity.
+ *
+ * @param ownIdentity
+ * The own identity
+ * @param identity
+ * The identity to get the trust for
+ * @return The trust for the given identity
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public Trust getTrust(OwnIdentity ownIdentity, Identity identity) throws PluginException {
+ Reply getTrustReply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentity").put("Truster", ownIdentity.getId()).put("Identity", identity.getId()).get());
+ String trust = getTrustReply.getFields().get("Trust");
+ String score = getTrustReply.getFields().get("Score");
+ String rank = getTrustReply.getFields().get("Rank");
+ Integer explicit = null;
+ Integer implicit = null;
+ Integer distance = null;
+ try {
+ explicit = Integer.valueOf(trust);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ try {
+ implicit = Integer.valueOf(score);
+ distance = Integer.valueOf(rank);
+ } catch (NumberFormatException nfe1) {
+ /* ignore. */
+ }
+ return new Trust(explicit, implicit, distance);
+ }
+
+ /**
+ * Sets the trust for the given identity.
+ *
+ * @param ownIdentity
+ * The trusting identity
+ * @param identity
+ * The trusted identity
+ * @param trust
+ * The amount of trust (-100 thru 100)
+ * @param comment
+ * The comment or explanation of the trust value
+ * @throws PluginException
+ * if an error occured talking to the Web of Trust plugin
+ */
+ public void setTrust(OwnIdentity ownIdentity, Identity identity, int trust, String comment) throws PluginException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "SetTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).put("Value", String.valueOf(trust)).put("Comment", comment).get());
+ }
+
+ /**
+ * Removes any trust assignment of the given own identity for the given
+ * identity.
+ *
+ * @param ownIdentity
+ * The own identity
+ * @param identity
+ * The identity to remove all trust for
+ * @throws WebOfTrustException
+ * if an error occurs
+ */
+ public void removeTrust(OwnIdentity ownIdentity, Identity identity) throws WebOfTrustException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).get());
+ }
+
+ /**
+ * Pings the Web of Trust plugin. If the plugin can not be reached, a
+ * {@link PluginException} is thrown.
+ *
+ * @throws PluginException
+ * if the plugin is not loaded
+ */
+ public void ping() throws PluginException {
+ performRequest(SimpleFieldSetConstructor.create().put("Message", "Ping").get());
+ }
+
+ //
+ // PRIVATE ACTIONS
+ //
+
+ /**
+ * Parses the contexts from the given fields.
+ *
+ * @param prefix
+ * The prefix to use to access the contexts
+ * @param fields
+ * The fields to parse the contexts from
+ * @return The parsed contexts
+ */
+ private Set<String> parseContexts(String prefix, SimpleFieldSet fields) {
+ Set<String> contexts = new HashSet<String>();
+ int contextCounter = -1;
+ while (true) {
+ String context = fields.get(prefix + "Context" + ++contextCounter);
+ if (context == null) {
+ break;
+ }
+ contexts.add(context);
+ }
+ return contexts;
+ }
+
+ /**
+ * Parses the properties from the given fields.
+ *
+ * @param prefix
+ * The prefix to use to access the properties
+ * @param fields
+ * The fields to parse the properties from
+ * @return The parsed properties
+ */
+ private Map<String, String> parseProperties(String prefix, SimpleFieldSet fields) {
+ Map<String, String> properties = new HashMap<String, String>();
+ int propertiesCounter = -1;
+ while (true) {
+ String propertyName = fields.get(prefix + "Property" + ++propertiesCounter + ".Name");
+ if (propertyName == null) {
+ break;
+ }
+ String propertyValue = fields.get(prefix + "Property" + propertiesCounter + ".Value");
+ properties.put(propertyName, propertyValue);
+ }
+ return properties;
+ }
+
+ /**
+ * Sends a request containing the given fields and waits for the target
+ * message.
+ *
+ * @param fields
+ * The fields of the message
+ * @return The reply message
+ * @throws PluginException
+ * if the request could not be sent
+ */
+ private Reply performRequest(SimpleFieldSet fields) throws PluginException {
+ return performRequest(fields, null);
+ }
+
+ /**
+ * Sends a request containing the given fields and waits for the target
+ * message.
+ *
+ * @param fields
+ * The fields of the message
+ * @param data
+ * The payload of the message
+ * @return The reply message
+ * @throws PluginException
+ * if the request could not be sent
+ */
+ private synchronized Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException {
+ reply = new Reply();
+ logger.log(Level.FINE, "Sending FCP Request: " + fields.get("Message"));
+ synchronized (reply) {
+ pluginConnector.sendRequest(WOT_PLUGIN_NAME, PLUGIN_CONNECTION_IDENTIFIER, fields, data);
+ try {
+ reply.wait();
+ } catch (InterruptedException ie1) {
+ logger.log(Level.WARNING, "Got interrupted while waiting for reply on " + fields.get("Message") + ".", ie1);
+ }
+ }
+ logger.log(Level.FINEST, "Received FCP Response for %s: %s", new Object[] { fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null });
+ if ((reply.getFields() == null) || "Error".equals(reply.getFields().get("Message"))) {
+ throw new PluginException("Could not perform request for " + fields.get("Message"));
+ }
+ return reply;
+ }
+
+ //
+ // INTERFACE ConnectorListener
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void receivedReply(@SuppressWarnings("hiding") PluginConnector pluginConnector, SimpleFieldSet fields, Bucket data) {
+ String messageName = fields.get("Message");
+ logger.log(Level.FINEST, "Received Reply from Plugin: " + messageName);
+ synchronized (reply) {
+ reply.setFields(fields);
+ reply.setData(data);
+ reply.notify();
+ }
+ }
+
+ /**
+ * Container for the data of the reply from a plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ private static class Reply {
+
+ /** The fields of the reply. */
+ private SimpleFieldSet fields;
+
+ /** The payload of the reply. */
+ private Bucket data;
+
+ /** Empty constructor. */
+ public Reply() {
+ /* do nothing. */
+ }
+
+ /**
+ * Returns the fields of the reply.
+ *
+ * @return The fields of the reply
+ */
+ public SimpleFieldSet getFields() {
+ return fields;
+ }
+
+ /**
+ * Sets the fields of the reply.
+ *
+ * @param fields
+ * The fields of the reply
+ */
+ public void setFields(SimpleFieldSet fields) {
+ this.fields = fields;
+ }
+
+ /**
+ * Returns the payload of the reply.
+ *
+ * @return The payload of the reply (may be {@code null})
+ */
+ @SuppressWarnings("unused")
+ public Bucket getData() {
+ return data;
+ }
+
+ /**
+ * Sets the payload of the reply.
+ *
+ * @param data
+ * The payload of the reply (may be {@code null})
+ */
+ public void setData(Bucket data) {
+ this.data = data;
+ }
+
+ }
+
+ /**
+ * Helper method to create {@link SimpleFieldSet}s with terser code.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ private static class SimpleFieldSetConstructor {
+
+ /** The field set being created. */
+ private SimpleFieldSet simpleFieldSet;
+
+ /**
+ * Creates a new simple field set constructor.
+ *
+ * @param shortLived
+ * {@code true} if the resulting simple field set should be
+ * short-lived, {@code false} otherwise
+ */
+ private SimpleFieldSetConstructor(boolean shortLived) {
+ simpleFieldSet = new SimpleFieldSet(shortLived);
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the created simple field set.
+ *
+ * @return The created simple field set
+ */
+ public SimpleFieldSet get() {
+ return simpleFieldSet;
+ }
+
+ /**
+ * Sets the field with the given name to the given value.
+ *
+ * @param name
+ * The name of the fleld
+ * @param value
+ * The value of the field
+ * @return This constructor (for method chaining)
+ */
+ public SimpleFieldSetConstructor put(String name, String value) {
+ simpleFieldSet.putOverwrite(name, value);
+ return this;
+ }
+
+ //
+ // ACTIONS
+ //
+
+ /**
+ * Creates a new simple field set constructor.
+ *
+ * @return The created simple field set constructor
+ */
+ public static SimpleFieldSetConstructor create() {
+ return create(true);
+ }
+
+ /**
+ * Creates a new simple field set constructor.
+ *
+ * @param shortLived
+ * {@code true} if the resulting simple field set should be
+ * short-lived, {@code false} otherwise
+ * @return The created simple field set constructor
+ */
+ public static SimpleFieldSetConstructor create(boolean shortLived) {
+ SimpleFieldSetConstructor simpleFieldSetConstructor = new SimpleFieldSetConstructor(shortLived);
+ return simpleFieldSetConstructor;
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Sone - WebOfTrustException.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.freenet.wot;
+
+/**
+ * Exception that signals an error processing web of trust identities, mostly
+ * when communicating with the web of trust plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class WebOfTrustException extends Exception {
+
+ /**
+ * Creates a new web of trust exception.
+ */
+ public WebOfTrustException() {
+ super();
+ }
+
+ /**
+ * Creates a new web of trust exception.
+ *
+ * @param message
+ * The message of the exception
+ */
+ public WebOfTrustException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new web of trust exception.
+ *
+ * @param cause
+ * The cause of the exception
+ */
+ public WebOfTrustException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new web of trust exception.
+ *
+ * @param message
+ * The message of the exception
+ * @param cause
+ * The cause of the exception
+ */
+ public WebOfTrustException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - IdentityTargets.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.main;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import net.pterodactylus.wotns.freenet.wot.Identity;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class IdentityTargets implements Iterable<Entry<String, String>> {
+
+ private final Identity identity;
+
+ private final Map<String, String> targets = new HashMap<String, String>();
+
+ public IdentityTargets(Identity identity) {
+ this.identity = identity;
+ }
+
+ public Map<String, String> getTargets() {
+ scanForTargets();
+ return Collections.unmodifiableMap(targets);
+ }
+
+ public String getTarget(String name) {
+ scanForTargets();
+ return targets.get(name);
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ private void scanForTargets() {
+ synchronized (targets) {
+ for (Entry<String, String> property : identity.getProperties().entrySet()) {
+ if (property.getKey().startsWith("tns.")) {
+ targets.put(property.getKey().substring(4), property.getValue());
+ }
+ }
+ }
+ }
+
+ //
+ // ITERABLE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator<Entry<String, String>> iterator() {
+ return targets.entrySet().iterator();
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - Resolver.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.main;
+
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+import net.pterodactylus.util.collection.Default;
+import net.pterodactylus.wotns.freenet.wot.Identity;
+import net.pterodactylus.wotns.freenet.wot.IdentityManager;
+import net.pterodactylus.wotns.freenet.wot.OwnIdentity;
+import net.pterodactylus.wotns.freenet.wot.Trust;
+import freenet.keys.FreenetURI;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Resolver {
+
+ private final IdentityManager identityManager;
+
+ private OwnIdentity ownIdentity;
+
+ public Resolver(IdentityManager identityManager) {
+ this.identityManager = identityManager;
+ }
+
+ public void setOwnIdentity(OwnIdentity ownIdentity) {
+ this.ownIdentity = ownIdentity;
+ }
+
+ //
+ // ACTIONS
+ //
+
+ public FreenetURI resolveURI(String shortUri) throws MalformedURLException {
+ int firstSlash = shortUri.indexOf('/');
+ if (firstSlash == -1) {
+ throw new MalformedURLException("At least one slash is required.");
+ }
+ String shortName = shortUri.substring(0, firstSlash);
+ String target = shortUri.substring(firstSlash + 1);
+ Identity identity = locateIdentity(shortName);
+ System.out.println("located identity: " + identity);
+ if (identity == null) {
+ return null;
+ }
+ return new FreenetURI(identity.getProperty("tns." + target));
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ private Identity locateIdentity(String shortName) {
+ int atSign = shortName.indexOf('@');
+ String identityName = shortName;
+ String keyStart = "";
+ if (atSign > -1) {
+ identityName = shortName.substring(0, atSign);
+ keyStart = shortName.substring(atSign + 1);
+ }
+ @SuppressWarnings("hiding")
+ final OwnIdentity ownIdentity;
+ if (this.ownIdentity == null) {
+ Set<OwnIdentity> ownIdentities = identityManager.getAllOwnIdentities();
+ if (!ownIdentities.isEmpty()) {
+ ownIdentity = ownIdentities.iterator().next();
+ } else {
+ ownIdentity = null;
+ }
+ } else {
+ ownIdentity = this.ownIdentity;
+ }
+ if (ownIdentity == null) {
+ return null;
+ }
+ System.out.println("using own identity " + ownIdentity + " to resolve " + shortName);
+ Set<Identity> trustedIdentities = Default.forNull(identityManager.getTrustedIdentities(ownIdentity), Collections.<Identity>emptySet());
+ List<Identity> matchingIdentities = new ArrayList<Identity>();
+ System.out.println("checking " + trustedIdentities);
+ for (Identity identity : trustedIdentities) {
+ if (identity.getNickname().equals(identityName) && identity.getId().startsWith(keyStart)) {
+ matchingIdentities.add(identity);
+ }
+ }
+ if (matchingIdentities.isEmpty()) {
+ return null;
+ }
+ Collections.sort(matchingIdentities, new Comparator<Identity>() {
+
+ @Override
+ public int compare(Identity leftIdentity, Identity rightIdentity) {
+ Trust leftTrust = leftIdentity.getTrust(ownIdentity);
+ Trust rightTrust = rightIdentity.getTrust(ownIdentity);
+ int leftTrustCombined = ((leftTrust.getExplicit() != null) ? leftTrust.getExplicit() : 0) + ((leftTrust.getImplicit() != null) ? leftTrust.getImplicit() : 0);
+ int rightTrustCombined = ((rightTrust.getExplicit() != null) ? rightTrust.getExplicit() : 0) + ((rightTrust.getImplicit() != null) ? rightTrust.getImplicit() : 0);
+ return leftTrustCombined - rightTrustCombined;
+ }
+ });
+ return matchingIdentities.get(0);
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - WoTNSPlugin.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.main;
+
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.version.Version;
+import net.pterodactylus.wotns.freenet.plugin.PluginConnector;
+import net.pterodactylus.wotns.freenet.wot.IdentityManager;
+import net.pterodactylus.wotns.freenet.wot.OwnIdentity;
+import net.pterodactylus.wotns.freenet.wot.WebOfTrustConnector;
+import net.pterodactylus.wotns.ui.web.WebInterface;
+import freenet.client.HighLevelSimpleClient;
+import freenet.clients.http.ToadletContainer;
+import freenet.l10n.PluginL10n;
+import freenet.l10n.BaseL10n.LANGUAGE;
+import freenet.pluginmanager.FredPlugin;
+import freenet.pluginmanager.FredPluginBaseL10n;
+import freenet.pluginmanager.FredPluginL10n;
+import freenet.pluginmanager.FredPluginThreadless;
+import freenet.pluginmanager.FredPluginVersioned;
+import freenet.pluginmanager.PluginRespirator;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class WoTNSPlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10n, FredPluginVersioned, FredPluginThreadless {
+
+ static {
+ Logging.setup("WoTNS");
+ Logging.setupConsoleLogging();
+ }
+
+ private static final Version VERSION = new Version(0, 1);
+
+ private PluginRespirator pluginRespirator;
+
+ private PluginL10n l10n;
+
+ private WebInterface webInterface;
+
+ private Resolver resolver;
+
+ private WebOfTrustConnector webOfTrustConnector;
+
+ private IdentityManager identityManager;
+
+ //
+ // ACCESSORS
+ //
+
+ public HighLevelSimpleClient getHighLevelSimpleClient() {
+ return pluginRespirator.getHLSimpleClient();
+ }
+
+ public ToadletContainer getToadletContainer() {
+ return pluginRespirator.getToadletContainer();
+ }
+
+ public IdentityManager getIdentityManager() {
+ return identityManager;
+ }
+
+ public Resolver getResolver() {
+ return resolver;
+ }
+
+ //
+ // FREDPLUGIN METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void runPlugin(PluginRespirator pluginRespirator) {
+ this.pluginRespirator = pluginRespirator;
+
+ PluginConnector pluginConnector = new PluginConnector(pluginRespirator);
+ webOfTrustConnector = new WebOfTrustConnector(pluginConnector);
+ identityManager = new IdentityManager(webOfTrustConnector);
+// identityManager.setContext("WoTNS");
+ identityManager.start();
+
+ resolver = new Resolver(identityManager);
+ OwnIdentity bombeIdentity = identityManager.getOwnIdentity("e3myoFyp5avg6WYN16ImHri6J7Nj8980Fm~aQe4EX1U");
+ resolver.setOwnIdentity(bombeIdentity);
+
+ webInterface = new WebInterface(this);
+
+ webInterface.start();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void terminate() {
+ identityManager.stop();
+ webOfTrustConnector.stop();
+ webInterface.stop();
+ Logging.shutdown();
+ }
+
+ //
+ // FREDPLUGINL10N METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getString(String key) {
+ return l10n.getBase().getString(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setLanguage(LANGUAGE newLanguage) {
+ l10n = new PluginL10n(this, newLanguage);
+ }
+
+ //
+ // FREDPLUGINBASEL10N METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getL10nFilesBasePath() {
+ return "i18n";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getL10nFilesMask() {
+ return "WoTNS.${lang}.properties";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getL10nOverrideFilesMask() {
+ return "WoTNS.${lang}.override.properties";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ClassLoader getPluginClassLoader() {
+ return WoTNSPlugin.class.getClassLoader();
+ }
+
+ //
+ // FREDPLUGINVERSIONED METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getVersion() {
+ return VERSION.toString();
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - IdentityAccessor.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.template;
+
+import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.wotns.freenet.wot.Identity;
+import net.pterodactylus.wotns.main.IdentityTargets;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class IdentityAccessor extends ReflectionAccessor {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object get(TemplateContext templateContext, Object object, String member) {
+ Identity identity = (Identity) object;
+ if ("targets".equals(member)) {
+ return new IdentityTargets(identity).getTargets();
+ }
+ return super.get(templateContext, object, member);
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - AddTargetPage.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.ui.web;
+
+import java.net.MalformedURLException;
+
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.wotns.freenet.wot.OwnIdentity;
+import net.pterodactylus.wotns.freenet.wot.WebOfTrustException;
+import net.pterodactylus.wotns.web.FreenetRequest;
+import freenet.keys.FreenetURI;
+
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class AddTargetPage extends BasicPage {
+
+ /**
+ * @param webInterface
+ * @param path
+ * @param template
+ */
+ public AddTargetPage(Template template, WebInterface webInterface) {
+ super(webInterface, "addTarget.html", template);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ String ownIdentityId = request.getHttpRequest().getPartAsStringFailsafe("ownIdentity", 43);
+ OwnIdentity ownIdentity = identityManager.getOwnIdentity(ownIdentityId);
+ if (ownIdentity == null) {
+ /* TODO - show error. */
+ return;
+ }
+ String name = request.getHttpRequest().getPartAsStringFailsafe("name", 64).trim();
+ if (name.length() == 0) {
+ /* TODO - show error. */
+ return;
+ }
+ String target = request.getHttpRequest().getPartAsStringFailsafe("target", 256).trim();
+ if (name.length() == 0) {
+ /* TODO - show error. */
+ return;
+ }
+ try {
+ new FreenetURI(target);
+ ownIdentity.setProperty("tns." + name, target);
+ throw new RedirectException("manage.html?ownIdentity=" + ownIdentityId);
+ } catch (MalformedURLException mue1) {
+ /* TODO - show error. */
+ } catch (WebOfTrustException wote1) {
+ /* TODO - show error. */
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - BasicPage.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.ui.web;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.wotns.freenet.wot.IdentityManager;
+import net.pterodactylus.wotns.freenet.wot.OwnIdentity;
+import net.pterodactylus.wotns.web.FreenetRequest;
+import net.pterodactylus.wotns.web.FreenetTemplatePage;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class BasicPage extends FreenetTemplatePage {
+
+ protected final WebInterface webInterface;
+ protected final IdentityManager identityManager;
+
+ public BasicPage(WebInterface webInterface, String path, Template template) {
+ super(path, webInterface.getTemplateContextFactory(), template, "noPermission.html");
+ this.webInterface = webInterface;
+ this.identityManager = webInterface.getWoTNSPlugin().getIdentityManager();
+ }
+
+ //
+ // PROTECTED METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+
+ List<OwnIdentity> ownIdentities = new ArrayList<OwnIdentity>(webInterface.getWoTNSPlugin().getIdentityManager().getAllOwnIdentities());
+ Collections.sort(ownIdentities, new Comparator<OwnIdentity>() {
+
+ @Override
+ public int compare(OwnIdentity leftOwnIdentity, OwnIdentity rightOwnIdentity) {
+ return leftOwnIdentity.getNickname().compareTo(rightOwnIdentity.getNickname());
+ }
+ });
+
+ templateContext.set("ownIdentities", ownIdentities);
+ templateContext.set("formPassword", webInterface.getWoTNSPlugin().getToadletContainer().getFormPassword());
+ }
+}
--- /dev/null
+/*
+ * WoTNS - IndexPage.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.ui.web;
+
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.wotns.web.FreenetRequest;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class IndexPage extends BasicPage {
+
+ /**
+ * @param path
+ * @param contentType
+ * @param templateContextFactory
+ * @param template
+ */
+ public IndexPage(Template template, WebInterface webInterface) {
+ super(webInterface, "index.html", template);
+ }
+
+ //
+ // FREENETTEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - ManagePage.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.ui.web;
+
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.wotns.freenet.wot.OwnIdentity;
+import net.pterodactylus.wotns.web.FreenetRequest;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ManagePage extends BasicPage {
+
+ /**
+ * @param webInterface
+ * @param path
+ * @param template
+ */
+ public ManagePage(Template template, WebInterface webInterface) {
+ super(webInterface, "manage.html", template);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ super.processTemplate(request, templateContext);
+ if (request.getMethod() == Method.POST) {
+ }
+ String ownIdentityId = request.getHttpRequest().getParam("ownIdentity");
+ OwnIdentity ownIdentity = webInterface.getWoTNSPlugin().getIdentityManager().getOwnIdentity(ownIdentityId);
+ templateContext.set("ownIdentity", ownIdentity);
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - ResolverPage.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.ui.web;
+
+import java.net.MalformedURLException;
+
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.wotns.main.Resolver;
+import net.pterodactylus.wotns.web.FreenetRequest;
+import freenet.keys.FreenetURI;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ResolverPage extends BasicPage {
+
+ private final Resolver resolver;
+
+ public ResolverPage(Template unknownNameTemplate, WebInterface webInterface, Resolver resolver) {
+ super(webInterface, "", unknownNameTemplate);
+ this.resolver = resolver;
+ }
+
+ //
+ // PAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isPrefixPage() {
+ return true;
+ }
+
+ //
+ // FREENETTEMPLATEPAGE METHODS
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ String uri = request.getUri().getPath();
+ String path = uri.substring(uri.indexOf('/', 1) + 1);
+ FreenetURI targetUri;
+ try {
+ targetUri = resolver.resolveURI(path);
+ if (targetUri != null) {
+ throw new RedirectException("/" + targetUri.toString());
+ }
+ } catch (MalformedURLException mue1) {
+ /* TODO - do something. */
+ }
+ templateContext.set("shortName", path);
+ }
+
+}
--- /dev/null
+/*
+ * WoTNS - WebInterface.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.ui.web;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.pterodactylus.util.template.HtmlFilter;
+import net.pterodactylus.util.template.ReflectionAccessor;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContextFactory;
+import net.pterodactylus.util.template.TemplateParser;
+import net.pterodactylus.wotns.freenet.wot.Identity;
+import net.pterodactylus.wotns.main.WoTNSPlugin;
+import net.pterodactylus.wotns.template.IdentityAccessor;
+import net.pterodactylus.wotns.web.PageToadlet;
+import net.pterodactylus.wotns.web.PageToadletFactory;
+import freenet.clients.http.ToadletContainer;
+
+/**
+ * TODO
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class WebInterface {
+
+ private final WoTNSPlugin wotNSPlugin;
+
+ private final TemplateContextFactory templateContextFactory = new TemplateContextFactory();
+
+ /** The registered toadlets. */
+ private final List<PageToadlet> pageToadlets = new ArrayList<PageToadlet>();
+
+ public WebInterface(WoTNSPlugin woTNSPlugin) {
+ this.wotNSPlugin = woTNSPlugin;
+
+ templateContextFactory.addAccessor(Object.class, new ReflectionAccessor());
+ templateContextFactory.addAccessor(Identity.class, new IdentityAccessor());
+ templateContextFactory.addFilter("html", new HtmlFilter());
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ public WoTNSPlugin getWoTNSPlugin() {
+ return wotNSPlugin;
+ }
+
+ public TemplateContextFactory getTemplateContextFactory() {
+ return templateContextFactory;
+ }
+
+ //
+ // ACTIONS
+ //
+
+ public void start() {
+ registerToadlets();
+ }
+
+ public void stop() {
+ unregisterToadlets();
+ }
+
+ //
+ // PRIVATE METHODS
+ //
+
+ private void registerToadlets() {
+ Template indexTemplate = TemplateParser.parse(createReader("/templates/index.html"));
+ Template unknownTemplate = TemplateParser.parse(createReader("/templates/unknown.html"));
+ Template manageTemplate = TemplateParser.parse(createReader("/templates/manage.html"));
+ Template addTargetTemplate = TemplateParser.parse(createReader("/templates/addTarget.html"));
+
+ PageToadletFactory pageToadletFactory = new PageToadletFactory(wotNSPlugin.getHighLevelSimpleClient(), "/tns/");
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new ResolverPage(unknownTemplate, this, wotNSPlugin.getResolver())));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new IndexPage(indexTemplate, this), "Index"));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new ManagePage(manageTemplate, this)));
+ pageToadlets.add(pageToadletFactory.createPageToadlet(new AddTargetPage(addTargetTemplate, this)));
+
+ ToadletContainer toadletContainer = wotNSPlugin.getToadletContainer();
+ toadletContainer.getPageMaker().addNavigationCategory("/tns/index.html", "Navigation.Menu.Name", "Navigation.Menu.Tooltip", wotNSPlugin);
+ for (PageToadlet toadlet : pageToadlets) {
+ String menuName = toadlet.getMenuName();
+ if (menuName != null) {
+ toadletContainer.register(toadlet, "Navigation.Menu.Name", toadlet.path(), true, "Navigation.Menu.Item." + menuName + ".Name", "Navigation.Menu.Item." + menuName + ".Tooltip", false, toadlet);
+ } else {
+ toadletContainer.register(toadlet, null, toadlet.path(), true, false);
+ }
+ }
+ }
+
+ /**
+ * Unregisters all toadlets.
+ */
+ private void unregisterToadlets() {
+ ToadletContainer toadletContainer = wotNSPlugin.getToadletContainer();
+ for (PageToadlet pageToadlet : pageToadlets) {
+ toadletContainer.unregister(pageToadlet);
+ }
+ toadletContainer.getPageMaker().removeNavigationCategory("Navigation.Menu.Name");
+ }
+
+ /**
+ * Creates a {@link Reader} from the {@link InputStream} for the resource
+ * with the given name.
+ *
+ * @param resourceName
+ * The name of the resource
+ * @return A {@link Reader} for the resource
+ */
+ private Reader createReader(String resourceName) {
+ try {
+ return new InputStreamReader(getClass().getResourceAsStream(resourceName), "UTF-8");
+ } catch (UnsupportedEncodingException uee1) {
+ return null;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - FreenetRequest.java - Copyright © 2011 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.web;
+
+import java.net.URI;
+
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.util.web.Request;
+import freenet.clients.http.ToadletContext;
+import freenet.support.api.HTTPRequest;
+
+/**
+ * Encapsulates all Freenet-specific properties of a request.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FreenetRequest extends Request {
+
+ /** The underlying HTTP request from Freenet. */
+ private final HTTPRequest httpRequest;
+
+ /** The toadlet context. */
+ private final ToadletContext toadletContext;
+
+ /**
+ * Creates a new freenet request.
+ *
+ * @param uri
+ * The URI that is being accessed
+ * @param method
+ * The method used to access this page
+ * @param httpRequest
+ * The underlying HTTP request from Freenet
+ * @param toadletContext
+ * The toadlet context
+ */
+ public FreenetRequest(URI uri, Method method, HTTPRequest httpRequest, ToadletContext toadletContext) {
+ super(uri, method);
+ this.httpRequest = httpRequest;
+ this.toadletContext = toadletContext;
+ }
+
+ //
+ // ACCESSORS
+ //
+
+ /**
+ * Returns the underlying HTTP request from Freenet.
+ *
+ * @return The underlying HTTP request from Freenet
+ */
+ public HTTPRequest getHttpRequest() {
+ return httpRequest;
+ }
+
+ /**
+ * Returns the toadlet context.
+ *
+ * @return The toadlet context
+ */
+ public ToadletContext getToadletContext() {
+ return toadletContext;
+ }
+
+}
--- /dev/null
+/*
+ * Sone - FreenetTemplatePage.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.web;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.template.TemplateContextFactory;
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.RedirectResponse;
+import net.pterodactylus.util.web.Response;
+import freenet.clients.http.LinkEnabledCallback;
+import freenet.clients.http.PageMaker;
+import freenet.clients.http.PageNode;
+import freenet.clients.http.ToadletContext;
+import freenet.support.HTMLNode;
+
+/**
+ * Base class for all {@link Page}s that are rendered with {@link Template}s and
+ * fit into Freenet’s web interface.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FreenetTemplatePage implements Page<FreenetRequest>, LinkEnabledCallback {
+
+ /** The logger. */
+ private static final Logger logger = Logging.getLogger(FreenetTemplatePage.class);
+
+ /** The path of the page. */
+ private final String path;
+
+ /** The template context factory. */
+ private final TemplateContextFactory templateContextFactory;
+
+ /** The template to render. */
+ private final Template template;
+
+ /** Where to redirect for invalid form passwords. */
+ private final String invalidFormPasswordRedirectTarget;
+
+ /**
+ * Creates a new template page.
+ *
+ * @param path
+ * The path of the page
+ * @param templateContextFactory
+ * The template context factory
+ * @param template
+ * The template to render
+ * @param invalidFormPasswordRedirectTarget
+ * The target to redirect to if a POST request does not contain
+ * the correct form password
+ */
+ public FreenetTemplatePage(String path, TemplateContextFactory templateContextFactory, Template template, String invalidFormPasswordRedirectTarget) {
+ this.path = path;
+ this.templateContextFactory = templateContextFactory;
+ this.template = template;
+ this.invalidFormPasswordRedirectTarget = invalidFormPasswordRedirectTarget;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Returns the title of the page.
+ *
+ * @param request
+ * The request to serve
+ * @return The title of the page
+ */
+ protected String getPageTitle(FreenetRequest request) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isPrefixPage() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Response handleRequest(FreenetRequest request, Response response) throws IOException {
+ String redirectTarget = getRedirectTarget(request);
+ if (redirectTarget != null) {
+ return new RedirectResponse(redirectTarget);
+ }
+
+ if (isFullAccessOnly() && !request.getToadletContext().isAllowedFullAccess()) {
+ return response.setStatusCode(401).setStatusText("Not authorized").setContentType("text/html");
+ }
+ ToadletContext toadletContext = request.getToadletContext();
+ if (request.getMethod() == Method.POST) {
+ /* require form password. */
+ String formPassword = request.getHttpRequest().getPartAsStringFailsafe("formPassword", 32);
+ if (!formPassword.equals(toadletContext.getContainer().getFormPassword())) {
+ return new RedirectResponse(invalidFormPasswordRedirectTarget);
+ }
+ }
+ PageMaker pageMaker = toadletContext.getPageMaker();
+ PageNode pageNode = pageMaker.getPageNode(getPageTitle(request), toadletContext);
+ for (String styleSheet : getStyleSheets()) {
+ pageNode.addCustomStyleSheet(styleSheet);
+ }
+ for (Map<String, String> linkNodeParameters : getAdditionalLinkNodes(request)) {
+ HTMLNode linkNode = pageNode.headNode.addChild("link");
+ for (Entry<String, String> parameter : linkNodeParameters.entrySet()) {
+ linkNode.addAttribute(parameter.getKey(), parameter.getValue());
+ }
+ }
+ String shortcutIcon = getShortcutIcon();
+ if (shortcutIcon != null) {
+ pageNode.addForwardLink("icon", shortcutIcon);
+ }
+
+ TemplateContext templateContext = templateContextFactory.createTemplateContext();
+ templateContext.mergeContext(template.getInitialContext());
+ try {
+ long start = System.nanoTime();
+ processTemplate(request, templateContext);
+ long finish = System.nanoTime();
+ logger.log(Level.FINEST, "Template was rendered in " + ((finish - start) / 1000) / 1000.0 + "ms.");
+ } catch (RedirectException re1) {
+ return new RedirectResponse(re1.getTarget());
+ }
+
+ StringWriter stringWriter = new StringWriter();
+ template.render(templateContext, stringWriter);
+ pageNode.content.addChild("%", stringWriter.toString());
+
+ postProcess(request, templateContext);
+
+ return response.setStatusCode(200).setStatusText("OK").setContentType("text/html").write(pageNode.outer.generate());
+ }
+
+ /**
+ * Can be overridden to return a custom set of style sheets that are to be
+ * included in the page’s header.
+ *
+ * @return Additional style sheets to load
+ */
+ protected Collection<String> getStyleSheets() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * Returns the name of the shortcut icon to include in the page’s header.
+ *
+ * @return The URL of the shortcut icon, or {@code null} for no icon
+ */
+ protected String getShortcutIcon() {
+ return null;
+ }
+
+ /**
+ * Can be overridden when extending classes need to set variables in the
+ * template before it is rendered.
+ *
+ * @param request
+ * The request that is rendered
+ * @param templateContext
+ * The template context to set variables in
+ * @throws RedirectException
+ * if the processing page wants to redirect after processing
+ */
+ protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+ /* do nothing. */
+ }
+
+ /**
+ * This method will be called after
+ * {@link #processTemplate(FreenetRequest, TemplateContext)} has processed
+ * the template and the template was rendered. This method will not be
+ * called if {@link #processTemplate(FreenetRequest, TemplateContext)}
+ * throws a {@link RedirectException}!
+ *
+ * @param request
+ * The request being processed
+ * @param templateContext
+ * The template context that supplied the rendered data
+ */
+ protected void postProcess(FreenetRequest request, TemplateContext templateContext) {
+ /* do nothing. */
+ }
+
+ /**
+ * Can be overridden to redirect the user to a different page, in case a log
+ * in is required, or something else is wrong.
+ *
+ * @param request
+ * The request that is processed
+ * @return The URL to redirect to, or {@code null} to not redirect
+ */
+ protected String getRedirectTarget(FreenetRequest request) {
+ return null;
+ }
+
+ /**
+ * Returns additional <link> nodes for the HTML’s <head> node.
+ *
+ * @param request
+ * The request for which to return the link nodes
+ * @return All link nodes that should be added to the HTML head
+ */
+ protected List<Map<String, String>> getAdditionalLinkNodes(FreenetRequest request) {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns whether this page should only be allowed for requests from hosts
+ * with full access.
+ *
+ * @return {@code true} if this page should only be allowed for hosts with
+ * full access, {@code false} to allow this page for any host
+ */
+ protected boolean isFullAccessOnly() {
+ return false;
+ }
+
+ //
+ // INTERFACE LinkEnabledCallback
+ //
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isEnabled(ToadletContext toadletContext) {
+ return !isFullAccessOnly();
+ }
+
+ /**
+ * Exception that can be thrown to signal that a subclassed {@link Page}
+ * wants to redirect the user during the
+ * {@link FreenetTemplatePage#processTemplate(FreenetRequest, TemplateContext)}
+ * method call.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+ public static class RedirectException extends Exception {
+
+ /** The target to redirect to. */
+ private final String target;
+
+ /**
+ * Creates a new redirect exception.
+ *
+ * @param target
+ * The target of the redirect
+ */
+ public RedirectException(String target) {
+ this.target = target;
+ }
+
+ /**
+ * Returns the target to redirect to.
+ *
+ * @return The target to redirect to
+ */
+ public String getTarget() {
+ return target;
+ }
+
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PageToadlet.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.web;
+
+import java.io.IOException;
+import java.net.URI;
+
+import net.pterodactylus.util.web.Header;
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Response;
+import freenet.client.HighLevelSimpleClient;
+import freenet.clients.http.LinkEnabledCallback;
+import freenet.clients.http.Toadlet;
+import freenet.clients.http.ToadletContext;
+import freenet.clients.http.ToadletContextClosedException;
+import freenet.support.MultiValueTable;
+import freenet.support.api.Bucket;
+import freenet.support.api.HTTPRequest;
+import freenet.support.io.Closer;
+
+/**
+ * {@link Toadlet} implementation that is wrapped around a {@link Page}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PageToadlet extends Toadlet implements LinkEnabledCallback {
+
+ /** The name of the menu item. */
+ private final String menuName;
+
+ /** The page that handles processing. */
+ private final Page<FreenetRequest> page;
+
+ /** The path prefix for the page. */
+ private final String pathPrefix;
+
+ /**
+ * Creates a new toadlet that hands off processing to a {@link Page}.
+ *
+ * @param highLevelSimpleClient
+ * The high-level simple client
+ * @param menuName
+ * The name of the menu item
+ * @param page
+ * The page to handle processing
+ * @param pathPrefix
+ * Prefix that is prepended to all {@link Page#getPath()} return
+ * values
+ */
+ protected PageToadlet(HighLevelSimpleClient highLevelSimpleClient, String menuName, Page<FreenetRequest> page, String pathPrefix) {
+ super(highLevelSimpleClient);
+ this.menuName = menuName;
+ this.page = page;
+ this.pathPrefix = pathPrefix;
+ }
+
+ /**
+ * Returns the name to display in the menu.
+ *
+ * @return The name in the menu
+ */
+ public String getMenuName() {
+ return menuName;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String path() {
+ return pathPrefix + page.getPath();
+ }
+
+ /**
+ * Handles a HTTP GET request.
+ *
+ * @param uri
+ * The URI of the request
+ * @param httpRequest
+ * The HTTP request
+ * @param toadletContext
+ * The toadlet context
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws ToadletContextClosedException
+ * if the toadlet context is closed
+ */
+ public void handleMethodGET(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
+ handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext));
+ }
+
+ /**
+ * Handles a HTTP POST request.
+ *
+ * @param uri
+ * The URI of the request
+ * @param httpRequest
+ * The HTTP request
+ * @param toadletContext
+ * The toadlet context
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws ToadletContextClosedException
+ * if the toadlet context is closed
+ */
+ public void handleMethodPOST(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
+ handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return getClass().getName() + "[path=" + path() + ",page=" + page + "]";
+ }
+
+ /**
+ * Handles a HTTP request.
+ *
+ * @param pageRequest
+ * The request to handle
+ * @throws IOException
+ * if an I/O error occurs
+ * @throws ToadletContextClosedException
+ * if the toadlet context is closed
+ */
+ private void handleRequest(FreenetRequest pageRequest) throws IOException, ToadletContextClosedException {
+ Bucket pageBucket = null;
+ try {
+ pageBucket = pageRequest.getToadletContext().getBucketFactory().makeBucket(-1);
+ Response pageResponse = new Response(pageBucket.getOutputStream());
+ pageResponse = page.handleRequest(pageRequest, pageResponse);
+ MultiValueTable<String, String> headers = new MultiValueTable<String, String>();
+ if (pageResponse.getHeaders() != null) {
+ for (Header header : pageResponse.getHeaders()) {
+ for (String value : header) {
+ headers.put(header.getName(), value);
+ }
+ }
+ }
+ writeReply(pageRequest.getToadletContext(), pageResponse.getStatusCode(), pageResponse.getContentType(), pageResponse.getStatusText(), headers, pageBucket);
+ } catch (Throwable t1) {
+ writeInternalError(t1, pageRequest.getToadletContext());
+ } finally {
+ Closer.close(pageBucket);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isEnabled(ToadletContext toadletContext) {
+ if (page instanceof LinkEnabledCallback) {
+ return ((LinkEnabledCallback) page).isEnabled(toadletContext);
+ }
+ return true;
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PageToadletFactory.java - Copyright © 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.wotns.web;
+
+import net.pterodactylus.util.web.Page;
+import freenet.client.HighLevelSimpleClient;
+
+/**
+ * Factory that creates {@link PageToadlet}s using a given
+ * {@link HighLevelSimpleClient}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PageToadletFactory {
+
+ /** The client to use when creating the toadlets. */
+ private final HighLevelSimpleClient highLevelSimpleClient;
+
+ /** The prefix for all pages’ paths. */
+ private final String pathPrefix;
+
+ /**
+ * Creates a new {@link PageToadlet} factory.
+ *
+ * @param highLevelSimpleClient
+ * The client to use when creating the toadlets
+ * @param pathPrefix
+ * The path that is prepended to all pages’ paths
+ */
+ public PageToadletFactory(HighLevelSimpleClient highLevelSimpleClient, String pathPrefix) {
+ this.highLevelSimpleClient = highLevelSimpleClient;
+ this.pathPrefix = pathPrefix;
+ }
+
+ /**
+ * Creates a {@link PageToadlet} that wraps the given page and does not
+ * appear in the node’s menu.
+ *
+ * @param page
+ * The page to wrap
+ * @return The toadlet wrapped around the page
+ */
+ public PageToadlet createPageToadlet(Page<FreenetRequest> page) {
+ return createPageToadlet(page, null);
+ }
+
+ /**
+ * Creates a {@link PageToadlet} that wraps the given page and appears in
+ * the node’s menu under the given name.
+ *
+ * @param page
+ * The page to wrap
+ * @param menuName
+ * The name of the menu item
+ * @return The toadlet wrapped around the page
+ */
+ public PageToadlet createPageToadlet(Page<FreenetRequest> page, String menuName) {
+ return new PageToadlet(highLevelSimpleClient, menuName, page, pathPrefix);
+ }
+
+}
--- /dev/null
+Navigation.Menu.Name=WoTNS
+Navigation.Menu.Tooltip=Configure the Web of Trust Name Service
+Navigation.Menu.Item.Index.Name=Configure
+Navigation.Menu.Item.Index.Tooltip=Configure the Web of Trust Name Service
--- /dev/null
+<h1>Add target.</h1>
+<p>An error occured.</p>
--- /dev/null
+<h1>WoTNS</h1>
+<p>Yay, Index!</p>
+<h1>Manage Names</h1>
+<form method="get" action="manage.html">
+ <select name="ownIdentity">
+ <option disabled="true">Choose identity…</option>
+ <%foreach ownIdentities ownIdentity>
+ <option value="<%ownIdentity.id|html>"><%ownIdentity.nickname|html></option>
+ <%/foreach>
+ </select>
+ <button type="submit" name="manage">Manage</button>
+</form>
--- /dev/null
+<h1>Manage “<%ownIdentity.nickname|html>”</h1>
+<%foreach ownIdentity.targets target>
+ <%first>
+ <h2>Existing Targets</h2>
+ <%/first>
+ <form action="editTarget.html" method="post">
+ <input type="hidden" name="formPassword" value="<%formPassword|html>"/>
+ <input type="hidden" name="name" value="<%target.key|html>"/>
+ <%target.key|html>:
+ <input type="text" name="target" value="<%target.value|html>"/>
+ <button type="submit" name="edit">Save</button>
+ <button type="submit" name="delete">Delete</button>
+ </form>
+<%/foreach>
+<h2>Add Target</h2>
+<form action="addTarget.html" method="post">
+ <input type="hidden" name="formPassword" value="<%formPassword|html>"/>
+ <input type="hidden" name="ownIdentity" value="<%ownIdentity.id|html>"/>
+ <input type="text" name="name" value=""/>
+ <input type="text" name="target" value=""/>
+ <button type="submit" name="add">Add</button>
+</form>
--- /dev/null
+<h1>Unknown name</h1>
+<p>The name “<%shortName|html>” is unknown.</p>
\ No newline at end of file