--- /dev/null
+package net.pterodactylus.fcp;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Freenet-specific Base64 implementation.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public class FreenetBase64 {
+
+ private static final char[] FREENET_BASE64_ALPHABET =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-".toCharArray();
+ private static final int[] REVERSE_FREENET_BASE64_ALPHABET = reverse(FREENET_BASE64_ALPHABET);
+
+ private static int[] reverse(char[] alphabet) {
+ String alphabetString = new String(alphabet);
+ int[] reversedAlphabet = new int[128];
+ for (int i = 0; i < 128; i++) {
+ if (alphabetString.indexOf(i) > -1) {
+ reversedAlphabet[i] = alphabetString.indexOf(i);
+ } else {
+ reversedAlphabet[i] = -1;
+ }
+ }
+ return reversedAlphabet;
+ }
+
+ public String encode(byte[] data) {
+ StringBuilder result = new StringBuilder();
+ int currentValue = 0;
+ int index = 0;
+ while (index < data.length) {
+ currentValue = (currentValue << 8) | data[index];
+ if (index % 3 == 2) {
+ result.append(FREENET_BASE64_ALPHABET[(currentValue >> 18) & 0x3f]);
+ result.append(FREENET_BASE64_ALPHABET[(currentValue >> 12) & 0x3f]);
+ result.append(FREENET_BASE64_ALPHABET[(currentValue >> 6) & 0x3f]);
+ result.append(FREENET_BASE64_ALPHABET[currentValue & 0x3f]);
+ }
+ index++;
+ }
+ if (index % 3 == 1) {
+ result.append(FREENET_BASE64_ALPHABET[(currentValue >> 2) & 0x3f]);
+ result.append(FREENET_BASE64_ALPHABET[(currentValue << 4) & 0x3f]);
+ result.append("==");
+ } else if (index % 3 == 2) {
+ result.append(FREENET_BASE64_ALPHABET[(currentValue >> 10) & 0x3f]);
+ result.append(FREENET_BASE64_ALPHABET[(currentValue >> 4) & 0x3f]);
+ result.append(FREENET_BASE64_ALPHABET[(currentValue << 2) & 0x3f]);
+ result.append("=");
+ }
+ return result.toString();
+ }
+
+ public byte[] decode(String data) {
+ ByteArrayOutputStream dataOutput = new ByteArrayOutputStream(data.length() * 3 / 4);
+ try {
+ int currentValue = 0;
+ int index = 0;
+ for (char c : data.toCharArray()) {
+ if (c == '=') {
+ break;
+ }
+ currentValue = (currentValue << 6) | REVERSE_FREENET_BASE64_ALPHABET[c];
+ if (index % 4 == 3) {
+ dataOutput.write((currentValue >> 16) & 0xff);
+ dataOutput.write((currentValue >> 8) & 0xff);
+ dataOutput.write(currentValue & 0xff);
+ }
+ index++;
+ }
+ if (index % 4 == 2) {
+ dataOutput.write((currentValue >> 4) & 0xff);
+ } else if (index % 4 == 3) {
+ dataOutput.write((currentValue >> 10) & 0xff);
+ dataOutput.write((currentValue >> 2) & 0xff);
+ }
+ return dataOutput.toByteArray();
+ } finally {
+ try {
+ dataOutput.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+}
--- /dev/null
+package net.pterodactylus.fcp;
+
+/**
+ * Non-validating wrapper around a Freenet key.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class Key {
+
+ private final String key;
+
+ public Key(String key) {
+ this.key = key;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+}
--- /dev/null
+package net.pterodactylus.fcp;
+
+/**
+ * The “LoadPlugin” message is used to load a plugin.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class LoadPlugin extends FcpMessage {
+
+ public enum UrlType {
+
+ OFFICIAL,
+ FILE,
+ FREENET,
+ URL
+
+ }
+
+ public enum OfficialSource {
+
+ FREENET,
+ HTTPS
+
+ }
+
+ public LoadPlugin(String identifier) {
+ super("LoadPlugin");
+ setField("Identifier", identifier);
+ }
+
+ public void setPluginUrl(String pluginUrl) {
+ setField("PluginURL", pluginUrl);
+ }
+
+ public void setUrlType(UrlType urlType) {
+ setField("URLType", urlType.toString().toLowerCase());
+ }
+
+ public void setStore(boolean store) {
+ setField("Store", String.valueOf(store));
+ }
+
+ public void setOfficialSource(OfficialSource officialSource) {
+ setField("OfficialSource", officialSource.toString().toLowerCase());
+ }
+
+}
--- /dev/null
+package net.pterodactylus.fcp;
+
+/**
+ * All possible peer note types.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public enum PeerNoteType {
+
+ PRIVATE_DARKNET_COMMENT(1);
+
+ private int value;
+
+ PeerNoteType(int value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(value);
+ }
+
+}
--- /dev/null
+package net.pterodactylus.fcp;
+
+/**
+ * The “ReloadPlugin” message is used to reload a plugin.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public class ReloadPlugin extends FcpMessage {
+
+ public ReloadPlugin(String identifier) {
+ super("ReloadPlugin");
+ setField("Identifier", identifier);
+ }
+
+ public void setPluginName(String pluginName) {
+ setField("PluginName", pluginName);
+ }
+
+ public void setMaxWaitTime(int maxWaitTime) {
+ setField("MaxWaitTime", String.valueOf(maxWaitTime));
+ }
+
+ public void setPurge(boolean purge) {
+ setField("Purge", String.valueOf(purge));
+ }
+
+ public void setStore(boolean store) {
+ setField("Store", String.valueOf(store));
+ }
+
+}
--- /dev/null
+package net.pterodactylus.fcp;
+
+/**
+ * Progress information about a request.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class RequestProgress {
+
+ private final int total;
+ private final int required;
+ private final int failed;
+ private final int fatallyFailed;
+ private final long lastProgress;
+ private final int succeeded;
+ private final boolean finalizedTotal;
+ private final int minSuccessFetchBlocks;
+
+ public RequestProgress(int total, int required, int failed, int fatallyFailed, long lastProgress, int succeeded,
+ boolean finalizedTotal, int minSuccessFetchBlocks) {
+ this.total = total;
+ this.required = required;
+ this.failed = failed;
+ this.fatallyFailed = fatallyFailed;
+ this.lastProgress = lastProgress;
+ this.succeeded = succeeded;
+ this.finalizedTotal = finalizedTotal;
+ this.minSuccessFetchBlocks = minSuccessFetchBlocks;
+ }
+
+ public int getTotal() {
+ return total;
+ }
+
+ public int getRequired() {
+ return required;
+ }
+
+ public int getFailed() {
+ return failed;
+ }
+
+ public int getFatallyFailed() {
+ return fatallyFailed;
+ }
+
+ public long getLastProgress() {
+ return lastProgress;
+ }
+
+ public int getSucceeded() {
+ return succeeded;
+ }
+
+ public boolean isFinalizedTotal() {
+ return finalizedTotal;
+ }
+
+ public int getMinSuccessFetchBlocks() {
+ return minSuccessFetchBlocks;
+ }
+
+}
--- /dev/null
+package net.pterodactylus.fcp;
+
+/**
+ * The “UnsubscribeUSK” message is used to cancel a {@link SubscribeUSK USK subscription}.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public class UnsubscribeUSK extends FcpMessage {
+
+ public UnsubscribeUSK(String identifier) {
+ super("UnsubscribeUSK");
+ setField("Identifier", identifier);
+ }
+
+}