From: David ‘Bombe’ Roden Date: Thu, 15 Sep 2011 13:22:47 +0000 (+0200) Subject: First extremely basic, kind-of-working version. X-Git-Tag: 0.0.1~17 X-Git-Url: https://git.pterodactylus.net/?p=WoTNS.git;a=commitdiff_plain;h=622c4a4d3ebed447d5708a41cf3e1e82e18fa29b First extremely basic, kind-of-working version. --- diff --git a/pom.xml b/pom.xml index b6d1e53..539c5ec 100644 --- a/pom.xml +++ b/pom.xml @@ -5,6 +5,11 @@ 0.1-SNAPSHOT + net.pterodactylus + utils + 0.10.1-SNAPSHOT + + junit junit 3.8.2 diff --git a/src/main/java/net/pterodactylus/wotns/freenet/L10nFilter.java b/src/main/java/net/pterodactylus/wotns/freenet/L10nFilter.java new file mode 100644 index 0000000..692b572 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/L10nFilter.java @@ -0,0 +1,73 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 parameters) { + if (parameters.isEmpty()) { + return l10n.getString(String.valueOf(data)); + } + List parameterValues = new ArrayList(); + 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()); + } +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/SimpleFieldSetBuilder.java b/src/main/java/net/pterodactylus/wotns/freenet/SimpleFieldSetBuilder.java new file mode 100644 index 0000000..1472a82 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/SimpleFieldSetBuilder.java @@ -0,0 +1,120 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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; + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/fcp/AbstractCommand.java b/src/main/java/net/pterodactylus/wotns/freenet/fcp/AbstractCommand.java new file mode 100644 index 0000000..01b103c --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/fcp/AbstractCommand.java @@ -0,0 +1,127 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/fcp/Command.java b/src/main/java/net/pterodactylus/wotns/freenet/fcp/Command.java new file mode 100644 index 0000000..4eac38c --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/fcp/Command.java @@ -0,0 +1,226 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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()); + } + + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/fcp/FcpException.java b/src/main/java/net/pterodactylus/wotns/freenet/fcp/FcpException.java new file mode 100644 index 0000000..1544919 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/fcp/FcpException.java @@ -0,0 +1,66 @@ +/* + * 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 . + */ + +package net.pterodactylus.wotns.freenet.fcp; + +/** + * Base exception for FCP communication. + * + * @author David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/plugin/ConnectorListener.java b/src/main/java/net/pterodactylus/wotns/freenet/plugin/ConnectorListener.java new file mode 100644 index 0000000..18b121d --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/plugin/ConnectorListener.java @@ -0,0 +1,50 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/plugin/ConnectorListenerManager.java b/src/main/java/net/pterodactylus/wotns/freenet/plugin/ConnectorListenerManager.java new file mode 100644 index 0000000..0a0c891 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/plugin/ConnectorListenerManager.java @@ -0,0 +1,60 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class ConnectorListenerManager extends AbstractListenerManager { + + /** + * 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); + } + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/plugin/PluginConnector.java b/src/main/java/net/pterodactylus/wotns/freenet/plugin/PluginConnector.java new file mode 100644 index 0000000..bb466f1 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/plugin/PluginConnector.java @@ -0,0 +1,203 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class PluginConnector implements FredPluginTalker { + + /** The plugin respirator. */ + private final PluginRespirator pluginRespirator; + + /** Connector listener managers for all plugin connections. */ + private final Map, ConnectorListenerManager> connectorListenerManagers = Collections.synchronizedMap(new HashMap, 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(pluginName, identifier)); + if (create && (connectorListenerManager == null)) { + connectorListenerManager = new ConnectorListenerManager(this); + connectorListenerManagers.put(new Pair(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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/plugin/PluginException.java b/src/main/java/net/pterodactylus/wotns/freenet/plugin/PluginException.java new file mode 100644 index 0000000..39d1d20 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/plugin/PluginException.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ + +package net.pterodactylus.wotns.freenet.plugin; + +import net.pterodactylus.wotns.freenet.wot.WebOfTrustException; + +/** + * Exception that signals an error when communicating with a plugin. + * + * @author David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/DefaultIdentity.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/DefaultIdentity.java new file mode 100644 index 0000000..e84ba6e --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/DefaultIdentity.java @@ -0,0 +1,308 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 contexts = Collections.synchronizedSet(new HashSet()); + + /** The properties of the identity. */ + private final Map properties = Collections.synchronizedMap(new HashMap()); + + /** Cached trust. */ + /* synchronize on itself. */ + private final WritableCache trustCache = new MemoryCache(new ValueRetriever() { + + @Override + @SuppressWarnings("synthetic-access") + public CacheItem retrieve(OwnIdentity ownIdentity) throws CacheException { + try { + return new DefaultCacheItem(webOfTrustConnector.getTrust(ownIdentity, DefaultIdentity.this)); + } catch (PluginException pe1) { + throw new CacheException("Could not retrieve trust for OwnIdentity: " + ownIdentity, pe1); + } + } + + }, new TimedMap>(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 getContexts() { + return Collections.unmodifiableSet(contexts); + } + + /** + * Sets the contexts of this identity. + *

