Merge branch 'fcp-interface' into next
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 11 May 2011 04:12:43 +0000 (06:12 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 11 May 2011 04:12:43 +0000 (06:12 +0200)
This fixes #21.

23 files changed:
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/web/OptionsPage.java
src/main/resources/i18n/sone.en.properties
src/main/resources/templates/options.html

index 473d60e..765b672 100644 (file)
@@ -40,6 +40,8 @@ import net.pterodactylus.sone.data.Profile;
 import net.pterodactylus.sone.data.Profile.Field;
 import net.pterodactylus.sone.data.Reply;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.fcp.FcpInterface;
+import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
 import net.pterodactylus.sone.freenet.wot.Identity;
 import net.pterodactylus.sone.freenet.wot.IdentityListener;
 import net.pterodactylus.sone.freenet.wot.IdentityManager;
@@ -116,6 +118,9 @@ public class Core implements IdentityListener, UpdateListener {
        /** The update checker. */
        private final UpdateChecker updateChecker;
 
+       /** The FCP interface. */
+       private volatile FcpInterface fcpInterface;
+
        /** Whether the core has been stopped. */
        private volatile boolean stopped;
 
@@ -258,6 +263,16 @@ public class Core implements IdentityListener, UpdateListener {
        }
 
        /**
+        * Sets the FCP interface to use.
+        *
+        * @param fcpInterface
+        *            The FCP interface to use
+        */
+       public void setFcpInterface(FcpInterface fcpInterface) {
+               this.fcpInterface = fcpInterface;
+       }
+
+       /**
         * Returns the status of the given Sone.
         *
         * @param sone
@@ -1706,6 +1721,8 @@ public class Core implements IdentityListener, UpdateListener {
                        configuration.getIntValue("Option/PositiveTrust").setValue(options.getIntegerOption("PositiveTrust").getReal());
                        configuration.getIntValue("Option/NegativeTrust").setValue(options.getIntegerOption("NegativeTrust").getReal());
                        configuration.getStringValue("Option/TrustComment").setValue(options.getStringOption("TrustComment").getReal());
+                       configuration.getBooleanValue("Option/ActivateFcpInterface").setValue(options.getBooleanOption("ActivateFcpInterface").getReal());
+                       configuration.getIntValue("Option/FcpFullAccessRequired").setValue(options.getIntegerOption("FcpFullAccessRequired").getReal());
                        configuration.getBooleanValue("Option/SoneRescueMode").setValue(options.getBooleanOption("SoneRescueMode").getReal());
                        configuration.getBooleanValue("Option/ClearOnNextRestart").setValue(options.getBooleanOption("ClearOnNextRestart").getReal());
                        configuration.getBooleanValue("Option/ReallyClearOnNextRestart").setValue(options.getBooleanOption("ReallyClearOnNextRestart").getReal());
@@ -1781,6 +1798,23 @@ public class Core implements IdentityListener, UpdateListener {
                options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75, new IntegerRangeValidator(0, 100)));
                options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-25, new IntegerRangeValidator(-100, 100)));
                options.addStringOption("TrustComment", new DefaultOption<String>("Set from Sone Web Interface"));
+               options.addBooleanOption("ActivateFcpInterface", new DefaultOption<Boolean>(false, new OptionWatcher<Boolean>() {
+
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void optionChanged(Option<Boolean> option, Boolean oldValue, Boolean newValue) {
+                               fcpInterface.setActive(newValue);
+                       }
+               }));
+               options.addIntegerOption("FcpFullAccessRequired", new DefaultOption<Integer>(2, new OptionWatcher<Integer>() {
+
+                       @Override
+                       @SuppressWarnings("synthetic-access")
+                       public void optionChanged(Option<Integer> option, Integer oldValue, Integer newValue) {
+                               fcpInterface.setFullAccessRequired(FullAccessRequired.values()[newValue]);
+                       }
+
+               }));
                options.addBooleanOption("SoneRescueMode", new DefaultOption<Boolean>(false));
                options.addBooleanOption("ClearOnNextRestart", new DefaultOption<Boolean>(false));
                options.addBooleanOption("ReallyClearOnNextRestart", new DefaultOption<Boolean>(false));
@@ -1802,6 +1836,8 @@ public class Core implements IdentityListener, UpdateListener {
                loadConfigurationValue("PositiveTrust");
                loadConfigurationValue("NegativeTrust");
                options.getStringOption("TrustComment").set(configuration.getStringValue("Option/TrustComment").getValue(null));
+               options.getBooleanOption("ActivateFcpInterface").set(configuration.getBooleanValue("Option/ActivateFcpInterface").getValue(null));
+               options.getIntegerOption("FcpFullAccessRequired").set(configuration.getIntValue("Option/FcpFullAccessRequired").getValue(null));
                options.getBooleanOption("SoneRescueMode").set(configuration.getBooleanValue("Option/SoneRescueMode").getValue(null));
 
                /* load known Sones. */
@@ -2203,6 +2239,57 @@ public class Core implements IdentityListener, UpdateListener {
                }
 
                /**
+                * Returns whether the {@link FcpInterface FCP interface} is currently
+                * active.
+                *
+                * @see FcpInterface#setActive(boolean)
+                * @return {@code true} if the FCP interface is currently active,
+                *         {@code false} otherwise
+                */
+               public boolean isFcpInterfaceActive() {
+                       return options.getBooleanOption("ActivateFcpInterface").get();
+               }
+
+               /**
+                * Sets whether the {@link FcpInterface FCP interface} is currently
+                * active.
+                *
+                * @see FcpInterface#setActive(boolean)
+                * @param fcpInterfaceActive
+                *            {@code true} to activate the FCP interface, {@code false}
+                *            to deactivate the FCP interface
+                * @return This preferences object
+                */
+               public Preferences setFcpInterfaceActive(boolean fcpInterfaceActive) {
+                       options.getBooleanOption("ActivateFcpInterface").set(fcpInterfaceActive);
+                       return this;
+               }
+
+               /**
+                * Returns the action level for which full access to the FCP interface
+                * is required.
+                *
+                * @return The action level for which full access to the FCP interface
+                *         is required
+                */
+               public FullAccessRequired getFcpFullAccessRequired() {
+                       return FullAccessRequired.values()[options.getIntegerOption("FcpFullAccessRequired").get()];
+               }
+
+               /**
+                * Sets the action level for which full access to the FCP interface is
+                * required
+                *
+                * @param fcpFullAccessRequired
+                *            The action level
+                * @return This preferences
+                */
+               public Preferences setFcpFullAccessRequired(FullAccessRequired fcpFullAccessRequired) {
+                       options.getIntegerOption("FcpFullAccessRequired").set((fcpFullAccessRequired != null) ? fcpFullAccessRequired.ordinal() : null);
+                       return this;
+               }
+
+               /**
                 * Returns whether the rescue mode is active.
                 *
                 * @return {@code true} if the rescue mode is active, {@code false}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/AbstractSoneCommand.java
new file mode 100644 (file)
index 0000000..ed5aca8
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Sone - FcpInterface.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import java.util.Collection;
+import java.util.List;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.AbstractCommand;
+import net.pterodactylus.sone.freenet.fcp.Command;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import net.pterodactylus.sone.template.SoneAccessor;
+import net.pterodactylus.util.filter.Filters;
+import freenet.node.FSParseException;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * Abstract base implementation of a {@link Command} with Sone-related helper
+ * methods.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public abstract class AbstractSoneCommand extends AbstractCommand {
+
+       /** The Sone core. */
+       private final Core core;
+
+       /** Whether this command needs write access. */
+       private final boolean writeAccess;
+
+       /**
+        * Creates a new abstract Sone FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       protected AbstractSoneCommand(Core core) {
+               this(core, false);
+       }
+
+       /**
+        * Creates a new abstract Sone FCP command.
+        *
+        * @param core
+        *            The Sone core
+        * @param writeAccess
+        *            {@code true} if this command requires write access,
+        *            {@code false} otherwise
+        */
+       protected AbstractSoneCommand(Core core, boolean writeAccess) {
+               this.core = core;
+               this.writeAccess = writeAccess;
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns the Sone core.
+        *
+        * @return The Sone core
+        */
+       protected Core getCore() {
+               return core;
+       }
+
+       /**
+        * Returns whether this command requires write access.
+        *
+        * @return {@code true} if this command require write access, {@code false}
+        *         otherwise
+        */
+       public boolean requiresWriteAccess() {
+               return writeAccess;
+       }
+
+       //
+       // PROTECTED METHODS
+       //
+
+       /**
+        * Returns a Sone whose ID is a parameter in the given simple field set.
+        *
+        * @param simpleFieldSet
+        *            The simple field set containing the ID of the Sone
+        * @param parameterName
+        *            The name under which the Sone ID is stored in the simple field
+        *            set
+        * @param localOnly
+        *            {@code true} to only return local Sones, {@code false} to
+        *            return any Sones
+        * @return The Sone
+        * @throws FcpException
+        *             if there is no Sone ID stored under the given parameter name,
+        *             or if the Sone ID is invalid
+        */
+       protected Sone getSone(SimpleFieldSet simpleFieldSet, String parameterName, boolean localOnly) throws FcpException {
+               try {
+                       String soneId = simpleFieldSet.getString(parameterName);
+                       Sone sone = localOnly ? core.getLocalSone(soneId, false) : core.getSone(soneId, false);
+                       if (sone == null) {
+                               throw new FcpException("Could not load Sone from “" + soneId + "”.");
+                       }
+                       return sone;
+               } catch (FSParseException fspe1) {
+                       throw new FcpException("Could not load Sone ID from “" + parameterName + "”.", fspe1);
+               }
+       }
+
+       /**
+        * Returns a post whose ID is a parameter in the given simple field set.
+        *
+        * @param simpleFieldSet
+        *            The simple field set containing the ID of the post
+        * @param parameterName
+        *            The name under which the post ID is stored in the simple field
+        *            set
+        * @return The post
+        * @throws FcpException
+        *             if there is no post ID stored under the given parameter name,
+        *             or if the post ID is invalid
+        */
+       protected Post getPost(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
+               try {
+                       String postId = simpleFieldSet.getString(parameterName);
+                       Post post = core.getPost(postId, false);
+                       if (post == null) {
+                               throw new FcpException("Could not load post from “" + postId + "”.");
+                       }
+                       return post;
+               } catch (FSParseException fspe1) {
+                       throw new FcpException("Could not post ID from “" + parameterName + "”.", fspe1);
+               }
+       }
+
+       /**
+        * Returns a reply whose ID is a parameter in the given simple field set.
+        *
+        * @param simpleFieldSet
+        *            The simple field set containing the ID of the reply
+        * @param parameterName
+        *            The name under which the reply ID is stored in the simple
+        *            field set
+        * @return The reply
+        * @throws FcpException
+        *             if there is no reply ID stored under the given parameter
+        *             name, or if the reply ID is invalid
+        */
+       protected Reply getReply(SimpleFieldSet simpleFieldSet, String parameterName) throws FcpException {
+               try {
+                       String replyId = simpleFieldSet.getString(parameterName);
+                       Reply reply = core.getReply(replyId, false);
+                       if (reply == null) {
+                               throw new FcpException("Could not load reply from “" + replyId + "”.");
+                       }
+                       return reply;
+               } catch (FSParseException fspe1) {
+                       throw new FcpException("Could not reply ID from “" + parameterName + "”.", fspe1);
+               }
+       }
+
+       /**
+        * Creates a simple field set from the given collection of Sones.
+        *
+        * @param sones
+        *            The Sones to encode
+        * @param prefix
+        *            The prefix for the field names (may be empty but not
+        *            {@code null})
+        * @return The simple field set containing the given Sones
+        */
+       protected SimpleFieldSet encodeSones(Collection<? extends Sone> sones, String prefix) {
+               SimpleFieldSetBuilder soneBuilder = new SimpleFieldSetBuilder();
+
+               int soneIndex = 0;
+               soneBuilder.put(prefix + "Count", sones.size());
+               for (Sone sone : sones) {
+                       String sonePrefix = prefix + soneIndex++ + ".";
+                       soneBuilder.put(sonePrefix + "ID", sone.getId());
+                       soneBuilder.put(sonePrefix + "Name", sone.getName());
+                       soneBuilder.put(sonePrefix + "NiceName", SoneAccessor.getNiceName(sone));
+                       soneBuilder.put(sonePrefix + "Time", sone.getTime());
+               }
+
+               return soneBuilder.get();
+       }
+
+       /**
+        * Creates a simple field set from the given post.
+        *
+        * @param post
+        *            The post to encode
+        * @param prefix
+        *            The prefix for the field names (may be empty but not
+        *            {@code null})
+        * @param includeReplies
+        *            {@code true} to include replies, {@code false} to not include
+        *            replies
+        * @return The simple field set containing the post
+        */
+       protected SimpleFieldSet encodePost(Post post, String prefix, boolean includeReplies) {
+               SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
+
+               postBuilder.put(prefix + "ID", post.getId());
+               postBuilder.put(prefix + "Sone", post.getSone().getId());
+               if (post.getRecipient() != null) {
+                       postBuilder.put(prefix + "Recipient", post.getRecipient().getId());
+               }
+               postBuilder.put(prefix + "Time", post.getTime());
+               postBuilder.put(prefix + "Text", post.getText());
+               postBuilder.put(encodeLikes(core.getLikes(post), prefix + "Likes."));
+
+               if (includeReplies) {
+                       List<Reply> replies = core.getReplies(post);
+                       postBuilder.put(encodeReplies(replies, prefix));
+               }
+
+               return postBuilder.get();
+       }
+
+       /**
+        * Creates a simple field set from the given collection of posts.
+        *
+        * @param posts
+        *            The posts to encode
+        * @param prefix
+        *            The prefix for the field names (may be empty but not
+        *            {@code null})
+        * @param includeReplies
+        *            {@code true} to include the replies, {@code false} to not
+        *            include the replies
+        * @return The simple field set containing the posts
+        */
+       protected SimpleFieldSet encodePosts(Collection<? extends Post> posts, String prefix, boolean includeReplies) {
+               SimpleFieldSetBuilder postBuilder = new SimpleFieldSetBuilder();
+
+               int postIndex = 0;
+               postBuilder.put(prefix + "Count", posts.size());
+               for (Post post : posts) {
+                       String postPrefix = prefix + postIndex++;
+                       postBuilder.put(encodePost(post, postPrefix + ".", includeReplies));
+                       if (includeReplies) {
+                               postBuilder.put(encodeReplies(Filters.filteredList(core.getReplies(post), Reply.FUTURE_REPLIES_FILTER), postPrefix + "."));
+                       }
+               }
+
+               return postBuilder.get();
+       }
+
+       /**
+        * Creates a simple field set from the given collection of replies.
+        *
+        * @param replies
+        *            The replies to encode
+        * @param prefix
+        *            The prefix for the field names (may be empty, but not
+        *            {@code null})
+        * @return The simple field set containing the replies
+        */
+       protected SimpleFieldSet encodeReplies(Collection<? extends Reply> replies, String prefix) {
+               SimpleFieldSetBuilder replyBuilder = new SimpleFieldSetBuilder();
+
+               int replyIndex = 0;
+               replyBuilder.put(prefix + "Replies.Count", replies.size());
+               for (Reply reply : replies) {
+                       String replyPrefix = prefix + "Replies." + replyIndex++ + ".";
+                       replyBuilder.put(replyPrefix + "ID", reply.getId());
+                       replyBuilder.put(replyPrefix + "Sone", reply.getSone().getId());
+                       replyBuilder.put(replyPrefix + "Time", reply.getTime());
+                       replyBuilder.put(replyPrefix + "Text", reply.getText());
+               }
+
+               return replyBuilder.get();
+       }
+
+       /**
+        * Creates a simple field set from the given collection of Sones that like
+        * an element.
+        *
+        * @param likes
+        *            The liking Sones
+        * @param prefix
+        *            The prefix for the field names (may be empty but not
+        *            {@code null})
+        * @return The simple field set containing the likes
+        */
+       protected SimpleFieldSet encodeLikes(Collection<? extends Sone> likes, String prefix) {
+               SimpleFieldSetBuilder likesBuilder = new SimpleFieldSetBuilder();
+
+               int likeIndex = 0;
+               likesBuilder.put(prefix + "Count", likes.size());
+               for (Sone sone : likes) {
+                       String sonePrefix = prefix + likeIndex++ + ".";
+                       likesBuilder.put(sonePrefix + "ID", sone.getId());
+               }
+
+               return likesBuilder.get();
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java
new file mode 100644 (file)
index 0000000..4dd0b8e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Sone - CreatePostCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * FCP command that creates a new {@link Post}.
+ *
+ * @see Core#createPost(Sone, Sone, String)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreatePostCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “CreatePost” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public CreatePostCommand(Core core) {
+               super(core, true);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Sone sone = getSone(parameters, "Sone", true);
+               String text = getString(parameters, "Text");
+               Sone recipient = null;
+               if (parameters.get("Recipient") != null) {
+                       recipient = getSone(parameters, "Recipient", false);
+               }
+               if (sone.equals(recipient)) {
+                       return new ErrorResponse("Sone and Recipient must not be the same.");
+               }
+               Post post = getCore().createPost(sone, recipient, text);
+               return new Response("PostCreated", new SimpleFieldSetBuilder().put("Post", post.getId()).get());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java
new file mode 100644 (file)
index 0000000..880d51e
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Sone - CreateReplyCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * FCP command that creates a new {@link Reply}.
+ *
+ * @see Core#createReply(Sone, Post, String)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreateReplyCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “CreateReply” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public CreateReplyCommand(Core core) {
+               super(core, true);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Sone sone = getSone(parameters, "Sone", true);
+               Post post = getPost(parameters, "Post");
+               String text = getString(parameters, "Text");
+               Reply reply = getCore().createReply(sone, post, text);
+               return new Response("ReplyCreated", new SimpleFieldSetBuilder().put("Reply", reply.getId()).get());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java
new file mode 100644 (file)
index 0000000..3c8b03a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Sone - DeletePostCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * FCP command that deletes a {@link Post}.
+ *
+ * @see Core#deletePost(Post)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeletePostCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “DeletePost” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public DeletePostCommand(Core core) {
+               super(core, true);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Post post = getPost(parameters, "Post");
+               if (!getCore().isLocalSone(post.getSone())) {
+                       return new ErrorResponse(401, "Not allowed.");
+               }
+               return new Response("PostDeleted", new SimpleFieldSetBuilder().get());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java
new file mode 100644 (file)
index 0000000..4ac9c15
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Sone - DeleteReplyCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * FCP command that deletes a {@link Reply}.
+ *
+ * @see Core#deleteReply(Reply)
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class DeleteReplyCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “DeleteReply” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public DeleteReplyCommand(Core core) {
+               super(core, true);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Reply reply = getReply(parameters, "Reply");
+               if (!getCore().isLocalSone(reply.getSone())) {
+                       return new ErrorResponse(401, "Not allowed.");
+               }
+               return new Response("ReplyDeleted", new SimpleFieldSetBuilder().get());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java
new file mode 100644 (file)
index 0000000..8959b6a
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Sone - FcpInterface.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.freenet.fcp.Command.AccessType;
+import net.pterodactylus.sone.freenet.fcp.Command.ErrorResponse;
+import net.pterodactylus.sone.freenet.fcp.Command.Response;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.validation.Validation;
+import freenet.pluginmanager.FredPluginFCP;
+import freenet.pluginmanager.PluginNotFoundException;
+import freenet.pluginmanager.PluginReplySender;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implementation of an FCP interface for other clients or plugins to
+ * communicate with Sone.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FcpInterface {
+
+       /**
+        * The action level that full access for the FCP connection is required.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       public enum FullAccessRequired {
+
+               /** No action requires full access. */
+               NO,
+
+               /** All writing actions require full access. */
+               WRITING,
+
+               /** All actions require full access. */
+               ALWAYS,
+
+       }
+
+       /** The logger. */
+       private static final Logger logger = Logging.getLogger(FcpInterface.class);
+
+       /** Whether the FCP interface is currently active. */
+       private volatile boolean active;
+
+       /** What function full access is required for. */
+       private volatile FullAccessRequired fullAccessRequired = FullAccessRequired.ALWAYS;
+
+       /** All available FCP commands. */
+       private final Map<String, AbstractSoneCommand> commands = Collections.synchronizedMap(new HashMap<String, AbstractSoneCommand>());
+
+       /**
+        * Creates a new FCP interface.
+        *
+        * @param core
+        *            The core
+        */
+       public FcpInterface(Core core) {
+               commands.put("Version", new VersionCommand(core));
+               commands.put("GetLocalSones", new GetLocalSonesCommand(core));
+               commands.put("GetSones", new GetSonesCommand(core));
+               commands.put("GetPost", new GetPostCommand(core));
+               commands.put("GetPosts", new GetPostsCommand(core));
+               commands.put("GetPostFeed", new GetPostFeedCommand(core));
+               commands.put("LikePost", new LikePostCommand(core));
+               commands.put("LikeReply", new LikeReplyCommand(core));
+               commands.put("CreatePost", new CreatePostCommand(core));
+               commands.put("CreateReply", new CreateReplyCommand(core));
+               commands.put("DeletePost", new DeletePostCommand(core));
+               commands.put("DeleteReply", new DeleteReplyCommand(core));
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Sets whether the FCP interface should handle requests. If {@code active}
+        * is {@code false}, all requests are answered with an error.
+        *
+        * @param active
+        *            {@code true} to activate the FCP interface, {@code false} to
+        *            deactivate the FCP interface
+        */
+       public void setActive(boolean active) {
+               this.active = active;
+       }
+
+       /**
+        * Sets the action level for which full FCP access is required.
+        *
+        * @param fullAccessRequired
+        *            The action level for which full FCP access is required
+        */
+       public void setFullAccessRequired(FullAccessRequired fullAccessRequired) {
+               Validation.begin().isNotNull("FullAccessRequired", fullAccessRequired).check();
+               this.fullAccessRequired = fullAccessRequired;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Handles a plugin FCP request.
+        *
+        * @param pluginReplySender
+        *            The reply sender
+        * @param parameters
+        *            The message parameters
+        * @param data
+        *            The message data (may be {@code null})
+        * @param accessType
+        *            One of {@link FredPluginFCP#ACCESS_DIRECT},
+        *            {@link FredPluginFCP#ACCESS_FCP_FULL},
+        *            {@link FredPluginFCP#ACCESS_FCP_RESTRICTED}
+        */
+       public void handle(PluginReplySender pluginReplySender, SimpleFieldSet parameters, Bucket data, int accessType) {
+               if (!active) {
+                       try {
+                               sendReply(pluginReplySender, null, new ErrorResponse(400, "FCP Interface deactivated"));
+                       } catch (PluginNotFoundException pnfe1) {
+                               logger.log(Level.FINE, "Could not set error to plugin.", pnfe1);
+                       }
+                       return;
+               }
+               AbstractSoneCommand command = commands.get(parameters.get("Message"));
+               if ((accessType == FredPluginFCP.ACCESS_FCP_RESTRICTED) && (((fullAccessRequired == FullAccessRequired.WRITING) && command.requiresWriteAccess()) || (fullAccessRequired == FullAccessRequired.ALWAYS))) {
+                       try {
+                               sendReply(pluginReplySender, null, new ErrorResponse(401, "Not authorized"));
+                       } catch (PluginNotFoundException pnfe1) {
+                               logger.log(Level.FINE, "Could not set error to plugin.", pnfe1);
+                       }
+                       return;
+               }
+               try {
+                       if (command == null) {
+                               sendReply(pluginReplySender, null, new ErrorResponse("Unrecognized Message: " + parameters.get("Message")));
+                               return;
+                       }
+                       String identifier = parameters.get("Identifier");
+                       if ((identifier == null) || (identifier.length() == 0)) {
+                               sendReply(pluginReplySender, null, new ErrorResponse("Missing Identifier."));
+                               return;
+                       }
+                       try {
+                               Response response = command.execute(parameters, data, AccessType.values()[accessType]);
+                               sendReply(pluginReplySender, identifier, response);
+                       } catch (FcpException fe1) {
+                               sendReply(pluginReplySender, identifier, new ErrorResponse("Error executing command: " + fe1.getMessage()));
+                       }
+               } catch (PluginNotFoundException pnfe1) {
+                       logger.log(Level.WARNING, "Could not find destination plugin: " + pluginReplySender);
+               }
+       }
+
+       //
+       // PRIVATE METHODS
+       //
+
+       /**
+        * Sends the given response to the given plugin.
+        *
+        * @param pluginReplySender
+        *            The reply sender
+        * @param identifier
+        *            The identifier (may be {@code null})
+        * @param response
+        *            The response to send
+        * @throws PluginNotFoundException
+        *             if the plugin can not be found
+        */
+       private void sendReply(PluginReplySender pluginReplySender, String identifier, Response response) throws PluginNotFoundException {
+               SimpleFieldSet replyParameters = response.getReplyParameters();
+               if (identifier != null) {
+                       replyParameters.putOverwrite("Identifier", identifier);
+               }
+               if (response.hasData()) {
+                       pluginReplySender.send(replyParameters, response.getData());
+               } else if (response.hasBucket()) {
+                       pluginReplySender.send(replyParameters, response.getBucket());
+               } else {
+                       pluginReplySender.send(replyParameters);
+               }
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java
new file mode 100644 (file)
index 0000000..c4206d5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Sone - GetLocalSonesCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implements the “GetLocalSones” FCP command that returns the list of local
+ * Sones to the sender.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class GetLocalSonesCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “GetLocalSones” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public GetLocalSonesCommand(Core core) {
+               super(core);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               return new Response("ListLocalSones", encodeSones(getCore().getLocalSones(), "LocalSones."));
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java
new file mode 100644 (file)
index 0000000..bb87407
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Sone - GetPostCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * The “GetPost” FCP command returns a single {@link Post} to an FCP client.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class GetPostCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “GetPost” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public GetPostCommand(Core core) {
+               super(core);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Post post = getPost(parameters, "Post");
+               boolean includeReplies = getBoolean(parameters, "IncludeReplies", true);
+
+               return new Response("Post", encodePost(post, "Post.", includeReplies));
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java
new file mode 100644 (file)
index 0000000..d4b175c
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Sone - GetPostFeedCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import net.pterodactylus.util.filter.Filters;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implementation of an FCP interface for other clients or plugins to
+ * communicate with Sone.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class GetPostFeedCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “GetPostFeed” command.
+        *
+        * @param core
+        *            The core
+        */
+       public GetPostFeedCommand(Core core) {
+               super(core);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Sone sone = getSone(parameters, "Sone", true);
+               int startPost = getInt(parameters, "StartPost", 0);
+               int maxPosts = getInt(parameters, "MaxPosts", -1);
+
+               Set<Post> allPosts = new HashSet<Post>();
+               allPosts.addAll(sone.getPosts());
+               for (String friendSoneId : sone.getFriends()) {
+                       if (!getCore().hasSone(friendSoneId)) {
+                               continue;
+                       }
+                       allPosts.addAll(getCore().getSone(friendSoneId).getPosts());
+               }
+               allPosts.addAll(getCore().getDirectedPosts(sone));
+               allPosts = Filters.filteredSet(allPosts, Post.FUTURE_POSTS_FILTER);
+
+               List<Post> sortedPosts = new ArrayList<Post>(allPosts);
+               Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+
+               if (sortedPosts.size() < startPost) {
+                       return new Response("PostFeed", encodePosts(Collections.<Post> emptyList(), "Posts.", false));
+               }
+
+               return new Response("PostFeed", encodePosts(sortedPosts.subList(startPost, (maxPosts == -1) ? sortedPosts.size() : Math.min(startPost + maxPosts, sortedPosts.size())), "Posts.", true));
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java
new file mode 100644 (file)
index 0000000..6a3b07e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Sone - GetPostsCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import java.util.Collections;
+import java.util.List;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implements the “GetPosts” FCP command that returns the list of posts a Sone
+ * made.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class GetPostsCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “GetPosts” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public GetPostsCommand(Core core) {
+               super(core);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Sone sone = getSone(parameters, "Sone", false);
+               int startPost = getInt(parameters, "StartPost", 0);
+               int maxPosts = getInt(parameters, "MaxPosts", -1);
+               List<Post> posts = sone.getPosts();
+               if (posts.size() < startPost) {
+                       return new Response("Posts", encodePosts(Collections.<Post> emptyList(), "Posts.", false));
+               }
+               return new Response("Posts", encodePosts(sone.getPosts().subList(startPost, (maxPosts == -1) ? posts.size() : Math.min(startPost + maxPosts, posts.size())), "Posts.", true));
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java
new file mode 100644 (file)
index 0000000..1729bb7
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Sone - GetSonesCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implements the “GetSones” FCP command that returns the list of known Sones.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class GetSonesCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “GetSones” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       public GetSonesCommand(Core core) {
+               super(core);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               int startSone = getInt(parameters, "StartSone", 0);
+               int maxSones = getInt(parameters, "MaxSones", -1);
+               List<Sone> sones = new ArrayList<Sone>(getCore().getSones());
+               if (sones.size() < startSone) {
+                       return new Response("Sones", encodeSones(Collections.<Sone> emptyList(), ""));
+               }
+               Collections.sort(sones, Sone.NICE_NAME_COMPARATOR);
+               return new Response("Sones", encodeSones(sones.subList(startSone, (maxSones == -1) ? sones.size() : Math.min(startSone + maxSones, sones.size())), ""));
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java
new file mode 100644 (file)
index 0000000..37f69ec
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Sone - LikePostCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implements the “LikePost” FCP command which allows the user to like a post.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class LikePostCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “LikePost” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       protected LikePostCommand(Core core) {
+               super(core, true);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Post post = getPost(parameters, "Post");
+               Sone sone = getSone(parameters, "Sone", true);
+               sone.addLikedPostId(post.getId());
+               return new Response("PostLiked", new SimpleFieldSetBuilder().put("LikeCount", getCore().getLikes(post).size()).get());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java
new file mode 100644 (file)
index 0000000..2c8f04f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Sone - LikeReplyCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.data.Reply;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.freenet.fcp.FcpException;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implements the “LikeReply” FCP command which allows the user to like a reply.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class LikeReplyCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “LikeReply” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       protected LikeReplyCommand(Core core) {
+               super(core, true);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException {
+               Reply reply = getReply(parameters, "Reply");
+               Sone sone = getSone(parameters, "Sone", true);
+               sone.addLikedReplyId(reply.getId());
+               return new Response("ReplyLiked", new SimpleFieldSetBuilder().put("LikeCount", getCore().getLikes(reply).size()).get());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java b/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java
new file mode 100644 (file)
index 0000000..1e135b5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Sone - VersionCommand.java - Copyright © 2011 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.fcp;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import net.pterodactylus.sone.main.SonePlugin;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Returns version information about the Sone plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class VersionCommand extends AbstractSoneCommand {
+
+       /**
+        * Creates a new “Version” FCP command.
+        *
+        * @param core
+        *            The Sone core
+        */
+       protected VersionCommand(Core core) {
+               super(core);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) {
+               return new Response("Version", new SimpleFieldSetBuilder().put("Version", SonePlugin.VERSION.toString()).put("ProtocolVersion", 1).get());
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java b/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java
new file mode 100644 (file)
index 0000000..1d57454
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet;
+
+import net.pterodactylus.util.validation.Validation;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * Helper class to construct {@link SimpleFieldSet} objects in a single call.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class SimpleFieldSetBuilder {
+
+       /** The simple field set that is being constructed. */
+       private final SimpleFieldSet simpleFieldSet;
+
+       /**
+        * Creates a new simple field set builder using a new, empty simple field
+        * set.
+        */
+       public SimpleFieldSetBuilder() {
+               this(new SimpleFieldSet(true));
+       }
+
+       /**
+        * Creates a new simple field set builder that will return the given simple
+        * field set on {@link #get()}.
+        *
+        * @param simpleFieldSet
+        *            The simple field set to build
+        */
+       public SimpleFieldSetBuilder(SimpleFieldSet simpleFieldSet) {
+               Validation.begin().isNotNull("Simple Field Set", simpleFieldSet).check();
+               this.simpleFieldSet = simpleFieldSet;
+       }
+
+       /**
+        * Returns the constructed simple field set.
+        *
+        * @return The construct simple field set
+        */
+       public SimpleFieldSet get() {
+               return simpleFieldSet;
+       }
+
+       /**
+        * Copies the given simple field set into the simple field set being built
+        * in this builder, overwriting all previously existing values.
+        *
+        * @param simpleFieldSet
+        *            The simple field set to copy
+        * @return This simple field set builder
+        */
+       public SimpleFieldSetBuilder put(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/sone/freenet/fcp/AbstractCommand.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java
new file mode 100644 (file)
index 0000000..538876a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.fcp;
+
+import freenet.node.FSParseException;
+import freenet.support.SimpleFieldSet;
+
+/**
+ * Basic implementation of a {@link Command} with various helper methods to
+ * simplify processing of input parameters.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public abstract class AbstractCommand implements Command {
+
+       //
+       // PROTECTED METHODS
+       //
+
+       /**
+        * Returns a String value from the given simple field set.
+        *
+        * @param simpleFieldSet
+        *            The simple field set to get the value from
+        * @param key
+        *            The key of the value
+        * @return The String value
+        * @throws FcpException
+        *             if there is no value for the given key in the simple field
+        *             set, or the value can not be converted to a String
+        */
+       protected String getString(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+               try {
+                       return simpleFieldSet.getString(key);
+               } catch (FSParseException fspe1) {
+                       throw new FcpException("Could not get parameter “" + key + "” as String.", fspe1);
+               }
+       }
+
+       /**
+        * Returns an int value from the given simple field set.
+        *
+        * @param simpleFieldSet
+        *            The simple field set to get the value from
+        * @param key
+        *            The key of the value
+        * @return The int value
+        * @throws FcpException
+        *             if there is no value for the given key in the simple field
+        *             set, or the value can not be converted to an int
+        */
+       protected int getInt(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+               try {
+                       return simpleFieldSet.getInt(key);
+               } catch (FSParseException fspe1) {
+                       throw new FcpException("Could not get parameter “" + key + "” as int.", fspe1);
+               }
+       }
+
+       /**
+        * Returns an int value from the given simple field set, returning a default
+        * value if the value can not be found or converted.
+        *
+        * @param simpleFieldSet
+        *            The simple field set to get the value from
+        * @param key
+        *            The key of the value
+        * @param defaultValue
+        *            The default value
+        * @return The int value
+        */
+       protected int getInt(SimpleFieldSet simpleFieldSet, String key, int defaultValue) {
+               return simpleFieldSet.getInt(key, defaultValue);
+       }
+
+       /**
+        * Returns a boolean value from the given simple field set.
+        *
+        * @param simpleFieldSet
+        *            The simple field set to get the value from
+        * @param key
+        *            The key of the value
+        * @return The boolean value
+        * @throws FcpException
+        *             if there is no value for the given key in the simple field
+        *             set, or the value can not be converted to a boolean
+        */
+       protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key) throws FcpException {
+               try {
+                       return simpleFieldSet.getBoolean(key);
+               } catch (FSParseException fspe1) {
+                       throw new FcpException("Could not get parameter “" + key + "” as boolean.", fspe1);
+               }
+       }
+
+       /**
+        * Returns a boolean value from the given simple field set, returning a
+        * default value if the value can not be found or converted.
+        *
+        * @param simpleFieldSet
+        *            The simple field set to get the value from
+        * @param key
+        *            The key of the value
+        * @param defaultValue
+        *            The default value
+        * @return The boolean value
+        */
+       protected boolean getBoolean(SimpleFieldSet simpleFieldSet, String key, boolean defaultValue) {
+               return simpleFieldSet.getBoolean(key, defaultValue);
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java
new file mode 100644 (file)
index 0000000..46032dd
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.fcp;
+
+import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+/**
+ * Implementation of an FCP interface for other clients or plugins to
+ * communicate with Sone.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public interface Command {
+
+       /**
+        * Executes the command, returning a reply that will be sent back to the
+        * requesting plugin.
+        *
+        * @param parameters
+        *            The parameters of the comand
+        * @param data
+        *            The data of the command (may be {@code null})
+        * @param accessType
+        *            The access type
+        * @return A reply to send back to the plugin
+        * @throws FcpException
+        *             if an error processing the parameters occurs
+        */
+       public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) throws FcpException;
+
+       /**
+        * The access type of the request.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       public static enum AccessType {
+
+               /** Access from another plugin. */
+               DIRECT,
+
+               /** Access via restricted FCP. */
+               RESTRICTED_FCP,
+
+               /** Access via FCP with full access. */
+               FULL_FCP,
+
+       }
+
+       /**
+        * Interface for command replies.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       public static class Response {
+
+               /** The message name of the reponse. */
+               private final String messageName;
+
+               /** The reply parameters. */
+               private final SimpleFieldSet replyParameters;
+
+               /** The reply data, may be {@code null}. */
+               private final byte[] data;
+
+               /** The data bucket, may be {@code null}. */
+               private final Bucket bucket;
+
+               /**
+                * Creates a new reply with the given parameters.
+                *
+                * @param messageName
+                *            The message name
+                * @param replyParameters
+                *            The reply parameters
+                */
+               public Response(String messageName, SimpleFieldSet replyParameters) {
+                       this(messageName, replyParameters, null, null);
+               }
+
+               /**
+                * Creates a new reply with the given parameters.
+                *
+                * @param messageName
+                *            The message name
+                * @param replyParameters
+                *            The reply parameters
+                * @param data
+                *            The data of the reply (may be {@code null})
+                */
+               public Response(String messageName, SimpleFieldSet replyParameters, byte[] data) {
+                       this(messageName, replyParameters, data, null);
+               }
+
+               /**
+                * Creates a new reply with the given parameters.
+                *
+                * @param messageName
+                *            The message name
+                * @param replyParameters
+                *            The reply parameters
+                * @param bucket
+                *            The bucket of the reply (may be {@code null})
+                */
+               public Response(String messageName, SimpleFieldSet replyParameters, Bucket bucket) {
+                       this(messageName, replyParameters, null, bucket);
+               }
+
+               /**
+                * Creates a new reply with the given parameters.
+                *
+                * @param messageName
+                *            The message name
+                * @param replyParameters
+                *            The reply parameters
+                * @param data
+                *            The data of the reply (may be {@code null})
+                * @param bucket
+                *            The bucket of the reply (may be {@code null})
+                */
+               private Response(String messageName, SimpleFieldSet replyParameters, byte[] data, Bucket bucket) {
+                       this.messageName = messageName;
+                       this.replyParameters = replyParameters;
+                       this.data = data;
+                       this.bucket = bucket;
+               }
+
+               /**
+                * Returns the reply parameters.
+                *
+                * @return The reply parameters
+                */
+               public SimpleFieldSet getReplyParameters() {
+                       return new SimpleFieldSetBuilder(replyParameters).put("Message", messageName).get();
+               }
+
+               /**
+                * Returns whether the reply has reply data.
+                *
+                * @see #getData()
+                * @return {@code true} if this reply has data, {@code false} otherwise
+                */
+               public boolean hasData() {
+                       return data != null;
+               }
+
+               /**
+                * Returns the data of the reply.
+                *
+                * @return The data of the reply
+                */
+               public byte[] getData() {
+                       return data;
+               }
+
+               /**
+                * Returns whether the reply has a data bucket.
+                *
+                * @see #getBucket()
+                * @return {@code true} if the reply has a data bucket, {@code false}
+                *         otherwise
+                */
+               public boolean hasBucket() {
+                       return bucket != null;
+               }
+
+               /**
+                * Returns the data bucket of the reply.
+                *
+                * @return The data bucket of the reply
+                */
+               public Bucket getBucket() {
+                       return bucket;
+               }
+
+       }
+
+       /**
+        * Response implementation that can return an error message and an optional
+        * error code.
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+        */
+       public class ErrorResponse extends Response {
+
+               /**
+                * Creates a new error response with the given message.
+                *
+                * @param message
+                *            The error message
+                */
+               public ErrorResponse(String message) {
+                       super("Error", new SimpleFieldSetBuilder().put("ErrorMessage", message).get());
+               }
+
+               /**
+                * Creates a new error response with the given code and message.
+                *
+                * @param code
+                *            The error code
+                * @param message
+                *            The error message
+                */
+               public ErrorResponse(int code, String message) {
+                       super("Error", new SimpleFieldSetBuilder().put("ErrorMessage", message).put("ErrorCode", code).get());
+               }
+
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java
new file mode 100644 (file)
index 0000000..d194840
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.fcp;
+
+/**
+ * Base exception for FCP communication.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FcpException extends Exception {
+
+       /**
+        * Creates a new FCP exception.
+        */
+       public FcpException() {
+               super();
+       }
+
+       /**
+        * Creates a new FCP exception.
+        *
+        * @param message
+        *            The message of the exception
+        */
+       public FcpException(String message) {
+               super(message);
+       }
+
+       /**
+        * Creates a new FCP exception.
+        *
+        * @param cause
+        *            The cause of the exception
+        */
+       public FcpException(Throwable cause) {
+               super(cause);
+       }
+
+       /**
+        * Creates a new FCP exception.
+        *
+        * @param message
+        *            The message of the exception
+        * @param cause
+        *            The cause of the exception
+        */
+       public FcpException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+}
index f27cc6e..6259fd3 100644 (file)
@@ -24,6 +24,7 @@ import java.util.logging.Logger;
 
 import net.pterodactylus.sone.core.Core;
 import net.pterodactylus.sone.core.FreenetInterface;
+import net.pterodactylus.sone.fcp.FcpInterface;
 import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend;
 import net.pterodactylus.sone.freenet.plugin.PluginConnector;
 import net.pterodactylus.sone.freenet.wot.IdentityManager;
@@ -40,10 +41,14 @@ import freenet.l10n.BaseL10n.LANGUAGE;
 import freenet.l10n.PluginL10n;
 import freenet.pluginmanager.FredPlugin;
 import freenet.pluginmanager.FredPluginBaseL10n;
+import freenet.pluginmanager.FredPluginFCP;
 import freenet.pluginmanager.FredPluginL10n;
 import freenet.pluginmanager.FredPluginThreadless;
 import freenet.pluginmanager.FredPluginVersioned;
+import freenet.pluginmanager.PluginReplySender;
 import freenet.pluginmanager.PluginRespirator;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
 
 /**
  * This class interfaces with Freenet. It is the class that is loaded by the
@@ -51,7 +56,7 @@ import freenet.pluginmanager.PluginRespirator;
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10n, FredPluginThreadless, FredPluginVersioned {
+public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, FredPluginBaseL10n, FredPluginThreadless, FredPluginVersioned {
 
        static {
                /* initialize logging. */
@@ -92,6 +97,9 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
        /** The web interface. */
        private WebInterface webInterface;
 
+       /** The FCP interface. */
+       private FcpInterface fcpInterface;
+
        /** The l10n helper. */
        private PluginL10n l10n;
 
@@ -184,6 +192,10 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
                        webInterface = new WebInterface(this);
                        core.addCoreListener(webInterface);
 
+                       /* create FCP interface. */
+                       fcpInterface = new FcpInterface(core);
+                       core.setFcpInterface(fcpInterface);
+
                        /* create the identity manager. */
                        identityManager.addIdentityListener(core);
 
@@ -233,6 +245,18 @@ public class SonePlugin implements FredPlugin, FredPluginL10n, FredPluginBaseL10
        }
 
        //
+       // INTERFACE FredPluginFCP
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public void handle(PluginReplySender pluginReplySender, SimpleFieldSet parameters, Bucket data, int accessType) {
+               fcpInterface.handle(pluginReplySender, parameters, data, accessType);
+       }
+
+       //
        // INTERFACE FredPluginL10n
        //
 
index 51ca6cd..6785e81 100644 (file)
@@ -22,6 +22,7 @@ import java.util.List;
 
 import net.pterodactylus.sone.core.Core.Preferences;
 import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
 import net.pterodactylus.sone.web.page.Page.Request.Method;
 import net.pterodactylus.util.number.Numbers;
 import net.pterodactylus.util.template.Template;
@@ -96,6 +97,11 @@ public class OptionsPage extends SoneTemplatePage {
                                trustComment = null;
                        }
                        preferences.setTrustComment(trustComment);
+                       boolean fcpInterfaceActive = request.getHttpRequest().isPartSet("fcp-interface-active");
+                       preferences.setFcpInterfaceActive(fcpInterfaceActive);
+                       Integer fcpFullAccessRequiredInteger = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("fcp-full-access-required", 1), preferences.getFcpFullAccessRequired().ordinal());
+                       FullAccessRequired fcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequiredInteger];
+                       preferences.setFcpFullAccessRequired(fcpFullAccessRequired);
                        boolean soneRescueMode = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("sone-rescue-mode", 5));
                        preferences.setSoneRescueMode(soneRescueMode);
                        boolean clearOnNextRestart = Boolean.parseBoolean(request.getHttpRequest().getPartAsStringFailsafe("clear-on-next-restart", 5));
@@ -117,6 +123,8 @@ public class OptionsPage extends SoneTemplatePage {
                templateContext.set("positive-trust", preferences.getPositiveTrust());
                templateContext.set("negative-trust", preferences.getNegativeTrust());
                templateContext.set("trust-comment", preferences.getTrustComment());
+               templateContext.set("fcp-interface-active", preferences.isFcpInterfaceActive());
+               templateContext.set("fcp-full-access-required", preferences.getFcpFullAccessRequired().ordinal());
                templateContext.set("sone-rescue-mode", preferences.isSoneRescueMode());
                templateContext.set("clear-on-next-restart", preferences.isClearOnNextRestart());
                templateContext.set("really-clear-on-next-restart", preferences.isReallyClearOnNextRestart());
index 0a44f2c..5032867 100644 (file)
@@ -43,6 +43,12 @@ Page.Options.Section.TrustOptions.Title=Trust Settings
 Page.Options.Option.PositiveTrust.Description=The amount of positive trust you want to assign to other Sones by clicking the checkmark below a post or reply.
 Page.Options.Option.NegativeTrust.Description=The amount of trust you want to assign to other Sones by clicking the red X below a post or reply. This value should be negative.
 Page.Options.Option.TrustComment.Description=The comment that will be set in the web of trust for any trust you assign from Sone.
+Page.Options.Section.FcpOptions.Title=FCP Interface Settings
+Page.Options.Option.FcpInterfaceActive.Description=Activate the FCP interface to allow other plugins and remote clients to access your Sone plugin.
+Page.Options.Option.FcpFullAccessRequired.Description=Require FCP connection from allowed hosts (see your {link}node’s configuration, section “FCP”{/link})
+Page.Options.Option.FcpFullAccessRequired.Value.No=No
+Page.Options.Option.FcpFullAccessRequired.Value.Writing=For Write Access
+Page.Options.Option.FcpFullAccessRequired.Value.Always=Always
 Page.Options.Section.RescueOptions.Title=Rescue Settings
 Page.Options.Option.SoneRescueMode.Description1=Try to rescue your Sones at the next start of the Sone plugin. The Rescue Mode will start at the latest known edition and will try to download all editions sequentially backwards, merging all discovered posts and replies together, until it is stopped or it has reached the first edition.
 Page.Options.Option.SoneRescueMode.Description2=When using the Rescue Mode because Sone lost its configuration it usually suffices to let the Rescue Mode only run for a short time; use a second tab to control how many posts of your Sone are visible again. As soon as the last valid edition is loaded you can then deactivate the Rescue Mode.
index 930d088..368c14e 100644 (file)
                <p><%= Page.Options.Option.TrustComment.Description|l10n|html></p>
                <p><input type="text" name="trust-comment" value="<% trust-comment|html>" /></p>
 
+               <h2><%= Page.Options.Section.FcpOptions.Title|l10n|html></h2>
+
+               <p><input type="checkbox" name="fcp-interface-active"<%if fcp-interface-active> checked="checked"<%/if> /> <%= Page.Options.Option.FcpInterfaceActive.Description|l10n|html></p>
+
+               <p>
+                       <%= Page.Options.Option.FcpFullAccessRequired.Description|l10n|html|replace needle="{link}" replacement='<a href="/config/fcp">'|replace needle="{/link}" replacement='</a>'>
+                       <select name="fcp-full-access-required">
+                               <option value="0"<%if fcp-full-access-required|match value="0"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.No|l10n|html></option>
+                               <option value="1"<%if fcp-full-access-required|match value="1"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.Writing|l10n|html></option>
+                               <option value="2"<%if fcp-full-access-required|match value="2"> selected="selected"<%/if>><%= Page.Options.Option.FcpFullAccessRequired.Value.Always|l10n|html></option>
+                       </select>
+               </p>
+
                <h2><%= Page.Options.Section.RescueOptions.Title|l10n|html></h2>
 
                <p><%= Page.Options.Option.SoneRescueMode.Description1|l10n|html></p>