+ * This method is only called by the {@link IdentityManager}. + * + * @param contexts + * The contexts to set + */ + void setContextsPrivate(Set 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. + *

+ * 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. + *

+ * 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 getProperties() { + synchronized (properties) { + return Collections.unmodifiableMap(properties); + } + } + + /** + * Sets all properties of this identity. + *

+ * This method is only called by the {@link IdentityManager}. + * + * @param properties + * The new properties of this identity + */ + void setPropertiesPrivate(Map properties) { + synchronized (this.properties) { + this.properties.clear(); + this.properties.putAll(properties); + } + } + + /** + * Sets the property with the given name to the given value. + *

+ * 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. + *

+ * 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 + "]"; + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/DefaultOwnIdentity.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/DefaultOwnIdentity.java new file mode 100644 index 0000000..6f322b3 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/DefaultOwnIdentity.java @@ -0,0 +1,192 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 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 properties) throws WebOfTrustException { + for (Entry 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 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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/Identity.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/Identity.java new file mode 100644 index 0000000..41e28ae --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/Identity.java @@ -0,0 +1,99 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 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 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); + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityListener.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityListener.java new file mode 100644 index 0000000..afe788e --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityListener.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ + +package net.pterodactylus.wotns.freenet.wot; + +import java.util.EventListener; + +/** + * Listener interface for {@link IdentityManager} events. + * + * @author David ‘Bombe’ Roden + */ +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); + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityListenerManager.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityListenerManager.java new file mode 100644 index 0000000..63ce4fa --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityListenerManager.java @@ -0,0 +1,113 @@ +/* + * 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 . + */ + +package net.pterodactylus.wotns.freenet.wot; + +import net.pterodactylus.util.event.AbstractListenerManager; + +/** + * Manager for {@link IdentityListener}s. + * + * @author David ‘Bombe’ Roden + */ +public class IdentityListenerManager extends AbstractListenerManager { + + /** + * 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); + } + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityManager.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityManager.java new file mode 100644 index 0000000..e9107fe --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/IdentityManager.java @@ -0,0 +1,342 @@ +/* + * 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 . + */ + +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. + *

+ * 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 David ‘Bombe’ Roden + */ +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 currentOwnIdentities = new HashMap(); + + /** The currently trusted identities. */ + private Map> currentTrustedIdentities = new HashMap>(); + + /** + * 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 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 getAllOwnIdentities() { + try { + Set ownIdentities = webOfTrustConnector.loadAllOwnIdentities(); + Map newOwnIdentities = new HashMap(); + 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 getTrustedIdentities(OwnIdentity ownIdentity) { + SetBuilder identities = new SetBuilder(); + 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> oldIdentities = Collections.emptyMap(); + while (!shouldStop()) { + Map> currentIdentities = new HashMap>(); + @SuppressWarnings("hiding") + Map currentOwnIdentities = new HashMap(); + + Set 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 trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context); + Map identities = new HashMap(); + 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 oldContexts = oldIdentity.getContexts(); + Set 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 oldProperties = oldIdentity.getProperties(); + Map newProperties = newIdentity.getProperties(); + if (oldProperties.size() != newProperties.size()) { + identityListenerManager.fireIdentityUpdated(ownIdentity, newIdentity); + continue; + } + for (Entry 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> entry : currentIdentities.entrySet()) { + Set identities = new HashSet(); + 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 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); + } + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/OwnIdentity.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/OwnIdentity.java new file mode 100644 index 0000000..b8d4d15 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/OwnIdentity.java @@ -0,0 +1,133 @@ +/* + * 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 . + */ + +package net.pterodactylus.wotns.freenet.wot; + +import java.util.Map; +import java.util.Set; + +/** + * Defines a local identity, an own identity. + * + * @author David ‘Bombe’ Roden + */ +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. + *

+ * 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. + *

+ * 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 contexts) throws WebOfTrustException; + + /** + * Removes the given context from this identity. + *

+ * 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. + *

+ * 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 properties) throws WebOfTrustException; + + /** + * Removes the property with the given name. + *

+ * 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; + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/Trust.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/Trust.java new file mode 100644 index 0000000..a8d7fc6 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/Trust.java @@ -0,0 +1,90 @@ +/* + * 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 . + */ + +package net.pterodactylus.wotns.freenet.wot; + +/** + * Container class for trust in the web of trust. + * + * @author David ‘Bombe’ Roden + */ +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 + "]"; + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/WebOfTrustConnector.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/WebOfTrustConnector.java new file mode 100644 index 0000000..8a16efc --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/WebOfTrustConnector.java @@ -0,0 +1,553 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 loadAllOwnIdentities() throws WebOfTrustException { + @SuppressWarnings("hiding") + Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get()); + SimpleFieldSet fields = reply.getFields(); + int ownIdentityCounter = -1; + Set ownIdentities = new HashSet(); + 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 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 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 identities = new HashSet(); + 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 parseContexts(String prefix, SimpleFieldSet fields) { + Set contexts = new HashSet(); + 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 parseProperties(String prefix, SimpleFieldSet fields) { + Map properties = new HashMap(); + 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 David ‘Bombe’ Roden + */ + 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 David ‘Bombe’ Roden + */ + 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; + } + + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/freenet/wot/WebOfTrustException.java b/src/main/java/net/pterodactylus/wotns/freenet/wot/WebOfTrustException.java new file mode 100644 index 0000000..345ea6a --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/freenet/wot/WebOfTrustException.java @@ -0,0 +1,67 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/main/IdentityTargets.java b/src/main/java/net/pterodactylus/wotns/main/IdentityTargets.java new file mode 100644 index 0000000..2eacd08 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/main/IdentityTargets.java @@ -0,0 +1,79 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class IdentityTargets implements Iterable> { + + private final Identity identity; + + private final Map targets = new HashMap(); + + public IdentityTargets(Identity identity) { + this.identity = identity; + } + + public Map 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 property : identity.getProperties().entrySet()) { + if (property.getKey().startsWith("tns.")) { + targets.put(property.getKey().substring(4), property.getValue()); + } + } + } + } + + // + // ITERABLE METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public Iterator> iterator() { + return targets.entrySet().iterator(); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/main/Resolver.java b/src/main/java/net/pterodactylus/wotns/main/Resolver.java new file mode 100644 index 0000000..4dacad9 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/main/Resolver.java @@ -0,0 +1,125 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 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 trustedIdentities = Default.forNull(identityManager.getTrustedIdentities(ownIdentity), Collections.emptySet()); + List matchingIdentities = new ArrayList(); + 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() { + + @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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/main/WoTNSPlugin.java b/src/main/java/net/pterodactylus/wotns/main/WoTNSPlugin.java new file mode 100644 index 0000000..f3ec6d4 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/main/WoTNSPlugin.java @@ -0,0 +1,189 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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(); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/template/IdentityAccessor.java b/src/main/java/net/pterodactylus/wotns/template/IdentityAccessor.java new file mode 100644 index 0000000..7039ef7 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/template/IdentityAccessor.java @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/ui/web/AddTargetPage.java b/src/main/java/net/pterodactylus/wotns/ui/web/AddTargetPage.java new file mode 100644 index 0000000..fb44cb5 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/ui/web/AddTargetPage.java @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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. */ + } + } + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/ui/web/BasicPage.java b/src/main/java/net/pterodactylus/wotns/ui/web/BasicPage.java new file mode 100644 index 0000000..ff5ffd2 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/ui/web/BasicPage.java @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 ownIdentities = new ArrayList(webInterface.getWoTNSPlugin().getIdentityManager().getAllOwnIdentities()); + Collections.sort(ownIdentities, new Comparator() { + + @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()); + } +} diff --git a/src/main/java/net/pterodactylus/wotns/ui/web/IndexPage.java b/src/main/java/net/pterodactylus/wotns/ui/web/IndexPage.java new file mode 100644 index 0000000..933f9a6 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/ui/web/IndexPage.java @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/ui/web/ManagePage.java b/src/main/java/net/pterodactylus/wotns/ui/web/ManagePage.java new file mode 100644 index 0000000..1681fbd --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/ui/web/ManagePage.java @@ -0,0 +1,55 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/ui/web/ResolverPage.java b/src/main/java/net/pterodactylus/wotns/ui/web/ResolverPage.java new file mode 100644 index 0000000..1a4f687 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/ui/web/ResolverPage.java @@ -0,0 +1,77 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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); + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/ui/web/WebInterface.java b/src/main/java/net/pterodactylus/wotns/ui/web/WebInterface.java new file mode 100644 index 0000000..4151531 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/ui/web/WebInterface.java @@ -0,0 +1,140 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class WebInterface { + + private final WoTNSPlugin wotNSPlugin; + + private final TemplateContextFactory templateContextFactory = new TemplateContextFactory(); + + /** The registered toadlets. */ + private final List pageToadlets = new ArrayList(); + + 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; + } + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/web/FreenetRequest.java b/src/main/java/net/pterodactylus/wotns/web/FreenetRequest.java new file mode 100644 index 0000000..bf15f5b --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/web/FreenetRequest.java @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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; + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/web/FreenetTemplatePage.java b/src/main/java/net/pterodactylus/wotns/web/FreenetTemplatePage.java new file mode 100644 index 0000000..4cc63bb --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/web/FreenetTemplatePage.java @@ -0,0 +1,301 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +public class FreenetTemplatePage implements Page, 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 linkNodeParameters : getAdditionalLinkNodes(request)) { + HTMLNode linkNode = pageNode.headNode.addChild("link"); + for (Entry 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 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> 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 David ‘Bombe’ Roden + */ + 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; + } + + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/web/PageToadlet.java b/src/main/java/net/pterodactylus/wotns/web/PageToadlet.java new file mode 100644 index 0000000..818a27c --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/web/PageToadlet.java @@ -0,0 +1,177 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 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 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 headers = new MultiValueTable(); + 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; + } + +} diff --git a/src/main/java/net/pterodactylus/wotns/web/PageToadletFactory.java b/src/main/java/net/pterodactylus/wotns/web/PageToadletFactory.java new file mode 100644 index 0000000..64fdcc0 --- /dev/null +++ b/src/main/java/net/pterodactylus/wotns/web/PageToadletFactory.java @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +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 David ‘Bombe’ Roden + */ +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 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 page, String menuName) { + return new PageToadlet(highLevelSimpleClient, menuName, page, pathPrefix); + } + +} diff --git a/src/main/resources/i18n/WoTNS.en.properties b/src/main/resources/i18n/WoTNS.en.properties new file mode 100644 index 0000000..b034e45 --- /dev/null +++ b/src/main/resources/i18n/WoTNS.en.properties @@ -0,0 +1,4 @@ +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 diff --git a/src/main/resources/templates/addTarget.html b/src/main/resources/templates/addTarget.html new file mode 100644 index 0000000..9bae233 --- /dev/null +++ b/src/main/resources/templates/addTarget.html @@ -0,0 +1,2 @@ +

Add target.

+

An error occured.

diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..a0c8bee --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,12 @@ +

WoTNS

+

Yay, Index!

+

Manage Names

+
+ + +
diff --git a/src/main/resources/templates/manage.html b/src/main/resources/templates/manage.html new file mode 100644 index 0000000..b0b2be6 --- /dev/null +++ b/src/main/resources/templates/manage.html @@ -0,0 +1,22 @@ +

Manage “<%ownIdentity.nickname|html>”

+<%foreach ownIdentity.targets target> + <%first> +

Existing Targets

+ <%/first> +
+ + + <%target.key|html>: + + + +
+<%/foreach> +

Add Target

+
+ + + + + +
diff --git a/src/main/resources/templates/unknown.html b/src/main/resources/templates/unknown.html new file mode 100644 index 0000000..6fab8b9 --- /dev/null +++ b/src/main/resources/templates/unknown.html @@ -0,0 +1,2 @@ +

Unknown name

+

The name “<%shortName|html>” is unknown.

\ No newline at end of file