From: David ‘Bombe’ Roden Date: Tue, 28 Apr 2020 08:44:24 +0000 (+0200) Subject: 🔀 Merge branch 'release/v82' X-Git-Tag: v82^0 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=HEAD;hp=b4d2d68b5ea4f4edc7337f380cfe078756678126 🔀 Merge branch 'release/v82' --- diff --git a/.builds/jdk-1.8.yml b/.builds/jdk-1.8.yml index 5edd26d..2c43d38 100644 --- a/.builds/jdk-1.8.yml +++ b/.builds/jdk-1.8.yml @@ -9,7 +9,7 @@ tasks: ./gradlew clean - build: | cd sone - ./gradlew build + ./gradlew -x findbugsTest build - test: | cd sone ./gradlew test diff --git a/.gitignore b/.gitignore index fce4db9..abaaab1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /.gradle/ /build/ + +sone.properties diff --git a/build.gradle b/build.gradle index 0beb42a..db5af2b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,14 @@ -group = 'net.pterodactylus' -version = '80' +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -buildscript { - ext.kotlinVersion = '1.2.71' - repositories { - mavenCentral() - } - dependencies { - classpath group: 'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: '1.4.0' - classpath group: 'org.jetbrains.kotlin', name: 'kotlin-gradle-plugin', version: kotlinVersion - classpath group: 'org.jetbrains.kotlin', name: 'kotlin-noarg', version: kotlinVersion - } +plugins { + id 'org.jetbrains.kotlin.jvm' version '1.3.70' + id 'org.jetbrains.kotlin.plugin.noarg' version '1.3.70' + id 'info.solidsoft.pitest' version '1.4.5' } +group = 'net.pterodactylus' +version = '82' + repositories { mavenCentral() maven { url "https://maven.pterodactylus.net/" } @@ -20,14 +16,18 @@ repositories { apply plugin: 'java' -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +sourceCompatibility = 1.8 +targetCompatibility = 1.8 tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -apply plugin: 'kotlin' +tasks.withType(KotlinCompile) { + kotlinOptions { + jvmTarget = "1.8" + } +} configurations { provided { @@ -37,35 +37,55 @@ configurations { } compile.extendsFrom provided } - + dependencies { provided group: 'org.freenetproject', name: 'fred', version: '0.7.5.1475' provided group: 'org.freenetproject', name: 'freenet-ext', version: '29' provided group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.54' - compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib' - compile group: 'net.pterodactylus', name: 'utils', version: '0.12.4' + compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8' + compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: '1.3.0-RC' + + compile group: 'net.pterodactylus', name: 'utils', version: '0.13.1' compile group: 'com.google.inject', name: 'guice', version: '4.2.2' - compile group: 'com.google.guava', name: 'guava', version: '27.0.1-android' + compile group: 'com.google.guava', name: 'guava', version: '27.0.1-jre' compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.1' compile group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.9.1' compile group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2' compile group: 'org.jsoup', name: 'jsoup', version: '1.10.2' + compile group: 'io.dropwizard.metrics', name: 'metrics-core', version: '4.1.0' + compile group: 'javax.activation', name: 'javax.activation-api', version: '1.2.0' testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit' testCompile group: 'junit', name: 'junit', version: '4.11' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.10.0' + testCompile group: 'org.mockito', name: 'mockito-core', version: '2.28.2' testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3' } apply from: 'version.gradle' -test { +task parallelTest(type: Test) { maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1 + useJUnit { + excludeCategories 'net.pterodactylus.sone.test.NotParallel' + } +} + +task notParallelTest(type: Test) { + maxParallelForks = 1 + useJUnit { + includeCategories 'net.pterodactylus.sone.test.NotParallel' + } + dependsOn parallelTest +} + +test { + exclude '**' + dependsOn parallelTest, notParallelTest } task fatJar(type: Jar) { - archiveName = project.name.toLowerCase() + '-jar-with-dependencies.jar' + archiveFileName = project.name.toLowerCase() + '-jar-with-dependencies.jar' from { (configurations.runtime - configurations.provided).collect { it.isDirectory() ? it : zipTree(it) } } manifest { attributes('Plugin-Main-Class': 'net.pterodactylus.sone.main.SonePlugin') @@ -86,14 +106,13 @@ javadoc { apply plugin: 'jacoco' jacoco { - toolVersion = '0.7.9' + toolVersion = '0.8.4' } jacocoTestReport.dependsOn test -apply plugin: 'info.solidsoft.pitest' - pitest { + pitestVersion = '1.4.10' outputFormats = ['HTML', 'XML'] timestampedReports = false timeoutFactor = 3.0 @@ -125,8 +144,6 @@ task countLines { dependsOn tasks.countLinesTest } -apply plugin: 'kotlin-noarg' - noArg { annotation('net.pterodactylus.sone.main.NoArg') } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index c19a936..1cdded7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index b0df7a9..da1b2f8 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -1,5 +1,5 @@ /* - * Sone - Core.java - Copyright © 2010–2019 David Roden + * Sone - Core.java - Copyright © 2010–2020 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 @@ -24,6 +24,7 @@ import static com.google.common.primitives.Longs.tryParse; import static java.lang.String.format; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; +import static net.pterodactylus.sone.data.AlbumKt.getAllImages; import java.util.ArrayList; import java.util.Collection; @@ -37,30 +38,20 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.*; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import com.codahale.metrics.*; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidAlbumFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidImageFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound; import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound; -import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; -import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent; -import net.pterodactylus.sone.core.event.MarkPostKnownEvent; -import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent; -import net.pterodactylus.sone.core.event.MarkSoneKnownEvent; -import net.pterodactylus.sone.core.event.NewPostFoundEvent; -import net.pterodactylus.sone.core.event.NewPostReplyFoundEvent; -import net.pterodactylus.sone.core.event.NewSoneFoundEvent; -import net.pterodactylus.sone.core.event.PostRemovedEvent; -import net.pterodactylus.sone.core.event.PostReplyRemovedEvent; -import net.pterodactylus.sone.core.event.SoneLockedEvent; -import net.pterodactylus.sone.core.event.SoneRemovedEvent; -import net.pterodactylus.sone.core.event.SoneUnlockedEvent; +import net.pterodactylus.sone.core.event.*; import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Image; @@ -71,6 +62,7 @@ import net.pterodactylus.sone.data.Profile.Field; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.SoneKt; import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent; import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.database.AlbumBuilder; @@ -98,8 +90,7 @@ import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.util.thread.NamedThreadFactory; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import com.google.common.collect.FluentIterable; +import com.google.common.base.Stopwatch; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @@ -121,6 +112,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** The start time. */ private final long startupTime = System.currentTimeMillis(); + private final AtomicBoolean debug = new AtomicBoolean(false); + /** The preferences. */ private final Preferences preferences; @@ -184,24 +177,13 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, /** The time the configuration was last touched. */ private volatile long lastConfigurationUpdate; - /** - * Creates a new core. - * - * @param configuration - * The configuration of the core - * @param freenetInterface - * The freenet interface - * @param identityManager - * The identity manager - * @param webOfTrustUpdater - * The WebOfTrust updater - * @param eventBus - * The event bus - * @param database - * The database - */ + private final MetricRegistry metricRegistry; + private final Histogram configurationSaveTimeHistogram; + + private final SoneUriCreator soneUriCreator; + @Inject - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database) { + public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; @@ -212,7 +194,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, this.webOfTrustUpdater = webOfTrustUpdater; this.eventBus = eventBus; this.database = database; + this.metricRegistry = metricRegistry; + this.soneUriCreator = soneUriCreator; preferences = new Preferences(eventBus); + this.configurationSaveTimeHistogram = metricRegistry.histogram("configuration.save.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0))); } // @@ -228,6 +213,16 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, return startupTime; } + @Nonnull + public boolean getDebug() { + return debug.get(); + } + + public void setDebug() { + debug.set(true); + eventBus.post(new DebugActivatedEvent()); + } + /** * Returns the options used by the core. * @@ -618,7 +613,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, sone.setLatestEdition(fromNullable(tryParse(property)).or(0L)); sone.setClient(new Client("Sone", SonePlugin.getPluginVersion())); sone.setKnown(true); - SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, ownIdentity.getId()); + SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, metricRegistry, soneUriCreator, ownIdentity.getId()); soneInserter.insertionDelayChanged(new InsertionDelayChangedEvent(preferences.getInsertionDelay())); eventBus.register(soneInserter); synchronized (soneInserters) { @@ -627,6 +622,11 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, loadSone(sone); database.storeSone(sone); sone.setStatus(SoneStatus.idle); + if (sone.getPosts().isEmpty() && sone.getReplies().isEmpty() && getAllImages(sone.getRootAlbum()).isEmpty()) { + // dirty hack + lockSone(sone); + eventBus.post(new SoneLockedOnStartup(sone)); + } soneInserter.start(); return sone; } @@ -738,75 +738,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } /** - * Sets the trust value of the given origin Sone for the target Sone. - * - * @param origin - * The origin Sone - * @param target - * The target Sone - * @param trustValue - * The trust value (from {@code -100} to {@code 100}) - */ - public void setTrust(Sone origin, Sone target, int trustValue) { - checkNotNull(origin, "origin must not be null"); - checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin must be a local Sone"); - checkNotNull(target, "target must not be null"); - checkArgument((trustValue >= -100) && (trustValue <= 100), "trustValue must be within [-100, 100]"); - webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), trustValue, preferences.getTrustComment()); - } - - /** - * Removes any trust assignment for the given target Sone. - * - * @param origin - * The trust origin - * @param target - * The trust target - */ - public void removeTrust(Sone origin, Sone target) { - checkNotNull(origin, "origin must not be null"); - checkNotNull(target, "target must not be null"); - checkArgument(origin.getIdentity() instanceof OwnIdentity, "origin must be a local Sone"); - webOfTrustUpdater.setTrust((OwnIdentity) origin.getIdentity(), target.getIdentity(), null, null); - } - - /** - * Assigns the configured positive trust value for the given target. - * - * @param origin - * The trust origin - * @param target - * The trust target - */ - public void trustSone(Sone origin, Sone target) { - setTrust(origin, target, preferences.getPositiveTrust()); - } - - /** - * Assigns the configured negative trust value for the given target. - * - * @param origin - * The trust origin - * @param target - * The trust target - */ - public void distrustSone(Sone origin, Sone target) { - setTrust(origin, target, preferences.getNegativeTrust()); - } - - /** - * Removes the trust assignment for the given target. - * - * @param origin - * The trust origin - * @param target - * The trust target - */ - public void untrustSone(Sone origin, Sone target) { - removeTrust(origin, target); - } - - /** * Updates the stored Sone with the given Sone. * * @param sone @@ -866,9 +797,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } for (PostReply postReply : soneComparison.getNewPostReplies()) { if (postReply.getSone().equals(newSone)) { - postReply.setKnown(true); + database.setPostReplyKnown(postReply); } else if (postReply.getTime() < database.getFollowingTime(newSone.getId())) { - postReply.setKnown(true); + database.setPostReplyKnown(postReply); } else if (!postReply.isKnown()) { events.add(new NewPostReplyFoundEvent(postReply)); } @@ -1045,7 +976,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, post.setKnown(true); } for (PostReply reply : replies) { - reply.setKnown(true); + database.setPostReplyKnown(reply); } logger.info(String.format("Sone loaded successfully: %s", sone)); @@ -1063,7 +994,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, * The text of the post * @return The created post */ - public Post createPost(Sone sone, Optional recipient, String text) { + public Post createPost(Sone sone, @Nullable Sone recipient, String text) { checkNotNull(text, "text must not be null"); checkArgument(text.trim().length() > 0, "text must not be empty"); if (!sone.isLocal()) { @@ -1072,8 +1003,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, } PostBuilder postBuilder = database.newPostBuilder(); postBuilder.from(sone.getId()).randomId().currentTime().withText(text.trim()); - if (recipient.isPresent()) { - postBuilder.to(recipient.get().getId()); + if (recipient != null) { + postBuilder.to(recipient.getId()); } final Post post = postBuilder.build(); database.storePost(post); @@ -1187,7 +1118,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ public void markReplyKnown(PostReply reply) { boolean previouslyKnown = reply.isKnown(); - reply.setKnown(true); + database.setPostReplyKnown(reply); eventBus.post(new MarkPostReplyKnownEvent(reply)); if (!previouslyKnown) { touchConfiguration(); @@ -1359,7 +1290,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, synchronized (soneInserters) { for (Entry soneInserter : soneInserters.entrySet()) { soneInserter.getValue().stop(); - saveSone(soneInserter.getKey()); + Sone latestSone = getLocalSone(soneInserter.getKey().getId()); + saveSone(latestSone); } } synchronized (soneRescuers) { @@ -1460,7 +1392,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue(sonePrefix + "/Likes/Reply/" + replyLikeCounter + "/ID").setValue(null); /* save albums. first, collect in a flat structure, top-level first. */ - List albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList(); + List albums = SoneKt.getAllAlbums(sone); int albumCounter = 0; for (Album album : albums) { @@ -1501,8 +1433,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, configuration.getStringValue(sonePrefix + "/Options/ShowCustomAvatars").setValue(sone.getOptions().getShowCustomAvatars().name()); configuration.getStringValue(sonePrefix + "/Options/LoadLinkedImages").setValue(sone.getOptions().getLoadLinkedImages().name()); - configuration.save(); - webOfTrustUpdater.setProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition", String.valueOf(sone.getLatestEdition())); logger.log(Level.INFO, String.format("Sone %s saved.", sone)); @@ -1540,7 +1470,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, database.save(); /* now save it. */ + Stopwatch stopwatch = Stopwatch.createStarted(); configuration.save(); + configurationSaveTimeHistogram.update(stopwatch.elapsed(TimeUnit.MICROSECONDS)); } catch (ConfigurationException ce1) { logger.log(Level.SEVERE, "Could not store configuration!", ce1); @@ -1580,7 +1512,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ @Subscribe public void ownIdentityAdded(OwnIdentityAddedEvent ownIdentityAddedEvent) { - OwnIdentity ownIdentity = ownIdentityAddedEvent.ownIdentity(); + OwnIdentity ownIdentity = ownIdentityAddedEvent.getOwnIdentity(); logger.log(Level.FINEST, String.format("Adding OwnIdentity: %s", ownIdentity)); if (ownIdentity.hasContext("Sone")) { addLocalSone(ownIdentity); @@ -1595,7 +1527,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ @Subscribe public void ownIdentityRemoved(OwnIdentityRemovedEvent ownIdentityRemovedEvent) { - OwnIdentity ownIdentity = ownIdentityRemovedEvent.ownIdentity(); + OwnIdentity ownIdentity = ownIdentityRemovedEvent.getOwnIdentity(); logger.log(Level.FINEST, String.format("Removing OwnIdentity: %s", ownIdentity)); trustedIdentities.removeAll(ownIdentity); } @@ -1608,9 +1540,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ @Subscribe public void identityAdded(IdentityAddedEvent identityAddedEvent) { - Identity identity = identityAddedEvent.identity(); + Identity identity = identityAddedEvent.getIdentity(); logger.log(Level.FINEST, String.format("Adding Identity: %s", identity)); - trustedIdentities.put(identityAddedEvent.ownIdentity(), identity); + trustedIdentities.put(identityAddedEvent.getOwnIdentity(), identity); addRemoteSone(identity); } @@ -1622,7 +1554,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ @Subscribe public void identityUpdated(IdentityUpdatedEvent identityUpdatedEvent) { - Identity identity = identityUpdatedEvent.identity(); + Identity identity = identityUpdatedEvent.getIdentity(); final Sone sone = getRemoteSone(identity.getId()); if (sone.isLocal()) { return; @@ -1646,8 +1578,8 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ @Subscribe public void identityRemoved(IdentityRemovedEvent identityRemovedEvent) { - OwnIdentity ownIdentity = identityRemovedEvent.ownIdentity(); - Identity identity = identityRemovedEvent.identity(); + OwnIdentity ownIdentity = identityRemovedEvent.getOwnIdentity(); + Identity identity = identityRemovedEvent.getIdentity(); trustedIdentities.remove(ownIdentity, identity); for (Entry> trustedIdentity : trustedIdentities.asMap().entrySet()) { if (trustedIdentity.getKey().equals(ownIdentity)) { @@ -1680,9 +1612,9 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, */ @Subscribe public void imageInsertFinished(ImageInsertFinishedEvent imageInsertFinishedEvent) { - logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", imageInsertFinishedEvent.image(), imageInsertFinishedEvent.resultingUri())); - imageInsertFinishedEvent.image().modify().setKey(imageInsertFinishedEvent.resultingUri().toString()).update(); - deleteTemporaryImage(imageInsertFinishedEvent.image().getId()); + logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", imageInsertFinishedEvent.getImage(), imageInsertFinishedEvent.getResultingUri())); + imageInsertFinishedEvent.getImage().modify().setKey(imageInsertFinishedEvent.getResultingUri().toString()).update(); + deleteTemporaryImage(imageInsertFinishedEvent.getImage().getId()); touchConfiguration(); } diff --git a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java index 1ed2024..35ac6a1 100644 --- a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java +++ b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java @@ -1,5 +1,5 @@ /* - * Sone - FreenetInterface.java - Copyright © 2010–2019 David Roden + * Sone - FreenetInterface.java - Copyright © 2010–2020 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 @@ -21,7 +21,6 @@ import static freenet.keys.USK.create; import static java.lang.String.format; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.freenet.Key.routingKey; import java.io.IOException; import java.net.MalformedURLException; @@ -75,6 +74,7 @@ import freenet.support.api.Bucket; import freenet.support.api.RandomAccessBucket; import freenet.support.io.ArrayBucket; import freenet.support.io.ResumeFailedException; +import net.pterodactylus.sone.freenet.*; /** * Contains all necessary functionality for interacting with the Freenet node. @@ -91,6 +91,8 @@ public class FreenetInterface { /** The node to interact with. */ private final Node node; + private final SoneUriCreator soneUriCreator; + /** The high-level client to use for requests. */ private final HighLevelSimpleClient client; private final RequestClient requestClient = new RequestClientBuilder().realTime().build(); @@ -104,18 +106,11 @@ public class FreenetInterface { private final RequestClient imageInserts = new RequestClientBuilder().realTime().build(); private final RequestClient imageLoader = new RequestClientBuilder().realTime().build(); - /** - * Creates a new Freenet interface. - * - * @param eventBus - * The event bus - * @param node - * The node to interact with - */ @Inject - public FreenetInterface(EventBus eventBus, Node node) { + public FreenetInterface(EventBus eventBus, Node node, SoneUriCreator soneUriCreator) { this.eventBus = eventBus; this.node = node; + this.soneUriCreator = soneUriCreator; this.client = node.clientCore.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS, false, true); } @@ -255,7 +250,7 @@ public class FreenetInterface { public void registerActiveUsk(FreenetURI requestUri, USKCallback uskCallback) { try { - soneUskCallbacks.put(routingKey(requestUri), uskCallback); + soneUskCallbacks.put(FreenetURIsKt.getRoutingKeyString(requestUri), uskCallback); node.clientCore.uskManager.subscribe(create(requestUri), uskCallback, true, requestClient); } catch (MalformedURLException mue1) { @@ -267,7 +262,7 @@ public class FreenetInterface { public void registerPassiveUsk(FreenetURI requestUri, USKCallback uskCallback) { try { - soneUskCallbacks.put(routingKey(requestUri), uskCallback); + soneUskCallbacks.put(FreenetURIsKt.getRoutingKeyString(requestUri), uskCallback); node.clientCore .uskManager .subscribe(create(requestUri), uskCallback, false, requestClient); @@ -291,9 +286,9 @@ public class FreenetInterface { } try { logger.log(Level.FINEST, String.format("Unsubscribing from USK for %s…", sone)); - node.clientCore.uskManager.unsubscribe(USK.create(sone.getRequestUri()), uskCallback); + node.clientCore.uskManager.unsubscribe(USK.create(soneUriCreator.getRequestUri(sone)), uskCallback); } catch (MalformedURLException mue1) { - logger.log(Level.FINE, String.format("Could not unsubscribe USK “%s”!", sone.getRequestUri()), mue1); + logger.log(Level.FINE, String.format("Could not unsubscribe USK “%s”!", soneUriCreator.getRequestUri(sone)), mue1); } } @@ -327,7 +322,7 @@ public class FreenetInterface { }; try { node.clientCore.uskManager.subscribe(USK.create(uri), uskCallback, true, requestClient); - uriUskCallbacks.put(uri, uskCallback); + uriUskCallbacks.put(USK.create(uri).clearCopy().getURI(), uskCallback); } catch (MalformedURLException mue1) { logger.log(Level.WARNING, String.format("Could not subscribe to USK: %s", uri), mue1); } @@ -340,12 +335,12 @@ public class FreenetInterface { * The URI to unregister the USK watcher for */ public void unregisterUsk(FreenetURI uri) { - USKCallback uskCallback = uriUskCallbacks.remove(uri); - if (uskCallback == null) { - logger.log(Level.INFO, String.format("Could not unregister unknown USK: %s", uri)); - return; - } try { + USKCallback uskCallback = uriUskCallbacks.remove(USK.create(uri).clearCopy().getURI()); + if (uskCallback == null) { + logger.log(Level.INFO, String.format("Could not unregister unknown USK: %s", uri)); + return; + } node.clientCore.uskManager.unsubscribe(USK.create(uri), uskCallback); } catch (MalformedURLException mue1) { logger.log(Level.INFO, String.format("Could not unregister invalid USK: %s", uri), mue1); diff --git a/src/main/java/net/pterodactylus/sone/core/ImageInserter.java b/src/main/java/net/pterodactylus/sone/core/ImageInserter.java index fc76ca9..76519f4 100644 --- a/src/main/java/net/pterodactylus/sone/core/ImageInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/ImageInserter.java @@ -1,5 +1,5 @@ /* - * Sone - ImageInserter.java - Copyright © 2011–2019 David Roden + * Sone - ImageInserter.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/core/Options.java b/src/main/java/net/pterodactylus/sone/core/Options.java index af88dd8..398374a 100644 --- a/src/main/java/net/pterodactylus/sone/core/Options.java +++ b/src/main/java/net/pterodactylus/sone/core/Options.java @@ -1,5 +1,5 @@ /* - * Sone - Options.java - Copyright © 2010–2019 David Roden + * Sone - Options.java - Copyright © 2010–2020 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 @@ -23,8 +23,6 @@ import java.util.Map; import net.pterodactylus.sone.utils.Option; -import com.google.common.base.Predicate; - /** * Stores various options that influence Sone’s behaviour. */ diff --git a/src/main/java/net/pterodactylus/sone/core/PreferenceChangedEvent.kt b/src/main/java/net/pterodactylus/sone/core/PreferenceChangedEvent.kt deleted file mode 100644 index 2ebb62d..0000000 --- a/src/main/java/net/pterodactylus/sone/core/PreferenceChangedEvent.kt +++ /dev/null @@ -1,3 +0,0 @@ -package net.pterodactylus.sone.core - -data class PreferenceChangedEvent(val preferenceName: String, val newValue: Any) diff --git a/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.java b/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.java deleted file mode 100644 index a730983..0000000 --- a/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.java +++ /dev/null @@ -1,103 +0,0 @@ -package net.pterodactylus.sone.core; - -import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired; -import net.pterodactylus.util.config.Configuration; -import net.pterodactylus.util.config.ConfigurationException; - -/** - * Loads preferences stored in a {@link Configuration} into a {@link - * Preferences} object. - */ -public class PreferencesLoader { - - private final Preferences preferences; - - public PreferencesLoader(Preferences preferences) { - this.preferences = preferences; - } - - public void loadFrom(Configuration configuration) { - loadInsertionDelay(configuration); - loadPostsPerPage(configuration); - loadImagesPerPage(configuration); - loadCharactersPerPost(configuration); - loadPostCutOffLength(configuration); - loadRequireFullAccess(configuration); - loadPositiveTrust(configuration); - loadNegativeTrust(configuration); - loadTrustComment(configuration); - loadFcpInterfaceActive(configuration); - loadFcpFullAccessRequired(configuration); - } - - private void loadInsertionDelay(Configuration configuration) { - preferences.setNewInsertionDelay(configuration.getIntValue( - "Option/InsertionDelay").getValue(null)); - } - - private void loadPostsPerPage(Configuration configuration) { - preferences.setNewPostsPerPage( - configuration.getIntValue("Option/PostsPerPage") - .getValue(null)); - } - - private void loadImagesPerPage(Configuration configuration) { - preferences.setNewImagesPerPage( - configuration.getIntValue("Option/ImagesPerPage") - .getValue(null)); - } - - private void loadCharactersPerPost(Configuration configuration) { - preferences.setNewCharactersPerPost( - configuration.getIntValue("Option/CharactersPerPost") - .getValue(null)); - } - - private void loadPostCutOffLength(Configuration configuration) { - try { - preferences.setNewPostCutOffLength( - configuration.getIntValue("Option/PostCutOffLength") - .getValue(null)); - } catch (IllegalArgumentException iae1) { - /* previous versions allowed -1, ignore and use default. */ - } - } - - private void loadRequireFullAccess(Configuration configuration) { - preferences.setNewRequireFullAccess( - configuration.getBooleanValue("Option/RequireFullAccess") - .getValue(null)); - } - - private void loadPositiveTrust(Configuration configuration) { - preferences.setNewPositiveTrust( - configuration.getIntValue("Option/PositiveTrust") - .getValue(null)); - } - - private void loadNegativeTrust(Configuration configuration) { - preferences.setNewNegativeTrust( - configuration.getIntValue("Option/NegativeTrust") - .getValue(null)); - } - - private void loadTrustComment(Configuration configuration) { - preferences.setNewTrustComment( - configuration.getStringValue("Option/TrustComment") - .getValue(null)); - } - - private void loadFcpInterfaceActive(Configuration configuration) { - preferences.setNewFcpInterfaceActive(configuration.getBooleanValue( - "Option/ActivateFcpInterface").getValue(null)); - } - - private void loadFcpFullAccessRequired(Configuration configuration) { - Integer fullAccessRequiredInteger = configuration - .getIntValue("Option/FcpFullAccessRequired").getValue(null); - preferences.setNewFcpFullAccessRequired( - (fullAccessRequiredInteger == null) ? null : - FullAccessRequired.values()[fullAccessRequiredInteger]); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java index ba1c632..43a87fb 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java @@ -1,5 +1,5 @@ /* - * Sone - SoneDownloaderImpl.java - Copyright © 2010–2019 David Roden + * Sone - SoneDownloaderImpl.java - Copyright © 2010–2020 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 @@ -17,7 +17,6 @@ package net.pterodactylus.sone.core; -import static freenet.support.io.Closer.close; import static java.lang.String.format; import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.DAYS; @@ -183,23 +182,19 @@ public class SoneDownloaderImpl extends AbstractService implements SoneDownloade * @return The parsed Sone, or {@code null} if the Sone could not be parsed */ private Sone parseSone(Sone originalSone, FetchResult fetchResult, FreenetURI requestUri) { - logger.log(Level.FINEST, String.format("Parsing FetchResult (%d bytes, %s) for %s…", fetchResult.size(), fetchResult.getMimeType(), originalSone)); + logger.finest(() -> format("Parsing FetchResult (%d bytes, %s) for %s…", fetchResult.size(), fetchResult.getMimeType(), originalSone)); Bucket soneBucket = fetchResult.asBucket(); - InputStream soneInputStream = null; - try { - soneInputStream = soneBucket.getInputStream(); - Sone parsedSone = soneParser.parseSone(originalSone, - soneInputStream); + try (InputStream soneInputStream = soneBucket.getInputStream()) { + Sone parsedSone = soneParser.parseSone(originalSone, soneInputStream); if (parsedSone != null) { - logger.log(Level.FINER, "Sone %s was successfully parsed.", parsedSone); + logger.finer(() -> format("Sone %s was successfully parsed.", parsedSone)); parsedSone.setLatestEdition(requestUri.getEdition()); } return parsedSone; } catch (Exception e1) { - logger.log(Level.WARNING, String.format("Could not parse Sone from %s!", requestUri), e1); + logger.log(Level.WARNING, e1, () -> format("Could not parse Sone from %s!", requestUri)); } finally { - close(soneInputStream); - close(soneBucket); + soneBucket.free(); } return null; } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneException.java b/src/main/java/net/pterodactylus/sone/core/SoneException.java index e460ee7..152f629 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneException.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneException.java @@ -1,5 +1,5 @@ /* - * Sone - SoneException.java - Copyright © 2010–2019 David Roden + * Sone - SoneException.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java b/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java index 8a0ff53..4aa71c1 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInsertException.java @@ -1,5 +1,5 @@ /* - * Sone - SoneInsertException.java - Copyright © 2011–2019 David Roden + * Sone - SoneInsertException.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 79a6259..fcbe1b5 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -1,5 +1,5 @@ /* - * Sone - SoneInserter.java - Copyright © 2010–2019 David Roden + * Sone - SoneInserter.java - Copyright © 2010–2020 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 @@ -19,13 +19,13 @@ package net.pterodactylus.sone.core; import static java.lang.String.format; import static java.lang.System.currentTimeMillis; +import static java.util.concurrent.TimeUnit.*; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.data.Album.NOT_EMPTY; +import static java.util.stream.Collectors.toList; +import static net.pterodactylus.sone.data.PostKt.newestPostFirst; +import static net.pterodactylus.sone.data.ReplyKt.newestReplyFirst; -import java.io.Closeable; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; +import java.io.*; import java.nio.charset.Charset; import java.util.HashMap; import java.util.HashSet; @@ -35,18 +35,18 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; +import com.codahale.metrics.*; +import com.google.common.base.*; import net.pterodactylus.sone.core.SoneModificationDetector.LockableFingerprintProvider; import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent; import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent; import net.pterodactylus.sone.core.event.SoneInsertedEvent; import net.pterodactylus.sone.core.event.SoneInsertingEvent; -import net.pterodactylus.sone.data.Album; -import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.data.Reply; +import net.pterodactylus.sone.data.AlbumKt; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.SoneKt; import net.pterodactylus.sone.main.SonePlugin; -import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.util.template.HtmlFilter; import net.pterodactylus.util.template.ReflectionAccessor; @@ -58,8 +58,6 @@ import net.pterodactylus.util.template.TemplateParser; import net.pterodactylus.util.template.XmlFilter; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import com.google.common.collect.FluentIterable; import com.google.common.collect.Ordering; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @@ -103,8 +101,11 @@ public class SoneInserter extends AbstractService { private final FreenetInterface freenetInterface; private final SoneModificationDetector soneModificationDetector; + private final SoneUriCreator soneUriCreator; private final long delay; private final String soneId; + private final Histogram soneInsertDurationHistogram; + private final Meter soneInsertErrorMeter; /** * Creates a new Sone inserter. @@ -118,8 +119,8 @@ public class SoneInserter extends AbstractService { * @param soneId * The ID of the Sone to insert */ - public SoneInserter(final Core core, EventBus eventBus, FreenetInterface freenetInterface, final String soneId) { - this(core, eventBus, freenetInterface, soneId, new SoneModificationDetector(new LockableFingerprintProvider() { + public SoneInserter(final Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator, final String soneId) { + this(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, soneId, new SoneModificationDetector(new LockableFingerprintProvider() { @Override public boolean isLocked() { Sone sone = core.getSone(soneId); @@ -141,11 +142,14 @@ public class SoneInserter extends AbstractService { } @VisibleForTesting - SoneInserter(Core core, EventBus eventBus, FreenetInterface freenetInterface, String soneId, SoneModificationDetector soneModificationDetector, long delay) { + SoneInserter(Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator, String soneId, SoneModificationDetector soneModificationDetector, long delay) { super("Sone Inserter for “" + soneId + "”", false); this.core = core; this.eventBus = eventBus; this.freenetInterface = freenetInterface; + this.soneInsertDurationHistogram = metricRegistry.histogram("sone.insert.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0))); + this.soneInsertErrorMeter = metricRegistry.meter("sone.insert.errors"); + this.soneUriCreator = soneUriCreator; this.soneId = soneId; this.soneModificationDetector = soneModificationDetector; this.delay = delay; @@ -229,8 +233,11 @@ public class SoneInserter extends AbstractService { sone.setStatus(SoneStatus.inserting); long insertTime = currentTimeMillis(); eventBus.post(new SoneInsertingEvent(sone)); - FreenetURI finalUri = freenetInterface.insertDirectory(sone.getInsertUri(), insertInformation.generateManifestEntries(), "index.html"); - eventBus.post(new SoneInsertedEvent(sone, currentTimeMillis() - insertTime, insertInformation.getFingerprint())); + Stopwatch stopwatch = Stopwatch.createStarted(); + FreenetURI finalUri = freenetInterface.insertDirectory(soneUriCreator.getInsertUri(sone), insertInformation.generateManifestEntries(), "index.html"); + stopwatch.stop(); + soneInsertDurationHistogram.update(stopwatch.elapsed(MICROSECONDS)); + eventBus.post(new SoneInsertedEvent(sone, stopwatch.elapsed(MILLISECONDS), insertInformation.getFingerprint())); /* at this point we might already be stopped. */ if (shouldStop()) { /* if so, bail out, don’t change anything. */ @@ -242,6 +249,7 @@ public class SoneInserter extends AbstractService { success = true; logger.log(Level.INFO, String.format("Inserted Sone “%s” at %s.", sone.getName(), finalUri)); } catch (SoneException se1) { + soneInsertErrorMeter.mark(); eventBus.post(new SoneInsertAbortedEvent(sone, se1)); logger.log(Level.WARNING, String.format("Could not insert Sone “%s”!", sone.getName()), se1); } finally { @@ -299,13 +307,12 @@ public class SoneInserter extends AbstractService { soneProperties.put("id", sone.getId()); soneProperties.put("name", sone.getName()); soneProperties.put("time", currentTimeMillis()); - soneProperties.put("requestUri", sone.getRequestUri()); soneProperties.put("profile", sone.getProfile()); - soneProperties.put("posts", Ordering.from(Post.NEWEST_FIRST).sortedCopy(sone.getPosts())); - soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); + soneProperties.put("posts", Ordering.from(newestPostFirst()).sortedCopy(sone.getPosts())); + soneProperties.put("replies", Ordering.from(newestReplyFirst()).sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds())); - soneProperties.put("albums", FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).filter(NOT_EMPTY).toList()); + soneProperties.put("albums", SoneKt.getAllAlbums(sone).stream().filter(AlbumKt.notEmpty()::invoke).collect(toList())); manifestCreator = new ManifestCreator(core, soneProperties); } @@ -366,19 +373,13 @@ public class SoneInserter extends AbstractService { } public ManifestElement createManifestElement(String name, String contentType, String templateName) { - InputStreamReader templateInputStreamReader = null; - InputStream templateInputStream = null; Template template; - try { - templateInputStream = getClass().getResourceAsStream(templateName); - templateInputStreamReader = new InputStreamReader(templateInputStream, utf8Charset); + try (InputStream templateInputStream = getClass().getResourceAsStream(templateName); + InputStreamReader templateInputStreamReader = new InputStreamReader(templateInputStream, utf8Charset)) { template = TemplateParser.parse(templateInputStreamReader); - } catch (TemplateException te1) { - logger.log(Level.SEVERE, String.format("Could not parse template “%s”!", templateName), te1); + } catch (IOException | TemplateException e1) { + logger.log(Level.SEVERE, String.format("Could not parse template “%s”!", templateName), e1); return null; - } finally { - Closer.close(templateInputStreamReader); - Closer.close(templateInputStream); } TemplateContext templateContext = templateContextFactory.createTemplateContext(); @@ -386,17 +387,14 @@ public class SoneInserter extends AbstractService { templateContext.set("currentSone", soneProperties); templateContext.set("currentEdition", core.getUpdateChecker().getLatestEdition()); templateContext.set("version", SonePlugin.getPluginVersion()); - StringWriter writer = new StringWriter(); - try { + try (StringWriter writer = new StringWriter()) { template.render(templateContext, writer); RandomAccessBucket bucket = new ArrayBucket(writer.toString().getBytes(Charsets.UTF_8)); buckets.add(bucket); return new ManifestElement(name, bucket, contentType, bucket.size()); - } catch (TemplateException te1) { - logger.log(Level.SEVERE, String.format("Could not render template “%s”!", templateName), te1); + } catch (IOException | TemplateException e1) { + logger.log(Level.SEVERE, String.format("Could not render template “%s”!", templateName), e1); return null; - } finally { - Closer.close(writer); } } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java b/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java index 92fa3dc..793d332 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java @@ -1,7 +1,5 @@ package net.pterodactylus.sone.core; -import static com.google.common.base.Optional.absent; -import static com.google.common.base.Optional.of; import static com.google.common.base.Ticker.systemTicker; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -11,7 +9,6 @@ import net.pterodactylus.sone.data.Sone; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; -import com.google.common.base.Optional; import com.google.common.base.Ticker; /** @@ -24,7 +21,7 @@ class SoneModificationDetector { private final Ticker ticker; private final LockableFingerprintProvider lockableFingerprintProvider; private final AtomicInteger insertionDelay; - private Optional lastModificationTime; + private Long lastModificationTime; private String lastInsertFingerprint; private String lastCheckFingerprint; @@ -42,18 +39,18 @@ class SoneModificationDetector { public boolean isEligibleForInsert() { if (lockableFingerprintProvider.isLocked()) { - lastModificationTime = absent(); + lastModificationTime = null; lastCheckFingerprint = ""; return false; } String fingerprint = lockableFingerprintProvider.getFingerprint(); if (fingerprint.equals(lastInsertFingerprint)) { - lastModificationTime = absent(); + lastModificationTime = null; lastCheckFingerprint = fingerprint; return false; } if (!Objects.equal(lastCheckFingerprint, fingerprint)) { - lastModificationTime = of(ticker.read()); + lastModificationTime = ticker.read(); lastCheckFingerprint = fingerprint; return false; } @@ -67,11 +64,11 @@ class SoneModificationDetector { public void setFingerprint(String fingerprint) { lastInsertFingerprint = fingerprint; lastCheckFingerprint = lastInsertFingerprint; - lastModificationTime = absent(); + lastModificationTime = null; } private boolean insertionDelayHasPassed() { - return NANOSECONDS.toSeconds(ticker.read() - lastModificationTime.get()) >= insertionDelay.get(); + return NANOSECONDS.toSeconds(ticker.read() - lastModificationTime) >= insertionDelay.get(); } public boolean isModified() { diff --git a/src/main/java/net/pterodactylus/sone/core/SoneParser.java b/src/main/java/net/pterodactylus/sone/core/SoneParser.java index 1f30565..e1fd9ff 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneParser.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneParser.java @@ -1,38 +1,24 @@ package net.pterodactylus.sone.core; -import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.utils.NumberParsers.parseInt; -import static net.pterodactylus.sone.utils.NumberParsers.parseLong; +import static java.util.concurrent.TimeUnit.*; +import static java.util.logging.Logger.*; +import static net.pterodactylus.sone.utils.NumberParsers.*; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.io.*; +import java.util.*; +import java.util.logging.*; -import javax.inject.Inject; +import javax.annotation.*; +import javax.inject.*; -import net.pterodactylus.sone.data.Album; -import net.pterodactylus.sone.data.Client; -import net.pterodactylus.sone.data.Image; -import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.data.PostReply; -import net.pterodactylus.sone.data.Profile; -import net.pterodactylus.sone.data.Profile.DuplicateField; -import net.pterodactylus.sone.data.Profile.EmptyFieldName; -import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.database.Database; -import net.pterodactylus.sone.database.PostBuilder; -import net.pterodactylus.sone.database.PostReplyBuilder; -import net.pterodactylus.sone.database.SoneBuilder; -import net.pterodactylus.util.xml.SimpleXML; -import net.pterodactylus.util.xml.XML; +import net.pterodactylus.sone.data.*; +import net.pterodactylus.sone.data.Profile.*; +import net.pterodactylus.sone.database.*; +import net.pterodactylus.util.xml.*; -import org.w3c.dom.Document; +import com.codahale.metrics.*; +import com.google.common.base.*; +import org.w3c.dom.*; /** * Parses a {@link Sone} from an XML {@link InputStream}. @@ -42,15 +28,19 @@ public class SoneParser { private static final Logger logger = getLogger(SoneParser.class.getName()); private static final int MAX_PROTOCOL_VERSION = 0; private final Database database; + private final Histogram soneParsingDurationHistogram; @Inject - public SoneParser(Database database) { + public SoneParser(Database database, MetricRegistry metricRegistry) { this.database = database; + this.soneParsingDurationHistogram = metricRegistry.histogram("sone.parse.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0))); } + @Nullable public Sone parseSone(Sone originalSone, InputStream soneInputStream) throws SoneException { /* TODO - impose a size limit? */ + Stopwatch stopwatch = Stopwatch.createStarted(); Document document; /* XML parsing is not thread-safe. */ synchronized (this) { @@ -257,6 +247,7 @@ public class SoneParser { SimpleXML albumsXml = soneXml.getNode("albums"); Map allImages = new HashMap<>(); List topLevelAlbums = new ArrayList<>(); + Map allAlbums = new HashMap<>(); if (albumsXml != null) { for (SimpleXML albumXml : albumsXml.getNodes("album")) { String id = albumXml.getValue("id", null); @@ -269,7 +260,7 @@ public class SoneParser { } Album parent = null; if (parentId != null) { - parent = database.getAlbum(parentId); + parent = allAlbums.get(parentId); if (parent == null) { logger.log(Level.WARNING, String.format("Downloaded Sone %s has album with invalid parent!", sone)); return null; @@ -288,6 +279,7 @@ public class SoneParser { } else { topLevelAlbums.add(album); } + allAlbums.put(album.getId(), album); SimpleXML imagesXml = albumXml.getNode("images"); if (imagesXml != null) { for (SimpleXML imageXml : imagesXml.getNodes("image")) { @@ -334,6 +326,11 @@ public class SoneParser { sone.getRootAlbum().addAlbum(album); } + // record the duration + stopwatch.stop(); + soneParsingDurationHistogram.update(stopwatch.elapsed(MICROSECONDS)); + logger.fine(() -> "Parsed " + originalSone.getIdentity().getId() + "@" + originalSone.getLatestEdition() + " in " + stopwatch.elapsed(MICROSECONDS) + "μs."); + return sone; } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java b/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java index 2246ac9..2a1d14d 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneRescuer.java @@ -1,5 +1,5 @@ /* - * Sone - SoneRescuer.java - Copyright © 2011–2019 David Roden + * Sone - SoneRescuer.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/core/SoneUri.java b/src/main/java/net/pterodactylus/sone/core/SoneUri.java deleted file mode 100644 index 373077f..0000000 --- a/src/main/java/net/pterodactylus/sone/core/SoneUri.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Sone - SoneUri.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core; - -import static java.util.logging.Logger.getLogger; - -import java.net.MalformedURLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import freenet.keys.FreenetURI; - -/** - * Helper class that creates {@link FreenetURI}s for Sone to insert to and - * request from. - */ -public class SoneUri { - - /** The logger. */ - private static final Logger logger = getLogger(SoneUri.class.getName()); - - /** - * Generate a Sone URI from the given URI. - * - * @param uri - * The URI to derive the Sone URI from - * @return The derived URI - */ - public static FreenetURI create(String uri) { - try { - return new FreenetURI(uri).setDocName("Sone").setMetaString(new String[0]); - } catch (MalformedURLException mue1) { - /* this should never happen. */ - logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uri), mue1); - return null; - } - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java b/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java deleted file mode 100644 index 9f6963f..0000000 --- a/src/main/java/net/pterodactylus/sone/core/UpdateChecker.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Sone - UpdateChecker.java - Copyright © 2011–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core; - -import static java.util.logging.Logger.getLogger; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.util.Date; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.inject.Singleton; - -import net.pterodactylus.sone.core.event.UpdateFoundEvent; -import net.pterodactylus.sone.main.PluginHomepage; -import net.pterodactylus.sone.main.SonePlugin; -import net.pterodactylus.util.io.Closer; -import net.pterodactylus.util.version.Version; - -import com.google.common.eventbus.EventBus; -import com.google.inject.Inject; - -import freenet.keys.FreenetURI; -import freenet.support.api.Bucket; - -/** - * Watches the official Sone homepage for new releases. - */ -@Singleton -public class UpdateChecker { - - /** The logger. */ - private static final Logger logger = getLogger(UpdateChecker.class.getName()); - - /** The event bus. */ - private final EventBus eventBus; - - /** The Freenet interface. */ - private final FreenetInterface freenetInterface; - - /** The current URI of the homepage. */ - private FreenetURI currentUri; - - /** The latest known edition. */ - private long latestEdition = SonePlugin.getLatestEdition(); - - /** The current latest known version. */ - private Version currentLatestVersion; - private final Version currentRunningVersion; - - /** The release date of the latest version. */ - private long latestVersionDate; - - private final PluginHomepage pluginHomepage; - - /** - * Creates a new update checker. - * - * @param eventBus - * The event bus - * @param freenetInterface - * The freenet interface to use - */ - @Inject - public UpdateChecker(EventBus eventBus, FreenetInterface freenetInterface, Version currentVersion, PluginHomepage pluginHomepage) { - this.eventBus = eventBus; - this.freenetInterface = freenetInterface; - this.currentRunningVersion = currentVersion; - this.currentLatestVersion = currentVersion; - this.pluginHomepage = pluginHomepage; - } - - // - // ACCESSORS - // - - /** - * Returns whether a version that is later than the currently running - * version has been found. - * - * @return {@code true} if a new version was found - */ - public boolean hasLatestVersion() { - return currentLatestVersion.compareTo(currentRunningVersion) > 0; - } - - /** - * Returns the latest version. If no new latest version has been found, the - * current version is returned. - * - * @return The latest known version - */ - public Version getLatestVersion() { - return currentLatestVersion; - } - - /** - * Returns the release time of the latest version. If no new latest version - * has been found, the returned value is undefined. - * - * @return The release time of the latest version, if a new version was - * found - */ - public long getLatestVersionDate() { - return latestVersionDate; - } - - /** - * Returns the latest known edition of the Sone homepage. - * - * @return The latest edition of the Sone homepage - */ - public long getLatestEdition() { - return latestEdition; - } - - // - // ACTIONS - // - - /** - * Starts the update checker. - */ - public void start() { - try { - currentUri = new FreenetURI(pluginHomepage.getHomepage()); - } catch (MalformedURLException mue1) { - /* this can not really happen unless I screw up. */ - logger.log(Level.SEVERE, "Sone Homepage URI invalid!", mue1); - } - freenetInterface.registerUsk(currentUri, new FreenetInterface.Callback() { - - @Override - @SuppressWarnings("synthetic-access") - public void editionFound(FreenetURI uri, long edition, boolean newKnownGood, boolean newSlot) { - logger.log(Level.FINEST, String.format("Found update for %s: %d, %s, %s", uri, edition, newKnownGood, newSlot)); - if (newKnownGood || newSlot) { - Fetched uriResult = freenetInterface.fetchUri(uri.setMetaString(new String[] { "sone.properties" })); - if (uriResult == null) { - logger.log(Level.WARNING, String.format("Could not fetch properties of latest homepage: %s", uri)); - return; - } - Bucket resultBucket = uriResult.getFetchResult().asBucket(); - try { - parseProperties(resultBucket.getInputStream(), edition); - latestEdition = edition; - } catch (IOException ioe1) { - logger.log(Level.WARNING, String.format("Could not parse sone.properties of %s!", uri), ioe1); - } finally { - resultBucket.free(); - } - } - } - }); - } - - /** - * Stops the update checker. - */ - public void stop() { - freenetInterface.unregisterUsk(currentUri); - } - - // - // PRIVATE ACTIONS - // - - /** - * Parses the properties of the latest version and fires events, if - * necessary. - * - * @see UpdateFoundEvent - * @param propertiesInputStream - * The input stream to parse - * @param edition - * The latest edition of the Sone homepage - * @throws IOException - * if an I/O error occured - */ - private void parseProperties(InputStream propertiesInputStream, long edition) throws IOException { - Properties properties = new Properties(); - InputStreamReader inputStreamReader = null; - try { - inputStreamReader = new InputStreamReader(propertiesInputStream, "UTF-8"); - properties.load(inputStreamReader); - } finally { - Closer.close(inputStreamReader); - } - String versionString = properties.getProperty("CurrentVersion/Version"); - String releaseTimeString = properties.getProperty("CurrentVersion/ReleaseTime"); - if ((versionString == null) || (releaseTimeString == null)) { - logger.log(Level.INFO, "Invalid data parsed from properties."); - return; - } - Version version = Version.parse(versionString); - long releaseTime = 0; - try { - releaseTime = Long.parseLong(releaseTimeString); - } catch (NumberFormatException nfe1) { - /* ignore. */ - } - if ((version == null) || (releaseTime == 0)) { - logger.log(Level.INFO, "Could not parse data from properties."); - return; - } - if (version.compareTo(currentLatestVersion) > 0) { - currentLatestVersion = version; - latestVersionDate = releaseTime; - boolean disruptive = disruptiveVersionBetweenCurrentAndFound(properties); - logger.log(Level.INFO, String.format("Found new version: %s (%tc%s)", version, new Date(releaseTime), disruptive ? ", disruptive" : "")); - eventBus.post(new UpdateFoundEvent(version, releaseTime, edition, disruptive)); - } - } - - private boolean disruptiveVersionBetweenCurrentAndFound(Properties properties) { - for (String key : properties.stringPropertyNames()) { - if (key.startsWith("DisruptiveVersion/")) { - Version disruptiveVersion = Version.parse(key.substring("DisruptiveVersion/".length())); - if (disruptiveVersion.compareTo(currentRunningVersion) > 0) { - return true; - } - } - } - return false; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java index 35c3203..95d066d 100644 --- a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java +++ b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdater.java @@ -1,6 +1,5 @@ package net.pterodactylus.sone.core; -import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.util.service.Service; @@ -12,7 +11,6 @@ import com.google.inject.ImplementedBy; @ImplementedBy(WebOfTrustUpdaterImpl.class) public interface WebOfTrustUpdater extends Service { - void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment); boolean addContextWait(OwnIdentity ownIdentity, String context); void removeContext(OwnIdentity ownIdentity, String context); void setProperty(OwnIdentity ownIdentity, String propertyName, String propertyValue); diff --git a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdaterImpl.java b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdaterImpl.java index 809ca20..da27152 100644 --- a/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdaterImpl.java +++ b/src/main/java/net/pterodactylus/sone/core/WebOfTrustUpdaterImpl.java @@ -1,5 +1,5 @@ /* - * Sone - WebOfTrustUpdaterImpl.java - Copyright © 2013–2019 David Roden + * Sone - WebOfTrustUpdaterImpl.java - Copyright © 2013–2020 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 @@ -26,11 +26,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.pterodactylus.sone.freenet.plugin.PluginException; -import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.sone.freenet.wot.OwnIdentity; -import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector; -import net.pterodactylus.sone.freenet.wot.WebOfTrustException; import net.pterodactylus.util.service.AbstractService; import com.google.common.annotations.VisibleForTesting; @@ -74,34 +71,6 @@ public class WebOfTrustUpdaterImpl extends AbstractService implements WebOfTrust // /** - * Updates the trust relation between the truster and the trustee. This method - * will return immediately and perform a trust update in the background. - * - * @param truster - * The identity giving the trust - * @param trustee - * The identity receiving the trust - * @param score - * The new level of trust (from -100 to 100, may be {@code null} to remove - * the trust completely) - * @param comment - * The comment of the trust relation - */ - @Override - public void setTrust(OwnIdentity truster, Identity trustee, Integer score, String comment) { - SetTrustJob setTrustJob = new SetTrustJob(truster, trustee, score, comment); - if (updateJobs.contains(setTrustJob)) { - updateJobs.remove(setTrustJob); - } - logger.log(Level.FINER, "Adding Trust Update Job: " + setTrustJob); - try { - updateJobs.put(setTrustJob); - } catch (InterruptedException e) { - /* the queue is unbounded so it should never block. */ - } - } - - /** * Adds the given context to the given own identity, waiting for completion of * the operation. * @@ -297,91 +266,6 @@ public class WebOfTrustUpdaterImpl extends AbstractService implements WebOfTrust } /** - * Update job that sets the trust relation between two identities. - */ - @VisibleForTesting - class SetTrustJob extends WebOfTrustUpdateJob { - - /** The identity giving the trust. */ - private final OwnIdentity truster; - - /** The identity receiving the trust. */ - private final Identity trustee; - - /** The score of the relation. */ - private final Integer score; - - /** The comment of the relation. */ - private final String comment; - - /** - * Creates a new set trust job. - * - * @param truster - * The identity giving the trust - * @param trustee - * The identity receiving the trust - * @param score - * The score of the trust (from -100 to 100, may be {@code null} to remote - * the trust relation completely) - * @param comment - * The comment of the trust relation - */ - public SetTrustJob(OwnIdentity truster, Identity trustee, Integer score, String comment) { - this.truster = checkNotNull(truster, "truster must not be null"); - this.trustee = checkNotNull(trustee, "trustee must not be null"); - this.score = score; - this.comment = comment; - } - - /** {@inheritDoc} */ - @Override - @SuppressWarnings("synthetic-access") - public void run() { - try { - if (score != null) { - webOfTrustConnector.setTrust(truster, trustee, score, comment); - trustee.setTrust(truster, new Trust(score, null, 0)); - } else { - webOfTrustConnector.removeTrust(truster, trustee); - trustee.removeTrust(truster); - } - finish(true); - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "Could not set Trust value for " + truster + " -> " + trustee + " to " + score + " (" + comment + ")!", wote1); - finish(false); - } - } - - // - // OBJECT METHODS - // - - /** {@inheritDoc} */ - @Override - public boolean equals(Object object) { - if ((object == null) || !object.getClass().equals(getClass())) { - return false; - } - SetTrustJob updateJob = (SetTrustJob) object; - return updateJob.truster.equals(truster) && updateJob.trustee.equals(trustee); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() { - return getClass().hashCode() ^ truster.hashCode() ^ trustee.hashCode(); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return String.format("%s[truster=%s,trustee=%s]", getClass().getSimpleName(), truster.getId(), trustee.getId()); - } - - } - - /** * Base class for context updates of an {@link OwnIdentity}. */ @VisibleForTesting diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageEvent.java deleted file mode 100644 index 6daef1a..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/ImageEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Sone - ImageEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Image; - -/** - * Base class for {@link Image} events. - */ -public abstract class ImageEvent { - - /** The image this event is about. */ - private final Image image; - - /** - * Creates a new image event. - * - * @param image - * The image this event is about - */ - protected ImageEvent(Image image) { - this.image = image; - } - - // - // ACCESSORS - // - - /** - * Returns the image this event is about. - * - * @return The image this event is about - */ - public Image image() { - return image; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.java deleted file mode 100644 index 2bfa3a2..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Sone - ImageInsertAbortedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Image; - -/** - * Event that signals that an {@link Image} insert is aborted. - */ -public class ImageInsertAbortedEvent extends ImageEvent { - - /** - * Creates a new “image insert aborted” event. - * - * @param image - * The image whose insert aborted - */ - public ImageInsertAbortedEvent(Image image) { - super(image); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.java deleted file mode 100644 index 430c64b..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Sone - ImageInsertFailedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Image; - -/** - * Event that signals that an {@link Image} insert has failed. - */ -public class ImageInsertFailedEvent extends ImageEvent { - - /** The cause of the insert failure. */ - private final Throwable cause; - - /** - * Creates a new “image insert failed” event. - * - * @param image - * The image whose insert failed - * @param cause - * The cause of the insert failure - */ - public ImageInsertFailedEvent(Image image, Throwable cause) { - super(image); - this.cause = cause; - } - - // - // ACCESSORS - // - - /** - * Returns the cause of the insert failure. - * - * @return The cause of the insert failure - */ - public Throwable cause() { - return cause; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.java deleted file mode 100644 index 4419311..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Sone - ImageInsertFinishedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Image; -import freenet.keys.FreenetURI; - -/** - * Event that signals that an {@link Image} insert is finished. - */ -public class ImageInsertFinishedEvent extends ImageEvent { - - /** The URI of the image. */ - private final FreenetURI resultingUri; - - /** - * Creates a new “image insert finished” event. - * - * @param image - * The image whose insert finished - * @param resultingUri - * The resulting URI of the image - */ - public ImageInsertFinishedEvent(Image image, FreenetURI resultingUri) { - super(image); - this.resultingUri = resultingUri; - } - - // - // ACCESSORS - // - - /** - * Returns the URI of the image. - * - * @return The URI of the image - */ - public FreenetURI resultingUri() { - return resultingUri; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.java deleted file mode 100644 index ac9e3e3..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Sone - ImageInsertStartedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Image; - -/** - * Event that signals that an {@link Image} is not being inserted. - */ -public class ImageInsertStartedEvent extends ImageEvent { - - /** - * Creates a new “image is inserted” event. - * - * @param image - * The image that is being inserted - */ - public ImageInsertStartedEvent(Image image) { - super(image); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/MarkPostKnownEvent.java b/src/main/java/net/pterodactylus/sone/core/event/MarkPostKnownEvent.java deleted file mode 100644 index 7c8a6c3..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/MarkPostKnownEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Sone - MarkPostKnownEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Post; - -/** - * Event that signals that a {@link Post} has been marked as - * {@link Post#isKnown() known}. - */ -public class MarkPostKnownEvent extends PostEvent { - - /** - * Creates a new “post marked known” event. - * - * @param post - * The post that was marked as known - */ - public MarkPostKnownEvent(Post post) { - super(post); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.java b/src/main/java/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.java deleted file mode 100644 index 4c0e2fc..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Sone - MarkPostReplyKnownEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.PostReply; - -/** - * Event that signals that a {@link PostReply} has been marked as - * {@link PostReply#isKnown() known}. - */ -public class MarkPostReplyKnownEvent extends PostReplyEvent { - - /** - * Creates a new “post reply marked known” event. - * - * @param postReply - * The post reply that was marked as known - */ - public MarkPostReplyKnownEvent(PostReply postReply) { - super(postReply); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.java b/src/main/java/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.java deleted file mode 100644 index 5bbdf5e..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Sone - MarkSoneKnownEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a {@link Sone} has been marked as - * {@link Sone#isKnown() known}. - */ -public class MarkSoneKnownEvent extends SoneEvent { - - /** - * Creates a new “Sone marked known” event. - * - * @param sone - * The Sone that was marked as known - */ - public MarkSoneKnownEvent(Sone sone) { - super(sone); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/NewSoneFoundEvent.java b/src/main/java/net/pterodactylus/sone/core/event/NewSoneFoundEvent.java deleted file mode 100644 index 8fb17d2..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/NewSoneFoundEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Sone - NewSoneFoundEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a new remote Sone was found. - */ -public class NewSoneFoundEvent extends SoneEvent { - - /** - * Creates a new “new Sone found” event. - * - * @param sone - * The Sone that was found - */ - public NewSoneFoundEvent(Sone sone) { - super(sone); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/PostEvent.java b/src/main/java/net/pterodactylus/sone/core/event/PostEvent.java deleted file mode 100644 index 93a61c6..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/PostEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Sone - PostEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Post; - -/** - * Base class for post events. - */ -public class PostEvent { - - /** The post the event is about. */ - private final Post post; - - /** - * Creates a new post event. - * - * @param post - * The post the event is about - */ - protected PostEvent(Post post) { - this.post = post; - } - - // - // ACCESSORS - // - - /** - * Returns the post the event is about. - * - * @return The post the event is about - */ - public Post post() { - return post; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/PostReplyEvent.java b/src/main/java/net/pterodactylus/sone/core/event/PostReplyEvent.java deleted file mode 100644 index e31870b..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/PostReplyEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Sone - PostReplyEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.PostReply; - -/** - * Base class for {@link PostReply} events. - */ -public class PostReplyEvent { - - /** The post reply the event is about. */ - private final PostReply postReply; - - /** - * Creates a new post reply event. - * - * @param postReply - * The post reply the event is about - */ - protected PostReplyEvent(PostReply postReply) { - this.postReply = postReply; - } - - // - // ACCESSORS - // - - /** - * Returns the post reply the event is about. - * - * @return The post reply the event is about - */ - public PostReply postReply() { - return postReply; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneEvent.java deleted file mode 100644 index b7497ff..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/SoneEvent.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Sone - SoneEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Base class for Sone events. - */ -public abstract class SoneEvent { - - /** The Sone this event is about. */ - private final Sone sone; - - /** - * Creates a new Sone event. - * - * @param sone - * The Sone this event is about - */ - protected SoneEvent(Sone sone) { - this.sone = sone; - } - - // - // ACCESSORS - // - - /** - * Returns the Sone this event is about. - * - * @return The Sone this event is about - */ - public Sone sone() { - return sone; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.java deleted file mode 100644 index a8c9bb6..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Sone - SoneInsertAbortedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a {@link Sone} insert was aborted. - */ -public class SoneInsertAbortedEvent extends SoneEvent { - - /** The cause of the abortion. */ - private final Throwable cause; - - /** - * Creates a new “Sone was inserted” event. - * - * @param sone - * The Sone that was inserted - * @param cause - * The cause of the abortion - */ - public SoneInsertAbortedEvent(Sone sone, Throwable cause) { - super(sone); - this.cause = cause; - } - - // - // ACCESSORS - // - - /** - * Returns the cause of the abortion. - * - * @return The cause of the abortion (may be {@code null}) - */ - public Throwable cause() { - return cause; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertedEvent.java deleted file mode 100644 index 53fa935..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertedEvent.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Sone - SoneInsertedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a {@link Sone} was inserted. - */ -public class SoneInsertedEvent extends SoneEvent { - - private final long insertDuration; - private final String insertFingerprint; - - public SoneInsertedEvent(Sone sone, long insertDuration, String insertFingerprint) { - super(sone); - this.insertDuration = insertDuration; - this.insertFingerprint = insertFingerprint; - } - - public long insertDuration() { - return insertDuration; - } - - public String insertFingerprint() { - return insertFingerprint; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertingEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneInsertingEvent.java deleted file mode 100644 index a9ef6fb..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/SoneInsertingEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Sone - SoneInsertingEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a {@link Sone} is now being inserted. - */ -public class SoneInsertingEvent extends SoneEvent { - - /** - * Creates a new “Sone is being inserted” event. - * - * @param sone - * The Sone that is being inserted - */ - public SoneInsertingEvent(Sone sone) { - super(sone); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneLockedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneLockedEvent.java deleted file mode 100644 index cdc9e3a..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/SoneLockedEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Sone - SoneLockedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a {@link Sone} was locked. Only - * {@link Sone#isLocal() local Sones} can be locked. - */ -public class SoneLockedEvent extends SoneEvent { - - /** - * Creates a new “Sone locked” event. - * - * @param sone - * The Sone that was locked - */ - public SoneLockedEvent(Sone sone) { - super(sone); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneRemovedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneRemovedEvent.java deleted file mode 100644 index e46272d..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/SoneRemovedEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Sone - SoneRemovedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a {@link Sone} was removed. - */ -public class SoneRemovedEvent extends SoneEvent { - - /** - * Creates a new “Sone removed” event. - * - * @param sone - * The Sone that was removed - */ - public SoneRemovedEvent(Sone sone) { - super(sone); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/SoneUnlockedEvent.java b/src/main/java/net/pterodactylus/sone/core/event/SoneUnlockedEvent.java deleted file mode 100644 index 106d32d..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/SoneUnlockedEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Sone - SoneUnlockedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.sone.data.Sone; - -/** - * Event that signals that a {@link Sone} was unlocked. Only - * {@link Sone#isLocal() local Sones} can be locked. - */ -public class SoneUnlockedEvent extends SoneEvent { - - /** - * Creates a new “Sone unlocked” event. - * - * @param sone - * The Sone that was unlocked - */ - public SoneUnlockedEvent(Sone sone) { - super(sone); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/core/event/UpdateFoundEvent.java b/src/main/java/net/pterodactylus/sone/core/event/UpdateFoundEvent.java deleted file mode 100644 index 2884090..0000000 --- a/src/main/java/net/pterodactylus/sone/core/event/UpdateFoundEvent.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Sone - UpdateFoundEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core.event; - -import net.pterodactylus.util.version.Version; - -/** - * Event that signals that an update for Sone was found. - */ -public class UpdateFoundEvent { - - private final Version version; - private final long releaseTime; - private final long latestEdition; - private final boolean disruptive; - - public UpdateFoundEvent(Version version, long releaseTime, long latestEdition, boolean disruptive) { - this.version = version; - this.releaseTime = releaseTime; - this.latestEdition = latestEdition; - this.disruptive = disruptive; - } - - public Version version() { - return version; - } - - public long releaseTime() { - return releaseTime; - } - - public long latestEdition() { - return latestEdition; - } - - public boolean disruptive() { - return disruptive; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/data/Album.java b/src/main/java/net/pterodactylus/sone/data/Album.java index c4af294..bf8f3ec 100644 --- a/src/main/java/net/pterodactylus/sone/data/Album.java +++ b/src/main/java/net/pterodactylus/sone/data/Album.java @@ -1,5 +1,5 @@ /* - * Sone - Album.java - Copyright © 2011–2019 David Roden + * Sone - Album.java - Copyright © 2011–2020 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 @@ -17,78 +17,13 @@ package net.pterodactylus.sone.data; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; - -import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; - -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; /** * Container for images that can also contain nested {@link Album}s. */ public interface Album extends Identified, Fingerprintable { - /** Function that flattens the given album and all albums beneath it. */ - Function> FLATTENER = new Function>() { - - @Override - @Nonnull - public List apply(Album album) { - if (album == null) { - return emptyList(); - } - List albums = new ArrayList<>(); - albums.add(album); - for (Album subAlbum : album.getAlbums()) { - albums.addAll(FluentIterable.from(ImmutableList.of(subAlbum)).transformAndConcat(FLATTENER).toList()); - } - return albums; - } - }; - - /** Function that transforms an album into the images it contains. */ - Function> IMAGES = new Function>() { - - @Override - @Nonnull - public List apply(Album album) { - return (album != null) ? album.getImages() : Collections.emptyList(); - } - }; - - /** - * Filter that removes all albums that do not have any images in any album - * below it. - */ - Predicate NOT_EMPTY = new Predicate() { - - @Override - public boolean apply(Album album) { - /* so, we flatten all albums below the given one and check whether at least one album… */ - return FluentIterable.from(asList(album)).transformAndConcat(FLATTENER).anyMatch(new Predicate() { - - @Override - public boolean apply(Album album) { - /* …contains any inserted images. */ - return !album.getImages().isEmpty() && FluentIterable.from(album.getImages()).allMatch(new Predicate() { - - @Override - public boolean apply(Image input) { - return input.isInserted(); - } - }); - } - }); - } - }; - /** * Returns the ID of this album. * diff --git a/src/main/java/net/pterodactylus/sone/data/Client.java b/src/main/java/net/pterodactylus/sone/data/Client.java index f23decf..7642957 100644 --- a/src/main/java/net/pterodactylus/sone/data/Client.java +++ b/src/main/java/net/pterodactylus/sone/data/Client.java @@ -1,5 +1,5 @@ /* - * Sone - Client.java - Copyright © 2010–2019 David Roden + * Sone - Client.java - Copyright © 2010–2020 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 @@ -81,4 +81,9 @@ public class Client { return Objects.hashCode(name, version); } + @Override + public String toString() { + return name + " " + version; + } + } diff --git a/src/main/java/net/pterodactylus/sone/data/Image.java b/src/main/java/net/pterodactylus/sone/data/Image.java index 93d0702..8414f3a 100644 --- a/src/main/java/net/pterodactylus/sone/data/Image.java +++ b/src/main/java/net/pterodactylus/sone/data/Image.java @@ -1,5 +1,5 @@ /* - * Sone - Image.java - Copyright © 2011–2019 David Roden + * Sone - Image.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/Post.java b/src/main/java/net/pterodactylus/sone/data/Post.java index b6648e2..d4d34e6 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -1,5 +1,5 @@ /* - * Sone - Post.java - Copyright © 2010–2019 David Roden + * Sone - Post.java - Copyright © 2010–2020 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 @@ -19,10 +19,7 @@ package net.pterodactylus.sone.data; import static com.google.common.base.Optional.absent; -import java.util.Comparator; - import com.google.common.base.Optional; -import com.google.common.base.Predicate; /** * A post is a short message that a user writes in his Sone to let other users @@ -30,26 +27,6 @@ import com.google.common.base.Predicate; */ public interface Post extends Identified { - /** Comparator for posts, sorts descending by time. */ - public static final Comparator NEWEST_FIRST = new Comparator() { - - @Override - public int compare(Post leftPost, Post rightPost) { - return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, rightPost.getTime() - leftPost.getTime())); - } - - }; - - /** Filter for posts with timestamps from the future. */ - public static final Predicate FUTURE_POSTS_FILTER = new Predicate() { - - @Override - public boolean apply(Post post) { - return (post != null) && (post.getTime() <= System.currentTimeMillis()); - } - - }; - // // ACCESSORS // diff --git a/src/main/java/net/pterodactylus/sone/data/PostReply.java b/src/main/java/net/pterodactylus/sone/data/PostReply.java index dc4a903..6db3876 100644 --- a/src/main/java/net/pterodactylus/sone/data/PostReply.java +++ b/src/main/java/net/pterodactylus/sone/data/PostReply.java @@ -1,5 +1,5 @@ /* - * Sone - PostReply.java - Copyright © 2010–2019 David Roden + * Sone - PostReply.java - Copyright © 2010–2020 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 @@ -18,7 +18,6 @@ package net.pterodactylus.sone.data; import com.google.common.base.Optional; -import com.google.common.base.Predicate; /** * A reply is like a {@link Post} but can never be posted on its own, it always @@ -27,18 +26,6 @@ import com.google.common.base.Predicate; public interface PostReply extends Reply { /** - * Filter that selects {@link PostReply}s that have a - * {@link Optional#isPresent() present} {@link #getPost() post}. - */ - public static final Predicate HAS_POST_FILTER = new Predicate() { - - @Override - public boolean apply(PostReply postReply) { - return (postReply != null) && postReply.getPost().isPresent(); - } - }; - - /** * Returns the ID of the post this reply refers to. * * @return The ID of the post this reply refers to diff --git a/src/main/java/net/pterodactylus/sone/data/Profile.java b/src/main/java/net/pterodactylus/sone/data/Profile.java index 8ea09f7..34246cb 100644 --- a/src/main/java/net/pterodactylus/sone/data/Profile.java +++ b/src/main/java/net/pterodactylus/sone/data/Profile.java @@ -1,5 +1,5 @@ /* - * Sone - Profile.java - Copyright © 2010–2019 David Roden + * Sone - Profile.java - Copyright © 2010–2020 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 @@ -19,7 +19,6 @@ package net.pterodactylus.sone.data; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.ArrayList; diff --git a/src/main/java/net/pterodactylus/sone/data/Reply.java b/src/main/java/net/pterodactylus/sone/data/Reply.java index c596605..0cf2a52 100644 --- a/src/main/java/net/pterodactylus/sone/data/Reply.java +++ b/src/main/java/net/pterodactylus/sone/data/Reply.java @@ -1,5 +1,5 @@ /* - * Sone - Reply.java - Copyright © 2010–2019 David Roden + * Sone - Reply.java - Copyright © 2010–2020 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 @@ -17,10 +17,6 @@ package net.pterodactylus.sone.data; -import java.util.Comparator; - -import com.google.common.base.Predicate; - /** * Defines methods common for all replies. * @@ -29,32 +25,6 @@ import com.google.common.base.Predicate; */ public interface Reply> extends Identified { - /** Comparator that sorts replies ascending by time. */ - public static final Comparator> TIME_COMPARATOR = new Comparator>() { - - /** - * {@inheritDoc} - */ - @Override - public int compare(Reply leftReply, Reply rightReply) { - return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, leftReply.getTime() - rightReply.getTime())); - } - - }; - - /** Filter for replies with timestamps from the future. */ - public static final Predicate> FUTURE_REPLY_FILTER = new Predicate>() { - - /** - * {@inheritDoc} - */ - @Override - public boolean apply(Reply reply) { - return (reply != null) && (reply.getTime() <= System.currentTimeMillis()); - } - - }; - /** * Returns the ID of the reply. * @@ -90,13 +60,4 @@ public interface Reply> extends Identified { */ public boolean isKnown(); - /** - * Sets whether this reply is known. - * - * @param known - * {@code true} if this reply is known, {@code false} otherwise - * @return This reply - */ - public T setKnown(boolean known); - } diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 273b73e..2b0a2eb 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -1,5 +1,5 @@ /* - * Sone - Sone.java - Copyright © 2010–2019 David Roden + * Sone - Sone.java - Copyright © 2010–2020 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 @@ -17,14 +17,7 @@ package net.pterodactylus.sone.data; -import static com.google.common.collect.FluentIterable.from; -import static java.util.Arrays.asList; -import static net.pterodactylus.sone.data.Album.FLATTENER; -import static net.pterodactylus.sone.data.Album.IMAGES; - import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Set; @@ -32,15 +25,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; -import net.pterodactylus.sone.template.SoneAccessor; import freenet.keys.FreenetURI; -import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.primitives.Ints; - /** * A Sone defines everything about a user: her profile, her status updates, her * replies, her likes and dislikes, etc. @@ -65,101 +52,6 @@ public interface Sone extends Identified, Fingerprintable, Comparable { downloading, } - /** comparator that sorts Sones by their nice name. */ - public static final Comparator NICE_NAME_COMPARATOR = new Comparator() { - - @Override - public int compare(Sone leftSone, Sone rightSone) { - int diff = SoneAccessor.getNiceName(leftSone).compareToIgnoreCase(SoneAccessor.getNiceName(rightSone)); - if (diff != 0) { - return diff; - } - return leftSone.getId().compareToIgnoreCase(rightSone.getId()); - } - - }; - - /** Comparator that sorts Sones by last activity (least recent active first). */ - public static final Comparator LAST_ACTIVITY_COMPARATOR = new Comparator() { - - @Override - public int compare(Sone firstSone, Sone secondSone) { - return (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, secondSone.getTime() - firstSone.getTime())); - } - }; - - /** Comparator that sorts Sones by numbers of posts (descending). */ - public static final Comparator POST_COUNT_COMPARATOR = new Comparator() { - - /** - * {@inheritDoc} - */ - @Override - public int compare(Sone leftSone, Sone rightSone) { - return (leftSone.getPosts().size() != rightSone.getPosts().size()) ? (rightSone.getPosts().size() - leftSone.getPosts().size()) : (rightSone.getReplies().size() - leftSone.getReplies().size()); - } - }; - - /** Comparator that sorts Sones by number of images (descending). */ - public static final Comparator IMAGE_COUNT_COMPARATOR = new Comparator() { - - /** - * {@inheritDoc} - */ - @Override - public int compare(Sone leftSone, Sone rightSone) { - int rightSoneImageCount = from(asList(rightSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size(); - int leftSoneImageCount = from(asList(leftSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size(); - /* sort descending. */ - return Ints.compare(rightSoneImageCount, leftSoneImageCount); - } - }; - - /** Filter to remove Sones that have not been downloaded. */ - public static final Predicate EMPTY_SONE_FILTER = new Predicate() { - - @Override - public boolean apply(Sone sone) { - return (sone != null) && (sone.getTime() != 0); - } - }; - - /** Filter that matches all {@link Sone#isLocal() local Sones}. */ - public static final Predicate LOCAL_SONE_FILTER = new Predicate() { - - @Override - public boolean apply(Sone sone) { - return (sone != null) && (sone.getIdentity() instanceof OwnIdentity); - } - - }; - - /** Filter that matches Sones that have at least one album. */ - public static final Predicate HAS_ALBUM_FILTER = new Predicate() { - - @Override - public boolean apply(Sone sone) { - return (sone != null) && !sone.getRootAlbum().getAlbums().isEmpty(); - } - }; - - public static final Function> toAllAlbums = new Function>() { - @Override - public List apply(@Nullable Sone sone) { - return (sone == null) ? Collections.emptyList() : FLATTENER.apply( - sone.getRootAlbum()); - } - }; - - public static final Function> toAllImages = new Function>() { - @Override - public List apply(@Nullable Sone sone) { - return (sone == null) ? Collections.emptyList() : - from(FLATTENER.apply(sone.getRootAlbum())) - .transformAndConcat(IMAGES).toList(); - } - }; - /** * Returns the identity of this Sone. * @@ -192,14 +84,6 @@ public interface Sone extends Identified, Fingerprintable, Comparable { FreenetURI getRequestUri(); /** - * Returns the insert URI of this Sone. - * - * @return The insert URI of this Sone - */ - @Nullable - FreenetURI getInsertUri(); - - /** * Returns the latest edition of this Sone. * * @return The latest edition of this Sone diff --git a/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java b/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java index 378a348..b76c3cd 100644 --- a/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java +++ b/src/main/java/net/pterodactylus/sone/data/TemporaryImage.java @@ -1,5 +1,5 @@ /* - * Sone - TemporaryImage.java - Copyright © 2011–2019 David Roden + * Sone - TemporaryImage.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractAlbumBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractAlbumBuilder.java index 460be4e..04b0eac 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AbstractAlbumBuilder.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractAlbumBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - AbstractAlbumBuilder.java - Copyright © 2013–2019 David Roden + * Sone - AbstractAlbumBuilder.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java index f545ae9..56ceb6d 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractImageBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - AbstractImageBuilder.java - Copyright © 2013–2019 David Roden + * Sone - AbstractImageBuilder.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java index cabe8d5..ca927b2 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - AbstractPostBuilder.java - Copyright © 2013–2019 David Roden + * Sone - AbstractPostBuilder.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java index 0fe124e..8dffdcd 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - AbstractPostReplyBuilder.java - Copyright © 2013–2019 David Roden + * Sone - AbstractPostReplyBuilder.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java b/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java index 052ca2d..7e95f38 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AbstractReplyBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - AbstractReplyBuilder.java - Copyright © 2013–2019 David Roden + * Sone - AbstractReplyBuilder.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AlbumBuilderImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/AlbumBuilderImpl.java index 601daa8..696d08d 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AlbumBuilderImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AlbumBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Sone - AlbumBuilderImpl.java - Copyright © 2013–2019 David Roden + * Sone - AlbumBuilderImpl.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java index 3ec362f..04ab8e9 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/AlbumImpl.java @@ -1,5 +1,5 @@ /* - * Sone - AlbumImpl.java - Copyright © 2011–2019 David Roden + * Sone - AlbumImpl.java - Copyright © 2011–2020 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 @@ -17,28 +17,17 @@ package net.pterodactylus.sone.data.impl; -import static com.google.common.base.Optional.absent; -import static com.google.common.base.Optional.fromNullable; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; -import static java.nio.charset.StandardCharsets.UTF_8; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import net.pterodactylus.sone.data.Album; -import net.pterodactylus.sone.data.Image; -import net.pterodactylus.sone.data.Sone; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Predicates; -import com.google.common.collect.Collections2; -import com.google.common.hash.Hasher; +import java.util.*; +import javax.annotation.*; + +import com.google.common.base.*; +import com.google.common.collect.*; import com.google.common.hash.Hashing; +import com.google.common.hash.*; +import net.pterodactylus.sone.data.*; + +import static com.google.common.base.Preconditions.*; +import static java.nio.charset.StandardCharsets.*; /** * Container for images that can also contain nested {@link AlbumImpl}s. @@ -258,32 +247,33 @@ public class AlbumImpl implements Album { public Modifier modify() throws IllegalStateException { // TODO: reenable check for local Sones return new Modifier() { - private Optional title = absent(); - - private Optional description = absent(); + @Nullable + private String title; + @Nullable + private String description; @Override public Modifier setTitle(String title) { - this.title = fromNullable(title); + this.title = title; return this; } @Override public Modifier setDescription(String description) { - this.description = fromNullable(description); + this.description = description; return this; } @Override public Album update() throws IllegalStateException { - if (title.isPresent() && title.get().trim().isEmpty()) { + if (title != null && title.trim().isEmpty()) { throw new AlbumTitleMustNotBeEmpty(); } - if (title.isPresent()) { - AlbumImpl.this.title = title.get(); + if (title != null) { + AlbumImpl.this.title = title; } - if (description.isPresent()) { - AlbumImpl.this.description = description.get(); + if (description != null) { + AlbumImpl.this.description = description; } return AlbumImpl.this; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java index e06e5a7..ddd96b9 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java @@ -53,11 +53,6 @@ public class IdOnlySone implements Sone { } @Override - public FreenetURI getInsertUri() { - return null; - } - - @Override public long getLatestEdition() { return 0; } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java index b62cc40..e74b71a 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/ImageBuilderImpl.java @@ -1,5 +1,5 @@ /* - * Sone - ImageBuilderImpl.java - Copyright © 2013–2019 David Roden + * Sone - ImageBuilderImpl.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java index a54a8de..0dd84fe 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/ImageImpl.java @@ -1,5 +1,5 @@ /* - * Sone - ImageImpl.java - Copyright © 2011–2019 David Roden + * Sone - ImageImpl.java - Copyright © 2011–2020 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 @@ -16,22 +16,14 @@ */ package net.pterodactylus.sone.data.impl; -import static com.google.common.base.Optional.absent; -import static com.google.common.base.Optional.fromNullable; -import static com.google.common.base.Optional.of; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static java.nio.charset.StandardCharsets.UTF_8; +import java.util.*; +import javax.annotation.*; -import java.util.UUID; +import com.google.common.hash.*; +import net.pterodactylus.sone.data.*; -import net.pterodactylus.sone.data.Album; -import net.pterodactylus.sone.data.Image; -import net.pterodactylus.sone.data.Sone; - -import com.google.common.base.Optional; -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; +import static com.google.common.base.Preconditions.*; +import static java.nio.charset.StandardCharsets.*; /** * Container for image metadata. @@ -146,93 +138,94 @@ public class ImageImpl implements Image { public Modifier modify() throws IllegalStateException { // TODO: reenable check for local images return new Modifier() { - private Optional sone = absent(); - - private Optional creationTime = absent(); - - private Optional key = absent(); - - private Optional title = absent(); - - private Optional description = absent(); - - private Optional width = absent(); - - private Optional height = absent(); + @Nullable + private Sone sone; + @Nullable + private Long creationTime; + @Nullable + private String key; + @Nullable + private String title; + @Nullable + private String description; + @Nullable + private Integer width; + @Nullable + private Integer height; @Override public Modifier setSone(Sone sone) { - this.sone = fromNullable(sone); + this.sone = sone; return this; } @Override public Modifier setCreationTime(long creationTime) { - this.creationTime = of(creationTime); + this.creationTime = creationTime; return this; } @Override public Modifier setKey(String key) { - this.key = fromNullable(key); + this.key = key; return this; } @Override public Modifier setTitle(String title) { - this.title = fromNullable(title); + this.title = title; return this; } @Override public Modifier setDescription(String description) { - this.description = fromNullable(description); + this.description = description; return this; } @Override public Modifier setWidth(int width) { - this.width = of(width); + this.width = width; return this; } @Override public Modifier setHeight(int height) { - this.height = of(height); + this.height = height; return this; } @Override public Image update() throws IllegalStateException { - checkState(!sone.isPresent() || (ImageImpl.this.sone == null) || sone.get().equals(ImageImpl.this.sone), "can not change Sone once set"); - checkState(!creationTime.isPresent() || ((ImageImpl.this.creationTime == 0) || (ImageImpl.this.creationTime == creationTime.get())), "can not change creation time once set"); - checkState(!key.isPresent() || (ImageImpl.this.key == null) || key.get().equals(ImageImpl.this.key), "can not change key once set"); - if (title.isPresent() && title.get().trim().isEmpty()) { + checkState(sone == null || (ImageImpl.this.sone == null) || sone.equals(ImageImpl.this.sone), "can not change Sone once set"); + checkState(creationTime == null || ((ImageImpl.this.creationTime == 0) || (ImageImpl.this.creationTime == creationTime)), "can not change creation time once set"); + checkState(key == null || (ImageImpl.this.key == null) || key.equals(ImageImpl.this.key), "can not change key once set"); + if (title != null && title.trim().isEmpty()) { throw new ImageTitleMustNotBeEmpty(); } - checkState(!width.isPresent() || (ImageImpl.this.width == 0) || width.get().equals(ImageImpl.this.width), "can not change width once set"); - checkState(!height.isPresent() || (ImageImpl.this.height == 0) || height.get().equals(ImageImpl.this.height), "can not change height once set"); + checkState(width == null || (ImageImpl.this.width == 0) || width.equals(ImageImpl.this.width), "can not change width once set"); + checkState(height == null || (ImageImpl.this.height == 0) || height.equals(ImageImpl.this.height), "can not change height once set"); - if (sone.isPresent()) { - ImageImpl.this.sone = sone.get(); + if (sone != null) { + ImageImpl.this.sone = sone; } - if (creationTime.isPresent()) { - ImageImpl.this.creationTime = creationTime.get(); + if (creationTime != null) { + ImageImpl.this.creationTime = creationTime; } - if (key.isPresent()) { - ImageImpl.this.key = key.get(); + if (key != null) { + ImageImpl.this.key = key; } - if (title.isPresent()) { - ImageImpl.this.title = title.get(); + if (title != null) { + ImageImpl.this.title = title; } - if (description.isPresent()) { - ImageImpl.this.description = description.get(); + if (description != null) { + ImageImpl.this.description = description; } - if (width.isPresent()) { - ImageImpl.this.width = width.get(); + if (width != null) { + ImageImpl.this.width = width; } - if (height.isPresent()) { - ImageImpl.this.height = height.get(); + if (height != null) { + ImageImpl.this.height = height; } return ImageImpl.this; diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index 3366448..f2f2ea6 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -1,5 +1,5 @@ /* - * Sone - SoneImpl.java - Copyright © 2010–2019 David Roden + * Sone - SoneImpl.java - Copyright © 2010–2020 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 @@ -21,6 +21,9 @@ import static com.google.common.base.Preconditions.checkNotNull; import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.logging.Logger.getLogger; +import static net.pterodactylus.sone.data.PostKt.newestPostFirst; +import static net.pterodactylus.sone.data.ReplyKt.newestReplyFirst; +import static net.pterodactylus.sone.data.SoneKt.*; import java.net.MalformedURLException; import java.util.ArrayList; @@ -36,17 +39,16 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.AlbumKt; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; -import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.SoneOptions; import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions; import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; import freenet.keys.FreenetURI; @@ -191,26 +193,6 @@ public class SoneImpl implements Sone { } /** - * Returns the insert URI of this Sone. - * - * @return The insert URI of this Sone - */ - @Nullable - public FreenetURI getInsertUri() { - if (!isLocal()) { - return null; - } - try { - return new FreenetURI(((OwnIdentity) getIdentity()).getInsertUri()) - .setDocName("Sone") - .setMetaString(new String[0]) - .setSuggestedEdition(latestEdition); - } catch (MalformedURLException e) { - throw new IllegalStateException(format("Own identity %s's insert URI is incorrect.", getIdentity()), e); - } - } - - /** * Returns the latest edition of this Sone. * * @return The latest edition of this Sone @@ -384,7 +366,7 @@ public class SoneImpl implements Sone { synchronized (this) { sortedPosts = new ArrayList<>(posts); } - Collections.sort(sortedPosts, Post.NEWEST_FIRST); + sortedPosts.sort(newestPostFirst()); return sortedPosts; } @@ -644,7 +626,7 @@ public class SoneImpl implements Sone { hash.putString(")", UTF_8); List replies = new ArrayList<>(getReplies()); - Collections.sort(replies, Reply.TIME_COMPARATOR); + replies.sort(newestReplyFirst().reversed()); hash.putString("Replies(", UTF_8); for (PostReply reply : replies) { hash.putString("Reply(", UTF_8).putString(reply.getId(), UTF_8).putString(")", UTF_8); @@ -669,7 +651,7 @@ public class SoneImpl implements Sone { hash.putString("Albums(", UTF_8); for (Album album : rootAlbum.getAlbums()) { - if (!Album.NOT_EMPTY.apply(album)) { + if (!AlbumKt.notEmpty().invoke(album)) { continue; } hash.putString(album.getFingerprint(), UTF_8); @@ -686,7 +668,7 @@ public class SoneImpl implements Sone { /** {@inheritDoc} */ @Override public int compareTo(Sone sone) { - return NICE_NAME_COMPARATOR.compare(this, sone); + return niceNameComparator().compare(this, sone); } // diff --git a/src/main/java/net/pterodactylus/sone/database/DatabaseException.java b/src/main/java/net/pterodactylus/sone/database/DatabaseException.java index 9763ad6..6a24756 100644 --- a/src/main/java/net/pterodactylus/sone/database/DatabaseException.java +++ b/src/main/java/net/pterodactylus/sone/database/DatabaseException.java @@ -1,5 +1,5 @@ /* - * Sone - DatabaseException.java - Copyright © 2013–2019 David Roden + * Sone - DatabaseException.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.kt deleted file mode 100644 index 6fce7e0..0000000 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Sone - MemoryDatabase.kt - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.database.memory - -import com.google.common.base.Preconditions.checkNotNull -import com.google.common.collect.HashMultimap -import com.google.common.collect.Multimap -import com.google.common.collect.TreeMultimap -import com.google.common.util.concurrent.AbstractService -import com.google.inject.Inject -import com.google.inject.Singleton -import net.pterodactylus.sone.data.Album -import net.pterodactylus.sone.data.Image -import net.pterodactylus.sone.data.Post -import net.pterodactylus.sone.data.PostReply -import net.pterodactylus.sone.data.Reply.TIME_COMPARATOR -import net.pterodactylus.sone.data.Sone -import net.pterodactylus.sone.data.Sone.toAllAlbums -import net.pterodactylus.sone.data.Sone.toAllImages -import net.pterodactylus.sone.data.impl.AlbumBuilderImpl -import net.pterodactylus.sone.data.impl.ImageBuilderImpl -import net.pterodactylus.sone.database.AlbumBuilder -import net.pterodactylus.sone.database.Database -import net.pterodactylus.sone.database.DatabaseException -import net.pterodactylus.sone.database.ImageBuilder -import net.pterodactylus.sone.database.PostBuilder -import net.pterodactylus.sone.database.PostDatabase -import net.pterodactylus.sone.database.PostReplyBuilder -import net.pterodactylus.sone.utils.unit -import net.pterodactylus.util.config.Configuration -import net.pterodactylus.util.config.ConfigurationException -import java.util.concurrent.locks.ReentrantReadWriteLock -import kotlin.concurrent.withLock - -/** - * Memory-based [PostDatabase] implementation. - */ -@Singleton -class MemoryDatabase @Inject constructor(private val configuration: Configuration) : AbstractService(), Database { - - private val lock = ReentrantReadWriteLock() - private val readLock by lazy { lock.readLock()!! } - private val writeLock by lazy { lock.writeLock()!! } - private val configurationLoader = ConfigurationLoader(configuration) - private val allSones = mutableMapOf() - private val allPosts = mutableMapOf() - private val sonePosts: Multimap = HashMultimap.create() - private val knownPosts = mutableSetOf() - private val allPostReplies = mutableMapOf() - private val sonePostReplies: Multimap = TreeMultimap.create(Comparator { leftString, rightString -> leftString.compareTo(rightString) }, TIME_COMPARATOR) - private val knownPostReplies = mutableSetOf() - private val allAlbums = mutableMapOf() - private val soneAlbums: Multimap = HashMultimap.create() - private val allImages = mutableMapOf() - private val soneImages: Multimap = HashMultimap.create() - private val memoryBookmarkDatabase = MemoryBookmarkDatabase(this, configurationLoader) - private val memoryFriendDatabase = MemoryFriendDatabase(configurationLoader) - - override val soneLoader get() = this::getSone - - override val sones get() = readLock.withLock { allSones.values.toSet() } - - override val localSones get() = readLock.withLock { allSones.values.filter(Sone::isLocal) } - - override val remoteSones get() = readLock.withLock { allSones.values.filterNot(Sone::isLocal) } - - override val bookmarkedPosts get() = memoryBookmarkDatabase.bookmarkedPosts - - override fun save() { - saveKnownPosts() - saveKnownPostReplies() - } - - override fun doStart() { - memoryBookmarkDatabase.start() - loadKnownPosts() - loadKnownPostReplies() - notifyStarted() - } - - override fun doStop() { - try { - memoryBookmarkDatabase.stop() - save() - notifyStopped() - } catch (de1: DatabaseException) { - notifyFailed(de1) - } - } - - override fun newSoneBuilder() = MemorySoneBuilder(this) - - override fun storeSone(sone: Sone) { - writeLock.withLock { - removeSone(sone) - - allSones[sone.id] = sone - sonePosts.putAll(sone.id, sone.posts) - for (post in sone.posts) { - allPosts[post.id] = post - } - sonePostReplies.putAll(sone.id, sone.replies) - for (postReply in sone.replies) { - allPostReplies[postReply.id] = postReply - } - soneAlbums.putAll(sone.id, toAllAlbums.apply(sone)!!) - for (album in toAllAlbums.apply(sone)!!) { - allAlbums[album.id] = album - } - soneImages.putAll(sone.id, toAllImages.apply(sone)!!) - for (image in toAllImages.apply(sone)!!) { - allImages[image.id] = image - } - } - } - - override fun removeSone(sone: Sone) { - writeLock.withLock { - allSones.remove(sone.id) - val removedPosts = sonePosts.removeAll(sone.id) - for (removedPost in removedPosts) { - allPosts.remove(removedPost.id) - } - val removedPostReplies = sonePostReplies.removeAll(sone.id) - for (removedPostReply in removedPostReplies) { - allPostReplies.remove(removedPostReply.id) - } - val removedAlbums = soneAlbums.removeAll(sone.id) - for (removedAlbum in removedAlbums) { - allAlbums.remove(removedAlbum.id) - } - val removedImages = soneImages.removeAll(sone.id) - for (removedImage in removedImages) { - allImages.remove(removedImage.id) - } - } - } - - override fun getSone(soneId: String) = readLock.withLock { allSones[soneId] } - - override fun getFriends(localSone: Sone): Collection = - if (!localSone.isLocal) { - emptySet() - } else { - memoryFriendDatabase.getFriends(localSone.id) - } - - override fun isFriend(localSone: Sone, friendSoneId: String) = - if (!localSone.isLocal) { - false - } else { - memoryFriendDatabase.isFriend(localSone.id, friendSoneId) - } - - override fun addFriend(localSone: Sone, friendSoneId: String) { - if (!localSone.isLocal) { - return - } - memoryFriendDatabase.addFriend(localSone.id, friendSoneId) - } - - override fun removeFriend(localSone: Sone, friendSoneId: String) { - if (!localSone.isLocal) { - return - } - memoryFriendDatabase.removeFriend(localSone.id, friendSoneId) - } - - override fun getFollowingTime(friendSoneId: String) = - memoryFriendDatabase.getFollowingTime(friendSoneId) - - override fun getPost(postId: String) = - readLock.withLock { allPosts[postId] } - - override fun getPosts(soneId: String): Collection = - sonePosts[soneId].toSet() - - override fun getDirectedPosts(recipientId: String) = - readLock.withLock { - allPosts.values.filter { - it.recipientId.orNull() == recipientId - } - } - - override fun newPostBuilder(): PostBuilder = MemoryPostBuilder(this, this) - - override fun storePost(post: Post) { - checkNotNull(post, "post must not be null") - writeLock.withLock { - allPosts[post.id] = post - sonePosts[post.sone.id].add(post) - } - } - - override fun removePost(post: Post) { - checkNotNull(post, "post must not be null") - writeLock.withLock { - allPosts.remove(post.id) - sonePosts[post.sone.id].remove(post) - post.sone.removePost(post) - } - } - - override fun getPostReply(id: String) = readLock.withLock { allPostReplies[id] } - - override fun getReplies(postId: String) = - readLock.withLock { - allPostReplies.values - .filter { it.postId == postId } - .sortedWith(TIME_COMPARATOR) - } - - override fun newPostReplyBuilder(): PostReplyBuilder = - MemoryPostReplyBuilder(this, this) - - override fun storePostReply(postReply: PostReply) = - writeLock.withLock { - allPostReplies[postReply.id] = postReply - } - - override fun removePostReply(postReply: PostReply) = - writeLock.withLock { - allPostReplies.remove(postReply.id) - }.unit - - override fun getAlbum(albumId: String) = readLock.withLock { allAlbums[albumId] } - - override fun newAlbumBuilder(): AlbumBuilder = AlbumBuilderImpl() - - override fun storeAlbum(album: Album) = - writeLock.withLock { - allAlbums[album.id] = album - soneAlbums.put(album.sone.id, album) - }.unit - - override fun removeAlbum(album: Album) = - writeLock.withLock { - allAlbums.remove(album.id) - soneAlbums.remove(album.sone.id, album) - }.unit - - override fun getImage(imageId: String) = readLock.withLock { allImages[imageId] } - - override fun newImageBuilder(): ImageBuilder = ImageBuilderImpl() - - override fun storeImage(image: Image): Unit = - writeLock.withLock { - allImages[image.id] = image - soneImages.put(image.sone.id, image) - } - - override fun removeImage(image: Image): Unit = - writeLock.withLock { - allImages.remove(image.id) - soneImages.remove(image.sone.id, image) - } - - override fun bookmarkPost(post: Post) = - memoryBookmarkDatabase.bookmarkPost(post) - - override fun unbookmarkPost(post: Post) = - memoryBookmarkDatabase.unbookmarkPost(post) - - override fun isPostBookmarked(post: Post) = - memoryBookmarkDatabase.isPostBookmarked(post) - - protected fun isPostKnown(post: Post) = readLock.withLock { post.id in knownPosts } - - fun setPostKnown(post: Post, known: Boolean): Unit = - writeLock.withLock { - if (known) - knownPosts.add(post.id) - else - knownPosts.remove(post.id) - saveKnownPosts() - } - - protected fun isPostReplyKnown(postReply: PostReply) = readLock.withLock { postReply.id in knownPostReplies } - - fun setPostReplyKnown(postReply: PostReply, known: Boolean): Unit = - writeLock.withLock { - if (known) - knownPostReplies.add(postReply.id) - else - knownPostReplies.remove(postReply.id) - saveKnownPostReplies() - } - - private fun loadKnownPosts() = - configurationLoader.loadKnownPosts() - .let { - writeLock.withLock { - knownPosts.clear() - knownPosts.addAll(it) - } - } - - private fun saveKnownPosts() = - try { - readLock.withLock { - knownPosts.forEachIndexed { index, knownPostId -> - configuration.getStringValue("KnownPosts/$index/ID").value = knownPostId - } - configuration.getStringValue("KnownPosts/${knownPosts.size}/ID").value = null - } - } catch (ce1: ConfigurationException) { - throw DatabaseException("Could not save database.", ce1) - } - - private fun loadKnownPostReplies(): Unit = - configurationLoader.loadKnownPostReplies().let { knownPostReplies -> - writeLock.withLock { - this.knownPostReplies.clear() - this.knownPostReplies.addAll(knownPostReplies) - } - } - - private fun saveKnownPostReplies() = - try { - readLock.withLock { - knownPostReplies.forEachIndexed { index, knownPostReply -> - configuration.getStringValue("KnownReplies/$index/ID").value = knownPostReply - } - configuration.getStringValue("KnownReplies/${knownPostReplies.size}/ID").value = null - } - } catch (ce1: ConfigurationException) { - throw DatabaseException("Could not save database.", ce1) - } - -} diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java index 30fc2b0..451b6b5 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPost.java @@ -1,5 +1,5 @@ /* - * Sone - MemoryPost.java - Copyright © 2010–2019 David Roden + * Sone - MemoryPost.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostBuilder.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostBuilder.java index dea3449..b9e66fe 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostBuilder.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - MemoryPostBuilder.java - Copyright © 2013–2019 David Roden + * Sone - MemoryPostBuilder.java - Copyright © 2013–2020 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 @@ -17,43 +17,30 @@ package net.pterodactylus.sone.database.memory; -import java.util.UUID; +import java.util.*; +import javax.annotation.*; -import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.data.impl.AbstractPostBuilder; -import net.pterodactylus.sone.database.PostBuilder; -import net.pterodactylus.sone.database.SoneProvider; +import net.pterodactylus.sone.data.*; +import net.pterodactylus.sone.data.impl.*; +import net.pterodactylus.sone.database.*; /** * {@link PostBuilder} implementation that creates a {@link MemoryPost}. */ class MemoryPostBuilder extends AbstractPostBuilder { - /** The database. */ private final MemoryDatabase database; - /** - * Creates a new memory post builder. - * - * @param memoryDatabase - * The database - * @param soneProvider - * The Sone provider - */ public MemoryPostBuilder(MemoryDatabase memoryDatabase, SoneProvider soneProvider) { super(soneProvider); database = memoryDatabase; } - /** - * {@inheritDocs} - */ + @Nonnull @Override public Post build() throws IllegalStateException { validate(); - Post post = new MemoryPost(database, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, recipientId, currentTime ? System.currentTimeMillis() : time, text); - post.setKnown(database.isPostKnown(post)); - return post; + return new MemoryPost(database, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, recipientId, currentTime ? System.currentTimeMillis() : time, text); } } diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java index 40f0775..5764622 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java @@ -1,5 +1,5 @@ /* - * Sone - MemoryPostReply.java - Copyright © 2013–2019 David Roden + * Sone - MemoryPostReply.java - Copyright © 2013–2020 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 @@ -124,15 +124,6 @@ class MemoryPostReply implements PostReply { return database.isPostReplyKnown(this); } - /** - * {@inheritDocs} - */ - @Override - public PostReply setKnown(boolean known) { - database.setPostReplyKnown(this, known); - return this; - } - // // POSTREPLY METHODS // @@ -177,4 +168,17 @@ class MemoryPostReply implements PostReply { return memoryPostReply.id.equals(id); } + @Override + public String toString() { + return "MemoryPostReply{" + + "database=" + database + + ", soneProvider=" + soneProvider + + ", id='" + id + '\'' + + ", soneId='" + soneId + '\'' + + ", time=" + time + + ", text='" + text + '\'' + + ", postId='" + postId + '\'' + + '}'; + } + } diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReplyBuilder.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReplyBuilder.java index 3bcfe33..b6e2e24 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReplyBuilder.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReplyBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - MemoryPostReplyBuilder.java - Copyright © 2013–2019 David Roden + * Sone - MemoryPostReplyBuilder.java - Copyright © 2013–2020 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 @@ -17,12 +17,12 @@ package net.pterodactylus.sone.database.memory; -import java.util.UUID; +import java.util.*; +import javax.annotation.*; -import net.pterodactylus.sone.data.PostReply; -import net.pterodactylus.sone.data.impl.AbstractPostReplyBuilder; -import net.pterodactylus.sone.database.PostReplyBuilder; -import net.pterodactylus.sone.database.SoneProvider; +import net.pterodactylus.sone.data.*; +import net.pterodactylus.sone.data.impl.*; +import net.pterodactylus.sone.database.*; /** * {@link PostReplyBuilder} implementation that creates {@link MemoryPostReply} @@ -30,35 +30,20 @@ import net.pterodactylus.sone.database.SoneProvider; */ class MemoryPostReplyBuilder extends AbstractPostReplyBuilder { - /** The database. */ private final MemoryDatabase database; - - /** The Sone provider. */ private final SoneProvider soneProvider; - /** - * Creates a new {@link MemoryPostReply} builder. - * - * @param database - * The database - * @param soneProvider - * The Sone provider - */ public MemoryPostReplyBuilder(MemoryDatabase database, SoneProvider soneProvider) { this.database = database; this.soneProvider = soneProvider; } - /** - * {@inheritDocs} - */ + @Nonnull @Override public PostReply build() throws IllegalStateException { validate(); - PostReply postReply = new MemoryPostReply(database, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, currentTime ? System.currentTimeMillis() : time, text, postId); - postReply.setKnown(database.isPostReplyKnown(postReply)); - return postReply; + return new MemoryPostReply(database, soneProvider, randomId ? UUID.randomUUID().toString() : id, senderId, currentTime ? System.currentTimeMillis() : time, text, postId); } } diff --git a/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java index 2c8456d..3cb1d6b 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - CreatePostCommand.java - Copyright © 2011–2019 David Roden + * Sone - CreatePostCommand.java - Copyright © 2011–2020 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 @@ -17,8 +17,6 @@ package net.pterodactylus.sone.fcp; -import com.google.common.base.Optional; - import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; @@ -29,7 +27,7 @@ import freenet.support.SimpleFieldSet; /** * FCP command that creates a new {@link Post}. * - * @see Core#createPost(Sone, Optional, String) + * @see Core#createPost(Sone, Sone, String) */ public class CreatePostCommand extends AbstractSoneCommand { @@ -57,7 +55,7 @@ public class CreatePostCommand extends AbstractSoneCommand { if (sone.equals(recipient)) { return new ErrorResponse("Sone and Recipient must not be the same."); } - Post post = getCore().createPost(sone, Optional.fromNullable(recipient), text); + 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 index 9f17940..de0f309 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/CreateReplyCommand.java @@ -1,5 +1,5 @@ /* - * Sone - CreateReplyCommand.java - Copyright © 2011–2019 David Roden + * Sone - CreateReplyCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java index c93029f..5d599e1 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/DeletePostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - DeletePostCommand.java - Copyright © 2011–2019 David Roden + * Sone - DeletePostCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java index 4531f93..59f744a 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/DeleteReplyCommand.java @@ -1,5 +1,5 @@ /* - * Sone - DeleteReplyCommand.java - Copyright © 2011–2019 David Roden + * Sone - DeleteReplyCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java index 0b321f4..3bb8777 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java +++ b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java @@ -1,5 +1,5 @@ /* - * Sone - FcpInterface.java - Copyright © 2011–2019 David Roden + * Sone - FcpInterface.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java index 1427fe7..3ef919e 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetLocalSonesCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetLocalSonesCommand.java - Copyright © 2011–2019 David Roden + * Sone - GetLocalSonesCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java index d1dc643..8033bcb 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetPostCommand.java - Copyright © 2011–2019 David Roden + * Sone - GetPostCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java index bf80dc2..05c2349 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetPostFeedCommand.java - Copyright © 2011–2019 David Roden + * Sone - GetPostFeedCommand.java - Copyright © 2011–2020 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 @@ -32,6 +32,9 @@ import com.google.common.collect.Collections2; import freenet.support.SimpleFieldSet; +import static net.pterodactylus.sone.data.PostKt.newestPostFirst; +import static net.pterodactylus.sone.data.PostKt.noFuturePost; + /** * Implementation of an FCP interface for other clients or plugins to * communicate with Sone. @@ -67,10 +70,10 @@ public class GetPostFeedCommand extends AbstractSoneCommand { allPosts.addAll(friendSone.getPosts()); } allPosts.addAll(getCore().getDirectedPosts(sone.getId())); - allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER); + allPosts = Collections2.filter(allPosts, noFuturePost()::invoke); List sortedPosts = new ArrayList<>(allPosts); - Collections.sort(sortedPosts, Post.NEWEST_FIRST); + sortedPosts.sort(newestPostFirst()); if (sortedPosts.size() < startPost) { return new Response("PostFeed", encodePosts(Collections. emptyList(), "Posts.", false)); diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java index 9cc131b..2f250a3 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostsCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetPostsCommand.java - Copyright © 2011–2019 David Roden + * Sone - GetPostsCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java index a4b936b..64d24fe 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetSoneCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetSoneCommand.java - Copyright © 2011–2019 David Roden + * Sone - GetSoneCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java index ec43143..eedf120 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java @@ -1,5 +1,5 @@ /* - * Sone - GetSonesCommand.java - Copyright © 2011–2019 David Roden + * Sone - GetSonesCommand.java - Copyright © 2011–2020 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 @@ -17,6 +17,7 @@ package net.pterodactylus.sone.fcp; +import static net.pterodactylus.sone.data.SoneKt.*; import static net.pterodactylus.sone.fcp.AbstractSoneCommandKt.encodeSones; import java.util.ArrayList; @@ -24,7 +25,8 @@ import java.util.Collections; import java.util.List; import net.pterodactylus.sone.core.Core; -import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.data.*; + import freenet.support.SimpleFieldSet; /** @@ -53,7 +55,7 @@ public class GetSonesCommand extends AbstractSoneCommand { if (sones.size() < startSone) { return new Response("Sones", encodeSones(Collections. emptyList(), "Sones.")); } - Collections.sort(sones, Sone.NICE_NAME_COMPARATOR); + sones.sort(niceNameComparator()); return new Response("Sones", encodeSones(sones.subList(startSone, (maxSones == -1) ? sones.size() : Math.min(startSone + maxSones, sones.size())), "Sones.")); } diff --git a/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java index edd5a43..058045a 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/LikePostCommand.java @@ -1,5 +1,5 @@ /* - * Sone - LikePostCommand.java - Copyright © 2011–2019 David Roden + * Sone - LikePostCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java index 800e43c..acdfb98 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/LikeReplyCommand.java @@ -1,5 +1,5 @@ /* - * Sone - LikeReplyCommand.java - Copyright © 2011–2019 David Roden + * Sone - LikeReplyCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/LockSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/LockSoneCommand.java index 691bc4b..fbceb9a 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/LockSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/LockSoneCommand.java @@ -1,5 +1,5 @@ /* - * Sone - LockSoneCommand.java - Copyright © 2013–2019 David Roden + * Sone - LockSoneCommand.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/UnlockSoneCommand.java b/src/main/java/net/pterodactylus/sone/fcp/UnlockSoneCommand.java index ca5a59e..d489603 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/UnlockSoneCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/UnlockSoneCommand.java @@ -1,5 +1,5 @@ /* - * Sone - UnlockSoneCommand.java - Copyright © 2013–2019 David Roden + * Sone - UnlockSoneCommand.java - Copyright © 2013–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java b/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java index 7acea2d..a7d1cd7 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java @@ -1,5 +1,5 @@ /* - * Sone - VersionCommand.java - Copyright © 2011–2019 David Roden + * Sone - VersionCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/Key.java b/src/main/java/net/pterodactylus/sone/freenet/Key.java deleted file mode 100644 index 6811642..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/Key.java +++ /dev/null @@ -1,65 +0,0 @@ -package net.pterodactylus.sone.freenet; - -import static freenet.support.Base64.encode; -import static java.lang.String.format; - -import freenet.keys.FreenetURI; - -import com.google.common.annotations.VisibleForTesting; - -/** - * Encapsulates the parts of a {@link FreenetURI} that do not change while - * being converted from SSK to USK and/or back. - */ -public class Key { - - private final byte[] routingKey; - private final byte[] cryptoKey; - private final byte[] extra; - - private Key(byte[] routingKey, byte[] cryptoKey, byte[] extra) { - this.routingKey = routingKey; - this.cryptoKey = cryptoKey; - this.extra = extra; - } - - @VisibleForTesting - public String getRoutingKey() { - return encode(routingKey); - } - - @VisibleForTesting - public String getCryptoKey() { - return encode(cryptoKey); - } - - @VisibleForTesting - public String getExtra() { - return encode(extra); - } - - public FreenetURI toUsk(String docName, long edition, String... paths) { - return new FreenetURI("USK", docName, paths, routingKey, cryptoKey, - extra, edition); - } - - public FreenetURI toSsk(String docName, String... paths) { - return new FreenetURI("SSK", docName, paths, routingKey, cryptoKey, - extra); - } - - public FreenetURI toSsk(String docName, long edition, String... paths) { - return new FreenetURI("SSK", format("%s-%d", docName, edition), paths, - routingKey, cryptoKey, extra, edition); - } - - public static Key from(FreenetURI freenetURI) { - return new Key(freenetURI.getRoutingKey(), freenetURI.getCryptoKey(), - freenetURI.getExtra()); - } - - public static String routingKey(FreenetURI freenetURI) { - return from(freenetURI).getRoutingKey(); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java b/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java deleted file mode 100644 index d667811..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/L10nFilter.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Sone - L10nFilter.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.annotation.Nonnull; - -import net.pterodactylus.util.template.Filter; -import net.pterodactylus.util.template.TemplateContext; - -import freenet.l10n.BaseL10n; - -/** - * {@link Filter} implementation replaces {@link String} values with their - * translated equivalents. - */ -public class L10nFilter implements Filter { - - private final BaseL10n l10n; - - public L10nFilter(BaseL10n l10n) { - this.l10n = l10n; - } - - /** - * {@inheritDoc} - */ - @Override - public String format(TemplateContext templateContext, Object data, Map parameters) { - List parameterValues = getParameters(data, parameters); - String text = getText(data); - if (parameterValues.isEmpty()) { - return l10n.getString(text); - } - return new MessageFormat(l10n.getString(text), new Locale(l10n.getSelectedLanguage().shortCode)).format(parameterValues.toArray()); - } - - @Nonnull - private String getText(Object data) { - return (data instanceof L10nText) ? ((L10nText) data).getText() : String.valueOf(data); - } - - @Nonnull - private List getParameters(Object data, Map parameters) { - if (data instanceof L10nText) { - return ((L10nText) data).getParameters(); - } - List parameterValues = new ArrayList<>(); - int parameterIndex = 0; - while (parameters.containsKey(String.valueOf(parameterIndex))) { - Object value = parameters.get(String.valueOf(parameterIndex)); - parameterValues.add(value); - ++parameterIndex; - } - return parameterValues; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java b/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java index cce544f..a8a2697 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java +++ b/src/main/java/net/pterodactylus/sone/freenet/PluginStoreConfigurationBackend.java @@ -1,5 +1,5 @@ /* - * Sone - PluginStoreConfigurationBackend.java - Copyright © 2010–2019 David Roden + * Sone - PluginStoreConfigurationBackend.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java b/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java index 48eb4bf..a974a0c 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java +++ b/src/main/java/net/pterodactylus/sone/freenet/SimpleFieldSetBuilder.java @@ -1,5 +1,5 @@ /* - * Sone - SimpleFieldSetBuilder.java - Copyright © 2011–2019 David Roden + * Sone - SimpleFieldSetBuilder.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java index 3ea4804..890e3ff 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/AbstractCommand.java @@ -1,5 +1,5 @@ /* - * Sone - AbstractCommand.java - Copyright © 2011–2019 David Roden + * Sone - AbstractCommand.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java index 63fc8eb..f3f32cf 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/Command.java @@ -1,5 +1,5 @@ /* - * Sone - Command.java - Copyright © 2011–2019 David Roden + * Sone - Command.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java b/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java index 0e156a1..db70018 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java +++ b/src/main/java/net/pterodactylus/sone/freenet/fcp/FcpException.java @@ -1,5 +1,5 @@ /* - * Sone - FcpException.java - Copyright © 2011–2019 David Roden + * Sone - FcpException.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginConnector.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginConnector.java deleted file mode 100644 index fbaabb3..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginConnector.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Sone - PluginConnector.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.plugin; - -import net.pterodactylus.sone.freenet.plugin.event.ReceivedReplyEvent; - -import com.google.common.eventbus.EventBus; -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import freenet.pluginmanager.FredPluginTalker; -import freenet.pluginmanager.PluginNotFoundException; -import freenet.pluginmanager.PluginRespirator; -import freenet.pluginmanager.PluginTalker; -import freenet.support.SimpleFieldSet; -import freenet.support.api.Bucket; - -/** - * Interface for talking to other plugins. Other plugins are identified by their - * name and a unique connection identifier. - */ -@Singleton -public class PluginConnector implements FredPluginTalker { - - /** The event bus. */ - private final EventBus eventBus; - - /** The plugin respirator. */ - private final PluginRespirator pluginRespirator; - - /** - * Creates a new plugin connector. - * - * @param eventBus - * The event bus - * @param pluginRespirator - * The plugin respirator - */ - @Inject - public PluginConnector(EventBus eventBus, PluginRespirator pluginRespirator) { - this.eventBus = eventBus; - this.pluginRespirator = pluginRespirator; - } - - // - // ACTIONS - // - - /** - * Sends a request to the given plugin. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the connection - * @param fields - * The fields of the message - * @throws PluginException - * if the plugin can not be found - */ - public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields) throws PluginException { - sendRequest(pluginName, identifier, fields, null); - } - - /** - * Sends a request to the given plugin. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the connection - * @param fields - * The fields of the message - * @param data - * The payload of the message (may be null) - * @throws PluginException - * if the plugin can not be found - */ - public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields, Bucket data) throws PluginException { - getPluginTalker(pluginName, identifier).send(fields, data); - } - - // - // PRIVATE METHODS - // - - /** - * Returns the plugin talker for the given plugin connection. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the connection - * @return The plugin talker - * @throws PluginException - * if the plugin can not be found - */ - private PluginTalker getPluginTalker(String pluginName, String identifier) throws PluginException { - try { - return pluginRespirator.getPluginTalker(this, pluginName, identifier); - } catch (PluginNotFoundException pnfe1) { - throw new PluginException(pnfe1); - } - } - - // - // INTERFACE FredPluginTalker - // - - /** - * {@inheritDoc} - */ - @Override - public void onReply(String pluginName, String identifier, SimpleFieldSet params, Bucket data) { - eventBus.post(new ReceivedReplyEvent(this, pluginName, identifier, params, data)); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginException.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginException.java deleted file mode 100644 index e263a60..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/plugin/PluginException.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Sone - PluginException.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.plugin; - -import net.pterodactylus.sone.freenet.wot.WebOfTrustException; - -/** - * Exception that signals an error when communicating with a plugin. - */ -public class PluginException extends WebOfTrustException { - - /** - * Creates a new plugin exception. - */ - public PluginException() { - super(); - } - - /** - * Creates a new plugin exception. - * - * @param message - * The message of the exception - */ - public PluginException(String message) { - super(message); - } - - /** - * Creates a new plugin exception. - * - * @param cause - * The cause of the exception - */ - public PluginException(Throwable cause) { - super(cause); - } - - /** - * Creates a new plugin exception. - * - * @param message - * The message of the exception - * @param cause - * The cause of the exception - */ - public PluginException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/plugin/event/ReceivedReplyEvent.java b/src/main/java/net/pterodactylus/sone/freenet/plugin/event/ReceivedReplyEvent.java deleted file mode 100644 index ce2ba7f..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/plugin/event/ReceivedReplyEvent.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Sone - ReceivedReplyEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.plugin.event; - -import net.pterodactylus.sone.freenet.plugin.PluginConnector; -import freenet.support.SimpleFieldSet; -import freenet.support.api.Bucket; - -/** - * Event that signals that a plugin reply was received. - */ -public class ReceivedReplyEvent { - - /** The connector that received the reply. */ - private final PluginConnector pluginConnector; - - /** The name of the plugin that sent the reply. */ - private final String pluginName; - - /** The identifier of the initial request. */ - private final String identifier; - - /** The fields containing the reply. */ - private final SimpleFieldSet fieldSet; - - /** The optional reply data. */ - private final Bucket data; - - /** - * Creates a new “reply received” event. - * - * @param pluginConnector - * The connector that received the event - * @param pluginName - * The name of the plugin that sent the reply - * @param identifier - * The identifier of the initial request - * @param fieldSet - * The fields containing the reply - * @param data - * The optional data of the reply - */ - public ReceivedReplyEvent(PluginConnector pluginConnector, String pluginName, String identifier, SimpleFieldSet fieldSet, Bucket data) { - this.pluginConnector = pluginConnector; - this.pluginName = pluginName; - this.identifier = identifier; - this.fieldSet = fieldSet; - this.data = data; - } - - // - // ACCESSORS - // - - /** - * Returns the plugin connector that received the reply. - * - * @return The plugin connector that received the reply - */ - public PluginConnector pluginConnector() { - return pluginConnector; - } - - /** - * Returns the name of the plugin that sent the reply. - * - * @return The name of the plugin that sent the reply - */ - public String pluginName() { - return pluginName; - } - - /** - * Returns the identifier of the initial request. - * - * @return The identifier of the initial request - */ - public String identifier() { - return identifier; - } - - /** - * Returns the fields containing the reply. - * - * @return The fields containing the reply - */ - public SimpleFieldSet fieldSet() { - return fieldSet; - } - - /** - * Returns the optional data of the reply. - * - * @return The optional data of the reply (may be {@code null}) - */ - public Bucket data() { - return data; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Context.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Context.java deleted file mode 100644 index b386bdb..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Context.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Sone - Context.java - Copyright © 2014–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import javax.annotation.Nullable; - -import com.google.common.base.Function; - -/** - * Custom container for the Web of Trust context. This allows easier - * configuration of dependency injection. - */ -public class Context { - - public static final Function extractContext = new Function() { - @Nullable - @Override - public String apply(@Nullable Context context) { - return (context == null) ? null : context.getContext(); - } - }; - - private final String context; - - public Context(String context) { - this.context = context; - } - - public String getContext() { - return context; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java deleted file mode 100644 index 0d92d61..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultIdentity.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Sone - DefaultIdentity.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * A Web of Trust identity. - */ -public class DefaultIdentity implements Identity { - - /** The ID of the identity. */ - private final String id; - - /** The nickname of the identity. */ - private final String nickname; - - /** The request URI of the identity. */ - private final String requestUri; - - /** The contexts of the identity. */ - private final Set contexts = Collections.synchronizedSet(new HashSet()); - - /** The properties of the identity. */ - private final Map properties = Collections.synchronizedMap(new HashMap()); - - /** Cached trust. */ - private final Map trustCache = Collections.synchronizedMap(new HashMap()); - - /** - * Creates a new identity. - * - * @param id - * The ID of the identity - * @param nickname - * The nickname of the identity - * @param requestUri - * The request URI of the identity - */ - public DefaultIdentity(String id, String nickname, String requestUri) { - this.id = id; - this.nickname = nickname; - this.requestUri = requestUri; - } - - // - // ACCESSORS - // - - @Override - public String getId() { - return id; - } - - @Override - public String getNickname() { - return nickname; - } - - @Override - public String getRequestUri() { - return requestUri; - } - - @Override - public Set getContexts() { - return Collections.unmodifiableSet(contexts); - } - - @Override - public boolean hasContext(String context) { - return contexts.contains(context); - } - - @Override - public void setContexts(Collection contexts) { - this.contexts.clear(); - this.contexts.addAll(contexts); - } - - @Override - public Identity addContext(String context) { - contexts.add(context); - return this; - } - - @Override - public Identity removeContext(String context) { - contexts.remove(context); - return this; - } - - @Override - public Map getProperties() { - return Collections.unmodifiableMap(properties); - } - - @Override - public void setProperties(Map properties) { - this.properties.clear(); - this.properties.putAll(properties); - } - - @Override - public String getProperty(String name) { - return properties.get(name); - } - - @Override - public Identity setProperty(String name, String value) { - properties.put(name, value); - return this; - } - - @Override - public Identity removeProperty(String name) { - properties.remove(name); - return this; - } - - @Override - public Trust getTrust(OwnIdentity ownIdentity) { - return trustCache.get(ownIdentity); - } - - @Override - public Identity setTrust(OwnIdentity ownIdentity, Trust trust) { - trustCache.put(ownIdentity, trust); - return this; - } - - @Override - public Identity removeTrust(OwnIdentity ownIdentity) { - trustCache.remove(ownIdentity); - return this; - } - - // - // OBJECT METHODS - // - - @Override - public int hashCode() { - return getId().hashCode(); - } - - @Override - public boolean equals(Object object) { - if (!(object instanceof Identity)) { - return false; - } - Identity identity = (Identity) object; - return identity.getId().equals(getId()); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "[id=" + id + ",nickname=" + nickname + ",contexts=" + contexts + ",properties=" + properties + "]"; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java deleted file mode 100644 index e2e3a74..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Sone - DefaultOwnIdentity.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * An own identity is an identity that the owner of the node has full control - * over. - */ -public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity { - - /** The insert URI of the identity. */ - private final String insertUri; - - /** - * Creates a new own identity. - * - * @param id - * The ID of the identity - * @param nickname - * The nickname of the identity - * @param requestUri - * The request URI of the identity - * @param insertUri - * The insert URI of the identity - */ - public DefaultOwnIdentity(String id, String nickname, String requestUri, String insertUri) { - super(id, nickname, requestUri); - this.insertUri = checkNotNull(insertUri); - } - - // - // ACCESSORS - // - - @Override - public String getInsertUri() { - return insertUri; - } - - @Override - public OwnIdentity addContext(String context) { - return (OwnIdentity) super.addContext(context); - } - - @Override - public OwnIdentity removeContext(String context) { - return (OwnIdentity) super.removeContext(context); - } - - @Override - public OwnIdentity setProperty(String name, String value) { - return (OwnIdentity) super.setProperty(name, value); - } - - @Override - public OwnIdentity removeProperty(String name) { - return (OwnIdentity) super.removeProperty(name); - } - - // - // OBJECT METHODS - // - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object object) { - return super.equals(object); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java index a99aac0..e6f4f62 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java @@ -1,5 +1,5 @@ /* - * Sone - Identity.java - Copyright © 2010–2019 David Roden + * Sone - Identity.java - Copyright © 2010–2020 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 @@ -17,13 +17,9 @@ package net.pterodactylus.sone.freenet.wot; -import java.util.Collection; -import java.util.Collections; import java.util.Map; import java.util.Set; -import com.google.common.base.Function; - /** * Interface for web of trust identities, defining all functions that can be * performed on an identity. An identity is only a container for identity data @@ -31,20 +27,6 @@ import com.google.common.base.Function; */ public interface Identity { - public static final Function> TO_CONTEXTS = new Function>() { - @Override - public Set apply(Identity identity) { - return (identity == null) ? Collections.emptySet() : identity.getContexts(); - } - }; - - public static final Function> TO_PROPERTIES = new Function>() { - @Override - public Map apply(Identity input) { - return (input == null) ? Collections.emptyMap() : input.getProperties(); - } - }; - /** * Returns the ID of the identity. * @@ -97,7 +79,7 @@ public interface Identity { * @param contexts * All contexts of the identity */ - public void setContexts(Collection contexts); + public void setContexts(Set contexts); /** * Removes the given context from this identity. @@ -149,6 +131,8 @@ public interface Identity { */ public Identity removeProperty(String name); + Map getTrust(); + /** * Retrieves the trust that this identity receives from the given own * identity. If this identity is not in the own identity’s trust tree, a diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.java deleted file mode 100644 index 8b28011..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Sone - IdentityChangeDetector.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import static com.google.common.base.Optional.absent; -import static com.google.common.base.Optional.fromNullable; -import static com.google.common.base.Predicates.not; -import static com.google.common.collect.FluentIterable.from; -import static net.pterodactylus.sone.freenet.wot.Identity.TO_CONTEXTS; -import static net.pterodactylus.sone.freenet.wot.Identity.TO_PROPERTIES; - -import java.util.Collection; -import java.util.Map; -import java.util.Map.Entry; - -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableMap; - -/** - * Detects changes between two lists of {@link Identity}s. The detector can find - * added and removed identities, and for identities that exist in both list - * their contexts and properties are checked for added, removed, or (in case of - * properties) changed values. - */ -public class IdentityChangeDetector { - - private final Map oldIdentities; - private Optional onNewIdentity = absent(); - private Optional onRemovedIdentity = absent(); - private Optional onChangedIdentity = absent(); - private Optional onUnchangedIdentity = absent(); - - public IdentityChangeDetector(Collection oldIdentities) { - this.oldIdentities = convertToMap(oldIdentities); - } - - public void onNewIdentity(IdentityProcessor onNewIdentity) { - this.onNewIdentity = fromNullable(onNewIdentity); - } - - public void onRemovedIdentity(IdentityProcessor onRemovedIdentity) { - this.onRemovedIdentity = fromNullable(onRemovedIdentity); - } - - public void onChangedIdentity(IdentityProcessor onChangedIdentity) { - this.onChangedIdentity = fromNullable(onChangedIdentity); - } - - public void onUnchangedIdentity(IdentityProcessor onUnchangedIdentity) { - this.onUnchangedIdentity = fromNullable(onUnchangedIdentity); - } - - public void detectChanges(final Collection newIdentities) { - notifyForRemovedIdentities(from(oldIdentities.values()).filter(notContainedIn(newIdentities))); - notifyForNewIdentities(from(newIdentities).filter(notContainedIn(oldIdentities.values()))); - notifyForChangedIdentities(from(newIdentities).filter(containedIn(oldIdentities)).filter(hasChanged(oldIdentities))); - notifyForUnchangedIdentities(from(newIdentities).filter(containedIn(oldIdentities)).filter(not(hasChanged(oldIdentities)))); - } - - private void notifyForRemovedIdentities(Iterable identities) { - notify(onRemovedIdentity, identities); - } - - private void notifyForNewIdentities(FluentIterable newIdentities) { - notify(onNewIdentity, newIdentities); - } - - private void notifyForChangedIdentities(FluentIterable identities) { - notify(onChangedIdentity, identities); - } - - private void notifyForUnchangedIdentities(FluentIterable identities) { - notify(onUnchangedIdentity, identities); - } - - private void notify(Optional identityProcessor, Iterable identities) { - if (!identityProcessor.isPresent()) { - return; - } - for (Identity identity : identities) { - identityProcessor.get().processIdentity(identity); - } - } - - private static Predicate hasChanged(final Map oldIdentities) { - return new Predicate() { - @Override - public boolean apply(Identity identity) { - return (identity != null) && identityHasChanged(oldIdentities.get(identity.getId()), identity); - } - }; - } - - private static boolean identityHasChanged(Identity oldIdentity, Identity newIdentity) { - return identityHasNewContexts(oldIdentity, newIdentity) - || identityHasRemovedContexts(oldIdentity, newIdentity) - || identityHasNewProperties(oldIdentity, newIdentity) - || identityHasRemovedProperties(oldIdentity, newIdentity) - || identityHasChangedProperties(oldIdentity, newIdentity); - } - - private static boolean identityHasNewContexts(Identity oldIdentity, Identity newIdentity) { - return from(TO_CONTEXTS.apply(newIdentity)).anyMatch(notAContextOf(oldIdentity)); - } - - private static boolean identityHasRemovedContexts(Identity oldIdentity, Identity newIdentity) { - return from(TO_CONTEXTS.apply(oldIdentity)).anyMatch(notAContextOf(newIdentity)); - } - - private static boolean identityHasNewProperties(Identity oldIdentity, Identity newIdentity) { - return from(TO_PROPERTIES.apply(newIdentity).entrySet()).anyMatch(notAPropertyOf(oldIdentity)); - } - - private static boolean identityHasRemovedProperties(Identity oldIdentity, Identity newIdentity) { - return from(TO_PROPERTIES.apply(oldIdentity).entrySet()).anyMatch(notAPropertyOf(newIdentity)); - } - - private static boolean identityHasChangedProperties(Identity oldIdentity, Identity newIdentity) { - return from(TO_PROPERTIES.apply(oldIdentity).entrySet()).anyMatch(hasADifferentValueThanIn(newIdentity)); - } - - private static Predicate containedIn(final Map identities) { - return new Predicate() { - @Override - public boolean apply(Identity identity) { - return (identity != null) && identities.containsKey(identity.getId()); - } - }; - } - - private static Predicate notAContextOf(final Identity identity) { - return new Predicate() { - @Override - public boolean apply(String context) { - return (identity != null) && !identity.getContexts().contains(context); - } - }; - } - - private static Predicate notContainedIn(final Collection newIdentities) { - return new Predicate() { - @Override - public boolean apply(Identity identity) { - return (identity != null) && !newIdentities.contains(identity); - } - }; - } - - private static Predicate> notAPropertyOf(final Identity identity) { - return new Predicate>() { - @Override - public boolean apply(Entry property) { - return (property != null) && !identity.getProperties().containsKey(property.getKey()); - } - }; - } - - private static Predicate> hasADifferentValueThanIn(final Identity newIdentity) { - return new Predicate>() { - @Override - public boolean apply(Entry property) { - return (property != null) && !newIdentity.getProperty(property.getKey()).equals(property.getValue()); - } - }; - } - - private static Map convertToMap(Collection identities) { - ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); - for (Identity identity : identities) { - mapBuilder.put(identity.getId(), identity); - } - return mapBuilder.build(); - } - - public interface IdentityProcessor { - - void processIdentity(Identity identity); - - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeEventSender.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeEventSender.java deleted file mode 100644 index fd57c38..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeEventSender.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Sone - IdentityChangeEventSender.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import java.util.Collection; -import java.util.Map; - -import net.pterodactylus.sone.freenet.wot.IdentityChangeDetector.IdentityProcessor; -import net.pterodactylus.sone.freenet.wot.event.IdentityAddedEvent; -import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent; -import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent; -import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent; -import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent; - -import com.google.common.eventbus.EventBus; - -/** - * Detects changes in {@link Identity}s trusted my multiple {@link - * OwnIdentity}s. - * - * @see IdentityChangeDetector - */ -public class IdentityChangeEventSender { - - private final EventBus eventBus; - private final Map> oldIdentities; - - public IdentityChangeEventSender(EventBus eventBus, Map> oldIdentities) { - this.eventBus = eventBus; - this.oldIdentities = oldIdentities; - } - - public void detectChanges(Map> identities) { - IdentityChangeDetector identityChangeDetector = new IdentityChangeDetector(oldIdentities.keySet()); - identityChangeDetector.onNewIdentity(addNewOwnIdentityAndItsTrustedIdentities(identities)); - identityChangeDetector.onRemovedIdentity(removeOwnIdentityAndItsTrustedIdentities(oldIdentities)); - identityChangeDetector.onUnchangedIdentity(detectChangesInTrustedIdentities(identities, oldIdentities)); - identityChangeDetector.detectChanges(identities.keySet()); - } - - private IdentityProcessor addNewOwnIdentityAndItsTrustedIdentities(final Map> newIdentities) { - return new IdentityProcessor() { - @Override - public void processIdentity(Identity identity) { - eventBus.post(new OwnIdentityAddedEvent((OwnIdentity) identity)); - for (Identity newIdentity : newIdentities.get((OwnIdentity) identity)) { - eventBus.post(new IdentityAddedEvent((OwnIdentity) identity, newIdentity)); - } - } - }; - } - - private IdentityProcessor removeOwnIdentityAndItsTrustedIdentities(final Map> oldIdentities) { - return new IdentityProcessor() { - @Override - public void processIdentity(Identity identity) { - eventBus.post(new OwnIdentityRemovedEvent((OwnIdentity) identity)); - for (Identity removedIdentity : oldIdentities.get((OwnIdentity) identity)) { - eventBus.post(new IdentityRemovedEvent((OwnIdentity) identity, removedIdentity)); - } - } - }; - } - - private IdentityProcessor detectChangesInTrustedIdentities(Map> newIdentities, Map> oldIdentities) { - return new DefaultIdentityProcessor(oldIdentities, newIdentities); - } - - private class DefaultIdentityProcessor implements IdentityProcessor { - - private final Map> oldIdentities; - private final Map> newIdentities; - - public DefaultIdentityProcessor(Map> oldIdentities, Map> newIdentities) { - this.oldIdentities = oldIdentities; - this.newIdentities = newIdentities; - } - - @Override - public void processIdentity(Identity ownIdentity) { - IdentityChangeDetector identityChangeDetector = new IdentityChangeDetector(oldIdentities.get((OwnIdentity) ownIdentity)); - identityChangeDetector.onNewIdentity(notifyForAddedIdentities((OwnIdentity) ownIdentity)); - identityChangeDetector.onRemovedIdentity(notifyForRemovedIdentities((OwnIdentity) ownIdentity)); - identityChangeDetector.onChangedIdentity(notifyForChangedIdentities((OwnIdentity) ownIdentity)); - identityChangeDetector.detectChanges(newIdentities.get((OwnIdentity) ownIdentity)); - } - - private IdentityProcessor notifyForChangedIdentities(final OwnIdentity ownIdentity) { - return new IdentityProcessor() { - @Override - public void processIdentity(Identity identity) { - eventBus.post(new IdentityUpdatedEvent(ownIdentity, identity)); - } - }; - } - - private IdentityProcessor notifyForRemovedIdentities(final OwnIdentity ownIdentity) { - return new IdentityProcessor() { - @Override - public void processIdentity(Identity identity) { - eventBus.post(new IdentityRemovedEvent(ownIdentity, identity)); - } - }; - } - - private IdentityProcessor notifyForAddedIdentities(final OwnIdentity ownIdentity) { - return new IdentityProcessor() { - @Override - public void processIdentity(Identity identity) { - eventBus.post(new IdentityAddedEvent(ownIdentity, identity)); - } - }; - } - - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityLoader.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityLoader.java deleted file mode 100644 index f16df1c..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityLoader.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Sone - IdentityLoader.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import static java.util.Collections.emptySet; -import static net.pterodactylus.sone.freenet.wot.Context.extractContext; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import net.pterodactylus.sone.freenet.plugin.PluginException; - -import com.google.common.base.Optional; -import com.google.inject.Inject; - -/** - * Loads {@link OwnIdentity}s and the {@link Identity}s they trust. - */ -public class IdentityLoader { - - private final WebOfTrustConnector webOfTrustConnector; - private final Optional context; - - public IdentityLoader(WebOfTrustConnector webOfTrustConnector) { - this(webOfTrustConnector, Optional.absent()); - } - - @Inject - public IdentityLoader(WebOfTrustConnector webOfTrustConnector, Optional context) { - this.webOfTrustConnector = webOfTrustConnector; - this.context = context; - } - - public Map> loadIdentities() throws WebOfTrustException { - Collection currentOwnIdentities = webOfTrustConnector.loadAllOwnIdentities(); - return loadTrustedIdentitiesForOwnIdentities(currentOwnIdentities); - } - - private Map> loadTrustedIdentitiesForOwnIdentities(Collection ownIdentities) throws PluginException { - Map> currentIdentities = new HashMap<>(); - - for (OwnIdentity ownIdentity : ownIdentities) { - if (identityDoesNotHaveTheCorrectContext(ownIdentity)) { - currentIdentities.put(ownIdentity, Collections.emptySet()); - continue; - } - - Set trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context.transform(extractContext)); - currentIdentities.put(ownIdentity, trustedIdentities); - } - - return currentIdentities; - } - - private boolean identityDoesNotHaveTheCorrectContext(OwnIdentity ownIdentity) { - return context.isPresent() && !ownIdentity.hasContext(context.transform(extractContext).get()); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java deleted file mode 100644 index c0f6f1b..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManager.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.pterodactylus.sone.freenet.wot; - -import java.util.Set; - -import net.pterodactylus.util.service.Service; - -import com.google.common.eventbus.EventBus; -import com.google.inject.ImplementedBy; - -/** - * Connects to a {@link WebOfTrustConnector} and sends identity events to an - * {@link EventBus}. - */ -@ImplementedBy(IdentityManagerImpl.class) -public interface IdentityManager extends Service { - - boolean isConnected(); - Set getAllOwnIdentities(); - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.java b/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.java deleted file mode 100644 index 6f46465..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Sone - IdentityManagerImpl.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import static java.util.logging.Logger.getLogger; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; - -import net.pterodactylus.sone.freenet.plugin.PluginException; -import net.pterodactylus.util.service.AbstractService; - -import com.google.common.collect.Sets; -import com.google.common.eventbus.EventBus; -import com.google.inject.Inject; -import com.google.inject.Singleton; - -/** - * The identity manager takes care of loading and storing identities, their - * contexts, and properties. It does so in a way that does not expose errors via - * exceptions but it only logs them and tries to return sensible defaults. - *

- * It is also responsible for polling identities from the Web of Trust plugin - * and sending events to the {@link EventBus} when {@link Identity}s and - * {@link OwnIdentity}s are discovered or disappearing. - */ -@Singleton -public class IdentityManagerImpl extends AbstractService implements IdentityManager { - - /** The logger. */ - private static final Logger logger = getLogger(IdentityManagerImpl.class.getName()); - - /** The event bus. */ - private final EventBus eventBus; - - private final IdentityLoader identityLoader; - - /** The Web of Trust connector. */ - private final WebOfTrustConnector webOfTrustConnector; - - /** The currently known own identities. */ - private final Set currentOwnIdentities = Sets.newHashSet(); - - /** - * Creates a new identity manager. - * - * @param eventBus - * The event bus - * @param webOfTrustConnector - * The Web of Trust connector - */ - @Inject - public IdentityManagerImpl(EventBus eventBus, WebOfTrustConnector webOfTrustConnector, IdentityLoader identityLoader) { - super("Sone Identity Manager", false); - this.eventBus = eventBus; - this.webOfTrustConnector = webOfTrustConnector; - this.identityLoader = identityLoader; - } - - // - // ACCESSORS - // - - /** - * Returns whether the Web of Trust plugin could be reached during the last - * try. - * - * @return {@code true} if the Web of Trust plugin is connected, - * {@code false} otherwise - */ - @Override - public boolean isConnected() { - try { - webOfTrustConnector.ping(); - return true; - } catch (PluginException pe1) { - /* not connected, ignore. */ - return false; - } - } - - /** - * Returns all own identities. - * - * @return All own identities - */ - @Override - public Set getAllOwnIdentities() { - synchronized (currentOwnIdentities) { - return new HashSet<>(currentOwnIdentities); - } - } - - // - // SERVICE METHODS - // - - /** - * {@inheritDoc} - */ - @Override - protected void serviceRun() { - Map> oldIdentities = new HashMap<>(); - - while (!shouldStop()) { - try { - Map> currentIdentities = identityLoader.loadIdentities(); - - IdentityChangeEventSender identityChangeEventSender = new IdentityChangeEventSender(eventBus, oldIdentities); - identityChangeEventSender.detectChanges(currentIdentities); - - oldIdentities = currentIdentities; - - synchronized (currentOwnIdentities) { - currentOwnIdentities.clear(); - currentOwnIdentities.addAll(currentIdentities.keySet()); - } - } catch (WebOfTrustException wote1) { - logger.log(Level.WARNING, "WoT has disappeared!", wote1); - } - - /* wait a minute before checking again. */ - sleep(60 * 1000); - } - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java index 500f2c7..25b77b9 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/OwnIdentity.java @@ -1,5 +1,5 @@ /* - * Sone - OwnIdentity.java - Copyright © 2010–2019 David Roden + * Sone - OwnIdentity.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Trust.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Trust.java deleted file mode 100644 index 2da00f6..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Trust.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Sone - Trust.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import static com.google.common.base.Objects.equal; - -import com.google.common.base.Objects; - -/** - * Container class for trust in the web of trust. - */ -public class Trust { - - /** Explicitely assigned trust. */ - private final Integer explicit; - - /** Implicitely calculated trust. */ - private final Integer implicit; - - /** The distance from the owner of the trust tree. */ - private final Integer distance; - - /** - * Creates a new trust container. - * - * @param explicit - * The explicit trust - * @param implicit - * The implicit trust - * @param distance - * The distance - */ - public Trust(Integer explicit, Integer implicit, Integer distance) { - this.explicit = explicit; - this.implicit = implicit; - this.distance = distance; - } - - /** - * Returns the trust explicitely assigned to an identity. - * - * @return The explicitely assigned trust, or {@code null} if the identity - * is not in the own identity’s trust tree - */ - public Integer getExplicit() { - return explicit; - } - - /** - * Returns the implicitely assigned trust, or the calculated trust. - * - * @return The calculated trust, or {@code null} if the identity is not in - * the own identity’s trust tree - */ - public Integer getImplicit() { - return implicit; - } - - /** - * Returns the distance of the trusted identity from the trusting identity. - * - * @return The distance from the own identity, or {@code null} if the - * identity is not in the own identity’s trust tree - */ - public Integer getDistance() { - return distance; - } - - @Override - public boolean equals(Object object) { - if (!(object instanceof Trust)) { - return false; - } - Trust trust = (Trust) object; - return equal(getExplicit(), trust.getExplicit()) && equal(getImplicit(), trust.getImplicit()) && equal(getDistance(), trust.getDistance()); - } - - @Override - public int hashCode() { - return Objects.hashCode(explicit, implicit, distance); - } - - /** {@inheritDoc} */ - @Override - public String toString() { - return getClass().getName() + "[explicit=" + explicit + ",implicit=" + implicit + ",distance=" + distance + "]"; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java deleted file mode 100644 index c1dbef9..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.java +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Sone - WebOfTrustConnector.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.utils.NumberParsers.parseInt; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import java.util.logging.Logger; - -import net.pterodactylus.sone.freenet.plugin.PluginConnector; -import net.pterodactylus.sone.freenet.plugin.PluginException; -import net.pterodactylus.sone.freenet.plugin.event.ReceivedReplyEvent; - -import com.google.common.base.Optional; -import com.google.common.collect.MapMaker; -import com.google.common.eventbus.Subscribe; -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import freenet.support.SimpleFieldSet; -import freenet.support.api.Bucket; - -/** - * Connector for the Web of Trust plugin. - */ -@Singleton -public class WebOfTrustConnector { - - /** The logger. */ - private static final Logger logger = getLogger(WebOfTrustConnector.class.getName()); - - /** The name of the WoT plugin. */ - private static final String WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust"; - - /** Counter for connection identifiers. */ - private final AtomicLong counter = new AtomicLong(); - - /** The plugin connector. */ - private final PluginConnector pluginConnector; - - /** Map for replies. */ - private final Map replies = new MapMaker().makeMap(); - - /** - * Creates a new Web of Trust connector that uses the given plugin - * connector. - * - * @param pluginConnector - * The plugin connector - */ - @Inject - public WebOfTrustConnector(PluginConnector pluginConnector) { - this.pluginConnector = pluginConnector; - } - - // - // ACTIONS - // - - /** - * Stops the web of trust connector. - */ - public void stop() { - /* does nothing. */ - } - - /** - * Loads all own identities from the Web of Trust plugin. - * - * @return All own identity - * @throws WebOfTrustException - * if the own identities can not be loaded - */ - public Set loadAllOwnIdentities() throws WebOfTrustException { - Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get()); - SimpleFieldSet fields = reply.getFields(); - int ownIdentityCounter = -1; - Set ownIdentities = new HashSet<>(); - while (true) { - String id = fields.get("Identity" + ++ownIdentityCounter); - if (id == null) { - break; - } - String requestUri = fields.get("RequestURI" + ownIdentityCounter); - String insertUri = fields.get("InsertURI" + ownIdentityCounter); - String nickname = fields.get("Nickname" + ownIdentityCounter); - DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(id, nickname, requestUri, insertUri); - ownIdentity.setContexts(parseContexts("Contexts" + ownIdentityCounter + ".", fields)); - ownIdentity.setProperties(parseProperties("Properties" + ownIdentityCounter + ".", fields)); - ownIdentities.add(ownIdentity); - } - return ownIdentities; - } - - /** - * Loads all identities that the given identities trusts with a score of - * more than 0. - * - * @param ownIdentity - * The own identity - * @return All trusted identities - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public Set loadTrustedIdentities(OwnIdentity ownIdentity) throws PluginException { - return loadTrustedIdentities(ownIdentity, Optional.absent()); - } - - /** - * Loads all identities that the given identities trusts with a score of - * more than 0 and the (optional) given context. - * - * @param ownIdentity - * The own identity - * @param context - * The context to filter, or {@code null} - * @return All trusted identities - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public Set loadTrustedIdentities(OwnIdentity ownIdentity, Optional context) throws PluginException { - Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", context.or("")).put("WantTrustValues", "true").get()); - SimpleFieldSet fields = reply.getFields(); - Set identities = new HashSet<>(); - int identityCounter = -1; - while (true) { - String id = fields.get("Identity" + ++identityCounter); - if (id == null) { - break; - } - String nickname = fields.get("Nickname" + identityCounter); - String requestUri = fields.get("RequestURI" + identityCounter); - DefaultIdentity identity = new DefaultIdentity(id, nickname, requestUri); - identity.setContexts(parseContexts("Contexts" + identityCounter + ".", fields)); - identity.setProperties(parseProperties("Properties" + identityCounter + ".", fields)); - Integer trust = parseInt(fields.get("Trust" + identityCounter), null); - int score = parseInt(fields.get("Score" + identityCounter), 0); - int rank = parseInt(fields.get("Rank" + identityCounter), 0); - identity.setTrust(ownIdentity, new Trust(trust, score, rank)); - identities.add(identity); - } - return identities; - } - - /** - * Adds the given context to the given identity. - * - * @param ownIdentity - * The identity to add the context to - * @param context - * The context to add - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public void addContext(OwnIdentity ownIdentity, String context) throws PluginException { - performRequest(SimpleFieldSetConstructor.create().put("Message", "AddContext").put("Identity", ownIdentity.getId()).put("Context", context).get()); - } - - /** - * Removes the given context from the given identity. - * - * @param ownIdentity - * The identity to remove the context from - * @param context - * The context to remove - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public void removeContext(OwnIdentity ownIdentity, String context) throws PluginException { - performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveContext").put("Identity", ownIdentity.getId()).put("Context", context).get()); - } - - /** - * Returns the value of the property with the given name. - * - * @param identity - * The identity whose properties to check - * @param name - * The name of the property to return - * @return The value of the property, or {@code null} if there is no value - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public String getProperty(Identity identity, String name) throws PluginException { - Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetProperty").put("Identity", identity.getId()).put("Property", name).get()); - return reply.getFields().get("Property"); - } - - /** - * Sets the property with the given name to the given value. - * - * @param ownIdentity - * The identity to set the property on - * @param name - * The name of the property to set - * @param value - * The value to set - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public void setProperty(OwnIdentity ownIdentity, String name, String value) throws PluginException { - performRequest(SimpleFieldSetConstructor.create().put("Message", "SetProperty").put("Identity", ownIdentity.getId()).put("Property", name).put("Value", value).get()); - } - - /** - * Removes the property with the given name. - * - * @param ownIdentity - * The identity to remove the property from - * @param name - * The name of the property to remove - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public void removeProperty(OwnIdentity ownIdentity, String name) throws PluginException { - performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveProperty").put("Identity", ownIdentity.getId()).put("Property", name).get()); - } - - /** - * Returns the trust for the given identity assigned to it by the given own - * identity. - * - * @param ownIdentity - * The own identity - * @param identity - * The identity to get the trust for - * @return The trust for the given identity - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public Trust getTrust(OwnIdentity ownIdentity, Identity identity) throws PluginException { - Reply getTrustReply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentity").put("Truster", ownIdentity.getId()).put("Identity", identity.getId()).get()); - String trust = getTrustReply.getFields().get("Trust"); - String score = getTrustReply.getFields().get("Score"); - String rank = getTrustReply.getFields().get("Rank"); - Integer explicit = null; - Integer implicit = null; - Integer distance = null; - try { - explicit = Integer.valueOf(trust); - } catch (NumberFormatException nfe1) { - /* ignore. */ - } - try { - implicit = Integer.valueOf(score); - distance = Integer.valueOf(rank); - } catch (NumberFormatException nfe1) { - /* ignore. */ - } - return new Trust(explicit, implicit, distance); - } - - /** - * Sets the trust for the given identity. - * - * @param ownIdentity - * The trusting identity - * @param identity - * The trusted identity - * @param trust - * The amount of trust (-100 thru 100) - * @param comment - * The comment or explanation of the trust value - * @throws PluginException - * if an error occured talking to the Web of Trust plugin - */ - public void setTrust(OwnIdentity ownIdentity, Identity identity, int trust, String comment) throws PluginException { - performRequest(SimpleFieldSetConstructor.create().put("Message", "SetTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).put("Value", String.valueOf(trust)).put("Comment", comment).get()); - } - - /** - * Removes any trust assignment of the given own identity for the given - * identity. - * - * @param ownIdentity - * The own identity - * @param identity - * The identity to remove all trust for - * @throws WebOfTrustException - * if an error occurs - */ - public void removeTrust(OwnIdentity ownIdentity, Identity identity) throws WebOfTrustException { - performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).get()); - } - - /** - * Pings the Web of Trust plugin. If the plugin can not be reached, a - * {@link PluginException} is thrown. - * - * @throws PluginException - * if the plugin is not loaded - */ - public void ping() throws PluginException { - performRequest(SimpleFieldSetConstructor.create().put("Message", "Ping").get()); - } - - // - // PRIVATE ACTIONS - // - - /** - * Parses the contexts from the given fields. - * - * @param prefix - * The prefix to use to access the contexts - * @param fields - * The fields to parse the contexts from - * @return The parsed contexts - */ - private static Set parseContexts(String prefix, SimpleFieldSet fields) { - Set contexts = new HashSet<>(); - int contextCounter = -1; - while (true) { - String context = fields.get(prefix + "Context" + ++contextCounter); - if (context == null) { - break; - } - contexts.add(context); - } - return contexts; - } - - /** - * Parses the properties from the given fields. - * - * @param prefix - * The prefix to use to access the properties - * @param fields - * The fields to parse the properties from - * @return The parsed properties - */ - private static Map parseProperties(String prefix, SimpleFieldSet fields) { - Map properties = new HashMap<>(); - int propertiesCounter = -1; - while (true) { - String propertyName = fields.get(prefix + "Property" + ++propertiesCounter + ".Name"); - if (propertyName == null) { - break; - } - String propertyValue = fields.get(prefix + "Property" + propertiesCounter + ".Value"); - properties.put(propertyName, propertyValue); - } - return properties; - } - - /** - * Sends a request containing the given fields and waits for the target - * message. - * - * @param fields - * The fields of the message - * @return The reply message - * @throws PluginException - * if the request could not be sent - */ - private Reply performRequest(SimpleFieldSet fields) throws PluginException { - return performRequest(fields, null); - } - - /** - * Sends a request containing the given fields and waits for the target - * message. - * - * @param fields - * The fields of the message - * @param data - * The payload of the message - * @return The reply message - * @throws PluginException - * if the request could not be sent - */ - private Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException { - String identifier = "FCP-Command-" + System.currentTimeMillis() + "-" + counter.getAndIncrement(); - Reply reply = new Reply(); - PluginIdentifier pluginIdentifier = new PluginIdentifier(WOT_PLUGIN_NAME, identifier); - replies.put(pluginIdentifier, reply); - - logger.log(Level.FINE, String.format("Sending FCP Request: %s", fields.get("Message"))); - synchronized (reply) { - try { - pluginConnector.sendRequest(WOT_PLUGIN_NAME, identifier, fields, data); - while (reply.getFields() == null) { - try { - reply.wait(); - } catch (InterruptedException ie1) { - logger.log(Level.WARNING, String.format("Got interrupted while waiting for reply on %s.", fields.get("Message")), ie1); - } - } - } finally { - replies.remove(pluginIdentifier); - } - } - logger.log(Level.FINEST, String.format("Received FCP Response for %s: %s", fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null)); - if ((reply.getFields() == null) || "Error".equals(reply.getFields().get("Message"))) { - throw new PluginException("Could not perform request for " + fields.get("Message")); - } - return reply; - } - - /** - * Notifies the connector that a plugin reply was received. - * - * @param receivedReplyEvent - * The event - */ - @Subscribe - public void receivedReply(ReceivedReplyEvent receivedReplyEvent) { - PluginIdentifier pluginIdentifier = new PluginIdentifier(receivedReplyEvent.pluginName(), receivedReplyEvent.identifier()); - Reply reply = replies.remove(pluginIdentifier); - if (reply == null) { - return; - } - logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", receivedReplyEvent.fieldSet().get("Message"))); - synchronized (reply) { - reply.setFields(receivedReplyEvent.fieldSet()); - reply.setData(receivedReplyEvent.data()); - reply.notify(); - } - } - - /** - * Container for the data of the reply from a plugin. - */ - private static class Reply { - - /** The fields of the reply. */ - private SimpleFieldSet fields; - - /** The payload of the reply. */ - private Bucket data; - - /** Empty constructor. */ - public Reply() { - /* do nothing. */ - } - - /** - * Returns the fields of the reply. - * - * @return The fields of the reply - */ - public SimpleFieldSet getFields() { - return fields; - } - - /** - * Sets the fields of the reply. - * - * @param fields - * The fields of the reply - */ - public void setFields(SimpleFieldSet fields) { - this.fields = fields; - } - - /** - * Returns the payload of the reply. - * - * @return The payload of the reply (may be {@code null}) - */ - @SuppressWarnings("unused") - public Bucket getData() { - return data; - } - - /** - * Sets the payload of the reply. - * - * @param data - * The payload of the reply (may be {@code null}) - */ - public void setData(Bucket data) { - this.data = data; - } - - } - - /** - * Helper method to create {@link SimpleFieldSet}s with terser code. - */ - private static class SimpleFieldSetConstructor { - - /** The field set being created. */ - private SimpleFieldSet simpleFieldSet; - - /** - * Creates a new simple field set constructor. - * - * @param shortLived - * {@code true} if the resulting simple field set should be - * short-lived, {@code false} otherwise - */ - private SimpleFieldSetConstructor(boolean shortLived) { - simpleFieldSet = new SimpleFieldSet(shortLived); - } - - // - // ACCESSORS - // - - /** - * Returns the created simple field set. - * - * @return The created simple field set - */ - public SimpleFieldSet get() { - return simpleFieldSet; - } - - /** - * Sets the field with the given name to the given value. - * - * @param name - * The name of the fleld - * @param value - * The value of the field - * @return This constructor (for method chaining) - */ - public SimpleFieldSetConstructor put(String name, String value) { - simpleFieldSet.putOverwrite(name, value); - return this; - } - - // - // ACTIONS - // - - /** - * Creates a new simple field set constructor. - * - * @return The created simple field set constructor - */ - public static SimpleFieldSetConstructor create() { - return create(true); - } - - /** - * Creates a new simple field set constructor. - * - * @param shortLived - * {@code true} if the resulting simple field set should be - * short-lived, {@code false} otherwise - * @return The created simple field set constructor - */ - public static SimpleFieldSetConstructor create(boolean shortLived) { - return new SimpleFieldSetConstructor(shortLived); - } - - } - - /** - * Container for identifying plugins. Plugins are identified by their plugin - * name and their unique identifier. - */ - private static class PluginIdentifier { - - /** The plugin name. */ - private final String pluginName; - - /** The plugin identifier. */ - private final String identifier; - - /** - * Creates a new plugin identifier. - * - * @param pluginName - * The name of the plugin - * @param identifier - * The identifier of the plugin - */ - public PluginIdentifier(String pluginName, String identifier) { - this.pluginName = pluginName; - this.identifier = identifier; - } - - // - // OBJECT METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return pluginName.hashCode() ^ identifier.hashCode(); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object object) { - if (!(object instanceof PluginIdentifier)) { - return false; - } - PluginIdentifier pluginIdentifier = (PluginIdentifier) object; - return pluginName.equals(pluginIdentifier.pluginName) && identifier.equals(pluginIdentifier.identifier); - } - - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustException.java b/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustException.java deleted file mode 100644 index 954fe04..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/WebOfTrustException.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Sone - WebOfTrustException.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot; - -/** - * Exception that signals an error processing web of trust identities, mostly - * when communicating with the web of trust plugin. - */ -public class WebOfTrustException extends Exception { - - /** - * Creates a new web of trust exception. - */ - public WebOfTrustException() { - super(); - } - - /** - * Creates a new web of trust exception. - * - * @param message - * The message of the exception - */ - public WebOfTrustException(String message) { - super(message); - } - - /** - * Creates a new web of trust exception. - * - * @param cause - * The cause of the exception - */ - public WebOfTrustException(Throwable cause) { - super(cause); - } - - /** - * Creates a new web of trust exception. - * - * @param message - * The message of the exception - * @param cause - * The cause of the exception - */ - public WebOfTrustException(String message, Throwable cause) { - super(message, cause); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.java deleted file mode 100644 index 2d7ab32..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Sone - IdentityAddedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot.event; - -import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; - -/** - * Event that signals that an {@link Identity} was added. - */ -public class IdentityAddedEvent extends IdentityEvent { - - /** - * Creates a new “identity added” event. - * - * @param ownIdentity - * The own identity that added the identity - * @param identity - * The identity that was added - */ - public IdentityAddedEvent(OwnIdentity ownIdentity, Identity identity) { - super(ownIdentity, identity); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityEvent.java deleted file mode 100644 index 8a23ae1..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityEvent.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Sone - IdentityEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot.event; - -import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; - -/** - * Base class for {@link Identity} events. - */ -public abstract class IdentityEvent { - - /** The own identity this event relates to. */ - private final OwnIdentity ownIdentity; - - /** The identity this event is about. */ - private final Identity identity; - - /** - * Creates a new identity-based event. - * - * @param ownIdentity - * The own identity that relates to the identity - * @param identity - * The identity this event is about - */ - protected IdentityEvent(OwnIdentity ownIdentity, Identity identity) { - this.ownIdentity = ownIdentity; - this.identity = identity; - } - - // - // ACCESSORS - // - - /** - * Returns the own identity this event relates to. - * - * @return The own identity this event relates to - */ - public OwnIdentity ownIdentity() { - return ownIdentity; - } - - /** - * Returns the identity this event is about. - * - * @return The identity this event is about - */ - public Identity identity() { - return identity; - } - - @Override - public int hashCode() { - return ownIdentity().hashCode() ^ identity().hashCode(); - } - - @Override - public boolean equals(Object object) { - if ((object == null) || !object.getClass().equals(getClass())) { - return false; - } - IdentityEvent identityEvent = (IdentityEvent) object; - return ownIdentity().equals(identityEvent.ownIdentity()) && identity().equals(identityEvent.identity()); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.java deleted file mode 100644 index 655015e..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Sone - IdentityRemovedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot.event; - -import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; - -/** - * Event that signals that an {@link Identity} was removed. - */ -public class IdentityRemovedEvent extends IdentityEvent { - - /** - * Creates a new “identity removed” event. - * - * @param ownIdentity - * The own identity that removed the identity - * @param identity - * The identity that was removed - */ - public IdentityRemovedEvent(OwnIdentity ownIdentity, Identity identity) { - super(ownIdentity, identity); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.java deleted file mode 100644 index a71ad5b..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Sone - IdentityUpdatedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot.event; - -import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; - -/** - * Event that signals that an {@link Identity} was updated. - */ -public class IdentityUpdatedEvent extends IdentityEvent { - - /** - * Creates a new “identity updated” event. - * - * @param ownIdentity - * The own identity that tracks the identity - * @param identity - * The identity that was updated - */ - public IdentityUpdatedEvent(OwnIdentity ownIdentity, Identity identity) { - super(ownIdentity, identity); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.java deleted file mode 100644 index fc34848..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Sone - OwnIdentityAddedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot.event; - -import net.pterodactylus.sone.freenet.wot.OwnIdentity; - -/** - * Event that signals that an {@link OwnIdentity} was added. - */ -public class OwnIdentityAddedEvent extends OwnIdentityEvent { - - /** - * Creates new “own identity added” event. - * - * @param ownIdentity - * The own identity that was added - */ - public OwnIdentityAddedEvent(OwnIdentity ownIdentity) { - super(ownIdentity); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityEvent.java deleted file mode 100644 index 9e88d20..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityEvent.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Sone - OwnIdentityEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot.event; - -import net.pterodactylus.sone.freenet.wot.OwnIdentity; - -/** - * Base class for {@link OwnIdentity} events. - */ -public abstract class OwnIdentityEvent { - - /** The own identity this event is about. */ - private final OwnIdentity ownIdentity; - - /** - * Creates a new own identity-based event. - * - * @param ownIdentity - * The own identity this event is about - */ - protected OwnIdentityEvent(OwnIdentity ownIdentity) { - this.ownIdentity = ownIdentity; - } - - // - // ACCESSORS - // - - /** - * Returns the own identity this event is about. - * - * @return The own identity this event is about - */ - public OwnIdentity ownIdentity() { - return ownIdentity; - } - - @Override - public int hashCode() { - return ownIdentity().hashCode(); - } - - @Override - public boolean equals(Object object) { - if ((object == null) || !object.getClass().equals(getClass())) { - return false; - } - OwnIdentityEvent ownIdentityEvent = (OwnIdentityEvent) object; - return ownIdentity().equals(ownIdentityEvent.ownIdentity()); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.java b/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.java deleted file mode 100644 index 73761b0..0000000 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Sone - OwnIdentityRemovedEvent.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.freenet.wot.event; - -import net.pterodactylus.sone.freenet.wot.OwnIdentity; - -/** - * Event that signals that an {@link OwnIdentity} was removed. - */ -public class OwnIdentityRemovedEvent extends OwnIdentityEvent { - - /** - * Creates a new “own identity removed” event. - * - * @param ownIdentity - * The own identity that was removed - */ - public OwnIdentityRemovedEvent(OwnIdentity ownIdentity) { - super(ownIdentity); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java b/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java index c42b056..2e18347 100644 --- a/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java +++ b/src/main/java/net/pterodactylus/sone/main/DebugLoaders.java @@ -1,6 +1,7 @@ package net.pterodactylus.sone.main; import java.io.File; +import javax.annotation.Nonnull; import net.pterodactylus.sone.template.FilesystemTemplate; import net.pterodactylus.sone.web.pages.ReloadingPage; @@ -21,16 +22,19 @@ public class DebugLoaders implements Loaders { this.filesystemPath = filesystemPath; } + @Nonnull @Override - public Template loadTemplate(String path) { + public Template loadTemplate(@Nonnull String path) { return new FilesystemTemplate(new File(filesystemPath, path).getAbsolutePath()); } + @Nonnull @Override - public Page loadStaticPage(String basePath, String prefix, String mimeType) { + public Page loadStaticPage(@Nonnull String basePath, @Nonnull String prefix, @Nonnull String mimeType) { return new ReloadingPage<>(basePath, new File(filesystemPath, prefix).getAbsolutePath(), mimeType); } + @Nonnull @Override public TemplateProvider getTemplateProvider() { return new FilesystemTemplateProvider(new File(filesystemPath, "/templates/").getAbsolutePath()); diff --git a/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java b/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java index 72d8d19..5072970 100644 --- a/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java +++ b/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java @@ -1,14 +1,10 @@ package net.pterodactylus.sone.main; -import static net.pterodactylus.util.template.TemplateParser.parse; +import java.io.*; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UnsupportedEncodingException; +import javax.annotation.Nonnull; import net.pterodactylus.sone.web.WebInterface; -import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.template.ClassPathTemplateProvider; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateProvider; @@ -16,33 +12,32 @@ import net.pterodactylus.util.web.Page; import net.pterodactylus.util.web.Request; import net.pterodactylus.util.web.StaticPage; +import static net.pterodactylus.util.template.TemplateParser.parse; + /** * Default {@link Loaders} implementation that loads resources from the classpath. */ public class DefaultLoaders implements Loaders { + @Nonnull @Override - public Template loadTemplate(String path) { - InputStream templateInputStream = null; - Reader reader = null; - try { - templateInputStream = getClass().getResourceAsStream(path); - reader = new InputStreamReader(templateInputStream, "UTF-8"); + public Template loadTemplate(@Nonnull String path) { + try (InputStream templateInputStream = getClass().getResourceAsStream(path); + Reader reader = new InputStreamReader(templateInputStream, "UTF-8");) { return parse(reader); - } catch (UnsupportedEncodingException uee1) { + } catch (IOException ioe1) { throw new RuntimeException("UTF-8 not supported."); - } finally { - Closer.close(reader); - Closer.close(templateInputStream); } } + @Nonnull @Override - public Page loadStaticPage(String pathPrefix, String basePath, String mimeType) { + public Page loadStaticPage(@Nonnull String pathPrefix, @Nonnull String basePath, @Nonnull String mimeType) { return new StaticPage(pathPrefix, basePath, mimeType) { }; } + @Nonnull @Override public TemplateProvider getTemplateProvider() { return new ClassPathTemplateProvider(WebInterface.class, "/templates/"); diff --git a/src/main/java/net/pterodactylus/sone/main/Loaders.java b/src/main/java/net/pterodactylus/sone/main/Loaders.java index 8ee5132..b07118b 100644 --- a/src/main/java/net/pterodactylus/sone/main/Loaders.java +++ b/src/main/java/net/pterodactylus/sone/main/Loaders.java @@ -1,5 +1,7 @@ package net.pterodactylus.sone.main; +import javax.annotation.Nonnull; + import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateProvider; import net.pterodactylus.util.web.Page; @@ -13,8 +15,8 @@ import com.google.inject.ImplementedBy; @ImplementedBy(DefaultLoaders.class) public interface Loaders { - Template loadTemplate(String path); - Page loadStaticPage(String basePath, String prefix, String mimeType); - TemplateProvider getTemplateProvider(); + @Nonnull Template loadTemplate(@Nonnull String path); + @Nonnull Page loadStaticPage(@Nonnull String basePath, @Nonnull String prefix, @Nonnull String mimeType); + @Nonnull TemplateProvider getTemplateProvider(); } diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index ceb336b..82ab065 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -1,5 +1,5 @@ /* - * Sone - SonePlugin.java - Copyright © 2010–2019 David Roden + * Sone - SonePlugin.java - Copyright © 2010–2020 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 @@ -17,62 +17,33 @@ package net.pterodactylus.sone.main; -import static com.google.common.base.Optional.of; -import static java.util.logging.Logger.getLogger; +import static java.util.logging.Logger.*; -import java.io.File; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.LogRecord; import java.util.logging.Logger; - -import javax.inject.Singleton; - -import net.pterodactylus.sone.core.Core; -import net.pterodactylus.sone.database.Database; -import net.pterodactylus.sone.database.PostProvider; -import net.pterodactylus.sone.database.SoneProvider; -import net.pterodactylus.sone.database.memory.MemoryDatabase; -import net.pterodactylus.sone.fcp.FcpInterface; -import net.pterodactylus.sone.freenet.PluginStoreConfigurationBackend; -import net.pterodactylus.sone.freenet.wot.Context; -import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector; -import net.pterodactylus.sone.web.WebInterface; -import net.pterodactylus.sone.web.WebInterfaceModule; -import net.pterodactylus.util.config.Configuration; -import net.pterodactylus.util.config.ConfigurationException; -import net.pterodactylus.util.config.MapConfigurationBackend; -import net.pterodactylus.util.version.Version; - -import com.google.common.base.Optional; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import com.google.common.eventbus.EventBus; -import com.google.inject.AbstractModule; -import com.google.inject.Guice; -import com.google.inject.Injector; -import com.google.inject.Module; -import com.google.inject.TypeLiteral; -import com.google.inject.matcher.Matchers; -import com.google.inject.spi.InjectionListener; -import com.google.inject.spi.TypeEncounter; -import com.google.inject.spi.TypeListener; - -import freenet.client.async.PersistenceDisabledException; -import freenet.l10n.BaseL10n; -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; +import java.util.logging.*; + +import javax.annotation.Nonnull; + +import net.pterodactylus.sone.core.*; +import net.pterodactylus.sone.core.event.*; +import net.pterodactylus.sone.fcp.*; +import net.pterodactylus.sone.freenet.wot.*; +import net.pterodactylus.sone.web.*; +import net.pterodactylus.sone.web.notification.NotificationHandler; +import net.pterodactylus.sone.web.notification.NotificationHandlerModule; + +import freenet.l10n.BaseL10n.*; +import freenet.l10n.*; +import freenet.pluginmanager.*; +import freenet.support.*; +import freenet.support.api.*; + +import com.google.common.annotations.*; +import com.google.common.eventbus.*; +import com.google.common.cache.*; +import com.google.inject.*; +import com.google.inject.name.*; +import kotlin.jvm.functions.*; /** * This class interfaces with Freenet. It is the class that is loaded by the @@ -85,12 +56,13 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr static { /* initialize logging. */ soneLogger.setUseParentHandlers(false); + soneLogger.setLevel(Level.ALL); soneLogger.addHandler(new Handler() { private final LoadingCache> classCache = CacheBuilder.newBuilder() .build(new CacheLoader>() { @Override - public Class load(String key) throws Exception { - return Class.forName(key); + public Class load(@Nonnull String key) throws Exception { + return SonePlugin.class.getClassLoader().loadClass(key); } }); @@ -122,19 +94,24 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr } /** The current year at time of release. */ - private static final int YEAR = 2019; + private static final int YEAR = 2020; private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/"; - private static final int LATEST_EDITION = 79; + private static final int LATEST_EDITION = 81; /** The logger. */ private static final Logger logger = getLogger(SonePlugin.class.getName()); + private final Function1 injectorCreator; + /** The plugin respirator. */ private PluginRespirator pluginRespirator; /** The core. */ private Core core; + /** The event bus. */ + private EventBus eventBus; + /** The web interface. */ private WebInterface webInterface; @@ -147,6 +124,15 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /** The web of trust connector. */ private WebOfTrustConnector webOfTrustConnector; + public SonePlugin() { + this(Guice::createInjector); + } + + @VisibleForTesting + public SonePlugin(Function1 injectorCreator) { + this.injectorCreator = injectorCreator; + } + // // ACCESSORS // @@ -206,92 +192,7 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr public void runPlugin(PluginRespirator pluginRespirator) { this.pluginRespirator = pluginRespirator; - /* create a configuration. */ - Configuration oldConfiguration; - Configuration newConfiguration = null; - boolean firstStart = !new File("sone.properties").exists(); - boolean newConfig = false; - try { - oldConfiguration = new Configuration(new MapConfigurationBackend(new File("sone.properties"), false)); - newConfiguration = oldConfiguration; - } catch (ConfigurationException ce1) { - newConfig = true; - logger.log(Level.INFO, "Could not load configuration file, trying plugin store…", ce1); - try { - newConfiguration = new Configuration(new MapConfigurationBackend(new File("sone.properties"), true)); - logger.log(Level.INFO, "Created new configuration file."); - } catch (ConfigurationException ce2) { - logger.log(Level.SEVERE, "Could not create configuration file, using Plugin Store!", ce2); - } - try { - oldConfiguration = new Configuration(new PluginStoreConfigurationBackend(pluginRespirator)); - logger.log(Level.INFO, "Plugin store loaded."); - } catch (PersistenceDisabledException pde1) { - logger.log(Level.SEVERE, "Could not load any configuration, using empty configuration!"); - oldConfiguration = new Configuration(new MapConfigurationBackend()); - } - } - - final Configuration startConfiguration; - if ((newConfiguration != null) && (oldConfiguration != newConfiguration)) { - logger.log(Level.INFO, "Setting configuration to file-based configuration."); - startConfiguration = newConfiguration; - } else { - startConfiguration = oldConfiguration; - } - final EventBus eventBus = new EventBus(); - - /* Freenet injector configuration. */ - FreenetModule freenetModule = new FreenetModule(pluginRespirator); - - /* Sone injector configuration. */ - AbstractModule soneModule = new AbstractModule() { - - @Override - protected void configure() { - bind(EventBus.class).toInstance(eventBus); - bind(Configuration.class).toInstance(startConfiguration); - Context context = new Context("Sone"); - bind(Context.class).toInstance(context); - bind(getOptionalContextTypeLiteral()).toInstance(of(context)); - bind(SonePlugin.class).toInstance(SonePlugin.this); - bind(Version.class).toInstance(Version.parse(getVersion().substring(1))); - bind(PluginVersion.class).toInstance(new PluginVersion(getVersion())); - bind(PluginYear.class).toInstance(new PluginYear(getYear())); - bind(PluginHomepage.class).toInstance(new PluginHomepage(getHomepage())); - bind(Database.class).to(MemoryDatabase.class).in(Singleton.class); - bind(BaseL10n.class).toInstance(l10n.getBase()); - bind(SoneProvider.class).to(Core.class).in(Singleton.class); - bind(PostProvider.class).to(Core.class).in(Singleton.class); - if (startConfiguration.getBooleanValue("Developer.LoadFromFilesystem").getValue(false)) { - String path = startConfiguration.getStringValue("Developer.FilesystemPath").getValue(null); - if (path != null) { - bind(Loaders.class).toInstance(new DebugLoaders(path)); - } - } - bindListener(Matchers.any(), new TypeListener() { - - @Override - public void hear(TypeLiteral typeLiteral, TypeEncounter typeEncounter) { - typeEncounter.register(new InjectionListener() { - - @Override - public void afterInjection(I injectee) { - eventBus.register(injectee); - } - }); - } - }); - } - - private TypeLiteral> getOptionalContextTypeLiteral() { - return new TypeLiteral>() { - }; - } - - }; - Module webInterfaceModule = new WebInterfaceModule(); - Injector injector = Guice.createInjector(freenetModule, soneModule, webInterfaceModule); + Injector injector = createInjector(); core = injector.getInstance(Core.class); /* create web of trust connector. */ @@ -303,11 +204,47 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr /* create the web interface. */ webInterface = injector.getInstance(WebInterface.class); + /* we need to request this to install all notification handlers. */ + injector.getInstance(NotificationHandler.class); + + /* and this is required to shutdown all tickers. */ + injector.getInstance(TickerShutdown.class); + /* start core! */ core.start(); + + /* start the web interface! */ webInterface.start(); - webInterface.setFirstStart(firstStart); - webInterface.setNewConfig(newConfig); + + /* send some events on startup */ + eventBus = injector.getInstance(EventBus.class); + + /* first start? */ + if (injector.getInstance(Key.get(Boolean.class, Names.named("FirstStart")))) { + eventBus.post(new FirstStart()); + } else { + /* new config? */ + if (injector.getInstance(Key.get(Boolean.class, Names.named("NewConfig")))) { + eventBus.post(new ConfigNotRead()); + } + } + + eventBus.post(new Startup()); + } + + @VisibleForTesting + protected Injector createInjector() { + FreenetModule freenetModule = new FreenetModule(pluginRespirator); + AbstractModule soneModule = new SoneModule(this, new EventBus()); + Module webInterfaceModule = new WebInterfaceModule(); + Module notificationHandlerModule = new NotificationHandlerModule(); + + return createInjector(freenetModule, soneModule, webInterfaceModule, notificationHandlerModule); + } + + @VisibleForTesting + protected Injector createInjector(Module... modules) { + return injectorCreator.invoke(modules); } /** @@ -315,6 +252,9 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr */ @Override public void terminate() { + /* send shutdown event. */ + eventBus.post(new Shutdown()); + try { /* stop the web interface. */ webInterface.stop(); diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.kt b/src/main/java/net/pterodactylus/sone/main/SonePlugin.kt deleted file mode 100644 index 5e0b2c1..0000000 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.kt +++ /dev/null @@ -1,7 +0,0 @@ -package net.pterodactylus.sone.main - -data class PluginVersion(val version: String) - -data class PluginYear(val year: Int) - -data class PluginHomepage(val homepage: String) diff --git a/src/main/java/net/pterodactylus/sone/notify/ListNotification.java b/src/main/java/net/pterodactylus/sone/notify/ListNotification.java deleted file mode 100644 index 6a7b086..0000000 --- a/src/main/java/net/pterodactylus/sone/notify/ListNotification.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Sone - ListNotification.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.notify; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import net.pterodactylus.util.notify.TemplateNotification; -import net.pterodactylus.util.template.Template; - -/** - * Notification that maintains a list of new elements. - * - * @param - * The type of the items - */ -public class ListNotification extends TemplateNotification { - - /** The key under which to store the elements in the template. */ - private final String key; - - /** The list of new elements. */ - private final List elements = new CopyOnWriteArrayList<>(); - - /** - * Creates a new list notification. - * - * @param id - * The ID of the notification - * @param key - * The key under which to store the elements in the template - * @param template - * The template to render - */ - public ListNotification(String id, String key, Template template) { - this(id, key, template, true); - } - - /** - * Creates a new list notification. - * - * @param id - * The ID of the notification - * @param key - * The key under which to store the elements in the template - * @param template - * The template to render - * @param dismissable - * {@code true} if this notification should be dismissable by the - * user, {@code false} otherwise - */ - public ListNotification(String id, String key, Template template, boolean dismissable) { - super(id, System.currentTimeMillis(), System.currentTimeMillis(), dismissable, template); - this.key = key; - template.getInitialContext().set(key, elements); - } - - /** - * Creates a new list notification that copies its ID and the template from - * the given list notification. - * - * @param listNotification - * The list notification to copy - */ - public ListNotification(ListNotification listNotification) { - super(listNotification.getId(), listNotification.getCreatedTime(), listNotification.getLastUpdatedTime(), listNotification.isDismissable(), new Template()); - this.key = listNotification.key; - getTemplate().add(listNotification.getTemplate()); - getTemplate().getInitialContext().set(key, elements); - } - - // - // ACTIONS - // - - /** - * Returns the current list of elements. - * - * @return The current list of elements - */ - public List getElements() { - return new ArrayList<>(elements); - } - - /** - * Sets the elements to show in this notification. This method will not call - * {@link #touch()}. - * - * @param elements - * The elements to show - */ - public void setElements(Collection elements) { - this.elements.clear(); - this.elements.addAll(elements); - touch(); - } - - /** - * Returns whether there are any new elements. - * - * @return {@code true} if there are no new elements, {@code false} if there - * are new elements - */ - public boolean isEmpty() { - return elements.isEmpty(); - } - - /** - * Adds a discovered element. - * - * @param element - * The new element - */ - public void add(T element) { - elements.add(element); - touch(); - } - - /** - * Removes the given element from the list of new elements. - * - * @param element - * The element to remove - */ - public void remove(T element) { - while (elements.remove(element)) { - /* do nothing, just remove all instances of the element. */ - } - if (elements.isEmpty()) { - dismiss(); - } - touch(); - } - - // - // ABSTRACTNOTIFICATION METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public void dismiss() { - super.dismiss(); - elements.clear(); - } - - // - // OBJECT METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - int hashCode = super.hashCode(); - for (T element : elements) { - hashCode ^= element.hashCode(); - } - return hashCode; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object object) { - if (!(object instanceof ListNotification)) { - return false; - } - ListNotification listNotification = (ListNotification) object; - if (!super.equals(listNotification)) { - return false; - } - if (!key.equals(listNotification.key)) { - return false; - } - return elements.equals(listNotification.elements); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilter.java b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilter.java index 50a1087..739907c 100644 --- a/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilter.java +++ b/src/main/java/net/pterodactylus/sone/notify/ListNotificationFilter.java @@ -1,5 +1,5 @@ /* - * Sone - ListNotificationFilter.java - Copyright © 2010–2019 David Roden + * Sone - ListNotificationFilter.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java index 19b600e..87378cc 100644 --- a/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/AlbumAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - AlbumAccessor.java - Copyright © 2011–2019 David Roden + * Sone - AlbumAccessor.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java b/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java index 8ce97ad..f960b08 100644 --- a/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - CollectionAccessor.java - Copyright © 2010–2019 David Roden + * Sone - CollectionAccessor.java - Copyright © 2010–2020 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 @@ -17,12 +17,13 @@ package net.pterodactylus.sone.template; +import static net.pterodactylus.sone.data.SoneKt.*; + import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; -import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.data.*; import net.pterodactylus.util.template.Accessor; import net.pterodactylus.util.template.ReflectionAccessor; import net.pterodactylus.util.template.TemplateContext; @@ -52,7 +53,7 @@ public class CollectionAccessor extends ReflectionAccessor { } sones.add((Sone) sone); } - Collections.sort(sones, Sone.NICE_NAME_COMPARATOR); + sones.sort(niceNameComparator()); StringBuilder soneNames = new StringBuilder(); for (Sone sone : sones) { if (soneNames.length() > 0) { diff --git a/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java b/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java index 1ff4f04..6439c44 100644 --- a/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/CssClassNameFilter.java @@ -1,5 +1,5 @@ /* - * Sone - CssClassNameFilter.java - Copyright © 2010–2019 David Roden + * Sone - CssClassNameFilter.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java b/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java index 172b4b2..2910ec8 100644 --- a/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java +++ b/src/main/java/net/pterodactylus/sone/template/GetPagePlugin.java @@ -1,5 +1,5 @@ /* - * Sone - GetPagePlugin.java - Copyright © 2010–2019 David Roden + * Sone - GetPagePlugin.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java b/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java index 0e6fda0..a43bd70 100644 --- a/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/HttpRequestAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - HttpRequestAccessor.java - Copyright © 2011–2019 David Roden + * Sone - HttpRequestAccessor.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java index ed139de..5b229dd 100644 --- a/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/IdentityAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - IdentityAccessor.java - Copyright © 2010–2019 David Roden + * Sone - IdentityAccessor.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java b/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java index 78c3aeb..6feb94e 100644 --- a/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ImageAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - ImageAccessor.java - Copyright © 2011–2019 David Roden + * Sone - ImageAccessor.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java index fc4b803..85a7562 100644 --- a/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java @@ -1,5 +1,5 @@ /* - * Sone - ImageLinkFilter.java - Copyright © 2011–2019 David Roden + * Sone - ImageLinkFilter.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java b/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java index 8dd2b04..a5f734d 100644 --- a/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/JavascriptFilter.java @@ -1,5 +1,5 @@ /* - * Sone - JavascriptFilter.java - Copyright © 2011–2019 David Roden + * Sone - JavascriptFilter.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/PostAccessor.java b/src/main/java/net/pterodactylus/sone/template/PostAccessor.java deleted file mode 100644 index 4025c7b..0000000 --- a/src/main/java/net/pterodactylus/sone/template/PostAccessor.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Sone - PostAccessor.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.template; - -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.util.template.ReflectionAccessor; -import net.pterodactylus.util.template.TemplateContext; - -import com.google.common.collect.Collections2; - -/** - * Accessor for {@link Post} objects that adds additional properties: - *

- *
replies
- *
All replies to this post, sorted by time, oldest first
- *
- */ -public class PostAccessor extends ReflectionAccessor { - - /** The core to get the replies from. */ - private final Core core; - - /** - * Creates a new post accessor. - * - * @param core - * The core to get the replies from - */ - public PostAccessor(Core core) { - this.core = core; - } - - /** - * {@inheritDoc} - */ - @Override - public Object get(TemplateContext templateContext, Object object, String member) { - Post post = (Post) object; - if ("replies".equals(member)) { - return Collections2.filter(core.getReplies(post.getId()), Reply.FUTURE_REPLY_FILTER); - } else if (member.equals("likes")) { - return core.getLikes(post); - } else if (member.equals("liked")) { - Sone currentSone = (Sone) templateContext.get("currentSone"); - return (currentSone != null) && (currentSone.isLikedPostId(post.getId())); - } else if (member.equals("new")) { - return !post.isKnown(); - } else if (member.equals("bookmarked")) { - return core.isBookmarked(post); - } - return super.get(templateContext, object, member); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java index a152038..7dc1eb5 100644 --- a/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ProfileAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - ProfileAccessor.java - Copyright © 2011–2019 David Roden + * Sone - ProfileAccessor.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java b/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java index c6dea31..fe1ccd3 100644 --- a/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/ReplyAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - ReplyAccessor.java - Copyright © 2010–2019 David Roden + * Sone - ReplyAccessor.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java index c093e6f..9623b62 100644 --- a/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ReplyGroupFilter.java @@ -1,5 +1,5 @@ /* - * Sone - ReplyGroupFilter.java - Copyright © 2010–2019 David Roden + * Sone - ReplyGroupFilter.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java index d004dfc..16cb062 100644 --- a/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/RequestChangeFilter.java @@ -1,5 +1,5 @@ /* - * Sone - RequestChangeFilter.java - Copyright © 2010–2019 David Roden + * Sone - RequestChangeFilter.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java index a8cbe57..b072dc9 100644 --- a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - SoneAccessor.java - Copyright © 2010–2019 David Roden + * Sone - SoneAccessor.java - Copyright © 2010–2020 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 @@ -17,11 +17,7 @@ package net.pterodactylus.sone.template; -import static com.google.common.collect.FluentIterable.from; -import static java.util.Arrays.asList; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.data.Album.FLATTENER; -import static net.pterodactylus.sone.data.Album.IMAGES; import java.util.logging.Level; import java.util.logging.Logger; @@ -30,6 +26,7 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.SoneKt; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.sone.text.TimeTextConverter; @@ -116,7 +113,7 @@ public class SoneAccessor extends ReflectionAccessor { } return trust; } else if (member.equals("allImages")) { - return from(asList(sone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES); + return SoneKt.getAllImages(sone); } else if (member.equals("albums")) { return sone.getRootAlbum().getAlbums(); } diff --git a/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java b/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java index 355b6a9..2a2e526 100644 --- a/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/SubstringFilter.java @@ -1,5 +1,5 @@ /* - * Sone - SubstringFilter.java - Copyright © 2010–2019 David Roden + * Sone - SubstringFilter.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java b/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java index 26a128b..6ccc897 100644 --- a/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/TrustAccessor.java @@ -1,5 +1,5 @@ /* - * Sone - TrustAccessor.java - Copyright © 2010–2019 David Roden + * Sone - TrustAccessor.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java b/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java index 6fb584e..0e12448 100644 --- a/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/UniqueElementFilter.java @@ -1,5 +1,5 @@ /* - * Sone - UniqueElementFilter.java - Copyright © 2011–2019 David Roden + * Sone - UniqueElementFilter.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java b/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java index 0d4872b..2d6c05c 100644 --- a/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/UnknownDateFilter.java @@ -1,5 +1,5 @@ /* - * Sone - UnknownDateFilter.java - Copyright © 2011–2019 David Roden + * Sone - UnknownDateFilter.java - Copyright © 2011–2020 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 @@ -19,18 +19,18 @@ package net.pterodactylus.sone.template; import java.util.Map; +import net.pterodactylus.sone.freenet.Translation; import net.pterodactylus.util.template.Filter; import net.pterodactylus.util.template.TemplateContext; -import freenet.l10n.BaseL10n; /** * {@link Filter} implementation that replaces a {@link Long} with a value of - * {@code 0} by a {@link String} from an {@link BaseL10n l10n handler}. + * {@code 0} by a {@link String} from a {@link Translation translation}. */ public class UnknownDateFilter implements Filter { - /** The l10n handler. */ - private BaseL10n l10nHandler; + /** The translation. */ + private final Translation translation; /** The key for the text to show. */ private final String unknownKey; @@ -38,13 +38,11 @@ public class UnknownDateFilter implements Filter { /** * Creates a new unknown date filter. * - * @param l10nHandler - * The l10n handler - * @param unknownKey - * The key of the text to show + * @param translation The translation + * @param unknownKey The key of the text to show */ - public UnknownDateFilter(BaseL10n l10nHandler, String unknownKey) { - this.l10nHandler = l10nHandler; + public UnknownDateFilter(Translation translation, String unknownKey) { + this.translation = translation; this.unknownKey = unknownKey; } @@ -55,7 +53,7 @@ public class UnknownDateFilter implements Filter { public Object format(TemplateContext templateContext, Object data, Map parameters) { if (data instanceof Long) { if ((Long) data == 0) { - return l10nHandler.getString(unknownKey); + return translation.translate(unknownKey); } } return data; diff --git a/src/main/java/net/pterodactylus/sone/text/Parser.java b/src/main/java/net/pterodactylus/sone/text/Parser.java index 0daf30c..06eed37 100644 --- a/src/main/java/net/pterodactylus/sone/text/Parser.java +++ b/src/main/java/net/pterodactylus/sone/text/Parser.java @@ -1,5 +1,5 @@ /* - * Sone - Parser.java - Copyright © 2010–2019 David Roden + * Sone - Parser.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/text/ParserContext.java b/src/main/java/net/pterodactylus/sone/text/ParserContext.java index 056fcc1..468b565 100644 --- a/src/main/java/net/pterodactylus/sone/text/ParserContext.java +++ b/src/main/java/net/pterodactylus/sone/text/ParserContext.java @@ -1,5 +1,5 @@ /* - * Sone - ParserContext.java - Copyright © 2010–2019 David Roden + * Sone - ParserContext.java - Copyright © 2010–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/text/PostPart.java b/src/main/java/net/pterodactylus/sone/text/PostPart.java index de957c5..fac2b81 100644 --- a/src/main/java/net/pterodactylus/sone/text/PostPart.java +++ b/src/main/java/net/pterodactylus/sone/text/PostPart.java @@ -1,5 +1,5 @@ /* - * Sone - PostPart.java - Copyright © 2011–2019 David Roden + * Sone - PostPart.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java index 13a7a26..598765a 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java @@ -1,5 +1,5 @@ /* - * Sone - SoneTextParserContext.java - Copyright © 2011–2019 David Roden + * Sone - SoneTextParserContext.java - Copyright © 2011–2020 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 @@ -18,7 +18,6 @@ package net.pterodactylus.sone.text; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.FreenetRequest; /** * {@link ParserContext} implementation for the {@link SoneTextParser}. It diff --git a/src/main/java/net/pterodactylus/sone/text/TextFilter.java b/src/main/java/net/pterodactylus/sone/text/TextFilter.java index 0703f32..885843e 100644 --- a/src/main/java/net/pterodactylus/sone/text/TextFilter.java +++ b/src/main/java/net/pterodactylus/sone/text/TextFilter.java @@ -1,5 +1,5 @@ /* - * Sone - TextFilter.java - Copyright © 2011–2019 David Roden + * Sone - TextFilter.java - Copyright © 2011–2020 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 diff --git a/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java b/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java deleted file mode 100644 index d9acaeb..0000000 --- a/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java +++ /dev/null @@ -1,83 +0,0 @@ -package net.pterodactylus.sone.utils; - -import com.google.common.base.Predicate; - -/** - * Basic implementation of an {@link Option}. - * - * @param - * The type of the option - */ -public class DefaultOption implements Option { - - /** The default value. */ - private final T defaultValue; - - /** The current value. */ - private volatile T value; - - /** The validator. */ - private Predicate validator; - - /** - * Creates a new default option. - * - * @param defaultValue - * The default value of the option - */ - public DefaultOption(T defaultValue) { - this(defaultValue, null); - } - - /** - * Creates a new default option. - * - * @param defaultValue - * The default value of the option - * @param validator - * The validator for value validation (may be {@code null}) - */ - public DefaultOption(T defaultValue, Predicate validator) { - this.defaultValue = defaultValue; - this.validator = validator; - } - - /** - * {@inheritDoc} - */ - @Override - public T get() { - return (value != null) ? value : defaultValue; - } - - /** - * Returns the real value of the option. This will also return an unset - * value (usually {@code null})! - * - * @return The real value of the option - */ - @Override - public T getReal() { - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean validate(T value) { - return (validator == null) || (value == null) || validator.apply(value); - } - - /** - * {@inheritDoc} - */ - @Override - public void set(T value) { - if ((value != null) && (validator != null) && (!validator.apply(value))) { - throw new IllegalArgumentException("New Value (" + value + ") could not be validated."); - } - this.value = value; - } - -} diff --git a/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java b/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java deleted file mode 100644 index 1fd7527..0000000 --- a/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Sone - IntegerRangePredicate.java - Copyright © 2013–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.utils; - -import com.google.common.base.Predicate; - -/** - * {@link Predicate} that verifies that an {@link Integer} value is not - * {@code null} and is between a lower and an upper bound. Both bounds are - * inclusive. - */ -public class IntegerRangePredicate implements Predicate { - - /** The lower bound. */ - private final int lowerBound; - - /** The upper bound. */ - private final int upperBound; - - /** - * Creates a new integer range predicate. - * - * @param lowerBound - * The lower bound - * @param upperBound - * The upper bound - */ - public IntegerRangePredicate(int lowerBound, int upperBound) { - this.lowerBound = lowerBound; - this.upperBound = upperBound; - } - - // - // PREDICATE METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public boolean apply(Integer value) { - return (value != null) && (value >= lowerBound) && (value <= upperBound); - } - - public static IntegerRangePredicate range(int lowerBound, int upperBound) { - return new IntegerRangePredicate(lowerBound, upperBound); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java b/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java index 471fc26..d9091c0 100644 --- a/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java +++ b/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java @@ -1,6 +1,5 @@ package net.pterodactylus.sone.utils; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.primitives.Ints; diff --git a/src/main/java/net/pterodactylus/sone/web/AllPages.kt b/src/main/java/net/pterodactylus/sone/web/AllPages.kt deleted file mode 100644 index 9eccccf..0000000 --- a/src/main/java/net/pterodactylus/sone/web/AllPages.kt +++ /dev/null @@ -1,55 +0,0 @@ -package net.pterodactylus.sone.web - -import net.pterodactylus.sone.web.pages.* -import javax.inject.Inject - -/** - * Container for all web pages. This uses field injection because there are way too many pages - * to sensibly use constructor injection. - */ -class AllPages { - - @Inject lateinit var aboutPage: AboutPage - @Inject lateinit var bookmarkPage: BookmarkPage - @Inject lateinit var bookmarksPage: BookmarksPage - @Inject lateinit var createAlbumPage: CreateAlbumPage - @Inject lateinit var createPostPage: CreatePostPage - @Inject lateinit var createReplyPage: CreateReplyPage - @Inject lateinit var createSonePage: CreateSonePage - @Inject lateinit var deleteAlbumPage: DeleteAlbumPage - @Inject lateinit var deleteImagePage: DeleteImagePage - @Inject lateinit var deletePostPage: DeletePostPage - @Inject lateinit var deleteProfileFieldPage: DeleteProfileFieldPage - @Inject lateinit var deleteReplyPage: DeleteReplyPage - @Inject lateinit var deleteSonePage: DeleteSonePage - @Inject lateinit var dismissNotificationPage: DismissNotificationPage - @Inject lateinit var distrustPage: DistrustPage - @Inject lateinit var editAlbumPage: EditAlbumPage - @Inject lateinit var editImagePage: EditImagePage - @Inject lateinit var editProfileFieldPage: EditProfileFieldPage - @Inject lateinit var editProfilePage: EditProfilePage - @Inject lateinit var followSonePage: FollowSonePage - @Inject lateinit var getImagePage: GetImagePage - @Inject lateinit var imageBrowserPage: ImageBrowserPage - @Inject lateinit var indexPage: IndexPage - @Inject lateinit var knownSonesPage: KnownSonesPage - @Inject lateinit var likePage: LikePage - @Inject lateinit var lockSonePage: LockSonePage - @Inject lateinit var loginPage: LoginPage - @Inject lateinit var logoutPage: LogoutPage - @Inject lateinit var markAsKnownPage: MarkAsKnownPage - @Inject lateinit var newPage: NewPage - @Inject lateinit var optionsPage: OptionsPage - @Inject lateinit var rescuePage: RescuePage - @Inject lateinit var searchPage: SearchPage - @Inject lateinit var trustPage: TrustPage - @Inject lateinit var unbookmarkPage: UnbookmarkPage - @Inject lateinit var unfollowSonePage: UnfollowSonePage - @Inject lateinit var unlikePage: UnlikePage - @Inject lateinit var unlockSonePage: UnlockSonePage - @Inject lateinit var untrustPage: UntrustPage - @Inject lateinit var uploadImagePage: UploadImagePage - @Inject lateinit var viewPostPage: ViewPostPage - @Inject lateinit var viewSonePage: ViewSonePage - -} diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 3d131c8..40e5708 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -1,5 +1,5 @@ /* - * Sone - WebInterface.java - Copyright © 2010–2019 David Roden + * Sone - WebInterface.java - Copyright © 2010–2020 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 @@ -19,51 +19,23 @@ package net.pterodactylus.sone.web; import static com.google.common.collect.FluentIterable.from; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.util.template.TemplateParser.parse; -import java.io.StringReader; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.TimeZone; -import java.util.UUID; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.inject.Named; import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.core.ElementLoader; -import net.pterodactylus.sone.core.event.ImageInsertAbortedEvent; -import net.pterodactylus.sone.core.event.ImageInsertFailedEvent; -import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent; -import net.pterodactylus.sone.core.event.ImageInsertStartedEvent; -import net.pterodactylus.sone.core.event.MarkPostKnownEvent; -import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent; -import net.pterodactylus.sone.core.event.MarkSoneKnownEvent; -import net.pterodactylus.sone.core.event.NewPostFoundEvent; -import net.pterodactylus.sone.core.event.NewPostReplyFoundEvent; -import net.pterodactylus.sone.core.event.NewSoneFoundEvent; -import net.pterodactylus.sone.core.event.PostRemovedEvent; -import net.pterodactylus.sone.core.event.PostReplyRemovedEvent; -import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent; -import net.pterodactylus.sone.core.event.SoneInsertedEvent; -import net.pterodactylus.sone.core.event.SoneInsertingEvent; -import net.pterodactylus.sone.core.event.SoneLockedEvent; -import net.pterodactylus.sone.core.event.SoneRemovedEvent; -import net.pterodactylus.sone.core.event.SoneUnlockedEvent; -import net.pterodactylus.sone.core.event.UpdateFoundEvent; -import net.pterodactylus.sone.data.Image; +import net.pterodactylus.sone.core.event.*; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.freenet.L10nFilter; +import net.pterodactylus.sone.freenet.Translation; import net.pterodactylus.sone.main.Loaders; import net.pterodactylus.sone.main.PluginHomepage; import net.pterodactylus.sone.main.PluginVersion; @@ -77,9 +49,6 @@ import net.pterodactylus.sone.template.LinkedElementRenderFilter; import net.pterodactylus.sone.template.ParserFilter; import net.pterodactylus.sone.template.RenderFilter; import net.pterodactylus.sone.template.ShortenFilter; -import net.pterodactylus.sone.text.Part; -import net.pterodactylus.sone.text.SonePart; -import net.pterodactylus.sone.text.SoneTextParser; import net.pterodactylus.sone.text.TimeTextConverter; import net.pterodactylus.sone.web.ajax.BookmarkAjaxPage; import net.pterodactylus.sone.web.ajax.CreatePostAjaxPage; @@ -88,7 +57,6 @@ import net.pterodactylus.sone.web.ajax.DeletePostAjaxPage; import net.pterodactylus.sone.web.ajax.DeleteProfileFieldAjaxPage; import net.pterodactylus.sone.web.ajax.DeleteReplyAjaxPage; import net.pterodactylus.sone.web.ajax.DismissNotificationAjaxPage; -import net.pterodactylus.sone.web.ajax.DistrustAjaxPage; import net.pterodactylus.sone.web.ajax.EditAlbumAjaxPage; import net.pterodactylus.sone.web.ajax.EditImageAjaxPage; import net.pterodactylus.sone.web.ajax.EditProfileFieldAjaxPage; @@ -105,79 +73,26 @@ import net.pterodactylus.sone.web.ajax.LikeAjaxPage; import net.pterodactylus.sone.web.ajax.LockSoneAjaxPage; import net.pterodactylus.sone.web.ajax.MarkAsKnownAjaxPage; import net.pterodactylus.sone.web.ajax.MoveProfileFieldAjaxPage; -import net.pterodactylus.sone.web.ajax.TrustAjaxPage; import net.pterodactylus.sone.web.ajax.UnbookmarkAjaxPage; import net.pterodactylus.sone.web.ajax.UnfollowSoneAjaxPage; import net.pterodactylus.sone.web.ajax.UnlikeAjaxPage; import net.pterodactylus.sone.web.ajax.UnlockSoneAjaxPage; -import net.pterodactylus.sone.web.ajax.UntrustAjaxPage; import net.pterodactylus.sone.web.page.FreenetRequest; import net.pterodactylus.sone.web.page.TemplateRenderer; -import net.pterodactylus.sone.web.pages.AboutPage; -import net.pterodactylus.sone.web.pages.BookmarkPage; -import net.pterodactylus.sone.web.pages.BookmarksPage; -import net.pterodactylus.sone.web.pages.CreateAlbumPage; -import net.pterodactylus.sone.web.pages.CreatePostPage; -import net.pterodactylus.sone.web.pages.CreateReplyPage; -import net.pterodactylus.sone.web.pages.CreateSonePage; -import net.pterodactylus.sone.web.pages.DeleteAlbumPage; -import net.pterodactylus.sone.web.pages.DeleteImagePage; -import net.pterodactylus.sone.web.pages.DeletePostPage; -import net.pterodactylus.sone.web.pages.DeleteProfileFieldPage; -import net.pterodactylus.sone.web.pages.DeleteReplyPage; -import net.pterodactylus.sone.web.pages.DeleteSonePage; -import net.pterodactylus.sone.web.pages.DismissNotificationPage; -import net.pterodactylus.sone.web.pages.DistrustPage; -import net.pterodactylus.sone.web.pages.EditAlbumPage; -import net.pterodactylus.sone.web.pages.EditImagePage; -import net.pterodactylus.sone.web.pages.EditProfileFieldPage; -import net.pterodactylus.sone.web.pages.EditProfilePage; -import net.pterodactylus.sone.web.pages.EmptyAlbumTitlePage; -import net.pterodactylus.sone.web.pages.EmptyImageTitlePage; -import net.pterodactylus.sone.web.pages.FollowSonePage; -import net.pterodactylus.sone.web.pages.GetImagePage; -import net.pterodactylus.sone.web.pages.ImageBrowserPage; -import net.pterodactylus.sone.web.pages.IndexPage; -import net.pterodactylus.sone.web.pages.InvalidPage; -import net.pterodactylus.sone.web.pages.KnownSonesPage; -import net.pterodactylus.sone.web.pages.LikePage; -import net.pterodactylus.sone.web.pages.LockSonePage; -import net.pterodactylus.sone.web.pages.LoginPage; -import net.pterodactylus.sone.web.pages.LogoutPage; -import net.pterodactylus.sone.web.pages.MarkAsKnownPage; -import net.pterodactylus.sone.web.pages.NewPage; -import net.pterodactylus.sone.web.pages.NoPermissionPage; -import net.pterodactylus.sone.web.pages.OptionsPage; -import net.pterodactylus.sone.web.pages.RescuePage; -import net.pterodactylus.sone.web.pages.SearchPage; -import net.pterodactylus.sone.web.pages.SoneTemplatePage; -import net.pterodactylus.sone.web.pages.TrustPage; -import net.pterodactylus.sone.web.pages.UnbookmarkPage; -import net.pterodactylus.sone.web.pages.UnfollowSonePage; -import net.pterodactylus.sone.web.pages.UnlikePage; -import net.pterodactylus.sone.web.pages.UnlockSonePage; -import net.pterodactylus.sone.web.pages.UntrustPage; -import net.pterodactylus.sone.web.pages.UploadImagePage; -import net.pterodactylus.sone.web.pages.ViewPostPage; -import net.pterodactylus.sone.web.pages.ViewSonePage; +import net.pterodactylus.sone.web.pages.*; import net.pterodactylus.util.notify.Notification; import net.pterodactylus.util.notify.NotificationManager; -import net.pterodactylus.util.notify.TemplateNotification; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateContextFactory; import net.pterodactylus.util.web.RedirectPage; import net.pterodactylus.util.web.TemplatePage; -import freenet.clients.http.SessionManager; -import freenet.clients.http.SessionManager.Session; -import freenet.clients.http.ToadletContext; -import freenet.l10n.BaseL10n; - +import com.codahale.metrics.*; import com.google.common.base.Optional; -import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; +import freenet.clients.http.ToadletContext; /** * Bundles functionality that a web interface of a Freenet plugin needs, e.g. @@ -192,7 +107,7 @@ public class WebInterface implements SessionProvider { private final Loaders loaders; /** The notification manager. */ - private final NotificationManager notificationManager = new NotificationManager(); + private final NotificationManager notificationManager; /** The Sone plugin. */ private final SonePlugin sonePlugin; @@ -204,9 +119,6 @@ public class WebInterface implements SessionProvider { private final TemplateContextFactory templateContextFactory; private final TemplateRenderer templateRenderer; - /** The Sone text parser. */ - private final SoneTextParser soneTextParser; - /** The parser filter. */ private final ParserFilter parserFilter; private final ShortenFilter shortenFilter; @@ -222,9 +134,9 @@ public class WebInterface implements SessionProvider { private final L10nFilter l10nFilter; private final PageToadletRegistry pageToadletRegistry; - - /** The “new Sone” notification. */ - private final ListNotification newSoneNotification; + private final MetricRegistry metricRegistry; + private final Translation translation; + private final SessionProvider sessionProvider; /** The “new post” notification. */ private final ListNotification newPostNotification; @@ -238,33 +150,6 @@ public class WebInterface implements SessionProvider { /** The invisible “local reply” notification. */ private final ListNotification localReplyNotification; - /** The “you have been mentioned” notification. */ - private final ListNotification mentionNotification; - - /** Notifications for sone inserts. */ - private final Map soneInsertNotifications = new HashMap<>(); - - /** Sone locked notification ticker objects. */ - private final Map> lockedSonesTickerObjects = Collections.synchronizedMap(new HashMap>()); - - /** The “Sone locked” notification. */ - private final ListNotification lockedSonesNotification; - - /** The “new version” notification. */ - private final TemplateNotification newVersionNotification; - - /** The “inserting images” notification. */ - private final ListNotification insertingImagesNotification; - - /** The “inserted images” notification. */ - private final ListNotification insertedImagesNotification; - - /** The “image insert failed” notification. */ - private final ListNotification imageInsertFailedNotification; - - /** Scheduled executor for time-based notifications. */ - private final ScheduledExecutorService ticker = Executors.newScheduledThreadPool(1); - @Inject public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilter listNotificationFilter, PostVisibilityFilter postVisibilityFilter, ReplyVisibilityFilter replyVisibilityFilter, @@ -273,7 +158,12 @@ public class WebInterface implements SessionProvider { ParserFilter parserFilter, ShortenFilter shortenFilter, RenderFilter renderFilter, LinkedElementRenderFilter linkedElementRenderFilter, - PageToadletRegistry pageToadletRegistry) { + PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter, + NotificationManager notificationManager, SessionProvider sessionProvider, + @Named("newRemotePost") ListNotification newPostNotification, + @Named("newRemotePostReply") ListNotification newReplyNotification, + @Named("localPost") ListNotification localPostNotification, + @Named("localReply") ListNotification localReplyNotification) { this.sonePlugin = sonePlugin; this.loaders = loaders; this.listNotificationFilter = listNotificationFilter; @@ -286,47 +176,20 @@ public class WebInterface implements SessionProvider { this.renderFilter = renderFilter; this.linkedElementRenderFilter = linkedElementRenderFilter; this.pageToadletRegistry = pageToadletRegistry; + this.metricRegistry = metricRegistry; + this.l10nFilter = l10nFilter; + this.translation = translation; + this.notificationManager = notificationManager; + this.sessionProvider = sessionProvider; + this.newPostNotification = newPostNotification; + this.newReplyNotification = newReplyNotification; + this.localPostNotification = localPostNotification; + this.localReplyNotification = localReplyNotification; formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword(); - soneTextParser = new SoneTextParser(getCore(), getCore()); - l10nFilter = new L10nFilter(getL10n()); this.templateContextFactory = templateContextFactory; templateContextFactory.addTemplateObject("webInterface", this); templateContextFactory.addTemplateObject("formPassword", formPassword); - - /* create notifications. */ - Template newSoneNotificationTemplate = loaders.loadTemplate("/templates/notify/newSoneNotification.html"); - newSoneNotification = new ListNotification<>("new-sone-notification", "sones", newSoneNotificationTemplate, false); - - Template newPostNotificationTemplate = loaders.loadTemplate("/templates/notify/newPostNotification.html"); - newPostNotification = new ListNotification<>("new-post-notification", "posts", newPostNotificationTemplate, false); - - Template localPostNotificationTemplate = loaders.loadTemplate("/templates/notify/newPostNotification.html"); - localPostNotification = new ListNotification<>("local-post-notification", "posts", localPostNotificationTemplate, false); - - Template newReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html"); - newReplyNotification = new ListNotification<>("new-reply-notification", "replies", newReplyNotificationTemplate, false); - - Template localReplyNotificationTemplate = loaders.loadTemplate("/templates/notify/newReplyNotification.html"); - localReplyNotification = new ListNotification<>("local-reply-notification", "replies", localReplyNotificationTemplate, false); - - Template mentionNotificationTemplate = loaders.loadTemplate("/templates/notify/mentionNotification.html"); - mentionNotification = new ListNotification<>("mention-notification", "posts", mentionNotificationTemplate, false); - - Template lockedSonesTemplate = loaders.loadTemplate("/templates/notify/lockedSonesNotification.html"); - lockedSonesNotification = new ListNotification<>("sones-locked-notification", "sones", lockedSonesTemplate); - - Template newVersionTemplate = loaders.loadTemplate("/templates/notify/newVersionNotification.html"); - newVersionNotification = new TemplateNotification("new-version-notification", newVersionTemplate); - - Template insertingImagesTemplate = loaders.loadTemplate("/templates/notify/inserting-images-notification.html"); - insertingImagesNotification = new ListNotification<>("inserting-images-notification", "images", insertingImagesTemplate); - - Template insertedImagesTemplate = loaders.loadTemplate("/templates/notify/inserted-images-notification.html"); - insertedImagesNotification = new ListNotification<>("inserted-images-notification", "images", insertedImagesTemplate); - - Template imageInsertFailedTemplate = loaders.loadTemplate("/templates/notify/image-insert-failed-notification.html"); - imageInsertFailedNotification = new ListNotification<>("image-insert-failed-notification", "images", imageInsertFailedTemplate); } // @@ -352,75 +215,15 @@ public class WebInterface implements SessionProvider { return templateContextFactory; } - private Session getCurrentSessionWithoutCreation(ToadletContext toadletContenxt) { - return getSessionManager().useSession(toadletContenxt); - } - - private Session getOrCreateCurrentSession(ToadletContext toadletContenxt) { - Session session = getCurrentSessionWithoutCreation(toadletContenxt); - if (session == null) { - session = getSessionManager().createSession(UUID.randomUUID().toString(), toadletContenxt); - } - return session; - } - - public Sone getCurrentSoneCreatingSession(ToadletContext toadletContext) { - Collection localSones = getCore().getLocalSones(); - if (localSones.size() == 1) { - return localSones.iterator().next(); - } - return getCurrentSone(getOrCreateCurrentSession(toadletContext)); - } - - public Sone getCurrentSoneWithoutCreatingSession(ToadletContext toadletContext) { - Collection localSones = getCore().getLocalSones(); - if (localSones.size() == 1) { - return localSones.iterator().next(); - } - return getCurrentSone(getCurrentSessionWithoutCreation(toadletContext)); - } - - /** - * Returns the currently logged in Sone. - * - * @param session - * The session - * @return The currently logged in Sone, or {@code null} if no Sone is - * currently logged in - */ - private Sone getCurrentSone(Session session) { - if (session == null) { - return null; - } - String soneId = (String) session.getAttribute("Sone.CurrentSone"); - if (soneId == null) { - return null; - } - return getCore().getLocalSone(soneId); - } - - @Override @Nullable - public Sone getCurrentSone(@Nonnull ToadletContext toadletContext, boolean createSession) { - return createSession ? getCurrentSoneCreatingSession(toadletContext) : getCurrentSoneWithoutCreatingSession(toadletContext); + @Override + public Sone getCurrentSone(@Nonnull ToadletContext toadletContext) { + return sessionProvider.getCurrentSone(toadletContext); } - /** - * Sets the currently logged in Sone. - * - * @param toadletContext - * The toadlet context - * @param sone - * The Sone to set as currently logged in - */ @Override public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) { - Session session = getOrCreateCurrentSession(toadletContext); - if (sone == null) { - session.removeAttribute("Sone.CurrentSone"); - } else { - session.setAttribute("Sone.CurrentSone", sone.getId()); - } + sessionProvider.setCurrentSone(toadletContext, sone); } /** @@ -442,22 +245,8 @@ public class WebInterface implements SessionProvider { return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone); } - /** - * Returns the l10n helper of the node. - * - * @return The node’s l10n helper - */ - public BaseL10n getL10n() { - return sonePlugin.l10n().getBase(); - } - - /** - * Returns the session manager of the node. - * - * @return The node’s session manager - */ - public SessionManager getSessionManager() { - return sonePlugin.pluginRespirator().getSessionManager("Sone"); + public Translation getTranslation() { + return translation; } /** @@ -469,16 +258,6 @@ public class WebInterface implements SessionProvider { return formPassword; } - /** - * Returns the posts that have been announced as new in the - * {@link #newPostNotification}. - * - * @return The new posts - */ - public Set getNewPosts() { - return ImmutableSet. builder().addAll(newPostNotification.getElements()).addAll(localPostNotification.getElements()).build(); - } - @Nonnull public Collection getNewPosts(@Nullable Sone currentSone) { Set allNewPosts = ImmutableSet. builder() @@ -488,16 +267,6 @@ public class WebInterface implements SessionProvider { return from(allNewPosts).filter(postVisibilityFilter.isVisible(currentSone)).toSet(); } - /** - * Returns the replies that have been announced as new in the - * {@link #newReplyNotification}. - * - * @return The new replies - */ - public Set getNewReplies() { - return ImmutableSet. builder().addAll(newReplyNotification.getElements()).addAll(localReplyNotification.getElements()).build(); - } - @Nonnull public Collection getNewReplies(@Nullable Sone currentSone) { Set allNewReplies = ImmutableSet.builder() @@ -507,51 +276,6 @@ public class WebInterface implements SessionProvider { return from(allNewReplies).filter(replyVisibilityFilter.isVisible(currentSone)).toSet(); } - /** - * Sets whether the current start of the plugin is the first start. It is - * considered a first start if the configuration file does not exist. - * - * @param firstStart - * {@code true} if no configuration file existed when Sone was - * loaded, {@code false} otherwise - */ - public void setFirstStart(boolean firstStart) { - if (firstStart) { - Template firstStartNotificationTemplate = loaders.loadTemplate("/templates/notify/firstStartNotification.html"); - Notification firstStartNotification = new TemplateNotification("first-start-notification", firstStartNotificationTemplate); - notificationManager.addNotification(firstStartNotification); - } - } - - /** - * Sets whether Sone was started with a fresh configuration file. - * - * @param newConfig - * {@code true} if Sone was started with a fresh configuration, - * {@code false} if the existing configuration could be read - */ - public void setNewConfig(boolean newConfig) { - if (newConfig && !hasFirstStartNotification()) { - Template configNotReadNotificationTemplate = loaders.loadTemplate("/templates/notify/configNotReadNotification.html"); - Notification configNotReadNotification = new TemplateNotification("config-not-read-notification", configNotReadNotificationTemplate); - notificationManager.addNotification(configNotReadNotification); - } - } - - // - // PRIVATE ACCESSORS - // - - /** - * Returns whether the first start notification is currently displayed. - * - * @return {@code true} if the first-start notification is currently - * displayed, {@code false} otherwise - */ - private boolean hasFirstStartNotification() { - return notificationManager.getNotification("first-start-notification") != null; - } - // // ACTIONS // @@ -561,36 +285,6 @@ public class WebInterface implements SessionProvider { */ public void start() { registerToadlets(); - - /* notification templates. */ - Template startupNotificationTemplate = loaders.loadTemplate("/templates/notify/startupNotification.html"); - - final TemplateNotification startupNotification = new TemplateNotification("startup-notification", startupNotificationTemplate); - notificationManager.addNotification(startupNotification); - - ticker.schedule(new Runnable() { - - @Override - public void run() { - startupNotification.dismiss(); - } - }, 2, TimeUnit.MINUTES); - - Template wotMissingNotificationTemplate = loaders.loadTemplate("/templates/notify/wotMissingNotification.html"); - final TemplateNotification wotMissingNotification = new TemplateNotification("wot-missing-notification", wotMissingNotificationTemplate); - ticker.scheduleAtFixedRate(new Runnable() { - - @Override - @SuppressWarnings("synthetic-access") - public void run() { - if (getCore().getIdentityManager().isConnected()) { - wotMissingNotification.dismiss(); - } else { - notificationManager.addNotification(wotMissingNotification); - } - } - - }, 15, 15, TimeUnit.SECONDS); } /** @@ -598,7 +292,6 @@ public class WebInterface implements SessionProvider { */ public void stop() { pageToadletRegistry.unregisterToadlets(); - ticker.shutdownNow(); } // @@ -640,9 +333,6 @@ public class WebInterface implements SessionProvider { pageToadletRegistry.addPage(new UploadImagePage(this, loaders, templateRenderer)); pageToadletRegistry.addPage(new EditImagePage(this, loaders, templateRenderer)); pageToadletRegistry.addPage(new DeleteImagePage(this, loaders, templateRenderer)); - pageToadletRegistry.addPage(new TrustPage(this, loaders, templateRenderer)); - pageToadletRegistry.addPage(new DistrustPage(this, loaders, templateRenderer)); - pageToadletRegistry.addPage(new UntrustPage(this, loaders, templateRenderer)); pageToadletRegistry.addPage(new MarkAsKnownPage(this, loaders, templateRenderer)); pageToadletRegistry.addPage(new BookmarkPage(this, loaders, templateRenderer)); pageToadletRegistry.addPage(new UnbookmarkPage(this, loaders, templateRenderer)); @@ -659,6 +349,8 @@ public class WebInterface implements SessionProvider { pageToadletRegistry.addPage(new EmptyImageTitlePage(this, loaders, templateRenderer)); pageToadletRegistry.addPage(new EmptyAlbumTitlePage(this, loaders, templateRenderer)); pageToadletRegistry.addPage(new DismissNotificationPage(this, loaders, templateRenderer)); + pageToadletRegistry.addPage(new DebugPage(this, loaders, templateRenderer)); + pageToadletRegistry.addDebugPage(new MetricsPage(this, loaders, templateRenderer, metricRegistry)); pageToadletRegistry.addPage(loaders.loadStaticPage("css/", "/static/css/", "text/css")); pageToadletRegistry.addPage(loaders.loadStaticPage("javascript/", "/static/javascript/", "text/javascript")); pageToadletRegistry.addPage(loaders.loadStaticPage("images/", "/static/images/", "image/png")); @@ -683,9 +375,6 @@ public class WebInterface implements SessionProvider { pageToadletRegistry.addPage(new UnfollowSoneAjaxPage(this)); pageToadletRegistry.addPage(new EditAlbumAjaxPage(this)); pageToadletRegistry.addPage(new EditImageAjaxPage(this, parserFilter, shortenFilter, renderFilter)); - pageToadletRegistry.addPage(new TrustAjaxPage(this)); - pageToadletRegistry.addPage(new DistrustAjaxPage(this)); - pageToadletRegistry.addPage(new UntrustAjaxPage(this)); pageToadletRegistry.addPage(new LikeAjaxPage(this)); pageToadletRegistry.addPage(new UnlikeAjaxPage(this)); pageToadletRegistry.addPage(new GetLikesAjaxPage(this)); @@ -698,328 +387,9 @@ public class WebInterface implements SessionProvider { pageToadletRegistry.registerToadlets(); } - /** - * Returns all {@link Sone#isLocal() local Sone}s that are referenced by - * {@link SonePart}s in the given text (after parsing it using - * {@link SoneTextParser}). - * - * @param text - * The text to parse - * @return All mentioned local Sones - */ - private Collection getMentionedSones(String text) { - /* we need no context to find mentioned Sones. */ - Set mentionedSones = new HashSet<>(); - for (Part part : soneTextParser.parse(text, null)) { - if (part instanceof SonePart) { - mentionedSones.add(((SonePart) part).getSone()); - } - } - return Collections2.filter(mentionedSones, Sone.LOCAL_SONE_FILTER); - } - - /** - * Returns the Sone insert notification for the given Sone. If no - * notification for the given Sone exists, a new notification is created and - * cached. - * - * @param sone - * The Sone to get the insert notification for - * @return The Sone insert notification - */ - private TemplateNotification getSoneInsertNotification(Sone sone) { - synchronized (soneInsertNotifications) { - TemplateNotification templateNotification = soneInsertNotifications.get(sone); - if (templateNotification == null) { - templateNotification = new TemplateNotification(loaders.loadTemplate("/templates/notify/soneInsertNotification.html")); - templateNotification.set("insertSone", sone); - soneInsertNotifications.put(sone, templateNotification); - } - return templateNotification; - } - } - - private boolean localSoneMentionedInNewPostOrReply(Post post) { - if (!post.getSone().isLocal()) { - if (!getMentionedSones(post.getText()).isEmpty() && !post.isKnown()) { - return true; - } - } - for (PostReply postReply : getCore().getReplies(post.getId())) { - if (postReply.getSone().isLocal()) { - continue; - } - if (!getMentionedSones(postReply.getText()).isEmpty() && !postReply.isKnown()) { - return true; - } - } - return false; - } - - // - // EVENT HANDLERS - // - - /** - * Notifies the web interface that a new {@link Sone} was found. - * - * @param newSoneFoundEvent - * The event - */ - @Subscribe - public void newSoneFound(NewSoneFoundEvent newSoneFoundEvent) { - newSoneNotification.add(newSoneFoundEvent.sone()); - if (!hasFirstStartNotification()) { - notificationManager.addNotification(newSoneNotification); - } - } - - /** - * Notifies the web interface that a new {@link Post} was found. - * - * @param newPostFoundEvent - * The event - */ - @Subscribe - public void newPostFound(NewPostFoundEvent newPostFoundEvent) { - Post post = newPostFoundEvent.post(); - boolean isLocal = post.getSone().isLocal(); - if (isLocal) { - localPostNotification.add(post); - } else { - newPostNotification.add(post); - } - if (!hasFirstStartNotification()) { - notificationManager.addNotification(isLocal ? localPostNotification : newPostNotification); - if (!getMentionedSones(post.getText()).isEmpty() && !isLocal) { - mentionNotification.add(post); - notificationManager.addNotification(mentionNotification); - } - } else { - getCore().markPostKnown(post); - } - } - - /** - * Notifies the web interface that a new {@link PostReply} was found. - * - * @param newPostReplyFoundEvent - * The event - */ - @Subscribe - public void newReplyFound(NewPostReplyFoundEvent newPostReplyFoundEvent) { - PostReply reply = newPostReplyFoundEvent.postReply(); - boolean isLocal = reply.getSone().isLocal(); - if (isLocal) { - localReplyNotification.add(reply); - } else { - newReplyNotification.add(reply); - } - if (!hasFirstStartNotification()) { - notificationManager.addNotification(isLocal ? localReplyNotification : newReplyNotification); - if (reply.getPost().isPresent() && localSoneMentionedInNewPostOrReply(reply.getPost().get())) { - mentionNotification.add(reply.getPost().get()); - notificationManager.addNotification(mentionNotification); - } - } else { - getCore().markReplyKnown(reply); - } - } - - /** - * Notifies the web interface that a {@link Sone} was marked as known. - * - * @param markSoneKnownEvent - * The event - */ - @Subscribe - public void markSoneKnown(MarkSoneKnownEvent markSoneKnownEvent) { - newSoneNotification.remove(markSoneKnownEvent.sone()); - } - - @Subscribe - public void markPostKnown(MarkPostKnownEvent markPostKnownEvent) { - removePost(markPostKnownEvent.post()); - } - - @Subscribe - public void markReplyKnown(MarkPostReplyKnownEvent markPostReplyKnownEvent) { - removeReply(markPostReplyKnownEvent.postReply()); - } - - @Subscribe - public void soneRemoved(SoneRemovedEvent soneRemovedEvent) { - newSoneNotification.remove(soneRemovedEvent.sone()); - } - - @Subscribe - public void postRemoved(PostRemovedEvent postRemovedEvent) { - removePost(postRemovedEvent.post()); - } - - private void removePost(Post post) { - newPostNotification.remove(post); - localPostNotification.remove(post); - if (!localSoneMentionedInNewPostOrReply(post)) { - mentionNotification.remove(post); - } - } - - @Subscribe - public void replyRemoved(PostReplyRemovedEvent postReplyRemovedEvent) { - removeReply(postReplyRemovedEvent.postReply()); - } - - private void removeReply(PostReply reply) { - newReplyNotification.remove(reply); - localReplyNotification.remove(reply); - if (reply.getPost().isPresent() && !localSoneMentionedInNewPostOrReply(reply.getPost().get())) { - mentionNotification.remove(reply.getPost().get()); - } - } - - /** - * Notifies the web interface that a Sone was locked. - * - * @param soneLockedEvent - * The event - */ - @Subscribe - public void soneLocked(SoneLockedEvent soneLockedEvent) { - final Sone sone = soneLockedEvent.sone(); - ScheduledFuture tickerObject = ticker.schedule(new Runnable() { - - @Override - @SuppressWarnings("synthetic-access") - public void run() { - lockedSonesNotification.add(sone); - notificationManager.addNotification(lockedSonesNotification); - } - }, 5, TimeUnit.MINUTES); - lockedSonesTickerObjects.put(sone, tickerObject); - } - - /** - * Notifies the web interface that a Sone was unlocked. - * - * @param soneUnlockedEvent - * The event - */ - @Subscribe - public void soneUnlocked(SoneUnlockedEvent soneUnlockedEvent) { - lockedSonesNotification.remove(soneUnlockedEvent.sone()); - lockedSonesTickerObjects.remove(soneUnlockedEvent.sone()).cancel(false); - } - - /** - * Notifies the web interface that a {@link Sone} is being inserted. - * - * @param soneInsertingEvent - * The event - */ - @Subscribe - public void soneInserting(SoneInsertingEvent soneInsertingEvent) { - TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertingEvent.sone()); - soneInsertNotification.set("soneStatus", "inserting"); - if (soneInsertingEvent.sone().getOptions().isSoneInsertNotificationEnabled()) { - notificationManager.addNotification(soneInsertNotification); - } - } - - /** - * Notifies the web interface that a {@link Sone} was inserted. - * - * @param soneInsertedEvent - * The event - */ - @Subscribe - public void soneInserted(SoneInsertedEvent soneInsertedEvent) { - TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertedEvent.sone()); - soneInsertNotification.set("soneStatus", "inserted"); - soneInsertNotification.set("insertDuration", soneInsertedEvent.insertDuration() / 1000); - if (soneInsertedEvent.sone().getOptions().isSoneInsertNotificationEnabled()) { - notificationManager.addNotification(soneInsertNotification); - } - } - - /** - * Notifies the web interface that a {@link Sone} insert was aborted. - * - * @param soneInsertAbortedEvent - * The event - */ - @Subscribe - public void soneInsertAborted(SoneInsertAbortedEvent soneInsertAbortedEvent) { - TemplateNotification soneInsertNotification = getSoneInsertNotification(soneInsertAbortedEvent.sone()); - soneInsertNotification.set("soneStatus", "insert-aborted"); - soneInsertNotification.set("insert-error", soneInsertAbortedEvent.cause()); - if (soneInsertAbortedEvent.sone().getOptions().isSoneInsertNotificationEnabled()) { - notificationManager.addNotification(soneInsertNotification); - } - } - - /** - * Notifies the web interface that a new Sone version was found. - * - * @param updateFoundEvent - * The event - */ - @Subscribe - public void updateFound(UpdateFoundEvent updateFoundEvent) { - newVersionNotification.set("latestVersion", updateFoundEvent.version()); - newVersionNotification.set("latestEdition", updateFoundEvent.latestEdition()); - newVersionNotification.set("releaseTime", updateFoundEvent.releaseTime()); - newVersionNotification.set("disruptive", updateFoundEvent.disruptive()); - notificationManager.addNotification(newVersionNotification); - } - - /** - * Notifies the web interface that an image insert was started - * - * @param imageInsertStartedEvent - * The event - */ - @Subscribe - public void imageInsertStarted(ImageInsertStartedEvent imageInsertStartedEvent) { - insertingImagesNotification.add(imageInsertStartedEvent.image()); - notificationManager.addNotification(insertingImagesNotification); - } - - /** - * Notifies the web interface that an {@link Image} insert was aborted. - * - * @param imageInsertAbortedEvent - * The event - */ - @Subscribe - public void imageInsertAborted(ImageInsertAbortedEvent imageInsertAbortedEvent) { - insertingImagesNotification.remove(imageInsertAbortedEvent.image()); - } - - /** - * Notifies the web interface that an {@link Image} insert is finished. - * - * @param imageInsertFinishedEvent - * The event - */ - @Subscribe - public void imageInsertFinished(ImageInsertFinishedEvent imageInsertFinishedEvent) { - insertingImagesNotification.remove(imageInsertFinishedEvent.image()); - insertedImagesNotification.add(imageInsertFinishedEvent.image()); - notificationManager.addNotification(insertedImagesNotification); - } - - /** - * Notifies the web interface that an {@link Image} insert has failed. - * - * @param imageInsertFailedEvent - * The event - */ @Subscribe - public void imageInsertFailed(ImageInsertFailedEvent imageInsertFailedEvent) { - insertingImagesNotification.remove(imageInsertFailedEvent.image()); - imageInsertFailedNotification.add(imageInsertFailedEvent.image()); - notificationManager.addNotification(imageInsertFailedNotification); + public void debugActivated(@Nonnull DebugActivatedEvent debugActivatedEvent) { + pageToadletRegistry.activateDebugMode(); } } diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java deleted file mode 100644 index 2b22377..0000000 --- a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Sone - PageToadlet.java - Copyright © 2010–2019 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.web.page; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; - -import net.pterodactylus.sone.utils.AutoCloseableBucket; -import net.pterodactylus.util.web.Header; -import net.pterodactylus.util.web.Method; -import net.pterodactylus.util.web.Page; -import net.pterodactylus.util.web.Response; - -import freenet.client.HighLevelSimpleClient; -import freenet.clients.http.LinkEnabledCallback; -import freenet.clients.http.LinkFilterExceptedToadlet; -import freenet.clients.http.SessionManager; -import freenet.clients.http.Toadlet; -import freenet.clients.http.ToadletContext; -import freenet.clients.http.ToadletContextClosedException; -import freenet.l10n.NodeL10n; -import freenet.support.MultiValueTable; -import freenet.support.api.HTTPRequest; - -/** - * {@link Toadlet} implementation that is wrapped around a {@link Page}. - */ -public class PageToadlet extends Toadlet implements LinkEnabledCallback, LinkFilterExceptedToadlet { - - private final SessionManager sessionManager; - - /** The name of the menu item. */ - private final String menuName; - - /** The page that handles processing. */ - private final Page page; - - /** The path prefix for the page. */ - private final String pathPrefix; - - /** - * Creates a new toadlet that hands off processing to a {@link Page}. - * - * @param highLevelSimpleClient - * The high-level simple client - * @param menuName - * The name of the menu item - * @param page - * The page to handle processing - * @param pathPrefix - * Prefix that is prepended to all {@link Page#getPath()} return - * values - */ - protected PageToadlet(HighLevelSimpleClient highLevelSimpleClient, SessionManager sessionManager, String menuName, Page page, String pathPrefix) { - super(highLevelSimpleClient); - this.sessionManager = sessionManager; - this.menuName = menuName; - this.page = page; - this.pathPrefix = pathPrefix; - } - - /** - * Returns the name to display in the menu. - * - * @return The name in the menu - */ - public String getMenuName() { - return menuName; - } - - /** - * {@inheritDoc} - */ - @Override - public String path() { - return pathPrefix + page.getPath(); - } - - /** - * Handles a HTTP GET request. - * - * @param uri - * The URI of the request - * @param httpRequest - * The HTTP request - * @param toadletContext - * The toadlet context - * @throws IOException - * if an I/O error occurs - * @throws ToadletContextClosedException - * if the toadlet context is closed - */ - public void handleMethodGET(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException { - handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext, NodeL10n.getBase(), sessionManager)); - } - - /** - * Handles a HTTP POST request. - * - * @param uri - * The URI of the request - * @param httpRequest - * The HTTP request - * @param toadletContext - * The toadlet context - * @throws IOException - * if an I/O error occurs - * @throws ToadletContextClosedException - * if the toadlet context is closed - */ - public void handleMethodPOST(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException { - handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext, NodeL10n.getBase(), sessionManager)); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return getClass().getName() + "[path=" + path() + ",page=" + page + "]"; - } - - /** - * Handles a HTTP request. - * - * @param pageRequest - * The request to handle - * @throws IOException - * if an I/O error occurs - * @throws ToadletContextClosedException - * if the toadlet context is closed - */ - private void handleRequest(FreenetRequest pageRequest) throws IOException, ToadletContextClosedException { - try (AutoCloseableBucket pageBucket = new AutoCloseableBucket(pageRequest.getToadletContext().getBucketFactory().makeBucket(-1)); - OutputStream pageBucketOutputStream = pageBucket.getBucket().getOutputStream()) { - Response pageResponse = page.handleRequest(pageRequest, new Response(pageBucketOutputStream)); - MultiValueTable headers = new MultiValueTable<>(); - if (pageResponse.getHeaders() != null) { - for (Header header : pageResponse.getHeaders()) { - for (String value : header) { - headers.put(header.getName(), value); - } - } - } - writeReply(pageRequest.getToadletContext(), pageResponse.getStatusCode(), pageResponse.getContentType(), pageResponse.getStatusText(), headers, pageBucket.getBucket()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled(ToadletContext toadletContext) { - if (page instanceof LinkEnabledCallback) { - return ((LinkEnabledCallback) page).isEnabled(toadletContext); - } - return true; - } - - // - // LINKFILTEREXCEPTEDTOADLET METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public boolean isLinkExcepted(URI link) { - return (page instanceof FreenetPage) && ((FreenetPage) page).isLinkExcepted(link); - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt index d01aeb4..409d18c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt @@ -23,9 +23,9 @@ class DefaultElementLoader(private val freenetInterface: FreenetInterface, ticke @Inject constructor(freenetInterface: FreenetInterface): this(freenetInterface, Ticker.systemTicker()) - private val loadingLinks: Cache = CacheBuilder.newBuilder().build() - private val failureCache: Cache = CacheBuilder.newBuilder().ticker(ticker).expireAfterWrite(30, MINUTES).build() - private val elementCache: Cache = CacheBuilder.newBuilder().build() + private val loadingLinks: Cache = CacheBuilder.newBuilder().build() + private val failureCache: Cache = CacheBuilder.newBuilder().ticker(ticker).expireAfterWrite(30, MINUTES).build() + private val elementCache: Cache = CacheBuilder.newBuilder().build() private val callback = object: FreenetInterface.BackgroundFetchCallback { override fun shouldCancel(uri: FreenetURI, mimeType: String, size: Long): Boolean { return (size > 2097152) || (!mimeType.startsWith("image/") && !mimeType.startsWith("text/html")) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/PreferenceChangedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferenceChangedEvent.kt new file mode 100644 index 0000000..2ebb62d --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/PreferenceChangedEvent.kt @@ -0,0 +1,3 @@ +package net.pterodactylus.sone.core + +data class PreferenceChangedEvent(val preferenceName: String, val newValue: Any) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt index 783552e..fe1e3c3 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt @@ -1,5 +1,5 @@ /* - * Sone - Preferences.kt - Copyright © 2013–2019 David Roden + * Sone - Preferences.kt - Copyright © 2013–2020 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 @@ -17,16 +17,19 @@ package net.pterodactylus.sone.core -import com.google.common.base.Predicates.* -import com.google.common.eventbus.* -import net.pterodactylus.sone.core.event.* -import net.pterodactylus.sone.fcp.FcpInterface.* -import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.* -import net.pterodactylus.sone.fcp.event.* -import net.pterodactylus.sone.utils.* -import net.pterodactylus.sone.utils.IntegerRangePredicate.* -import net.pterodactylus.util.config.* -import java.lang.Integer.* +import com.google.common.eventbus.EventBus +import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent +import net.pterodactylus.sone.core.event.StrictFilteringActivatedEvent +import net.pterodactylus.sone.core.event.StrictFilteringDeactivatedEvent +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS +import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent +import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent +import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged +import net.pterodactylus.sone.utils.DefaultOption +import net.pterodactylus.util.config.Configuration +import net.pterodactylus.util.config.ConfigurationException +import java.lang.Integer.MAX_VALUE /** * Convenience interface for external classes that want to access the core’s @@ -34,7 +37,7 @@ import java.lang.Integer.* */ class Preferences(private val eventBus: EventBus) { - private val _insertionDelay = DefaultOption(60, range(0, MAX_VALUE)) + private val _insertionDelay = DefaultOption(60) { it in 0..MAX_VALUE } val insertionDelay: Int get() = _insertionDelay.get() var newInsertionDelay: Int? get() = unsupported @@ -44,7 +47,7 @@ class Preferences(private val eventBus: EventBus) { eventBus.post(PreferenceChangedEvent("InsertionDelay", insertionDelay)) } - private val _postsPerPage = DefaultOption(10, range(1, MAX_VALUE)) + private val _postsPerPage = DefaultOption(10) { it in 1..MAX_VALUE } val postsPerPage: Int get() = _postsPerPage.get() var newPostsPerPage: Int? get() = unsupported @@ -53,19 +56,19 @@ class Preferences(private val eventBus: EventBus) { eventBus.post(PreferenceChangedEvent("PostsPerPage", postsPerPage)) } - private val _imagesPerPage = DefaultOption(9, range(1, MAX_VALUE)) + private val _imagesPerPage = DefaultOption(9) { it in 1..MAX_VALUE } val imagesPerPage: Int get() = _imagesPerPage.get() var newImagesPerPage: Int? get() = unsupported - set (value: Int?) = _imagesPerPage.set(value) + set(value: Int?) = _imagesPerPage.set(value) - private val _charactersPerPost = DefaultOption(400, or(range(50, MAX_VALUE), equalTo(-1))) + private val _charactersPerPost = DefaultOption(400) { it == -1 || it in 50..MAX_VALUE } val charactersPerPost: Int get() = _charactersPerPost.get() var newCharactersPerPost: Int? get() = unsupported set(value) = _charactersPerPost.set(value) - private val _postCutOffLength = DefaultOption(200, range(50, MAX_VALUE)) + private val _postCutOffLength = DefaultOption(200) { it in 50..MAX_VALUE } val postCutOffLength: Int get() = _postCutOffLength.get() var newPostCutOffLength: Int? get() = unsupported @@ -77,24 +80,6 @@ class Preferences(private val eventBus: EventBus) { get() = unsupported set(value) = _requireFullAccess.set(value) - private val _positiveTrust = DefaultOption(75, range(0, 100)) - val positiveTrust: Int get() = _positiveTrust.get() - var newPositiveTrust: Int? - get() = unsupported - set(value) = _positiveTrust.set(value) - - private val _negativeTrust = DefaultOption(-25, range(-100, 100)) - val negativeTrust: Int get() = _negativeTrust.get() - var newNegativeTrust: Int? - get() = unsupported - set(value) = _negativeTrust.set(value) - - private val _trustComment = DefaultOption("Set from Sone Web Interface") - val trustComment: String get() = _trustComment.get() - var newTrustComment: String? - get() = unsupported - set(value) = _trustComment.set(value) - private val _fcpInterfaceActive = DefaultOption(false) val fcpInterfaceActive: Boolean get() = _fcpInterfaceActive.get() var newFcpInterfaceActive: Boolean? @@ -116,6 +101,17 @@ class Preferences(private val eventBus: EventBus) { eventBus.post(FullAccessRequiredChanged(fcpFullAccessRequired)) } + private val _strictFiltering = DefaultOption(false) + val strictFiltering: Boolean get() = _strictFiltering.get() + var newStrictFiltering: Boolean? = false + set(value) { + _strictFiltering.set(value) + when (strictFiltering) { + true -> eventBus.post(StrictFilteringActivatedEvent()) + else -> eventBus.post(StrictFilteringDeactivatedEvent()) + } + } + @Throws(ConfigurationException::class) fun saveTo(configuration: Configuration) { configuration.getIntValue("Option/ConfigurationVersion").value = 0 @@ -125,11 +121,9 @@ class Preferences(private val eventBus: EventBus) { configuration.getIntValue("Option/CharactersPerPost").value = _charactersPerPost.real configuration.getIntValue("Option/PostCutOffLength").value = _postCutOffLength.real configuration.getBooleanValue("Option/RequireFullAccess").value = _requireFullAccess.real - configuration.getIntValue("Option/PositiveTrust").value = _positiveTrust.real - configuration.getIntValue("Option/NegativeTrust").value = _negativeTrust.real - configuration.getStringValue("Option/TrustComment").value = _trustComment.real configuration.getBooleanValue("Option/ActivateFcpInterface").value = _fcpInterfaceActive.real configuration.getIntValue("Option/FcpFullAccessRequired").value = toInt(_fcpFullAccessRequired.real) + configuration.getBooleanValue("Option/StrictFiltering").value = _strictFiltering.real } private fun toInt(fullAccessRequired: FullAccessRequired?): Int? { diff --git a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt new file mode 100644 index 0000000..62b60ae --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt @@ -0,0 +1,63 @@ +package net.pterodactylus.sone.core + +import net.pterodactylus.sone.fcp.FcpInterface.* +import net.pterodactylus.util.config.* + +/** + * Loads preferences stored in a [Configuration] into a [Preferences] object. + */ +class PreferencesLoader(private val preferences: Preferences) { + + fun loadFrom(configuration: Configuration) { + loadInsertionDelay(configuration) + loadPostsPerPage(configuration) + loadImagesPerPage(configuration) + loadCharactersPerPost(configuration) + loadPostCutOffLength(configuration) + loadRequireFullAccess(configuration) + loadFcpInterfaceActive(configuration) + loadFcpFullAccessRequired(configuration) + loadStrictFiltering(configuration) + } + + private fun loadInsertionDelay(configuration: Configuration) { + preferences.newInsertionDelay = configuration.getIntValue("Option/InsertionDelay").getValue(null) + } + + private fun loadPostsPerPage(configuration: Configuration) { + preferences.newPostsPerPage = configuration.getIntValue("Option/PostsPerPage").getValue(null) + } + + private fun loadImagesPerPage(configuration: Configuration) { + preferences.newImagesPerPage = configuration.getIntValue("Option/ImagesPerPage").getValue(null) + } + + private fun loadCharactersPerPost(configuration: Configuration) { + preferences.newCharactersPerPost = configuration.getIntValue("Option/CharactersPerPost").getValue(null) + } + + private fun loadPostCutOffLength(configuration: Configuration) { + try { + preferences.newPostCutOffLength = configuration.getIntValue("Option/PostCutOffLength").getValue(null) + } catch (iae1: IllegalArgumentException) { /* previous versions allowed -1, ignore and use default. */ + } + } + + private fun loadRequireFullAccess(configuration: Configuration) { + preferences.newRequireFullAccess = configuration.getBooleanValue("Option/RequireFullAccess").getValue(null) + } + + private fun loadFcpInterfaceActive(configuration: Configuration) { + preferences.newFcpInterfaceActive = configuration.getBooleanValue("Option/ActivateFcpInterface").getValue(null) + } + + private fun loadFcpFullAccessRequired(configuration: Configuration) { + val fullAccessRequiredInteger = configuration.getIntValue("Option/FcpFullAccessRequired").getValue(null) + preferences.newFcpFullAccessRequired = fullAccessRequiredInteger?.let { FullAccessRequired.values()[it] } + } + + private fun loadStrictFiltering(configuration: Configuration) { + preferences.newStrictFiltering = configuration.getBooleanValue("Option/StrictFiltering").getValue(null) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt b/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt new file mode 100644 index 0000000..183303a --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt @@ -0,0 +1,26 @@ +package net.pterodactylus.sone.core + +import freenet.keys.FreenetURI +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.freenet.wot.OwnIdentity + +/** + * Injectable helper class that can create request and insert URIs for [Sones][Sone]. + */ +open class SoneUriCreator { + + fun getRequestUri(sone: Sone): FreenetURI = sone.identity.requestUri + .let(::FreenetURI) + .sonify(sone.latestEdition) + + open fun getInsertUri(sone: Sone): FreenetURI? = (sone.identity as? OwnIdentity)?.insertUri + ?.let(::FreenetURI) + ?.sonify(sone.latestEdition) + +} + +private fun FreenetURI.sonify(edition: Long): FreenetURI = + setKeyType("USK") + .setDocName("Sone") + .setMetaString(emptyArray()) + .setSuggestedEdition(edition) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/UpdateChecker.kt b/src/main/kotlin/net/pterodactylus/sone/core/UpdateChecker.kt new file mode 100644 index 0000000..0e67d0a --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/UpdateChecker.kt @@ -0,0 +1,123 @@ +/* + * Sone - UpdateChecker.kt - Copyright © 2011–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core + +import com.google.common.eventbus.* +import com.google.common.primitives.* +import com.google.inject.Inject +import freenet.keys.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.main.* +import net.pterodactylus.sone.utils.* +import net.pterodactylus.util.version.Version +import java.io.* +import java.util.* +import java.util.logging.* +import java.util.logging.Logger.* +import javax.inject.Singleton + +/** + * Watches the official Sone homepage for new releases. + */ +@Singleton +class UpdateChecker @Inject constructor( + private val eventBus: EventBus, + private val freenetInterface: FreenetInterface, + private val currentRunningVersion: Version, + pluginHomepage: PluginHomepage) { + + private val logger: Logger = getLogger(UpdateChecker::class.java.name) + + private val currentUri by lazy { FreenetURI(pluginHomepage.homepage) } + + var latestEdition = SonePlugin.getLatestEdition() + private set + + var latestVersion: Version = currentRunningVersion + private set + + var latestVersionDate: Long = 0 + private set + + fun hasLatestVersion() = + latestVersion > currentRunningVersion + + fun start() { + freenetInterface.registerUsk(currentUri) { uri, edition, newKnownGood, newSlot -> + logger.log(Level.FINEST, String.format("Found update for %s: %d, %s, %s", uri, edition, newKnownGood, newSlot)) + if (newKnownGood || newSlot) { + try { + freenetInterface.fetchUri(uri.setMetaString(arrayOf("sone.properties"))) + ?.onNull { + logger.log(Level.WARNING, String.format("Could not fetch properties of latest homepage: %s", uri)) + }?.fetchResult + ?.asBucket()?.use { resultBucket -> + resultBucket.inputStream + .let { parseProperties(it) } + .let { extractCurrentVersion(it) } + .onNull { logger.log(Level.INFO, "Invalid data parsed from properties.") } + ?.takeIf { it.version > latestVersion } + ?.also { updateVersionInformation(it, edition) } + ?.also { logger.info { "Found new version: %s (%tc%s)".format(it.version, it.time, if (it.disruptive) ", disruptive" else "") } } + ?.also { eventBus.post(UpdateFoundEvent(it.version, it.time, edition, it.disruptive)) } + } + } catch (ioe1: IOException) { + logger.log(Level.WARNING, String.format("Could not parse sone.properties of %s!", uri), ioe1) + } + } + } + } + + fun stop() { + freenetInterface.unregisterUsk(currentUri) + } + + private fun updateVersionInformation(versionInformation: VersionInformation, edition: Long) { + latestVersion = versionInformation.version + latestVersionDate = versionInformation.time + latestEdition = edition + } + + private fun parseProperties(propertiesInputStream: InputStream) = + Properties().apply { + InputStreamReader(propertiesInputStream, "UTF-8").use { inputStreamReader -> + load(inputStreamReader) + } + } + + private fun extractCurrentVersion(properties: Properties) = + properties.getProperty("CurrentVersion/Version") + ?.let { Version.parse(it) } + ?.let { version -> + properties.getProperty("CurrentVersion/ReleaseTime") + ?.let { Longs.tryParse(it) } + ?.let { time -> + VersionInformation(version, time, disruptiveVersionBetweenCurrentAndFound(properties)) + } + } + + private fun disruptiveVersionBetweenCurrentAndFound(properties: Properties) = + properties.stringPropertyNames() + .filter { it.startsWith("DisruptiveVersion/") } + .map { it.removePrefix("DisruptiveVersion/") } + .map { Version.parse(it) } + .any { it > currentRunningVersion } + +} + +private data class VersionInformation(val version: Version, val time: Long, val disruptive: Boolean) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt b/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt index 28bac6d..54af30a 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt @@ -42,7 +42,7 @@ abstract class BasicUpdateSoneProcessor(private val database: Database, private .map { PostRemovedEvent(it) } .forEach(eventBus::post) newPostReplies - .onEach { postReply -> if (postReply.time <= sone.followingTime) postReply.isKnown = true } + .onEach { postReply -> if (postReply.time <= sone.followingTime) database.setPostReplyKnown(postReply) } .mapNotNull { postReply -> postReply.isKnown.ifFalse { NewPostReplyFoundEvent(postReply) } } .forEach(eventBus::post) removedPostReplies diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/ConfigNotRead.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/ConfigNotRead.kt new file mode 100644 index 0000000..97f2843 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/ConfigNotRead.kt @@ -0,0 +1,26 @@ +/** + * Sone - ConfigNotRead.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +/** + * Event that signals that Sone could not read an existing configuration + * successfully, and a new configuration was created. This is different from + * [FirstStart] in that `FirstStart` signals that there *was* no existing + * configuration to be read. + */ +class ConfigNotRead diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/DebugActivatedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/DebugActivatedEvent.kt new file mode 100644 index 0000000..037cdb5 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/DebugActivatedEvent.kt @@ -0,0 +1,20 @@ +/** + * Sone - DebugActivatedEvent.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +class DebugActivatedEvent diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/FirstStart.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/FirstStart.kt new file mode 100644 index 0000000..c58db50 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/FirstStart.kt @@ -0,0 +1,24 @@ +/** + * Sone - FirstStart.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +/** + * Event that signals that Sone was started for the first time. This event + * will only be triggered once, on startup. + */ +class FirstStart diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/ImageEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageEvent.kt new file mode 100644 index 0000000..12d9ca1 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - ImageEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Base class for [Image] events. + */ +abstract class ImageEvent(val image: Image) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.kt new file mode 100644 index 0000000..37a221f --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertAbortedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - ImageInsertAbortedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that an [Image] insert is aborted. + */ +class ImageInsertAbortedEvent(image: Image) : ImageEvent(image) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.kt new file mode 100644 index 0000000..e19b3d8 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertFailedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - ImageInsertFailedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that an [Image] insert has failed. + */ +class ImageInsertFailedEvent(image: Image, val cause: Throwable) : ImageEvent(image) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.kt new file mode 100644 index 0000000..46daec6 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertFinishedEvent.kt @@ -0,0 +1,26 @@ +/* + * Sone - ImageInsertFinishedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import freenet.keys.* +import net.pterodactylus.sone.data.* + +/** + * Event that signals that an [Image] insert is finished. + */ +class ImageInsertFinishedEvent(image: Image, val resultingUri: FreenetURI) : ImageEvent(image) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.kt new file mode 100644 index 0000000..57478fa --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/ImageInsertStartedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - ImageInsertStartedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that an [Image] is not being inserted. + */ +class ImageInsertStartedEvent(image: Image) : ImageEvent(image) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/MarkPostKnownEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/MarkPostKnownEvent.kt new file mode 100644 index 0000000..e479202 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/MarkPostKnownEvent.kt @@ -0,0 +1,26 @@ +/* + * Sone - MarkPostKnownEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Post] has been marked as + * [known][Post.isKnown]. + */ +class MarkPostKnownEvent(post: Post) : PostEvent(post) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.kt new file mode 100644 index 0000000..af9dce4 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/MarkPostReplyKnownEvent.kt @@ -0,0 +1,26 @@ +/* + * Sone - MarkPostReplyKnownEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [PostReply] has been marked as + * [known][PostReply.isKnown]. + */ +class MarkPostReplyKnownEvent(postReply: PostReply) : PostReplyEvent(postReply) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.kt new file mode 100644 index 0000000..b3b8fbc --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/MarkSoneKnownEvent.kt @@ -0,0 +1,26 @@ +/* + * Sone - MarkSoneKnownEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Sone] has been marked as + * [known][Sone.isKnown]. + */ +class MarkSoneKnownEvent(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/MentionOfLocalSoneFoundEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/MentionOfLocalSoneFoundEvent.kt new file mode 100644 index 0000000..8b9b542 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/MentionOfLocalSoneFoundEvent.kt @@ -0,0 +1,27 @@ +/** + * Sone - MentionOfLocalSoneFoundEvent.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a new post or reply was found that mentioned a local + * Sone, which happens if the [SoneTextParser] locates a [SonePart] in a post + * or reply. + */ +data class MentionOfLocalSoneFoundEvent(val post: Post) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/MentionOfLocalSoneRemovedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/MentionOfLocalSoneRemovedEvent.kt new file mode 100644 index 0000000..2413a80 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/MentionOfLocalSoneRemovedEvent.kt @@ -0,0 +1,27 @@ +/** + * Sone - MentionOfLocalSoneRemovedEvent.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a post or reply that mentioned a local Sone was + * removed so that the given post and its replies do not contain a mention of + * a local Sone anymore. + */ +data class MentionOfLocalSoneRemovedEvent(val post: Post) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostFoundEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostFoundEvent.kt index ba7f957..876f79c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostFoundEvent.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostFoundEvent.kt @@ -1,5 +1,5 @@ /* - * Sone - NewPostFoundEvent.kt - Copyright © 2013–2019 David Roden + * Sone - NewPostFoundEvent.kt - Copyright © 2013–2020 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 @@ -22,9 +22,4 @@ import net.pterodactylus.sone.data.Post /** * Event that signals that a new post was found. */ -data class NewPostFoundEvent(val post: Post) { - - @Deprecated(message = "will go away", replaceWith = ReplaceWith("post")) - fun post() = post - -} +data class NewPostFoundEvent(val post: Post) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostReplyFoundEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostReplyFoundEvent.kt index a70d1b9..ec339d5 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostReplyFoundEvent.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/NewPostReplyFoundEvent.kt @@ -1,5 +1,5 @@ /* - * Sone - NewPostReplyFoundEvent.kt - Copyright © 2013–2019 David Roden + * Sone - NewPostReplyFoundEvent.kt - Copyright © 2013–2020 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 @@ -22,9 +22,4 @@ import net.pterodactylus.sone.data.PostReply /** * Event that signals that a new [PostReply] was found. */ -data class NewPostReplyFoundEvent(val postReply: PostReply) { - - @Deprecated(message = "will go away", replaceWith = ReplaceWith("postReply")) - fun postReply() = postReply - -} +data class NewPostReplyFoundEvent(val postReply: PostReply) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/NewSoneFoundEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/NewSoneFoundEvent.kt new file mode 100644 index 0000000..bb7c933 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/NewSoneFoundEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - NewSoneFoundEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a new remote Sone was found. + */ +class NewSoneFoundEvent(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/PostEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/PostEvent.kt new file mode 100644 index 0000000..becafdd --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/PostEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - PostEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Base class for post events. + */ +abstract class PostEvent(val post: Post) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/PostRemovedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/PostRemovedEvent.kt index 117d800..f68214b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/event/PostRemovedEvent.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/PostRemovedEvent.kt @@ -1,5 +1,5 @@ /* - * Sone - PostRemovedEvent.kt - Copyright © 2013–2019 David Roden + * Sone - PostRemovedEvent.kt - Copyright © 2013–2020 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 @@ -22,9 +22,4 @@ import net.pterodactylus.sone.data.Post /** * Event that signals that a [Post] was removed. */ -data class PostRemovedEvent(val post: Post) { - - @Deprecated(message = "will go away", replaceWith = ReplaceWith("post")) - fun post() = post - -} +data class PostRemovedEvent(val post: Post) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/PostReplyEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/PostReplyEvent.kt new file mode 100644 index 0000000..72e0daa --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/PostReplyEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - PostReplyEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Base class for [PostReply] events. + */ +open class PostReplyEvent(val postReply: PostReply) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/PostReplyRemovedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/PostReplyRemovedEvent.kt index 6aa495b..6c71b8e 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/event/PostReplyRemovedEvent.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/PostReplyRemovedEvent.kt @@ -1,5 +1,5 @@ /* - * Sone - PostReplyRemovedEvent.kt - Copyright © 2013–2019 David Roden + * Sone - PostReplyRemovedEvent.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/Shutdown.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/Shutdown.kt new file mode 100644 index 0000000..975dd96 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/Shutdown.kt @@ -0,0 +1,23 @@ +/** + * Sone - Shutdown.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +/** + * Event that signals the shutdown of Sone. + */ +class Shutdown diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneEvent.kt new file mode 100644 index 0000000..d84bab5 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - SoneEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Base class for Sone events. + */ +abstract class SoneEvent(val sone: Sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.kt new file mode 100644 index 0000000..73ea564 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertAbortedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - SoneInsertAbortedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Sone] insert was aborted. + */ +class SoneInsertAbortedEvent(sone: Sone, val cause: Throwable) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertedEvent.kt new file mode 100644 index 0000000..f22dabe --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - SoneInsertedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Sone] was inserted. + */ +class SoneInsertedEvent(sone: Sone, val insertDuration: Long, val insertFingerprint: String) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertingEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertingEvent.kt new file mode 100644 index 0000000..f928556 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneInsertingEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - SoneInsertingEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Sone] is now being inserted. + */ +class SoneInsertingEvent(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedEvent.kt new file mode 100644 index 0000000..1d77318 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedEvent.kt @@ -0,0 +1,26 @@ +/* + * Sone - SoneLockedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Sone] was locked. Only + * [local Sones][Sone.isLocal] can be locked. + */ +class SoneLockedEvent(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedOnStartup.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedOnStartup.kt new file mode 100644 index 0000000..ac11c4d --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneLockedOnStartup.kt @@ -0,0 +1,25 @@ +/** + * Sone - SoneLockedOnStartup.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Signals that a Sone was locked on startup because it’s empty. + */ +class SoneLockedOnStartup(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneRemovedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneRemovedEvent.kt new file mode 100644 index 0000000..ea08dae --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneRemovedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - SoneRemovedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Sone] was removed. + */ +class SoneRemovedEvent(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/SoneUnlockedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneUnlockedEvent.kt new file mode 100644 index 0000000..3cc4203 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/SoneUnlockedEvent.kt @@ -0,0 +1,26 @@ +/* + * Sone - SoneUnlockedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a [Sone] was unlocked. Only + * [local Sones][Sone.isLocal] can be locked. + */ +class SoneUnlockedEvent(sone: Sone) : SoneEvent(sone) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/Startup.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/Startup.kt new file mode 100644 index 0000000..79bd7a9 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/Startup.kt @@ -0,0 +1,23 @@ +/** + * Sone - Startup.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +/** + * Event that signals the startup of Sone. + */ +class Startup diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/StrictFilteringEvents.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/StrictFilteringEvents.kt new file mode 100644 index 0000000..ea12459 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/StrictFilteringEvents.kt @@ -0,0 +1,13 @@ +package net.pterodactylus.sone.core.event + +/** + * Event that signals that the “[strict filtering][net.pterodactylus.sone.core.Preferences.strictFiltering]” + * preference was activated. + */ +class StrictFilteringActivatedEvent + +/** + * Event that signals that the “[strict filtering][net.pterodactylus.sone.core.Preferences.strictFiltering]” + * preference was deactivated. + */ +class StrictFilteringDeactivatedEvent diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/UpdateFoundEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/UpdateFoundEvent.kt new file mode 100644 index 0000000..82a4cf5 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/UpdateFoundEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - UpdateFoundEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +import net.pterodactylus.util.version.* + +/** + * Event that signals that an update for Sone was found. + */ +data class UpdateFoundEvent(val version: Version, val releaseTime: Long, val latestEdition: Long, val isDisruptive: Boolean) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/WebOfTrustAppeared.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/WebOfTrustAppeared.kt new file mode 100644 index 0000000..46fc327 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/WebOfTrustAppeared.kt @@ -0,0 +1,23 @@ +/** + * Sone - WebOfTrustAppeared.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +/** + * Event that signals that the web of trust is reachable. + */ +class WebOfTrustAppeared diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/WebOfTrustDisappeared.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/WebOfTrustDisappeared.kt new file mode 100644 index 0000000..8382895 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/WebOfTrustDisappeared.kt @@ -0,0 +1,23 @@ +/** + * Sone - WebOfTrustDisappeared.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.core.event + +/** + * Event that signals that the web of trust is not reachable. + */ +class WebOfTrustDisappeared diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Album.kt b/src/main/kotlin/net/pterodactylus/sone/data/Album.kt new file mode 100644 index 0000000..991c0ec --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Album.kt @@ -0,0 +1,38 @@ +/** + * Sone - Album.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +/** Returns all images contained in this album and all its albums. */ +val Album.allImages: Collection + get() = + images + albums.flatMap(Album::allImages) + +/** + * Returns this album and all albums contained in this album (recursively). + * A child album is always listed after its parent. + */ +val Album.allAlbums: List + get() = + listOf(this) + albums.flatMap(Album::allAlbums) + +@get:JvmName("notEmpty") +val notEmpty: (Album) -> Boolean = { album -> + album.allImages.let { images -> + images.isNotEmpty() && images.any(Image::isInserted) + } +} diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Fingerprintable.kt b/src/main/kotlin/net/pterodactylus/sone/data/Fingerprintable.kt index 97a54f8..7bba095 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Fingerprintable.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Fingerprintable.kt @@ -1,5 +1,5 @@ /* - * Sone - Fingerprintable.kt - Copyright © 2011–2019 David Roden + * Sone - Fingerprintable.kt - Copyright © 2011–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Identified.kt b/src/main/kotlin/net/pterodactylus/sone/data/Identified.kt index 1cbad81..28e456f 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Identified.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Identified.kt @@ -1,5 +1,5 @@ /* - * Sone - Identified.kt - Copyright © 2013–2019 David Roden + * Sone - Identified.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt new file mode 100644 index 0000000..d87bd3c --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt @@ -0,0 +1,16 @@ +package net.pterodactylus.sone.data + +import java.util.Comparator.comparing + +/** + * Predicate that returns whether a post is _not_ from the future, + * i.e. whether it should be visible now. + */ +@get:JvmName("noFuturePost") +val noFuturePost: (Post) -> Boolean = { it.time <= System.currentTimeMillis() } + +/** + * Comparator that orders posts by their time, newest posts first. + */ +@get:JvmName("newestPostFirst") +val newestPostFirst: Comparator = comparing(Post::getTime).reversed() diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt new file mode 100644 index 0000000..cfc940a --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt @@ -0,0 +1,34 @@ +/** + * Sone - Reply.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +import java.util.Comparator.comparing + +/** + * Comparator that orders replies by their time, newest replies first. + */ +@get:JvmName("newestReplyFirst") +val newestReplyFirst: Comparator> = + comparing(Reply<*>::getTime).reversed() + +/** + * Predicate that returns whether a reply is _not_ from the future, + * i.e. whether it should be visible now. + */ +val noFutureReply: (Reply<*>) -> Boolean = + { it.getTime() <= System.currentTimeMillis() } diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt new file mode 100644 index 0000000..34403a1 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt @@ -0,0 +1,61 @@ +/** + * Sone - Sone.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +import net.pterodactylus.sone.template.* +import java.util.Comparator.* + +private val caseInsensitiveCompare = { left: String, right: String -> left.compareTo(right, true) } + +/** + * Comparator that sorts Sones by their [nice name][SoneAccessor.getNiceName] + * and, failing that, by [ID][Sone.id]. + */ +@get:JvmName("niceNameComparator") // TODO: remove once Sone is 100% Kotlin +val niceNameComparator: Comparator = + comparing(SoneAccessor::getNiceName, caseInsensitiveCompare).thenComparing(Sone::id) + +/** + * Comparator that sorts Sones by their [last activity][Sone.getTime], least + * recently active Sones first. + */ +@get:JvmName("lastActivityComparator") // TODO: remove once Sone is 100% Kotlin +val lastActivityComparator: Comparator = + comparing(Sone::getTime).reversed() + +/** + * Comparator that sorts Sones by their [post count][Sone.getPosts] (most posts + * first) and, failing that, by their [reply count][Sone.getReplies] (most + * replies first). + */ +@get:JvmName("postCountComparator") // TODO: remove once Sone is 100% Kotlin +val postCountComparator: Comparator = + comparing { it.posts.size } + .thenComparing { it.replies.size } + .reversed() + +val imageCountComparator: Comparator = + comparing { it.rootAlbum.allImages.size }.reversed() + +val Sone.allAlbums: List + get() = + rootAlbum.albums.flatMap(Album::allAlbums) + +val Sone.allImages: Collection + get() = + rootAlbum.allImages diff --git a/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilder.kt b/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilder.kt index acace46..dd42f07 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilder.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilder.kt @@ -1,5 +1,5 @@ /* - * Sone - AlbumBuilder.kt - Copyright © 2013–2019 David Roden + * Sone - AlbumBuilder.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilderFactory.kt b/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilderFactory.kt index f153f33..e27528f 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilderFactory.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/AlbumBuilderFactory.kt @@ -1,5 +1,5 @@ /* - * Sone - AlbumBuilderFactory.kt - Copyright © 2013–2019 David Roden + * Sone - AlbumBuilderFactory.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/AlbumDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/AlbumDatabase.kt index e61d429..3ccaef4 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/AlbumDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/AlbumDatabase.kt @@ -1,5 +1,5 @@ /* - * Sone - AlbumDatabase.kt - Copyright © 2013–2019 David Roden + * Sone - AlbumDatabase.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/AlbumProvider.kt b/src/main/kotlin/net/pterodactylus/sone/database/AlbumProvider.kt index db0146d..b1fcf62 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/AlbumProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/AlbumProvider.kt @@ -1,5 +1,5 @@ /* - * Sone - AlbumProvider.kt - Copyright © 2013–2019 David Roden + * Sone - AlbumProvider.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/AlbumStore.kt b/src/main/kotlin/net/pterodactylus/sone/database/AlbumStore.kt index ddc7bb2..3d93ff1 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/AlbumStore.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/AlbumStore.kt @@ -1,5 +1,5 @@ /* - * Sone - AlbumStore.kt - Copyright © 2013–2019 David Roden + * Sone - AlbumStore.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/Database.kt b/src/main/kotlin/net/pterodactylus/sone/database/Database.kt index 17337e2..28417e8 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/Database.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/Database.kt @@ -1,5 +1,5 @@ /* - * Sone - Database.kt - Copyright © 2013–2019 David Roden + * Sone - Database.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilder.kt b/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilder.kt index c9ddbaa..d104602 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilder.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilder.kt @@ -1,5 +1,5 @@ /* - * Sone - ImageBuilder.kt - Copyright © 2013–2019 David Roden + * Sone - ImageBuilder.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilderFactory.kt b/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilderFactory.kt index f9f8be7..faad2ca 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilderFactory.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/ImageBuilderFactory.kt @@ -1,5 +1,5 @@ /* - * Sone - ImageBuilderFactory.kt - Copyright © 2013–2019 David Roden + * Sone - ImageBuilderFactory.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/ImageDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/ImageDatabase.kt index b55c073..81eecc6 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/ImageDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/ImageDatabase.kt @@ -1,5 +1,5 @@ /* - * Sone - ImageDatabase.kt - Copyright © 2013–2019 David Roden + * Sone - ImageDatabase.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/ImageProvider.kt b/src/main/kotlin/net/pterodactylus/sone/database/ImageProvider.kt index 0cde52a..9aa81ac 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/ImageProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/ImageProvider.kt @@ -1,5 +1,5 @@ /* - * Sone - ImageProvider.kt - Copyright © 2013–2019 David Roden + * Sone - ImageProvider.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/ImageStore.kt b/src/main/kotlin/net/pterodactylus/sone/database/ImageStore.kt index 4be9dd9..4bff9e7 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/ImageStore.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/ImageStore.kt @@ -1,5 +1,5 @@ /* - * Sone - ImageStore.kt - Copyright © 2013–2019 David Roden + * Sone - ImageStore.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostBuilder.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostBuilder.kt index 542bf78..634eda6 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostBuilder.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostBuilder.kt @@ -1,5 +1,5 @@ /* - * Sone - PostBuilder.kt - Copyright © 2013–2019 David Roden + * Sone - PostBuilder.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostBuilderFactory.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostBuilderFactory.kt index e20248f..ba1b3a1 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostBuilderFactory.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostBuilderFactory.kt @@ -1,5 +1,5 @@ /* - * Sone - PostBuilderFactory.kt - Copyright © 2013–2019 David Roden + * Sone - PostBuilderFactory.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostDatabase.kt index 02b9fd1..cef238a 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostDatabase.kt @@ -1,5 +1,5 @@ /* - * Sone - PostDatabase.kt - Copyright © 2013–2019 David Roden + * Sone - PostDatabase.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostProvider.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostProvider.kt index abf7ab9..2641e3b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostProvider.kt @@ -1,5 +1,5 @@ /* - * Sone - PostProvider.kt - Copyright © 2011–2019 David Roden + * Sone - PostProvider.kt - Copyright © 2011–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilder.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilder.kt index d8d7f8d..9ff01b9 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilder.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilder.kt @@ -1,5 +1,5 @@ /* - * Sone - PostReplyBuilder.kt - Copyright © 2013–2019 David Roden + * Sone - PostReplyBuilder.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilderFactory.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilderFactory.kt index 50b58e8..b2788b5 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilderFactory.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyBuilderFactory.kt @@ -1,5 +1,5 @@ /* - * Sone - PostReplyBuilderFactory.kt - Copyright © 2013–2019 David Roden + * Sone - PostReplyBuilderFactory.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyDatabase.kt index 316c772..458ba0c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyDatabase.kt @@ -1,5 +1,5 @@ /* - * Sone - PostReplyDatabase.kt - Copyright © 2013–2019 David Roden + * Sone - PostReplyDatabase.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyProvider.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyProvider.kt index cc797d7..c29e443 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyProvider.kt @@ -1,5 +1,5 @@ /* - * Sone - PostReplyProvider.kt - Copyright © 2013–2019 David Roden + * Sone - PostReplyProvider.kt - Copyright © 2013–2020 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 @@ -17,11 +17,14 @@ package net.pterodactylus.sone.database +import com.google.inject.* import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.database.memory.* /** * Interface for objects that can provide [PostReply]s. */ +@ImplementedBy(MemoryDatabase::class) interface PostReplyProvider { fun getPostReply(id: String): PostReply? diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt index f4839b1..859cbb0 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt @@ -1,5 +1,5 @@ /* - * Sone - PostReplyStore.kt - Copyright © 2013–2019 David Roden + * Sone - PostReplyStore.kt - Copyright © 2013–2020 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 @@ -26,5 +26,6 @@ interface PostReplyStore { fun storePostReply(postReply: PostReply) fun removePostReply(postReply: PostReply) + fun setPostReplyKnown(postReply: PostReply) } diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostStore.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostStore.kt index 84ea39e..f033d59 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostStore.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostStore.kt @@ -1,5 +1,5 @@ /* - * Sone - PostStore.kt - Copyright © 2013–2019 David Roden + * Sone - PostStore.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/ReplyBuilder.kt b/src/main/kotlin/net/pterodactylus/sone/database/ReplyBuilder.kt index bfc74ac..47d0e0d 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/ReplyBuilder.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/ReplyBuilder.kt @@ -1,5 +1,5 @@ /* - * Sone - ReplyBuilder.kt - Copyright © 2013–2019 David Roden + * Sone - ReplyBuilder.kt - Copyright © 2013–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/SoneProvider.kt b/src/main/kotlin/net/pterodactylus/sone/database/SoneProvider.kt index 4156d66..fa57340 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/SoneProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/SoneProvider.kt @@ -1,5 +1,5 @@ /* - * Sone - SoneProvider.kt - Copyright © 2011–2019 David Roden + * Sone - SoneProvider.kt - Copyright © 2011–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt new file mode 100644 index 0000000..3b5a6a9 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -0,0 +1,353 @@ +/* + * Sone - MemoryDatabase.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.database.memory + +import com.google.common.base.Preconditions.checkNotNull +import com.google.common.collect.HashMultimap +import com.google.common.collect.Multimap +import com.google.common.collect.TreeMultimap +import com.google.common.util.concurrent.AbstractService +import com.google.common.util.concurrent.RateLimiter +import com.google.inject.Inject +import com.google.inject.Singleton +import net.pterodactylus.sone.data.Album +import net.pterodactylus.sone.data.Image +import net.pterodactylus.sone.data.Post +import net.pterodactylus.sone.data.PostReply +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.data.allAlbums +import net.pterodactylus.sone.data.allImages +import net.pterodactylus.sone.data.impl.AlbumBuilderImpl +import net.pterodactylus.sone.data.impl.ImageBuilderImpl +import net.pterodactylus.sone.data.newestReplyFirst +import net.pterodactylus.sone.database.AlbumBuilder +import net.pterodactylus.sone.database.Database +import net.pterodactylus.sone.database.DatabaseException +import net.pterodactylus.sone.database.ImageBuilder +import net.pterodactylus.sone.database.PostBuilder +import net.pterodactylus.sone.database.PostDatabase +import net.pterodactylus.sone.database.PostReplyBuilder +import net.pterodactylus.sone.utils.ifTrue +import net.pterodactylus.sone.utils.unit +import net.pterodactylus.util.config.Configuration +import net.pterodactylus.util.config.ConfigurationException +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.withLock + +/** + * Memory-based [PostDatabase] implementation. + */ +@Singleton +class MemoryDatabase @Inject constructor(private val configuration: Configuration) : AbstractService(), Database { + + private val lock = ReentrantReadWriteLock() + private val readLock by lazy { lock.readLock()!! } + private val writeLock by lazy { lock.writeLock()!! } + private val configurationLoader = ConfigurationLoader(configuration) + private val allSones = mutableMapOf() + private val allPosts = mutableMapOf() + private val sonePosts: Multimap = HashMultimap.create() + private val knownPosts = mutableSetOf() + private val allPostReplies = mutableMapOf() + private val sonePostReplies: Multimap = TreeMultimap.create(Comparator { leftString, rightString -> leftString.compareTo(rightString) }, newestReplyFirst) + private val knownPostReplies = mutableSetOf() + private val allAlbums = mutableMapOf() + private val soneAlbums: Multimap = HashMultimap.create() + private val allImages = mutableMapOf() + private val soneImages: Multimap = HashMultimap.create() + private val memoryBookmarkDatabase = MemoryBookmarkDatabase(this, configurationLoader) + private val memoryFriendDatabase = MemoryFriendDatabase(configurationLoader) + private val saveRateLimiter: RateLimiter = RateLimiter.create(1.0) + private val saveKnownPostsRateLimiter: RateLimiter = RateLimiter.create(1.0) + private val saveKnownPostRepliesRateLimiter: RateLimiter = RateLimiter.create(1.0) + + override val soneLoader get() = this::getSone + + override val sones get() = readLock.withLock { allSones.values.toSet() } + + override val localSones get() = readLock.withLock { allSones.values.filter(Sone::isLocal) } + + override val remoteSones get() = readLock.withLock { allSones.values.filterNot(Sone::isLocal) } + + override val bookmarkedPosts get() = memoryBookmarkDatabase.bookmarkedPosts + + override fun save() { + if (saveRateLimiter.tryAcquire()) { + saveKnownPosts() + saveKnownPostReplies() + } + } + + override fun doStart() { + memoryBookmarkDatabase.start() + loadKnownPosts() + loadKnownPostReplies() + notifyStarted() + } + + override fun doStop() { + try { + memoryBookmarkDatabase.stop() + save() + notifyStopped() + } catch (de1: DatabaseException) { + notifyFailed(de1) + } + } + + override fun newSoneBuilder() = MemorySoneBuilder(this) + + override fun storeSone(sone: Sone) { + writeLock.withLock { + removeSone(sone) + + allSones[sone.id] = sone + sonePosts.putAll(sone.id, sone.posts) + for (post in sone.posts) { + allPosts[post.id] = post + } + sonePostReplies.putAll(sone.id, sone.replies) + for (postReply in sone.replies) { + allPostReplies[postReply.id] = postReply + } + sone.allAlbums.let { albums -> + soneAlbums.putAll(sone.id, albums) + albums.forEach { album -> allAlbums[album.id] = album } + } + sone.rootAlbum.allImages.let { images -> + soneImages.putAll(sone.id, images) + images.forEach { image -> allImages[image.id] = image } + } + } + } + + override fun removeSone(sone: Sone) { + writeLock.withLock { + allSones.remove(sone.id) + val removedPosts = sonePosts.removeAll(sone.id) + for (removedPost in removedPosts) { + allPosts.remove(removedPost.id) + } + val removedPostReplies = sonePostReplies.removeAll(sone.id) + for (removedPostReply in removedPostReplies) { + allPostReplies.remove(removedPostReply.id) + } + val removedAlbums = soneAlbums.removeAll(sone.id) + for (removedAlbum in removedAlbums) { + allAlbums.remove(removedAlbum.id) + } + val removedImages = soneImages.removeAll(sone.id) + for (removedImage in removedImages) { + allImages.remove(removedImage.id) + } + } + } + + override fun getSone(soneId: String) = readLock.withLock { allSones[soneId] } + + override fun getFriends(localSone: Sone): Collection = + if (!localSone.isLocal) { + emptySet() + } else { + memoryFriendDatabase.getFriends(localSone.id) + } + + override fun isFriend(localSone: Sone, friendSoneId: String) = + if (!localSone.isLocal) { + false + } else { + memoryFriendDatabase.isFriend(localSone.id, friendSoneId) + } + + override fun addFriend(localSone: Sone, friendSoneId: String) { + if (!localSone.isLocal) { + return + } + memoryFriendDatabase.addFriend(localSone.id, friendSoneId) + } + + override fun removeFriend(localSone: Sone, friendSoneId: String) { + if (!localSone.isLocal) { + return + } + memoryFriendDatabase.removeFriend(localSone.id, friendSoneId) + } + + override fun getFollowingTime(friendSoneId: String) = + memoryFriendDatabase.getFollowingTime(friendSoneId) + + override fun getPost(postId: String) = + readLock.withLock { allPosts[postId] } + + override fun getPosts(soneId: String): Collection = + sonePosts[soneId].toSet() + + override fun getDirectedPosts(recipientId: String) = + readLock.withLock { + allPosts.values.filter { + it.recipientId.orNull() == recipientId + } + } + + override fun newPostBuilder(): PostBuilder = MemoryPostBuilder(this, this) + + override fun storePost(post: Post) { + checkNotNull(post, "post must not be null") + writeLock.withLock { + allPosts[post.id] = post + sonePosts[post.sone.id].add(post) + } + } + + override fun removePost(post: Post) { + checkNotNull(post, "post must not be null") + writeLock.withLock { + allPosts.remove(post.id) + sonePosts[post.sone.id].remove(post) + post.sone.removePost(post) + } + } + + override fun getPostReply(id: String) = readLock.withLock { allPostReplies[id] } + + override fun getReplies(postId: String) = + readLock.withLock { + allPostReplies.values + .filter { it.postId == postId } + .sortedWith(newestReplyFirst.reversed()) + } + + override fun newPostReplyBuilder(): PostReplyBuilder = + MemoryPostReplyBuilder(this, this) + + override fun storePostReply(postReply: PostReply) = + writeLock.withLock { + allPostReplies[postReply.id] = postReply + } + + override fun removePostReply(postReply: PostReply) = + writeLock.withLock { + allPostReplies.remove(postReply.id) + }.unit + + override fun getAlbum(albumId: String) = readLock.withLock { allAlbums[albumId] } + + override fun newAlbumBuilder(): AlbumBuilder = AlbumBuilderImpl() + + override fun storeAlbum(album: Album) = + writeLock.withLock { + allAlbums[album.id] = album + soneAlbums.put(album.sone.id, album) + }.unit + + override fun removeAlbum(album: Album) = + writeLock.withLock { + allAlbums.remove(album.id) + soneAlbums.remove(album.sone.id, album) + }.unit + + override fun getImage(imageId: String) = readLock.withLock { allImages[imageId] } + + override fun newImageBuilder(): ImageBuilder = ImageBuilderImpl() + + override fun storeImage(image: Image): Unit = + writeLock.withLock { + allImages[image.id] = image + soneImages.put(image.sone.id, image) + } + + override fun removeImage(image: Image): Unit = + writeLock.withLock { + allImages.remove(image.id) + soneImages.remove(image.sone.id, image) + } + + override fun bookmarkPost(post: Post) = + memoryBookmarkDatabase.bookmarkPost(post) + + override fun unbookmarkPost(post: Post) = + memoryBookmarkDatabase.unbookmarkPost(post) + + override fun isPostBookmarked(post: Post) = + memoryBookmarkDatabase.isPostBookmarked(post) + + protected fun isPostKnown(post: Post) = readLock.withLock { post.id in knownPosts } + + fun setPostKnown(post: Post, known: Boolean): Unit = + writeLock.withLock { + if (known) + knownPosts.add(post.id) + else + knownPosts.remove(post.id) + saveKnownPosts() + } + + protected fun isPostReplyKnown(postReply: PostReply) = readLock.withLock { postReply.id in knownPostReplies } + + override fun setPostReplyKnown(postReply: PostReply): Unit = + writeLock.withLock { + knownPostReplies.add(postReply.id) + saveKnownPostReplies() + } + + private fun loadKnownPosts() = + configurationLoader.loadKnownPosts() + .let { + writeLock.withLock { + knownPosts.clear() + knownPosts.addAll(it) + } + } + + private fun saveKnownPosts() = + saveKnownPostsRateLimiter.tryAcquire().ifTrue { + try { + readLock.withLock { + knownPosts.forEachIndexed { index, knownPostId -> + configuration.getStringValue("KnownPosts/$index/ID").value = knownPostId + } + configuration.getStringValue("KnownPosts/${knownPosts.size}/ID").value = null + } + } catch (ce1: ConfigurationException) { + throw DatabaseException("Could not save database.", ce1) + } + } + + private fun loadKnownPostReplies(): Unit = + configurationLoader.loadKnownPostReplies().let { knownPostReplies -> + writeLock.withLock { + this.knownPostReplies.clear() + this.knownPostReplies.addAll(knownPostReplies) + } + } + + private fun saveKnownPostReplies() = + saveKnownPostRepliesRateLimiter.tryAcquire().ifTrue { + try { + readLock.withLock { + knownPostReplies.forEachIndexed { index, knownPostReply -> + configuration.getStringValue("KnownReplies/$index/ID").value = knownPostReply + } + configuration.getStringValue("KnownReplies/${knownPostReplies.size}/ID").value = null + } + } catch (ce1: ConfigurationException) { + throw DatabaseException("Could not save database.", ce1) + } + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt b/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt index 915c536..f884e34 100644 --- a/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt +++ b/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt @@ -1,5 +1,5 @@ /* - * Sone - AbstractSoneCommand.kt - Copyright © 2011–2019 David Roden + * Sone - AbstractSoneCommand.kt - Copyright © 2011–2020 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 @@ -44,12 +44,12 @@ abstract class AbstractSoneCommand val requiresWriteAccess: Boolean = false) : AbstractCommand() { @Throws(FcpException::class) - protected fun getSone(simpleFieldSet: SimpleFieldSet, parameterName: String, localOnly: Boolean): Sone = - getSone(simpleFieldSet, parameterName, localOnly, true).get() + protected fun SimpleFieldSet.getSone(parameterName: String, localOnly: Boolean): Sone = + getSone(parameterName, localOnly, true).get() @Throws(FcpException::class) - protected fun getSone(simpleFieldSet: SimpleFieldSet, parameterName: String, localOnly: Boolean, mandatory: Boolean): Optional { - val soneId = simpleFieldSet.get(parameterName) + protected fun SimpleFieldSet.getSone(parameterName: String, localOnly: Boolean, mandatory: Boolean): Optional { + val soneId = get(parameterName) .throwOnNullIf(mandatory) { FcpException("Could not load Sone ID from “$parameterName”.") } ?: return Optional.absent() val sone = core.getSone(soneId) @@ -60,9 +60,9 @@ abstract class AbstractSoneCommand } @Throws(FcpException::class) - protected fun getPost(simpleFieldSet: SimpleFieldSet, parameterName: String): Post { + protected fun SimpleFieldSet.getPost(parameterName: String): Post { try { - val postId = simpleFieldSet.getString(parameterName) + val postId = getString(parameterName) return core.getPost(postId) ?: throw FcpException("Could not load post from “$postId”.") } catch (fspe1: FSParseException) { @@ -71,9 +71,9 @@ abstract class AbstractSoneCommand } @Throws(FcpException::class) - protected fun getReply(simpleFieldSet: SimpleFieldSet, parameterName: String): PostReply { + protected fun SimpleFieldSet.getReply(parameterName: String): PostReply { try { - val replyId = simpleFieldSet.getString(parameterName) + val replyId = getString(parameterName) return core.getPostReply(replyId) ?: throw FcpException("Could not load reply from “$replyId”.") } catch (fspe1: FSParseException) { diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/AsyncFreenetInterface.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/AsyncFreenetInterface.kt new file mode 100644 index 0000000..8219d7e --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/AsyncFreenetInterface.kt @@ -0,0 +1,27 @@ +package net.pterodactylus.sone.freenet + +import freenet.client.* +import freenet.keys.* +import kotlinx.coroutines.* +import net.pterodactylus.sone.core.* + +class AsyncFreenetInterface(private val freenetClient: FreenetClient) { + + suspend fun fetchUri(freenetUri: FreenetURI): Fetched { + var currentUri = freenetUri + var result: FetchResult? = null + while (result == null) { + try { + result = withContext(Dispatchers.Default) { freenetClient.fetch(currentUri) } + } catch (fetchException: FetchException) { + if (fetchException.mode == FetchException.FetchExceptionMode.PERMANENT_REDIRECT) { + currentUri = fetchException.newURI + continue + } else + throw fetchException + } + } + return Fetched(currentUri, result) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/BaseL10nTranslation.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/BaseL10nTranslation.kt new file mode 100644 index 0000000..1b41f70 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/BaseL10nTranslation.kt @@ -0,0 +1,16 @@ +package net.pterodactylus.sone.freenet + +import freenet.l10n.* +import java.util.* + +/** + * [Translation] implementation based on Fred’s [BaseL10n]. + */ +class BaseL10nTranslation(private val baseL10n: BaseL10n) : Translation { + + override val currentLocale: Locale + get() = Locale(baseL10n.selectedLanguage.shortCode) + + override fun translate(key: String): String = baseL10n.getString(key) + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/FreenetClient.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/FreenetClient.kt new file mode 100644 index 0000000..8fbbe57 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/FreenetClient.kt @@ -0,0 +1,20 @@ +package net.pterodactylus.sone.freenet + +import freenet.client.* +import freenet.keys.* + +/** + * Facade for Freenet’s [freenet.client.HighLevelSimpleClient] to allow testing. + */ +interface FreenetClient { + + fun fetch(freenetKey: FreenetURI): FetchResult + +} + +class DefaultFreenetClient(private val highLevelSimpleClient: HighLevelSimpleClient) : FreenetClient { + + override fun fetch(freenetKey: FreenetURI): FetchResult = + highLevelSimpleClient.fetch(freenetKey) + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/FreenetURIs.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/FreenetURIs.kt new file mode 100644 index 0000000..6d40b17 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/FreenetURIs.kt @@ -0,0 +1,6 @@ +package net.pterodactylus.sone.freenet + +import freenet.keys.* +import net.pterodactylus.sone.utils.* + +val FreenetURI.routingKeyString: String get() = routingKey.asFreenetBase64 diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/L10nFilter.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/L10nFilter.kt new file mode 100644 index 0000000..50d240b --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/L10nFilter.kt @@ -0,0 +1,51 @@ +/* + * Sone - L10nFilter.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet + +import net.pterodactylus.util.template.* +import java.text.* + +/** + * [Filter] implementation replaces [String] values with their + * translated equivalents. + */ +class L10nFilter(private val translation: Translation) : Filter { + + override fun format(templateContext: TemplateContext?, data: Any?, parameters: Map?): String { + val parameterValues = getParameters(data, parameters) + val text = getText(data) + return if (parameterValues.isEmpty()) { + translation.translate(text) + } else + MessageFormat(translation.translate(text), translation.currentLocale).format(parameterValues.toTypedArray()) + } + + private fun getText(data: Any?) = (data as? L10nText)?.text ?: data.toString() + + private fun getParameters(data: Any?, parameters: Map?) = + if (data is L10nText) + data.parameters + else + (parameters ?: emptyMap()).let { params -> + generateSequence(0) { it + 1 } + .takeWhile { it.toString() in params } + .map { params[it.toString()] } + .toList() + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/Translation.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/Translation.kt new file mode 100644 index 0000000..d583c49 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/Translation.kt @@ -0,0 +1,38 @@ +/** + * Sone - Translation.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet + +import java.util.* + +/** + * Facade for Fred’s [freenet.l10n.BaseL10n] object. + */ +interface Translation { + + /** The currently selected locale. */ + val currentLocale: Locale + + /** + * Returns the translated string for the given key, defaulting to `""`. + * + * @param key The key to return the translated string for + * @return The translated string, or `""` if there is no translation + */ + fun translate(key: String): String + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/FredPluginConnector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/FredPluginConnector.kt new file mode 100644 index 0000000..98849c4 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/FredPluginConnector.kt @@ -0,0 +1,35 @@ +/* Fred’s plugin stuff is mostly deprecated. ¯\_(ツ)_/¯ */ +@file:Suppress("DEPRECATION") + +package net.pterodactylus.sone.freenet.plugin + +import freenet.pluginmanager.* +import freenet.support.* +import freenet.support.api.* +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.* +import javax.inject.* + +/** + * [PluginConnector] implementation that uses a [PluginRespiratorFacade] and coroutines to send + * a request to another plugin and receive a reply. + */ +class FredPluginConnector @Inject constructor(private val pluginRespiratorFacade: PluginRespiratorFacade) : PluginConnector { + + override suspend fun sendRequest(pluginName: String, fields: SimpleFieldSet, data: Bucket?): PluginReply { + val receivedReply = Channel() + val responseReceiver = FredPluginTalker { _, _, responseFields, responseData -> + GlobalScope.launch { + receivedReply.send(PluginReply(responseFields, responseData)) + } + } + try { + val pluginTalker = pluginRespiratorFacade.getPluginTalker(responseReceiver, pluginName, "") + pluginTalker.send(fields, data) + return receivedReply.receive() + } catch (e: PluginNotFoundException) { + throw PluginException(cause = e) + } + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginConnector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginConnector.kt new file mode 100644 index 0000000..88feb13 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginConnector.kt @@ -0,0 +1,43 @@ +/* + * Sone - PluginConnector.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.plugin + +import freenet.support.* +import freenet.support.api.* + +/** + * Interface for talking to other plugins. Other plugins are identified by their + * name and a unique connection identifier. + */ +interface PluginConnector { + + /** + * Sends a message to another plugin running in the same node. + * + * @param pluginName The fully qualified name of the plugin + * @param fields The message being sent + * @param data Optional data + * @return The reply from the plugin + * @throws PluginException if the plugin identified by [pluginName] does not exist + */ + @Throws(PluginException::class) + suspend fun sendRequest(pluginName: String, fields: SimpleFieldSet, data: Bucket? = null): PluginReply + +} + +data class PluginReply(val fields: SimpleFieldSet, val data: Bucket?) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginException.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginException.kt new file mode 100644 index 0000000..fadd8b5 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginException.kt @@ -0,0 +1,25 @@ +/* + * Sone - PluginException.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.plugin + +import net.pterodactylus.sone.freenet.wot.* + +/** + * Exception that signals an error when communicating with a plugin. + */ +class PluginException(message: String? = null, cause: Throwable? = null) : WebOfTrustException(message, cause) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginRespiratorFacade.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginRespiratorFacade.kt new file mode 100644 index 0000000..e1d5591 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/plugin/PluginRespiratorFacade.kt @@ -0,0 +1,66 @@ +/** + * Sone - PluginRespiratorFacade.kt - Copyright © 2019–2020 David ‘Bombe’ 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 . + */ + +/* Yes, this handle Fred-based stuff that’s mostly deprecated. */ +@file:Suppress("DEPRECATION") + +package net.pterodactylus.sone.freenet.plugin + +import freenet.pluginmanager.* +import freenet.support.* +import freenet.support.api.* +import javax.inject.* + +/** + * Facade for the only method of a [plugin respirator][PluginRespirator] that Sone actually uses, + * for easier testing. + */ +interface PluginRespiratorFacade { + + @Throws(PluginNotFoundException::class) + fun getPluginTalker(pluginTalker: FredPluginTalker, pluginName: String, identifier: String): PluginTalkerFacade + +} + +/** + * Facade for a [plugin talker][PluginTalker], for easier testing. + */ +interface PluginTalkerFacade { + + fun send(pluginParameters: SimpleFieldSet, data: Bucket?) + +} + +/** + * Fred-based [PluginRespiratorFacade] implementation that proxies the given real [PluginRespirator]. + */ +class FredPluginRespiratorFacade @Inject constructor(private val pluginRespirator: PluginRespirator) : PluginRespiratorFacade { + + override fun getPluginTalker(pluginTalker: FredPluginTalker, pluginName: String, identifier: String) = + FredPluginTalkerFacade(pluginRespirator.getPluginTalker(pluginTalker, pluginName, identifier)) + +} + +/** + * Fred-based [PluginTalkerFacade] implementation that proxies the given real [PluginTalker]. + */ +class FredPluginTalkerFacade(private val pluginTalker: PluginTalker) : PluginTalkerFacade { + + override fun send(pluginParameters: SimpleFieldSet, data: Bucket?) = + pluginTalker.send(pluginParameters, data) + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/Context.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/Context.kt new file mode 100644 index 0000000..8b9dda6 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/Context.kt @@ -0,0 +1,24 @@ +/* + * Sone - Context.kt - Copyright © 2014–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +/** + * Custom container for the Web of Trust context. This allows easier + * configuration of dependency injection. + */ +class Context(val context: String) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultIdentity.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultIdentity.kt new file mode 100644 index 0000000..88527b3 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultIdentity.kt @@ -0,0 +1,115 @@ +/* + * Sone - DefaultIdentity.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +import java.util.Collections.synchronizedMap +import java.util.Collections.synchronizedSet +import kotlin.collections.set + +/** + * A Web of Trust identity. + */ +open class DefaultIdentity(private val id: String, private val nickname: String?, private val requestUri: String) : Identity { + + private val contexts = mutableSetOf().synchronized() + private val properties = mutableMapOf().synchronized() + private val trustCache = mutableMapOf().synchronized() + + override fun getId() = id + override fun getNickname() = nickname + override fun getRequestUri() = requestUri + override fun getContexts() = synchronized(contexts) { contexts.toSet() } + + override fun hasContext(context: String) = context in contexts + + override fun setContexts(contexts: Set) { + synchronized(this.contexts) { + this.contexts.clear() + this.contexts.addAll(contexts) + } + } + + override fun addContext(context: String): Identity = apply { + synchronized(this.contexts) { + contexts += context + } + } + + override fun removeContext(context: String): Identity = apply { + synchronized(this.contexts) { + contexts -= context + } + } + + override fun getProperties() = synchronized(properties) { properties.toMap() } + + override fun setProperties(properties: Map) { + synchronized(this.properties) { + this.properties.clear() + this.properties.putAll(properties) + } + } + + override fun getProperty(name: String) = synchronized(properties) { properties[name] } + + override fun setProperty(name: String, value: String): Identity = apply { + synchronized(properties) { + properties[name] = value + } + } + + override fun removeProperty(name: String): Identity = apply { + synchronized(properties) { + properties -= name + } + } + + override fun getTrust(): Map = synchronized(trustCache) { + trustCache.toMap() + } + + override fun getTrust(ownIdentity: OwnIdentity) = synchronized(trustCache) { + trustCache[ownIdentity] + } + + override fun setTrust(ownIdentity: OwnIdentity, trust: Trust) = apply { + synchronized(trustCache) { + trustCache[ownIdentity] = trust + } + } + + override fun removeTrust(ownIdentity: OwnIdentity) = apply { + synchronized(trustCache) { + trustCache -= ownIdentity + } + } + + override fun hashCode() = id.hashCode() + + override fun equals(other: Any?) = if (other !is Identity) { + false + } else { + other.id == getId() + } + + override fun toString() = "${javaClass.simpleName}[id=$id,nickname=$nickname,contexts=$contexts,properties=$properties]" + +} + +private fun Set.synchronized(): MutableSet = synchronizedSet(this) +private fun Map.synchronized(): MutableMap = synchronizedMap(this) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.kt new file mode 100644 index 0000000..59b6c17 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultOwnIdentity.kt @@ -0,0 +1,38 @@ +/* + * Sone - DefaultOwnIdentity.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +/** + * An own identity is an identity that the owner of the node has full control + * over. + */ +class DefaultOwnIdentity(id: String, nickname: String, requestUri: String, private val insertUri: String) : DefaultIdentity(id, nickname, requestUri), OwnIdentity { + + override fun getInsertUri(): String { + return insertUri + } + + override fun addContext(context: String) = super.addContext(context) as OwnIdentity + + override fun removeContext(context: String) = super.removeContext(context) as OwnIdentity + + override fun setProperty(name: String, value: String) = super.setProperty(name, value) as OwnIdentity + + override fun removeProperty(name: String) = super.removeProperty(name) as OwnIdentity + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt new file mode 100644 index 0000000..ffcafb3 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt @@ -0,0 +1,68 @@ +/* + * Sone - IdentityChangeDetector.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +/** + * Detects changes between two lists of [Identity]s. The detector can find + * added and removed identities, and for identities that exist in both list + * their contexts and properties are checked for added, removed, or (in case of + * properties) changed values. + */ +class IdentityChangeDetector(oldIdentities: Collection) { + + private val oldIdentities: Map = oldIdentities.associateBy { it.id } + var onNewIdentity: IdentityProcessor? = null + var onRemovedIdentity: IdentityProcessor? = null + var onChangedIdentity: IdentityProcessor? = null + var onUnchangedIdentity: IdentityProcessor? = null + + fun detectChanges(newIdentities: Collection) { + onRemovedIdentity.notify(oldIdentities.values.filter { it !in newIdentities }) + onNewIdentity.notify(newIdentities.filter { it !in oldIdentities.values }) + onChangedIdentity.notify(newIdentities.filter { it.id in oldIdentities }.filter { identityHasChanged(oldIdentities[it.id]!!, it) }) + onUnchangedIdentity.notify(newIdentities.filter { it.id in oldIdentities }.filterNot { identityHasChanged(oldIdentities[it.id]!!, it) }) + } + + private fun identityHasChanged(oldIdentity: Identity, newIdentity: Identity?) = + identityHasNewContexts(oldIdentity, newIdentity!!) + || identityHasRemovedContexts(oldIdentity, newIdentity) + || identityHasNewProperties(oldIdentity, newIdentity) + || identityHasRemovedProperties(oldIdentity, newIdentity) + || identityHasChangedProperties(oldIdentity, newIdentity) + + private fun identityHasNewContexts(oldIdentity: Identity, newIdentity: Identity) = + newIdentity.contexts.any { it !in oldIdentity.contexts } + + private fun identityHasRemovedContexts(oldIdentity: Identity, newIdentity: Identity) = + oldIdentity.contexts.any { it !in newIdentity.contexts } + + private fun identityHasNewProperties(oldIdentity: Identity, newIdentity: Identity) = + newIdentity.properties.keys.any { it !in oldIdentity.properties } + + private fun identityHasRemovedProperties(oldIdentity: Identity, newIdentity: Identity) = + oldIdentity.properties.keys.any { it !in newIdentity.properties } + + private fun identityHasChangedProperties(oldIdentity: Identity, newIdentity: Identity) = + oldIdentity.properties.entries.any { newIdentity.properties[it.key] != it.value } + +} + +typealias IdentityProcessor = (Identity) -> Unit + +private fun IdentityProcessor?.notify(identities: Iterable) = + this?.let { identities.forEach(this::invoke) } diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeEventSender.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeEventSender.kt new file mode 100644 index 0000000..e6a33af --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeEventSender.kt @@ -0,0 +1,63 @@ +/* + * Sone - IdentityChangeEventSender.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +import com.google.common.eventbus.* +import net.pterodactylus.sone.freenet.wot.event.* + +/** + * Detects changes in [Identity]s trusted by multiple [OwnIdentity]s. + * + * @see IdentityChangeDetector + */ +class IdentityChangeEventSender(private val eventBus: EventBus, private val oldIdentities: Map>) { + + fun detectChanges(identities: Map>) { + val identityChangeDetector = IdentityChangeDetector(oldIdentities.keys) + identityChangeDetector.onNewIdentity = addNewOwnIdentityAndItsTrustedIdentities(identities) + identityChangeDetector.onRemovedIdentity = removeOwnIdentityAndItsTrustedIdentities(oldIdentities) + identityChangeDetector.onUnchangedIdentity = detectChangesInTrustedIdentities(identities, oldIdentities) + identityChangeDetector.detectChanges(identities.keys) + } + + private fun addNewOwnIdentityAndItsTrustedIdentities(newIdentities: Map>) = + { identity: Identity -> + eventBus.post(OwnIdentityAddedEvent(identity as OwnIdentity)) + newIdentities[identity] + ?.map { IdentityAddedEvent(identity, it) } + ?.forEach(eventBus::post) ?: Unit + } + + private fun removeOwnIdentityAndItsTrustedIdentities(oldIdentities: Map>) = + { identity: Identity -> + eventBus.post(OwnIdentityRemovedEvent(identity as OwnIdentity)) + oldIdentities[identity] + ?.map { IdentityRemovedEvent(identity, it) } + ?.forEach(eventBus::post) ?: Unit + } + + private fun detectChangesInTrustedIdentities(newIdentities: Map>, oldIdentities: Map>) = + { ownIdentity: Identity -> + val identityChangeDetector = IdentityChangeDetector(oldIdentities[ownIdentity as OwnIdentity]!!) + identityChangeDetector.onNewIdentity = { eventBus.post(IdentityAddedEvent(ownIdentity, it)) } + identityChangeDetector.onRemovedIdentity = { eventBus.post(IdentityRemovedEvent(ownIdentity, it)) } + identityChangeDetector.onChangedIdentity = { eventBus.post(IdentityUpdatedEvent(ownIdentity, it)) } + identityChangeDetector.detectChanges(newIdentities[ownIdentity]!!) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt new file mode 100644 index 0000000..f6e1d59 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt @@ -0,0 +1,85 @@ +/* + * Sone - IdentityLoader.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +import com.google.common.base.* +import com.google.inject.* +import net.pterodactylus.sone.freenet.plugin.* +import java.util.concurrent.TimeUnit.* +import java.util.logging.* + +/** + * Loads [OwnIdentity]s and the [Identity]s they trust. + */ +class IdentityLoader @Inject constructor(private val webOfTrustConnector: WebOfTrustConnector, private val context: Context? = null) { + + private val logger: Logger = Logger.getLogger(IdentityLoader::class.java.name) + + @Throws(WebOfTrustException::class) + fun loadTrustedIdentities() = + time({ stopwatch, identities -> "Loaded ${identities.size} own identities in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) { + webOfTrustConnector.loadAllOwnIdentities() + }.let(this::loadTrustedIdentitiesForOwnIdentities) + + fun loadAllIdentities() = + time({ stopwatch, identities -> "Loaded ${identities.size} own identities in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) { + webOfTrustConnector.loadAllOwnIdentities() + }.let(this::loadAllIdentitiesForOwnIdentities) + + @Throws(PluginException::class) + private fun loadTrustedIdentitiesForOwnIdentities(ownIdentities: Collection) = + ownIdentities + .also { logger.fine { "Getting trusted identities for ${it.size} own identities..." } } + .associateWith { ownIdentity -> + logger.fine { "Getting trusted identities for $ownIdentity..." } + if (ownIdentity.doesNotHaveCorrectContext()) { + logger.fine { "Skipping $ownIdentity because of incorrect context." } + emptySet() + } else { + logger.fine { "Loading trusted identities for $ownIdentity from WoT..." } + time({ stopwatch, identities -> "Loaded ${identities.size} identities for ${ownIdentity.nickname} in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) { + webOfTrustConnector.loadTrustedIdentities(ownIdentity, context?.context) + } + } + } + + private fun loadAllIdentitiesForOwnIdentities(ownIdentities: Collection) = + ownIdentities + .also { logger.fine { "Getting trusted identities for ${it.size} own identities..." } } + .associateWith { ownIdentity -> + logger.fine { "Getting trusted identities for $ownIdentity..." } + if (ownIdentity.doesNotHaveCorrectContext()) { + logger.fine { "Skipping $ownIdentity because of incorrect context." } + emptySet() + } else { + logger.fine { "Loading trusted identities for $ownIdentity from WoT..." } + time({ stopwatch, identities -> "Loaded ${identities.size} identities for ${ownIdentity.nickname} in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) { + webOfTrustConnector.loadAllIdentities(ownIdentity, context?.context) + } + } + } + + private fun OwnIdentity.doesNotHaveCorrectContext() = + context?.let { it.context !in contexts } ?: false + + private fun time(logMessage: (Stopwatch, R) -> String, loader: () -> R) = + Stopwatch.createStarted().let { stopwatch -> + loader().also { logger.fine(logMessage(stopwatch, it)) } + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManager.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManager.kt new file mode 100644 index 0000000..e90af91 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManager.kt @@ -0,0 +1,18 @@ +package net.pterodactylus.sone.freenet.wot + +import net.pterodactylus.util.service.Service + +import com.google.common.eventbus.EventBus +import com.google.inject.ImplementedBy + +/** + * Connects to a [WebOfTrustConnector] and sends identity events to an + * [EventBus]. + */ +@ImplementedBy(IdentityManagerImpl::class) +interface IdentityManager : Service { + + val isConnected: Boolean + val allOwnIdentities: Set + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt new file mode 100644 index 0000000..829affc --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -0,0 +1,137 @@ +/* + * Sone - IdentityManagerImpl.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +import com.google.common.eventbus.EventBus +import com.google.common.eventbus.Subscribe +import com.google.inject.Inject +import com.google.inject.Singleton +import net.pterodactylus.sone.core.event.StrictFilteringActivatedEvent +import net.pterodactylus.sone.core.event.StrictFilteringDeactivatedEvent +import net.pterodactylus.util.service.AbstractService +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.atomic.AtomicBoolean +import java.util.logging.Level +import java.util.logging.Logger +import java.util.logging.Logger.getLogger + +/** + * The identity manager takes care of loading and storing identities, their + * contexts, and properties. It does so in a way that does not expose errors via + * exceptions but it only logs them and tries to return sensible defaults. + * + * + * It is also responsible for polling identities from the Web of Trust plugin + * and sending events to the [EventBus] when [Identity]s and + * [OwnIdentity]s are discovered or disappearing. + */ +@Singleton +class IdentityManagerImpl @Inject constructor( + private val eventBus: EventBus, + private val webOfTrustConnector: WebOfTrustConnector, + private val identityLoader: IdentityLoader +) : AbstractService("Sone Identity Manager", false), IdentityManager { + + private val currentOwnIdentities = mutableSetOf() + private val strictFiltering = AtomicBoolean(false) + + override val isConnected: Boolean + get() = notThrowing { webOfTrustConnector.ping() } + + override val allOwnIdentities: Set + get() = synchronized(currentOwnIdentities) { + currentOwnIdentities.toSet() + } + + override fun serviceRun() { + var oldIdentities = mapOf>() + + while (!shouldStop()) { + try { + val currentIdentities = identityLoader.loadAllIdentities().applyStrictFiltering() + + val identityChangeEventSender = IdentityChangeEventSender(eventBus, oldIdentities) + identityChangeEventSender.detectChanges(currentIdentities) + + oldIdentities = currentIdentities + + synchronized(currentOwnIdentities) { + currentOwnIdentities.clear() + currentOwnIdentities.addAll(currentIdentities.keys) + } + } catch (wote1: WebOfTrustException) { + logger.log(Level.WARNING, "WoT has disappeared!", wote1) + } catch (e: Exception) { + logger.log(Level.SEVERE, "Uncaught exception in IdentityManager thread!", e) + } + + /* wait a minute before checking again. */ + sleep(SECONDS.toMillis(60)) + } + } + + private fun Map>.applyStrictFiltering() = + if (strictFiltering.get()) { + val identitiesWithTrust = values.flatten() + .groupBy { it.id } + .mapValues { (_, identities) -> + identities.reduce { accIdentity, identity -> + identity.trust.forEach { (ownIdentity: OwnIdentity?, trust: Trust?) -> + accIdentity.setTrust(ownIdentity, trust) + } + accIdentity + } + } + + mapValues { (_, trustedIdentities) -> + trustedIdentities.filter { trustedIdentity -> + identitiesWithTrust[trustedIdentity.id]!!.trust.all { it.value.hasZeroOrPositiveTrust() } + } + } + } else { + this + } + + @Subscribe + fun strictFilteringActivated(event: StrictFilteringActivatedEvent) { + strictFiltering.set(true) + } + + @Subscribe + fun strictFilteringDeactivated(event: StrictFilteringDeactivatedEvent) { + strictFiltering.set(false) + } + +} + +private val logger: Logger = getLogger(IdentityManagerImpl::class.java.name) + +private fun notThrowing(action: () -> Unit): Boolean = + try { + action() + true + } catch (e: Exception) { + false + } + +private fun Trust.hasZeroOrPositiveTrust() = + if (explicit == null) { + implicit == null || implicit >= 0 + } else { + explicit >= 0 + } diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/PluginWebOfTrustConnector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/PluginWebOfTrustConnector.kt new file mode 100644 index 0000000..d829734 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/PluginWebOfTrustConnector.kt @@ -0,0 +1,131 @@ +/* + * Sone - PluginWebOfTrustConnector.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +import com.google.inject.Inject +import freenet.support.SimpleFieldSet +import kotlinx.coroutines.runBlocking +import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder +import net.pterodactylus.sone.freenet.plugin.PluginConnector +import net.pterodactylus.sone.freenet.plugin.PluginException +import net.pterodactylus.sone.freenet.plugin.PluginReply +import java.lang.String.format +import java.util.logging.Level +import java.util.logging.Logger +import java.util.logging.Logger.getLogger + +/** + * Connector for the Web of Trust plugin. + */ +class PluginWebOfTrustConnector @Inject constructor(private val pluginConnector: PluginConnector) : WebOfTrustConnector { + + private val logger: Logger = getLogger(PluginWebOfTrustConnector::class.java.name) + + @Throws(PluginException::class) + override fun loadAllOwnIdentities(): Set = + performRequest(SimpleFieldSetBuilder().put("Message", "GetOwnIdentities").get()) + .fields + .parseIdentities { parseOwnIdentity(it) } + + @Throws(PluginException::class) + override fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String?): Set = + performRequest(SimpleFieldSetBuilder().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.id).put("Selection", "+").put("Context", context ?: "").put("WantTrustValues", "true").get()) + .fields + .parseIdentities { parseTrustedIdentity(it, ownIdentity) } + + override fun loadAllIdentities(ownIdentity: OwnIdentity, context: String?): Set = + performRequest(SimpleFieldSetBuilder().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.id).put("Selection", "+").put("Context", context ?: "").put("WantTrustValues", "true").get()) + .fields + .parseIdentities { parseTrustedIdentity(it, ownIdentity) } + + performRequest(SimpleFieldSetBuilder().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.id).put("Selection", "-").put("Context", context ?: "").put("WantTrustValues", "true").get()) + .fields + .parseIdentities { parseTrustedIdentity(it, ownIdentity) } + + @Throws(PluginException::class) + override fun addContext(ownIdentity: OwnIdentity, context: String) { + performRequest(SimpleFieldSetBuilder().put("Message", "AddContext").put("Identity", ownIdentity.id).put("Context", context).get()) + } + + @Throws(PluginException::class) + override fun removeContext(ownIdentity: OwnIdentity, context: String) { + performRequest(SimpleFieldSetBuilder().put("Message", "RemoveContext").put("Identity", ownIdentity.id).put("Context", context).get()) + } + + override fun setProperty(ownIdentity: OwnIdentity, name: String, value: String) { + performRequest(SimpleFieldSetBuilder().put("Message", "SetProperty").put("Identity", ownIdentity.id).put("Property", name).put("Value", value).get()) + } + + override fun removeProperty(ownIdentity: OwnIdentity, name: String) { + performRequest(SimpleFieldSetBuilder().put("Message", "RemoveProperty").put("Identity", ownIdentity.id).put("Property", name).get()) + } + + override fun ping() { + performRequest(SimpleFieldSetBuilder().put("Message", "Ping").get()) + } + + private fun performRequest(fields: SimpleFieldSet): PluginReply { + logger.log(Level.FINE, format("Sending FCP Request: %s", fields.get("Message"))) + return runBlocking { + pluginConnector.sendRequest(WOT_PLUGIN_NAME, fields).also { + logger.log(Level.FINEST, format("Received FCP Response for %s: %s", fields.get("Message"), it.fields.get("Message"))) + if ("Error" == it.fields.get("Message")) { + throw PluginException("Could not perform request for " + fields.get("Message")) + } + } + } + } + +} + +private const val WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust" + +private fun SimpleFieldSet.parseIdentities(parser: SimpleFieldSet.(Int) -> I) = + scanPrefix { "Identity$it" } + .map { parser(this, it) } + .toSet() + +private fun SimpleFieldSet.parseOwnIdentity(index: Int) = + DefaultOwnIdentity(get("Identity$index"), get("Nickname$index"), get("RequestURI$index"), get("InsertURI$index")) + .setContextsAndProperties(this@parseOwnIdentity, index) + +private fun SimpleFieldSet.parseTrustedIdentity(index: Int, ownIdentity: OwnIdentity) = + DefaultIdentity(get("Identity$index"), get("Nickname$index"), get("RequestURI$index")) + .setContextsAndProperties(this@parseTrustedIdentity, index) + .apply { setTrust(ownIdentity, this@parseTrustedIdentity.parseTrust(index.toString())) } + +private fun I.setContextsAndProperties(simpleFieldSet: SimpleFieldSet, index: Int) = apply { + contexts = simpleFieldSet.contexts("Contexts$index.") + properties = simpleFieldSet.properties("Properties$index.") +} + +private fun SimpleFieldSet.parseTrust(index: String = "") = + Trust(get("Trust$index")?.toIntOrNull(), get("Score$index")?.toIntOrNull(), get("Rank$index")?.toIntOrNull()) + +private fun SimpleFieldSet.contexts(prefix: String) = + scanPrefix { "${prefix}Context$it" } + .map { get("${prefix}Context$it") } + .toSet() + +private fun SimpleFieldSet.properties(prefix: String) = + scanPrefix { "${prefix}Property${it}.Name" } + .map { get("${prefix}Property${it}.Name") to get("${prefix}Property${it}.Value") } + .toMap() + +private fun SimpleFieldSet.scanPrefix(prefix: (Int) -> String) = + generateSequence(0, Int::inc) + .takeWhile { get(prefix(it)) != null } diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/Trust.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/Trust.kt new file mode 100644 index 0000000..779dd20 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/Trust.kt @@ -0,0 +1,23 @@ +/* + * Sone - Trust.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +/** + * Container class for trust in the web of trust. + */ +data class Trust(val explicit: Int?, val implicit: Int?, val distance: Int?) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.kt new file mode 100644 index 0000000..a407e2a --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.kt @@ -0,0 +1,96 @@ +package net.pterodactylus.sone.freenet.wot + +import net.pterodactylus.sone.freenet.plugin.* + +/** + * Connector for the web of trust plugin. + */ +interface WebOfTrustConnector { + + /** + * Loads all own identities from the Web of Trust plugin. + * + * @return All own identity + * @throws WebOfTrustException if the own identities can not be loaded + */ + @Throws(WebOfTrustException::class) + fun loadAllOwnIdentities(): Set + + /** + * Loads all identities that the given identities trusts with a score of + * more than 0 and the (optional) given context. + * + * @param ownIdentity The own identity + * @param context The context to filter, or `null` + * @return All trusted identities + * @throws PluginException if an error occured talking to the Web of Trust plugin + */ + @Throws(PluginException::class) + fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String? = null): Set + + /** + * Loads all identities known to the given own identity that have the (optional) given context. + * + * @param ownIdentity The own identity + * @param context The context to filter, or `null` + * @return All trusted identities + * @throws PluginException if an error occured talking to the Web of Trust plugin + */ + fun loadAllIdentities(ownIdentity: OwnIdentity, context: String? = null): Set + + /** + * Adds the given context to the given identity. + * + * @param ownIdentity The identity to add the context to + * @param context The context to add + * @throws PluginException if an error occured talking to the Web of Trust plugin + */ + @Throws(PluginException::class) + fun addContext(ownIdentity: OwnIdentity, context: String) + + /** + * Removes the given context from the given identity. + * + * @param ownIdentity The identity to remove the context from + * @param context The context to remove + * @throws PluginException if an error occured talking to the Web of Trust plugin + */ + @Throws(PluginException::class) + fun removeContext(ownIdentity: OwnIdentity, context: String) + + /** + * Sets the property with the given name to the given value. + * + * @param ownIdentity The identity to set the property on + * @param name The name of the property to set + * @param value The value to set + * @throws PluginException if an error occured talking to the Web of Trust plugin + */ + @Throws(PluginException::class) + fun setProperty(ownIdentity: OwnIdentity, name: String, value: String) + + /** + * Removes the property with the given name. + * + * @param ownIdentity The identity to remove the property from + * @param name The name of the property to remove + * @throws PluginException if an error occured talking to the Web of Trust plugin + */ + @Throws(PluginException::class) + fun removeProperty(ownIdentity: OwnIdentity, name: String) + + /** + * Pings the Web of Trust plugin. If the plugin can not be reached, a + * [PluginException] is thrown. + * + * @throws PluginException if the plugin is not loaded + */ + @Throws(PluginException::class) + fun ping() + + /** + * Stops the web of trust connector. + */ + fun stop() = Unit + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustException.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustException.kt new file mode 100644 index 0000000..7d730e9 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustException.kt @@ -0,0 +1,24 @@ +/* + * Sone - WebOfTrustException.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +/** + * Exception that signals an error processing web of trust identities, mostly + * when communicating with the web of trust plugin. + */ +open class WebOfTrustException(message: String? = null, cause: Throwable?) : Exception(message, cause) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustPinger.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustPinger.kt new file mode 100644 index 0000000..db9809e --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustPinger.kt @@ -0,0 +1,56 @@ +/** + * Sone - WebOfTrustPinger.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.freenet.plugin.* +import net.pterodactylus.sone.utils.* +import java.util.concurrent.atomic.* +import java.util.function.* +import javax.inject.* + +/** + * [Runnable] that is scheduled via an [Executor][java.util.concurrent.Executor], + * checks whether the web of trust plugin can be communicated with, sends + * events if its status changes and reschedules itself. + */ +class WebOfTrustPinger @Inject constructor( + private val eventBus: EventBus, + @Named("webOfTrustReacher") private val webOfTrustReacher: Runnable, + @Named("webOfTrustReschedule") private val reschedule: Consumer) : Runnable { + + private val lastState = AtomicBoolean(false) + + override fun run() { + try { + webOfTrustReacher() + if (!lastState.get()) { + eventBus.post(WebOfTrustAppeared()) + lastState.set(true) + } + } catch (e: PluginException) { + if (lastState.get()) { + eventBus.post(WebOfTrustDisappeared()) + lastState.set(false) + } + } + reschedule(this) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.kt new file mode 100644 index 0000000..d316841 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityAddedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - IdentityAddedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot.event + +import net.pterodactylus.sone.freenet.wot.* + +/** + * Event that signals that an [Identity] was added. + */ +data class IdentityAddedEvent(val ownIdentity: OwnIdentity, val identity: Identity) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.kt new file mode 100644 index 0000000..72c262c --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityRemovedEvent.kt @@ -0,0 +1,26 @@ +/* + * Sone - IdentityRemovedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot.event + +import net.pterodactylus.sone.freenet.wot.Identity +import net.pterodactylus.sone.freenet.wot.OwnIdentity + +/** + * Event that signals that an [Identity] was removed. + */ +data class IdentityRemovedEvent(val ownIdentity: OwnIdentity, val identity: Identity) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.kt new file mode 100644 index 0000000..edf34c1 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/IdentityUpdatedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - IdentityUpdatedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot.event + +import net.pterodactylus.sone.freenet.wot.* + +/** + * Event that signals that an [Identity] was updated. + */ +data class IdentityUpdatedEvent(val ownIdentity: OwnIdentity, val identity: Identity) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.kt new file mode 100644 index 0000000..3620237 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/OwnIdentityAddedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - OwnIdentityAddedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot.event + +import net.pterodactylus.sone.freenet.wot.OwnIdentity + +/** + * Event that signals that an [OwnIdentity] was added. + */ +data class OwnIdentityAddedEvent(val ownIdentity: OwnIdentity) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.kt new file mode 100644 index 0000000..3f6fdfc --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/event/OwnIdentityRemovedEvent.kt @@ -0,0 +1,25 @@ +/* + * Sone - OwnIdentityRemovedEvent.kt - Copyright © 2013–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.freenet.wot.event + +import net.pterodactylus.sone.freenet.wot.* + +/** + * Event that signals that an [OwnIdentity] was removed. + */ +data class OwnIdentityRemovedEvent(val ownIdentity: OwnIdentity) diff --git a/src/main/kotlin/net/pterodactylus/sone/main/FreenetModule.kt b/src/main/kotlin/net/pterodactylus/sone/main/FreenetModule.kt index 9b5fa2e..1438c2a 100644 --- a/src/main/kotlin/net/pterodactylus/sone/main/FreenetModule.kt +++ b/src/main/kotlin/net/pterodactylus/sone/main/FreenetModule.kt @@ -5,6 +5,7 @@ import freenet.client.* import freenet.clients.http.* import freenet.node.* import freenet.pluginmanager.* +import net.pterodactylus.sone.freenet.plugin.* import javax.inject.Provider import javax.inject.Singleton @@ -14,8 +15,9 @@ import javax.inject.Singleton class FreenetModule(private val pluginRespirator: PluginRespirator) : Module { override fun configure(binder: Binder): Unit = binder.run { - bind(PluginRespirator::class.java).toProvider(Provider { pluginRespirator }) - pluginRespirator.node!!.let { node -> bind(Node::class.java).toProvider(Provider { node }) } + bind(PluginRespiratorFacade::class.java).toProvider(Provider { FredPluginRespiratorFacade(pluginRespirator) }).`in`(Singleton::class.java) + bind(PluginConnector::class.java).to(FredPluginConnector::class.java).`in`(Singleton::class.java) + bind(Node::class.java).toProvider(Provider { pluginRespirator.node }) bind(HighLevelSimpleClient::class.java).toProvider(Provider { pluginRespirator.hlSimpleClient!! }) bind(ToadletContainer::class.java).toProvider(Provider { pluginRespirator.toadletContainer }) bind(PageMaker::class.java).toProvider(Provider { pluginRespirator.pageMaker }) diff --git a/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt b/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt new file mode 100644 index 0000000..a95cf78 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt @@ -0,0 +1,90 @@ +package net.pterodactylus.sone.main + +import com.codahale.metrics.* +import com.google.common.base.* +import com.google.common.eventbus.* +import com.google.inject.* +import com.google.inject.matcher.* +import com.google.inject.name.Names.* +import com.google.inject.spi.* +import net.pterodactylus.sone.core.SoneUriCreator +import net.pterodactylus.sone.database.* +import net.pterodactylus.sone.database.memory.* +import net.pterodactylus.sone.freenet.* +import net.pterodactylus.sone.freenet.wot.* +import net.pterodactylus.sone.web.FreenetSessionProvider +import net.pterodactylus.sone.web.SessionProvider +import net.pterodactylus.util.config.* +import net.pterodactylus.util.config.ConfigurationException +import net.pterodactylus.util.logging.* +import net.pterodactylus.util.version.Version +import java.io.* +import java.util.concurrent.* +import java.util.concurrent.Executors.* +import java.util.logging.* +import javax.inject.* +import javax.inject.Singleton + +open class SoneModule(private val sonePlugin: SonePlugin, private val eventBus: EventBus) : AbstractModule() { + + override fun configure() { + val sonePropertiesFile = File("sone.properties") + val firstStart = !sonePropertiesFile.exists() + var newConfig = false + val configuration = try { + Configuration(MapConfigurationBackend(sonePropertiesFile, false)) + } catch (ce: ConfigurationException) { + sonePropertiesFile.delete() + newConfig = true + Configuration(MapConfigurationBackend(sonePropertiesFile, true)) + } + val context = Context("Sone") + val loaders = configuration.getStringValue("Developer.LoadFromFilesystem") + .getValue(null) + ?.let { + configuration.getStringValue("Developer.FilesystemPath") + .getValue(null) + ?.let { DebugLoaders(it) } + } + + bind(Configuration::class.java).toInstance(configuration) + bind(EventBus::class.java).toInstance(eventBus) + bind(Boolean::class.java).annotatedWith(named("FirstStart")).toInstance(firstStart) + bind(Boolean::class.java).annotatedWith(named("NewConfig")).toInstance(newConfig) + bind(Context::class.java).toInstance(context) + bind(object : TypeLiteral>() {}).toInstance(Optional.of(context)) + bind(SonePlugin::class.java).toInstance(sonePlugin) + bind(Version::class.java).toInstance(sonePlugin.version.drop(1).parseVersion()) + bind(PluginVersion::class.java).toInstance(PluginVersion(sonePlugin.version)) + bind(PluginYear::class.java).toInstance(PluginYear(sonePlugin.year)) + bind(PluginHomepage::class.java).toInstance(PluginHomepage(sonePlugin.homepage)) + bind(Database::class.java).to(MemoryDatabase::class.java).`in`(Singleton::class.java) + bind(Translation::class.java).toInstance(BaseL10nTranslation(sonePlugin.l10n().base)) + loaders?.let { bind(Loaders::class.java).toInstance(it) } + bind(MetricRegistry::class.java).`in`(Singleton::class.java) + bind(WebOfTrustConnector::class.java).to(PluginWebOfTrustConnector::class.java).`in`(Singleton::class.java) + bind(TickerShutdown::class.java).`in`(Singleton::class.java) + bind(SoneUriCreator::class.java).`in`(Singleton::class.java) + bind(SessionProvider::class.java).to(FreenetSessionProvider::class.java).`in`(Singleton::class.java) + + bindListener(Matchers.any(), object : TypeListener { + override fun hear(typeLiteral: TypeLiteral, typeEncounter: TypeEncounter) { + typeEncounter.register(InjectionListener { injectee -> + logger.fine { "Injecting $injectee..." } + eventBus.register(injectee) + }) + } + }) + } + + @Provides + @Singleton + @Named("notification") + fun getNotificationTicker(): ScheduledExecutorService = + newSingleThreadScheduledExecutor() + + private val logger: Logger = Logging.getLogger(javaClass) + +} + +private fun String.parseVersion(): Version = Version.parse(this) diff --git a/src/main/kotlin/net/pterodactylus/sone/main/SoneModuleCreator.kt b/src/main/kotlin/net/pterodactylus/sone/main/SoneModuleCreator.kt deleted file mode 100644 index 58c8d9e..0000000 --- a/src/main/kotlin/net/pterodactylus/sone/main/SoneModuleCreator.kt +++ /dev/null @@ -1,65 +0,0 @@ -package net.pterodactylus.sone.main - -import com.google.common.base.* -import com.google.common.eventbus.* -import com.google.inject.* -import com.google.inject.matcher.* -import com.google.inject.name.Names.* -import com.google.inject.spi.* -import net.pterodactylus.sone.database.* -import net.pterodactylus.sone.database.memory.* -import net.pterodactylus.sone.freenet.wot.* -import net.pterodactylus.util.config.* -import net.pterodactylus.util.config.ConfigurationException -import net.pterodactylus.util.version.Version -import java.io.* - -class SoneModuleCreator { - - fun createModule(sonePlugin: SonePlugin) = object : AbstractModule() { - override fun configure() { - val sonePropertiesFile = File("sone.properties") - val firstStart = !sonePropertiesFile.exists() - var newConfig = false - val configuration = try { - Configuration(MapConfigurationBackend(sonePropertiesFile, false)) - } catch (ce: ConfigurationException) { - sonePropertiesFile.delete() - newConfig = true - Configuration(MapConfigurationBackend(sonePropertiesFile, true)) - } - val context = Context("Sone") - val loaders = configuration.getStringValue("Developer.LoadFromFilesystem") - .getValue(null) - ?.let { - configuration.getStringValue("Developer.FilesystemPath") - .getValue(null) - ?.let { DebugLoaders(it) } - } - val eventBus = EventBus() - - bind(Configuration::class.java).toInstance(configuration) - bind(EventBus::class.java).toInstance(eventBus) - bind(Boolean::class.java).annotatedWith(named("FirstStart")).toInstance(firstStart) - bind(Boolean::class.java).annotatedWith(named("NewConfig")).toInstance(newConfig) - bind(Context::class.java).toInstance(context) - bind(object : TypeLiteral>() {}).toInstance(Optional.of(context)) - bind(SonePlugin::class.java).toInstance(sonePlugin) - bind(Version::class.java).toInstance(sonePlugin.version.parseVersion()) - bind(PluginVersion::class.java).toInstance(PluginVersion(sonePlugin.version)) - bind(PluginYear::class.java).toInstance(PluginYear(sonePlugin.year)) - bind(PluginHomepage::class.java).toInstance(PluginHomepage(sonePlugin.homepage)) - bind(Database::class.java).to(MemoryDatabase::class.java).`in`(Singleton::class.java) - loaders?.let { bind(Loaders::class.java).toInstance(it) } - - bindListener(Matchers.any(), object : TypeListener { - override fun hear(typeLiteral: TypeLiteral, typeEncounter: TypeEncounter) { - typeEncounter.register(InjectionListener { injectee -> eventBus.register(injectee) }) - } - }) - } - } - -} - -private fun String.parseVersion(): Version = Version.parse(this) diff --git a/src/main/kotlin/net/pterodactylus/sone/main/SonePlugin.kt b/src/main/kotlin/net/pterodactylus/sone/main/SonePlugin.kt new file mode 100644 index 0000000..5e0b2c1 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/main/SonePlugin.kt @@ -0,0 +1,7 @@ +package net.pterodactylus.sone.main + +data class PluginVersion(val version: String) + +data class PluginYear(val year: Int) + +data class PluginHomepage(val homepage: String) diff --git a/src/main/kotlin/net/pterodactylus/sone/main/TickerShutdown.kt b/src/main/kotlin/net/pterodactylus/sone/main/TickerShutdown.kt new file mode 100644 index 0000000..de15cbe --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/main/TickerShutdown.kt @@ -0,0 +1,36 @@ +/** + * Sone - TickerShutdown.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.main + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import java.util.concurrent.* +import javax.inject.* + +/** + * Wrapper around all [tickers][ScheduledExecutorService] used in Sone, + * ensuring proper shutdown. + */ +class TickerShutdown @Inject constructor(@Named("notification") private val notificationTicker: ScheduledExecutorService) { + + @Subscribe + fun shutdown(@Suppress("UNUSED_PARAMETER") shutdown: Shutdown) { + notificationTicker.shutdown() + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/notify/ListNotification.kt b/src/main/kotlin/net/pterodactylus/sone/notify/ListNotification.kt new file mode 100644 index 0000000..60d7093 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/notify/ListNotification.kt @@ -0,0 +1,95 @@ +/* + * Sone - ListNotification.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.notify + +import net.pterodactylus.util.notify.* +import net.pterodactylus.util.template.* +import java.lang.System.* +import java.util.concurrent.* + +/** + * Notification that maintains a list of elements. + * + * @param + * The type of the items + */ +class ListNotification : TemplateNotification { + + private val key: String + private val realElements = CopyOnWriteArrayList() + + val elements: List get() = realElements.toList() + + val isEmpty + get() = elements.isEmpty() + + @JvmOverloads + constructor(id: String, key: String, template: Template, dismissable: Boolean = true) : super(id, currentTimeMillis(), currentTimeMillis(), dismissable, template) { + this.key = key + template.initialContext.set(key, realElements) + } + + constructor(listNotification: ListNotification) : super(listNotification.id, listNotification.createdTime, listNotification.lastUpdatedTime, listNotification.isDismissable, Template()) { + this.key = listNotification.key + template.add(listNotification.template) + template.initialContext.set(key, realElements) + } + + fun setElements(elements: Collection) { + realElements.clear() + realElements.addAll(elements.distinct()) + touch() + } + + fun add(element: T) { + if (element !in realElements) { + realElements.add(element) + touch() + } + } + + fun remove(element: T) { + while (realElements.remove(element)) { + /* do nothing, just remove all instances of the element. */ + } + if (realElements.isEmpty()) { + dismiss() + } + touch() + } + + override fun dismiss() { + super.dismiss() + realElements.clear() + } + + override fun hashCode() = + realElements.fold(super.hashCode()) { hash, element -> hash xor element.hashCode() } + + override fun equals(other: Any?): Boolean { + if (other !is ListNotification<*>) { + return false + } + val listNotification = other as ListNotification<*>? + if (!super.equals(listNotification)) { + return false + } + return (key == listNotification.key) && (realElements == listNotification.realElements) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/notify/Notifications.kt b/src/main/kotlin/net/pterodactylus/sone/notify/Notifications.kt new file mode 100644 index 0000000..5df9484 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/notify/Notifications.kt @@ -0,0 +1,32 @@ +/** + * Sone - Notifications.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.notify + +import net.pterodactylus.util.notify.* + +/** + * Returns whether the notification manager contains a notification with the given ID. + */ +operator fun NotificationManager.contains(id: String) = + getNotification(id) != null + +/** + * Returns whether the notification manager currently has a “first start” notification. + */ +fun NotificationManager.hasFirstStartNotification() = + "first-start-notification" in this diff --git a/src/main/kotlin/net/pterodactylus/sone/template/DurationFormatFilter.kt b/src/main/kotlin/net/pterodactylus/sone/template/DurationFormatFilter.kt new file mode 100644 index 0000000..db49ef1 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/template/DurationFormatFilter.kt @@ -0,0 +1,67 @@ +package net.pterodactylus.sone.template + +import net.pterodactylus.util.template.* +import java.time.* + +class DurationFormatFilter : Filter { + + override fun format(templateContext: TemplateContext?, data: Any?, parameters: Map?): Any? { + if (data is Number) { + val scale = parameters?.get("scale") + val duration = when (scale) { + "ms" -> Duration.ofSeconds(data.toLong() / 1_000, (data.toDouble() * 1_000_000 % 1_000_000_000).toLong()) + "μs" -> Duration.ofSeconds(data.toLong() / 1_000_000, (data.toDouble() * 1_000 % 1_000_000_000).toLong()) + "ns" -> Duration.ofSeconds(data.toLong() / 1_000_000_000, data.toLong() % 1_000_000_000) + else -> Duration.ofSeconds(data.toLong(), (data.toDouble() * 1_000_000_000 % 1_000_000_000).toLong()) + } + return FixedDuration.values() + .map { it to it.number(duration) } + .firstOrNull { it.second >= 1 } + ?.let { "${"%.1f".format(it.second)}${it.first.symbol}" } + ?: "0s" + } + return data + } + +} + +@Suppress("unused") +private enum class FixedDuration { + + WEEKS { + override fun number(duration: Duration) = DAYS.number(duration) / 7.0 + override val symbol = "w" + }, + DAYS { + override fun number(duration: Duration) = HOURS.number(duration) / 24 + override val symbol = "d" + }, + HOURS { + override fun number(duration: Duration) = MINUTES.number(duration) / 60 + override val symbol = "h" + }, + MINUTES { + override fun number(duration: Duration) = SECONDS.number(duration) / 60 + override val symbol = "m" + }, + SECONDS { + override fun number(duration: Duration) = duration.seconds + duration.nano / 1_000_000_000.0 + override val symbol = "s" + }, + MILLIS { + override fun number(duration: Duration) = duration.nano / 1_000_000.0 + override val symbol = "ms" + }, + MICROS { + override fun number(duration: Duration) = duration.nano / 1_000.0 + override val symbol = "μs" + }, + NANOS { + override fun number(duration: Duration) = duration.nano.toDouble() + override val symbol = "ns" + }; + + abstract fun number(duration: Duration): Double + abstract val symbol: String + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/template/HistogramRenderer.kt b/src/main/kotlin/net/pterodactylus/sone/template/HistogramRenderer.kt new file mode 100644 index 0000000..dba32b2 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/template/HistogramRenderer.kt @@ -0,0 +1,46 @@ +package net.pterodactylus.sone.template + +import com.codahale.metrics.* +import net.pterodactylus.sone.utils.* +import net.pterodactylus.util.template.* + +/** + * [Filter] that renders a [Histogram] as a table row. + */ +class HistogramRenderer : Filter { + + override fun format(templateContext: TemplateContext, data: Any?, parameters: Map?): Any? { + templateContext["metricName"] = (parameters?.get("name") as String?)?.dotToCamel()?.let { "Page.Metrics.$it.Title" } + (data as? Histogram)?.snapshot?.run { + templateContext["count"] = data.count + templateContext["min"] = min + templateContext["max"] = max + templateContext["mean"] = mean + templateContext["median"] = median + templateContext["percentile75"] = get75thPercentile() + templateContext["percentile95"] = get95thPercentile() + templateContext["percentile98"] = get98thPercentile() + templateContext["percentile99"] = get99thPercentile() + templateContext["percentile999"] = get999thPercentile() + } + return template.render(templateContext) + } + +} + +private val template = """ + <% metricName|l10n|html> + <% count|html> + <% min|duration scale=='μs'|html> + <% max|duration scale=='μs'|html> + <% mean|duration scale=='μs'|html> + <% median|duration scale=='μs'|html> + <% percentile75|duration scale=='μs'|html> + <% percentile95|duration scale=='μs'|html> + <% percentile98|duration scale=='μs'|html> + <% percentile99|duration scale=='μs'|html> + <% percentile999|duration scale=='μs'|html> +""".asTemplate() + +private fun String.dotToCamel() = + split(".").joinToString("", transform = String::capitalize) diff --git a/src/main/kotlin/net/pterodactylus/sone/template/PostAccessor.kt b/src/main/kotlin/net/pterodactylus/sone/template/PostAccessor.kt new file mode 100644 index 0000000..42239aa --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/template/PostAccessor.kt @@ -0,0 +1,55 @@ +/* + * Sone - PostAccessor.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.template + +import net.pterodactylus.sone.core.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.utils.* +import net.pterodactylus.util.template.* + +/** + * Accessor for [Post] objects that adds additional properties: + * + * * `replies`: All replies to this post, sorted by time, oldest first + * * `likes`: All Sones that have liked the post + * * `liked`: `true` if the current Sone from the [template context][TemplateContext] has liked the post + * * `new`: `true` if the post is not known + * * `bookmarked`: `true` if the post is bookmarked + */ +class PostAccessor(private val core: Core) : ReflectionAccessor() { + + override fun get(templateContext: TemplateContext?, `object`: Any?, member: String): Any? = + (`object` as Post).let { post -> + when (member) { + "replies" -> core.getReplies(post) + "likes" -> core.getLikes(post) + "liked" -> templateContext.currentSone?.isLikedPostId(post.id) ?: false + "new" -> !post.isKnown + "bookmarked" -> core.isBookmarked(post) + "replySone" -> core.getReplies(post).lastOrNull { it.sone.isLocal }?.sone + ?: post.recipient.let { it.takeIf { it.isLocal } } + ?: post.sone.takeIf { it.isLocal } + ?: templateContext.currentSone + else -> super.get(templateContext, `object`, member) + } + } + +} + +private fun Core.getReplies(post: Post) = getReplies(post.id).filter(noFutureReply) +private val TemplateContext?.currentSone: Sone? get() = this?.get("currentSone") as? Sone diff --git a/src/main/kotlin/net/pterodactylus/sone/text/SoneMentionDetector.kt b/src/main/kotlin/net/pterodactylus/sone/text/SoneMentionDetector.kt new file mode 100644 index 0000000..3e15ed3 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/text/SoneMentionDetector.kt @@ -0,0 +1,94 @@ +/** + * Sone - SoneMentionDetector.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.text + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.database.* +import net.pterodactylus.sone.utils.* +import javax.inject.* + +/** + * Listens to [NewPostFoundEvent]s and [NewPostReplyFoundEvent], parses the + * texts and emits a [MentionOfLocalSoneFoundEvent] if a [SoneTextParser] + * finds a [SonePart] that points to a local [Sone]. + */ +class SoneMentionDetector @Inject constructor(private val eventBus: EventBus, private val soneTextParser: SoneTextParser, private val postReplyProvider: PostReplyProvider) { + + @Subscribe + fun onNewPost(newPostFoundEvent: NewPostFoundEvent) { + newPostFoundEvent.post.let { post -> + post.sone.isLocal.onFalse { + if (post.text.hasLinksToLocalSones()) { + mentionedPosts += post + eventBus.post(MentionOfLocalSoneFoundEvent(post)) + } + } + } + } + + @Subscribe + fun onNewPostReply(event: NewPostReplyFoundEvent) { + event.postReply.let { postReply -> + postReply.sone.isLocal.onFalse { + if (postReply.text.hasLinksToLocalSones()) { + postReply.post + .also { mentionedPosts += it } + .let(::MentionOfLocalSoneFoundEvent) + ?.also(eventBus::post) + } + } + } + } + + @Subscribe + fun onPostRemoved(event: PostRemovedEvent) { + unmentionPost(event.post) + } + + @Subscribe + fun onPostMarkedKnown(event: MarkPostKnownEvent) { + unmentionPost(event.post) + } + + @Subscribe + fun onReplyRemoved(event: PostReplyRemovedEvent) { + event.postReply.post.let { + if ((!it.text.hasLinksToLocalSones() || it.isKnown) && (it.replies.filterNot { it == event.postReply }.none { it.text.hasLinksToLocalSones() && !it.isKnown })) { + unmentionPost(it) + } + } + } + + private fun unmentionPost(post: Post) { + if (post in mentionedPosts) { + eventBus.post(MentionOfLocalSoneRemovedEvent(post)) + mentionedPosts -= post + } + } + + private val mentionedPosts = mutableSetOf() + + private fun String.hasLinksToLocalSones() = soneTextParser.parse(this, null) + .filterIsInstance() + .any { it.sone.isLocal } + + private val Post.replies get() = postReplyProvider.getReplies(id) + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt b/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt index 554c19b..24b04fa 100644 --- a/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt +++ b/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt @@ -1,12 +1,12 @@ package net.pterodactylus.sone.text import freenet.keys.* -import freenet.support.* import net.pterodactylus.sone.data.* import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.database.* import net.pterodactylus.sone.text.LinkType.* import net.pterodactylus.sone.text.LinkType.USK +import net.pterodactylus.sone.utils.* import org.bitpedia.util.* import java.net.* import javax.inject.* @@ -71,7 +71,7 @@ class SoneTextParser @Inject constructor(private val soneProvider: SoneProvider? ?.takeIf { (it.size > 1) || ((it.size == 1) && (it.single() != "")) } ?.lastOrNull() ?: uri.docName - ?: "${uri.keyType}@${uri.routingKey.freenetBase64}" + ?: "${uri.keyType}@${uri.routingKey.asFreenetBase64}" }.let { FreenetLinkPart(linkWithoutBacklink.removeSuffix("/"), it, trusted = context?.routingKey?.contentEquals(FreenetURI(linkWithoutBacklink).routingKey) == true) } } catch (e: MalformedURLException) { PlainTextPart(linkWithoutBacklink) @@ -115,7 +115,7 @@ private fun List.mergeAdjacentPlainTextParts() = fold(emptyList()) { private fun List.removeEmptyPlainTextParts() = filterNot { it == PlainTextPart("") } -private val String.decodedId: String get() = Base64.encode(Base32.decode(this)) +private val String.decodedId: String get() = Base32.decode(this).asFreenetBase64 private val String.withoutProtocol get() = substring(indexOf("//") + 2) private val String.withoutUrlParameters get() = split('?').first() @@ -138,7 +138,7 @@ private val String.withoutMiddlePathComponents } private val String.withoutTrailingSlash get() = if (endsWith("/")) substring(0, length - 1) else this private val SoneTextParserContext.routingKey: ByteArray? get() = postingSone?.routingKey -private val Sone.routingKey: ByteArray get() = Base64.decode(id) +private val Sone.routingKey: ByteArray get() = id.fromFreenetBase64 private enum class LinkType(private val scheme: String, private val freenetLink: Boolean) { @@ -199,5 +199,3 @@ private fun isPunctuation(char: Char) = char in punctuationChars private val whitespace = Regex("[\\u000a\u0020\u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u200c\u200d\u202f\u205f\u2060\u2800\u3000]") private data class NextLink(val position: Int, val linkType: LinkType, val link: String, val remainder: String) - -private val ByteArray.freenetBase64 get() = Base64.encode(this)!! diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucket.kt b/src/main/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucket.kt deleted file mode 100644 index 58c181f..0000000 --- a/src/main/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucket.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.pterodactylus.sone.utils - -import freenet.support.api.Bucket - -class AutoCloseableBucket(val bucket: Bucket) : AutoCloseable { - - override fun close() { - bucket.free() - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Booleans.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Booleans.kt index bfcb319..1d3e097 100644 --- a/src/main/kotlin/net/pterodactylus/sone/utils/Booleans.kt +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Booleans.kt @@ -9,3 +9,19 @@ fun Boolean.ifTrue(block: () -> R): R? = if (this) block() else null * Returns the value of [block] if `this` is false, returns `null` otherwise. */ fun Boolean.ifFalse(block: () -> R): R? = if (!this) block() else null + +/** + * Returns `this` but runs the given block if `this` is `true`. + * + * @param block The block to run if `this` is `true` + * @return `this` + */ +fun Boolean.onTrue(block: () -> Unit): Boolean = also { if (this) block() } + +/** + * Returns `this` but runs the given block if `this` is `false`. + * + * @param block The block to run if `this` is `false` + * @return `this` + */ +fun Boolean.onFalse(block: () -> Unit): Boolean = this.also { if (!this) block() } diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt b/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt new file mode 100644 index 0000000..fd4215f --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt @@ -0,0 +1,28 @@ +package net.pterodactylus.sone.utils + +/** + * Basic implementation of an [Option]. + * + * @param The type of the option + */ +class DefaultOption @JvmOverloads constructor( + private val defaultValue: T, + private val validator: ((T) -> Boolean)? = null +) : Option { + + @Volatile + private var value: T? = null + + override fun get() = value ?: defaultValue + + override fun getReal(): T? = value + + override fun validate(value: T?): Boolean = + value == null || validator?.invoke(value) ?: true + + override fun set(value: T?) { + require(validate(value)) { "New Value ($value) could not be validated." } + this.value = value + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Freenet.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Freenet.kt new file mode 100644 index 0000000..2eb8ab0 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Freenet.kt @@ -0,0 +1,23 @@ +/** + * Sone - Freenet.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.utils + +import freenet.support.* + +val ByteArray.asFreenetBase64: String get() = Base64.encode(this) +val String.fromFreenetBase64: ByteArray get() = Base64.decode(this) diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Functions.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Functions.kt new file mode 100644 index 0000000..99f43b6 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Functions.kt @@ -0,0 +1,9 @@ +package net.pterodactylus.sone.utils + +import java.util.function.* + +/** Allows easy invocation of Java Consumers. */ +operator fun Consumer.invoke(t: T) = accept(t) + +/** Allows easy invocation of Java Runnables. */ +operator fun Runnable.invoke() = run() diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Objects.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Objects.kt index 6d1d413..d9ec162 100644 --- a/src/main/kotlin/net/pterodactylus/sone/utils/Objects.kt +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Objects.kt @@ -5,3 +5,9 @@ val Any?.unit get() = Unit fun T?.throwOnNullIf(throwCondition: Boolean, exception: () -> Throwable) = if (this == null && throwCondition) throw exception() else this + +fun T?.onNull(block: () -> Unit) = this.also { + if (this == null) { + block() + } +} diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/Renderables.kt b/src/main/kotlin/net/pterodactylus/sone/utils/Renderables.kt new file mode 100644 index 0000000..3ba9f8f --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/utils/Renderables.kt @@ -0,0 +1,27 @@ +/** + * Sone - Renderables.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.utils + +import net.pterodactylus.util.io.* +import java.io.* + +/** + * Renders the [Renderable] into a [String]. + */ +fun Renderable.render() = + StringWriter().use { it.also(::render) }.toString() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/AllPages.kt b/src/main/kotlin/net/pterodactylus/sone/web/AllPages.kt new file mode 100644 index 0000000..c538f0e --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/AllPages.kt @@ -0,0 +1,52 @@ +package net.pterodactylus.sone.web + +import net.pterodactylus.sone.web.pages.* +import javax.inject.Inject + +/** + * Container for all web pages. This uses field injection because there are way too many pages + * to sensibly use constructor injection. + */ +class AllPages { + + @Inject lateinit var aboutPage: AboutPage + @Inject lateinit var bookmarkPage: BookmarkPage + @Inject lateinit var bookmarksPage: BookmarksPage + @Inject lateinit var createAlbumPage: CreateAlbumPage + @Inject lateinit var createPostPage: CreatePostPage + @Inject lateinit var createReplyPage: CreateReplyPage + @Inject lateinit var createSonePage: CreateSonePage + @Inject lateinit var deleteAlbumPage: DeleteAlbumPage + @Inject lateinit var deleteImagePage: DeleteImagePage + @Inject lateinit var deletePostPage: DeletePostPage + @Inject lateinit var deleteProfileFieldPage: DeleteProfileFieldPage + @Inject lateinit var deleteReplyPage: DeleteReplyPage + @Inject lateinit var deleteSonePage: DeleteSonePage + @Inject lateinit var dismissNotificationPage: DismissNotificationPage + @Inject lateinit var editAlbumPage: EditAlbumPage + @Inject lateinit var editImagePage: EditImagePage + @Inject lateinit var editProfileFieldPage: EditProfileFieldPage + @Inject lateinit var editProfilePage: EditProfilePage + @Inject lateinit var followSonePage: FollowSonePage + @Inject lateinit var getImagePage: GetImagePage + @Inject lateinit var imageBrowserPage: ImageBrowserPage + @Inject lateinit var indexPage: IndexPage + @Inject lateinit var knownSonesPage: KnownSonesPage + @Inject lateinit var likePage: LikePage + @Inject lateinit var lockSonePage: LockSonePage + @Inject lateinit var loginPage: LoginPage + @Inject lateinit var logoutPage: LogoutPage + @Inject lateinit var markAsKnownPage: MarkAsKnownPage + @Inject lateinit var newPage: NewPage + @Inject lateinit var optionsPage: OptionsPage + @Inject lateinit var rescuePage: RescuePage + @Inject lateinit var searchPage: SearchPage + @Inject lateinit var unbookmarkPage: UnbookmarkPage + @Inject lateinit var unfollowSonePage: UnfollowSonePage + @Inject lateinit var unlikePage: UnlikePage + @Inject lateinit var unlockSonePage: UnlockSonePage + @Inject lateinit var uploadImagePage: UploadImagePage + @Inject lateinit var viewPostPage: ViewPostPage + @Inject lateinit var viewSonePage: ViewSonePage + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt b/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt new file mode 100644 index 0000000..8928f12 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt @@ -0,0 +1,53 @@ +/** + * Sone - FreenetSessionProvider.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web + +import freenet.clients.http.SessionManager +import freenet.clients.http.ToadletContext +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.database.SoneProvider +import java.util.UUID +import javax.inject.Inject + +/** + * [SoneProvider] implementation based on Freenet’s [SessionManager]. + */ +class FreenetSessionProvider @Inject constructor(private val soneProvider: SoneProvider, private val sessionManager: SessionManager) : SessionProvider { + + override fun getCurrentSone(toadletContext: ToadletContext): Sone? = + soneProvider.localSones.singleOrNull() + ?: sessionManager.useSession(toadletContext) + ?.let { it.getAttribute("Sone.CurrentSone") as? String } + ?.let(soneProvider.soneLoader) + ?.takeIf { it.isLocal } + + override fun setCurrentSone(toadletContext: ToadletContext, sone: Sone?) { + if (sone == null) { + sessionManager.useSession(toadletContext) + ?.removeAttribute("Sone.CurrentSone") + } else { + sessionManager.getOrCreateSession(toadletContext) + ?.setAttribute("Sone.CurrentSone", sone.id) + } + } + + private fun SessionManager.getOrCreateSession(toadletContext: ToadletContext) = + useSession(toadletContext) + ?: createSession(UUID.randomUUID().toString(), toadletContext) + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/PageToadletRegistry.kt b/src/main/kotlin/net/pterodactylus/sone/web/PageToadletRegistry.kt index 97e6cfe..651bffd 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/PageToadletRegistry.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/PageToadletRegistry.kt @@ -18,21 +18,28 @@ class PageToadletRegistry @Inject constructor( ) { private val pages = mutableListOf>() + private val debugPages = mutableListOf>() private val registeredToadlets = mutableListOf() private val registered = AtomicBoolean(false) + private val debugActivated = AtomicBoolean(false) fun addPage(page: Page) { if (registered.get()) throw IllegalStateException() pages += page } + fun addDebugPage(page: Page) { + if (registered.get()) throw IllegalStateException() + debugPages += page + } + fun registerToadlets() { registered.set(true) pageMaker.addNavigationCategory("/Sone/index.html", soneMenuName, "$soneMenu.Tooltip", sonePlugin) addPages() } - private fun addPages() = + private fun addPages(pages: List> = this.pages) = pages .map { pageToadletFactory.createPageToadlet(it) } .onEach(registeredToadlets::plusAssign) @@ -55,4 +62,11 @@ class PageToadletRegistry @Inject constructor( registeredToadlets.forEach(toadletContainer::unregister) } + fun activateDebugMode() { + if (!debugActivated.get()) { + addPages(debugPages) + debugActivated.set(true) + } + } + } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt b/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt index 463ddaa..93cd6af 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt @@ -8,7 +8,7 @@ import net.pterodactylus.sone.data.Sone */ interface SessionProvider { - fun getCurrentSone(toadletContext: ToadletContext, createSession: Boolean = true): Sone? + fun getCurrentSone(toadletContext: ToadletContext): Sone? fun setCurrentSone(toadletContext: ToadletContext, sone: Sone?) } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt b/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt index dd5e9f6..3d87aa7 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt @@ -1,7 +1,6 @@ package net.pterodactylus.sone.web import com.google.inject.* -import freenet.l10n.* import freenet.support.api.* import net.pterodactylus.sone.core.* import net.pterodactylus.sone.data.* @@ -11,6 +10,7 @@ import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.main.* import net.pterodactylus.sone.template.* import net.pterodactylus.sone.text.* +import net.pterodactylus.util.notify.* import net.pterodactylus.util.template.* import javax.inject.* import javax.inject.Singleton @@ -65,6 +65,7 @@ class WebInterfaceModule : AbstractModule() { addFilter("reparse", ReparseFilter()) addFilter("unknown", unknownDateFilter) addFilter("format", FormatFilter()) + addFilter("duration", DurationFormatFilter()) addFilter("sort", CollectionSortFilter()) addFilter("image-link", imageLinkFilter) addFilter("replyGroup", ReplyGroupFilter()) @@ -72,6 +73,7 @@ class WebInterfaceModule : AbstractModule() { addFilter("unique", UniqueElementFilter()) addFilter("mod", ModFilter()) addFilter("paginate", PaginationFilter()) + addFilter("render-histogram", HistogramRenderer()) addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER) addProvider(loaders.templateProvider) @@ -98,8 +100,8 @@ class WebInterfaceModule : AbstractModule() { ProfileAccessor(core) @Provides - fun getL10nFilter(l10n: BaseL10n) = - L10nFilter(l10n) + fun getL10nFilter(translation: Translation) = + L10nFilter(translation) @Provides fun getParserFilter(core: Core, soneTextParser: SoneTextParser) = @@ -114,8 +116,8 @@ class WebInterfaceModule : AbstractModule() { LinkedElementsFilter(elementLoader) @Provides - fun getUnknownDateFilter(l10n: BaseL10n) = - UnknownDateFilter(l10n, "View.Sone.Text.UnknownDate") + fun getUnknownDateFilter(translation: Translation) = + UnknownDateFilter(translation, "View.Sone.Text.UnknownDate") @Provides fun getImageLinkFilter(core: Core) = @@ -125,4 +127,9 @@ class WebInterfaceModule : AbstractModule() { @Named("toadletPathPrefix") fun getPathPrefix(): String = "/Sone/" + @Provides + @Singleton + fun getNotificationManager() = + NotificationManager() + } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.kt index 79e66ad..28af3e3 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPage.kt @@ -19,7 +19,7 @@ class CreatePostAjaxPage @Inject constructor(webInterface: WebInterface) : Logge ?.let { text -> val sender = request.parameters["sender"].emptyToNull?.let(core::getSone) ?: currentSone val recipient = request.parameters["recipient"]?.let(core::getSone) - core.createPost(sender, recipient.asOptional(), text).let { post -> + core.createPost(sender, recipient, text).let { post -> createSuccessJsonObject().apply { put("postId", post.id) put("sone", sender.id) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.kt deleted file mode 100644 index cbeed6e..0000000 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/DistrustAjaxPage.kt +++ /dev/null @@ -1,29 +0,0 @@ -package net.pterodactylus.sone.web.ajax - -import net.pterodactylus.sone.core.* -import net.pterodactylus.sone.data.* -import net.pterodactylus.sone.utils.* -import net.pterodactylus.sone.web.* -import net.pterodactylus.sone.web.page.* -import javax.inject.* - -/** - * AJAX page that lets the user distrust a Sone. - * - * @see Core.distrustSone(Sone, Sone) - */ -@ToadletPath("distrustSone.ajax") -class DistrustAjaxPage @Inject constructor(webInterface: WebInterface) : LoggedInJsonPage(webInterface) { - - override fun createJsonObject(currentSone: Sone, request: FreenetRequest) = - request.parameters["sone"] - ?.let(core::getSone) - ?.let { sone -> - createSuccessJsonObject() - .put("trustValue", core.preferences.negativeTrust) - .also { - core.distrustSone(currentSone, sone) - } - } ?: createErrorJsonObject("invalid-sone-id") - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt index db6e5c5..c60fe56 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt @@ -3,8 +3,7 @@ package net.pterodactylus.sone.web.ajax import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.SoneOptions import net.pterodactylus.sone.main.SonePlugin -import net.pterodactylus.sone.utils.jsonArray -import net.pterodactylus.sone.utils.jsonObject +import net.pterodactylus.sone.utils.* import net.pterodactylus.sone.web.WebInterface import net.pterodactylus.sone.web.page.* import net.pterodactylus.util.notify.Notification @@ -22,7 +21,7 @@ class GetNotificationsAjaxPage @Inject constructor(webInterface: WebInterface) : override val requiresLogin = false override fun createJsonObject(request: FreenetRequest) = - getCurrentSone(request.toadletContext, false).let { currentSone -> + getCurrentSone(request.toadletContext).let { currentSone -> webInterface.getNotifications(currentSone) .sortedBy(Notification::getCreatedTime) .let { notifications -> @@ -74,5 +73,3 @@ private val SoneOptions?.asJsonObject "ShowNotification/NewReplies" to options.isShowNewReplyNotifications ) } ?: jsonObject {} - -private fun Notification.render() = StringWriter().use { it.also { render(it) } }.toString() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt index 75f3c3c..5d17262 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt @@ -35,7 +35,7 @@ class GetStatusAjaxPage(webInterface: WebInterface, private val elementLoader: E } override fun createJsonObject(request: FreenetRequest) = - getCurrentSone(request.toadletContext, false).let { currentSone -> + getCurrentSone(request.toadletContext).let { currentSone -> createSuccessJsonObject().apply { this["loggedIn"] = currentSone != null this["options"] = currentSone?.options?.toJsonOptions() ?: jsonObject {} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetTranslationAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetTranslationAjaxPage.kt index 41188ea..e2c41b6 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetTranslationAjaxPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetTranslationAjaxPage.kt @@ -16,6 +16,6 @@ class GetTranslationAjaxPage @Inject constructor(webInterface: WebInterface) : J override fun createJsonObject(request: FreenetRequest) = createSuccessJsonObject() - .put("value", webInterface.l10n.getString(request.parameters["key"])) + .put("value", webInterface.translation.translate(request.parameters["key"] ?: "")) } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt index 99c0828..356aca6 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt @@ -31,8 +31,8 @@ abstract class JsonPage(protected val webInterface: WebInterface) : Page. + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for [ConfigNotRead] events. + */ +class ConfigNotReadHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("configNotRead") private val notification: TemplateNotification) { + + @Subscribe + fun configNotRead(@Suppress("UNUSED_PARAMETER") configNotRead: ConfigNotRead) { + notificationManager.addNotification(notification) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/FirstStartHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/FirstStartHandler.kt new file mode 100644 index 0000000..3dc8689 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/FirstStartHandler.kt @@ -0,0 +1,35 @@ +/** + * Sone - FirstStartHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handles the notification shown on first start of Sone. + */ +class FirstStartHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("firstStart") private val notification: TemplateNotification) { + + @Subscribe + fun firstStart(@Suppress("UNUSED_PARAMETER") firstStart: FirstStart) { + notificationManager.addNotification(notification) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/ImageInsertHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/ImageInsertHandler.kt new file mode 100644 index 0000000..bab7599 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/ImageInsertHandler.kt @@ -0,0 +1,66 @@ +/** + * Sone - ImageInsertHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Notification handler for the various image-insert-related events. + * + * @see ImageInsertStartedEvent + * @see ImageInsertAbortedEvent + * @see ImageInsertFailedEvent + * @see ImageInsertFinishedEvent + */ +class ImageInsertHandler @Inject constructor( + private val notificationManager: NotificationManager, + @Named("imageInserting") private val imageInsertingNotification: ListNotification, + @Named("imageFailed") private val imageFailedNotification: ListNotification, + @Named("imageInserted") private val imageInsertedNotification: ListNotification) { + + @Subscribe + fun imageInsertStarted(imageInsertStartedEvent: ImageInsertStartedEvent) { + imageInsertingNotification.add(imageInsertStartedEvent.image) + notificationManager.addNotification(imageInsertingNotification) + } + + @Subscribe + fun imageInsertAborted(imageInsertAbortedEvent: ImageInsertAbortedEvent) { + imageInsertingNotification.remove(imageInsertAbortedEvent.image) + } + + @Subscribe + fun imageInsertFailed(imageInsertFailedEvent: ImageInsertFailedEvent) { + imageInsertingNotification.remove(imageInsertFailedEvent.image) + imageFailedNotification.add(imageInsertFailedEvent.image) + notificationManager.addNotification(imageFailedNotification) + } + + @Subscribe + fun imageInsertFinished(imageInsertFinishedEvent: ImageInsertFinishedEvent) { + imageInsertingNotification.remove(imageInsertFinishedEvent.image) + imageInsertedNotification.add(imageInsertFinishedEvent.image) + notificationManager.addNotification(imageInsertedNotification) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/LocalPostHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/LocalPostHandler.kt new file mode 100644 index 0000000..e7b8f05 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/LocalPostHandler.kt @@ -0,0 +1,59 @@ +/** + * Sone - LocalPostHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for local posts. + */ +class LocalPostHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("localPost") private val notification: ListNotification) { + + @Subscribe + fun newPostFound(newPostFoundEvent: NewPostFoundEvent) { + newPostFoundEvent.post.onLocal { post -> + notification.add(post) + if (!notificationManager.hasFirstStartNotification()) { + notificationManager.addNotification(notification) + } + } + } + + @Subscribe + fun postRemoved(postRemovedEvent: PostRemovedEvent) { + postRemovedEvent.post.onLocal { post -> + notification.remove(post) + } + } + + @Subscribe + fun postMarkedAsKnown(markPostKnownEvent: MarkPostKnownEvent) { + markPostKnownEvent.post.onLocal { post -> + notification.remove(post) + } + } + +} + +private fun Post.onLocal(action: (Post) -> Unit) = + if (sone.isLocal) action(this) else Unit diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/LocalReplyHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/LocalReplyHandler.kt new file mode 100644 index 0000000..7e392d0 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/LocalReplyHandler.kt @@ -0,0 +1,54 @@ +/** + * Sone - LocalReplyHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for local replies. + */ +class LocalReplyHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("localReply") private val notification: ListNotification) { + + @Subscribe + fun newReplyFound(event: NewPostReplyFoundEvent) = + event.postReply.onLocal { + notification.add(it) + if (!notificationManager.hasFirstStartNotification()) { + notificationManager.addNotification(notification) + } + } + + @Subscribe + fun replyRemoved(event: PostReplyRemovedEvent) { + notification.remove(event.postReply) + } + + @Subscribe + fun replyMarkedAsKnown(event: MarkPostReplyKnownEvent) { + notification.remove(event.postReply) + } + +} + +private fun PostReply.onLocal(action: (PostReply) -> Unit) = + if (sone.isLocal) action(this) else Unit diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/MarkPostKnownDuringFirstStartHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/MarkPostKnownDuringFirstStartHandler.kt new file mode 100644 index 0000000..57962c2 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/MarkPostKnownDuringFirstStartHandler.kt @@ -0,0 +1,43 @@ +/** + * Sone - MarkPostKnownDuringFirstStartHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.sone.utils.* +import net.pterodactylus.util.notify.* +import java.util.function.* +import javax.inject.* + +/** + * Handler that marks a [new][NewPostFoundEvent] [post][Post] as known while + * the [notification manager][NotificationManager] shows a [first start notification] + * [NotificationManager.hasFirstStartNotification]. + */ +class MarkPostKnownDuringFirstStartHandler @Inject constructor(private val notificationManager: NotificationManager, private val markPostAsKnown: Consumer) { + + @Subscribe + fun newPostFound(newPostFoundEvent: NewPostFoundEvent) { + if (notificationManager.hasFirstStartNotification()) { + markPostAsKnown(newPostFoundEvent.post) + } + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/MarkPostReplyKnownDuringFirstStartHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/MarkPostReplyKnownDuringFirstStartHandler.kt new file mode 100644 index 0000000..64285ee --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/MarkPostReplyKnownDuringFirstStartHandler.kt @@ -0,0 +1,43 @@ +/** + * Sone - MarkPostReplyKnownDuringFirstStartHandler.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.sone.utils.* +import net.pterodactylus.util.notify.* +import java.util.function.* +import javax.inject.* + +/** + * Handler that marks post replies [as known][net.pterodactylus.sone.core.Core.markReplyKnown] + * while the [first start notification][net.pterodactylus.util.notify.NotificationManager.hasFirstStartNotification] + * is shown. + */ +class MarkPostReplyKnownDuringFirstStartHandler @Inject constructor(private val notificationManager: NotificationManager, private val markAsKnown: Consumer) { + + @Subscribe + fun newPostReply(event: NewPostReplyFoundEvent) { + if (notificationManager.hasFirstStartNotification()) { + markAsKnown(event.postReply) + } + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/NewRemotePostHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/NewRemotePostHandler.kt new file mode 100644 index 0000000..91eaae0 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/NewRemotePostHandler.kt @@ -0,0 +1,53 @@ +/** + * Sone - NewRemotePostHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for [NewPostFoundEvent]s that adds the new post to the “new posts” notification and + * displays the notification if the “first start” notification is not being shown. + */ +class NewRemotePostHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("newRemotePost") private val notification: ListNotification) { + + @Subscribe + fun newPostFound(newPostFoundEvent: NewPostFoundEvent) { + if (!newPostFoundEvent.post.sone.isLocal) { + notification.add(newPostFoundEvent.post) + if (!notificationManager.hasFirstStartNotification()) { + notificationManager.addNotification(notification) + } + } + } + + @Subscribe + fun postRemoved(event: PostRemovedEvent) { + notification.remove(event.post) + } + + @Subscribe + fun postMarkedKnown(event: MarkPostKnownEvent) { + notification.remove(event.post) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/NewSoneHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/NewSoneHandler.kt new file mode 100644 index 0000000..eb384c7 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/NewSoneHandler.kt @@ -0,0 +1,50 @@ +/** + * Sone - NewSoneHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Notification handler for “new Sone discovered” events. + */ +class NewSoneHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("newSone") private val notification: ListNotification) { + + @Subscribe + fun newSoneFound(newSoneFoundEvent: NewSoneFoundEvent) { + if (!notificationManager.hasFirstStartNotification()) { + notification.add(newSoneFoundEvent.sone) + notificationManager.addNotification(notification) + } + } + + @Subscribe + fun markedSoneKnown(markSoneKnownEvent: MarkSoneKnownEvent) { + notification.remove(markSoneKnownEvent.sone) + } + + @Subscribe + fun soneRemoved(soneRemovedEvent: SoneRemovedEvent) { + notification.remove(soneRemovedEvent.sone) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/NewVersionHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/NewVersionHandler.kt new file mode 100644 index 0000000..f48d89c --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/NewVersionHandler.kt @@ -0,0 +1,39 @@ +/** + * Sone - NewVersionHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for the “new version” notification. + */ +class NewVersionHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("newVersion") private val notification: TemplateNotification) { + + @Subscribe + fun newVersionFound(updateFoundEvent: UpdateFoundEvent) { + notification.set("latestVersion", updateFoundEvent.version) + notification.set("releaseTime", updateFoundEvent.releaseTime) + notification.set("latestEdition", updateFoundEvent.latestEdition) + notification.set("disruptive", updateFoundEvent.isDisruptive) + notificationManager.addNotification(notification) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandler.kt new file mode 100644 index 0000000..a24a6d8 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandler.kt @@ -0,0 +1,49 @@ +/** + * Sone - NotificationHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import net.pterodactylus.sone.freenet.wot.* +import net.pterodactylus.sone.text.* +import javax.inject.* + +/** + * Container that causes notification handlers to be created and (more importantly) registered + * on creation with the event bus. + */ +@Suppress("UNUSED_PARAMETER") +class NotificationHandler @Inject constructor( + markPostKnownDuringFirstStartHandler: MarkPostKnownDuringFirstStartHandler, + markPostReplyKnownDuringFirstStartHandler: MarkPostReplyKnownDuringFirstStartHandler, + newSoneHandler: NewSoneHandler, + newRemotePostHandler: NewRemotePostHandler, + remotePostReplyHandler: RemotePostReplyHandler, + soneLockedOnStartupHandler: SoneLockedOnStartupHandler, + soneLockedHandler: SoneLockedHandler, + localPostHandler: LocalPostHandler, + localReplyHandler: LocalReplyHandler, + newVersionHandler: NewVersionHandler, + imageInsertHandler: ImageInsertHandler, + firstStartHandler: FirstStartHandler, + configNotReadHandler: ConfigNotReadHandler, + startupHandler: StartupHandler, + webOfTrustPinger: WebOfTrustPinger, + webOfTrustHandler: WebOfTrustHandler, + soneMentionDetector: SoneMentionDetector, + soneMentionedHandler: SoneMentionedHandler, + soneInsertHandler: SoneInsertHandler +) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandlerModule.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandlerModule.kt new file mode 100644 index 0000000..18aefd6 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/NotificationHandlerModule.kt @@ -0,0 +1,192 @@ +/** + * Sone - NotificationHandlerModule.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.inject.* +import com.google.inject.binder.* +import net.pterodactylus.sone.core.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.freenet.wot.* +import net.pterodactylus.sone.main.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.sone.text.* +import net.pterodactylus.util.notify.* +import java.util.concurrent.* +import java.util.concurrent.TimeUnit.* +import java.util.function.* +import javax.inject.* +import javax.inject.Singleton + +/** + * Guice module for creating all notification handlers. + */ +class NotificationHandlerModule : AbstractModule() { + + override fun configure() { + bind(NotificationHandler::class.java).`in`(Singleton::class.java) + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + bind().asSingleton() + } + + @Provides + fun getMarkPostKnownHandler(core: Core): Consumer = Consumer { core.markPostKnown(it) } + + @Provides + fun getMarkPostReplyKnownHandler(core: Core): Consumer = Consumer { core.markReplyKnown(it) } + + @Provides + @Singleton + @Named("soneLockedOnStartup") + fun getSoneLockedOnStartupNotification(loaders: Loaders) = + ListNotification("sone-locked-on-startup", "sones", loaders.loadTemplate("/templates/notify/soneLockedOnStartupNotification.html")) + + @Provides + @Named("newSone") + fun getNewSoneNotification(loaders: Loaders) = + ListNotification("new-sone-notification", "sones", loaders.loadTemplate("/templates/notify/newSoneNotification.html"), dismissable = false) + + @Provides + @Singleton + @Named("newRemotePost") + fun getNewPostNotification(loaders: Loaders) = + ListNotification("new-post-notification", "posts", loaders.loadTemplate("/templates/notify/newPostNotification.html"), dismissable = false) + + @Provides + @Singleton + @Named("newRemotePostReply") + fun getNewRemotePostReplyNotification(loaders: Loaders) = + ListNotification("new-reply-notification", "replies", loaders.loadTemplate("/templates/notify/newReplyNotification.html"), dismissable = false) + + @Provides + @Singleton + @Named("soneLocked") + fun getSoneLockedNotification(loaders: Loaders) = + ListNotification("sones-locked-notification", "sones", loaders.loadTemplate("/templates/notify/lockedSonesNotification.html"), dismissable = true) + + @Provides + @Singleton + @Named("localPost") + fun getLocalPostNotification(loaders: Loaders) = + ListNotification("local-post-notification", "posts", loaders.loadTemplate("/templates/notify/newPostNotification.html"), dismissable = false) + + @Provides + @Singleton + @Named("localReply") + fun getLocalReplyNotification(loaders: Loaders) = + ListNotification("local-reply-notification", "replies", loaders.loadTemplate("/templates/notify/newReplyNotification.html"), dismissable = false) + + @Provides + @Singleton + @Named("newVersion") + fun getNewVersionNotification(loaders: Loaders) = + TemplateNotification("new-version-notification", loaders.loadTemplate("/templates/notify/newVersionNotification.html")) + + @Provides + @Singleton + @Named("imageInserting") + fun getImageInsertingNotification(loaders: Loaders) = + ListNotification("inserting-images-notification", "images", loaders.loadTemplate("/templates/notify/inserting-images-notification.html"), dismissable = true) + + @Provides + @Singleton + @Named("imageFailed") + fun getImageInsertingFailedNotification(loaders: Loaders) = + ListNotification("image-insert-failed-notification", "images", loaders.loadTemplate("/templates/notify/image-insert-failed-notification.html"), dismissable = true) + + @Provides + @Singleton + @Named("imageInserted") + fun getImageInsertedNotification(loaders: Loaders) = + ListNotification("inserted-images-notification", "images", loaders.loadTemplate("/templates/notify/inserted-images-notification.html"), dismissable = true) + + @Provides + @Singleton + @Named("firstStart") + fun getFirstStartNotification(loaders: Loaders) = + TemplateNotification("first-start-notification", loaders.loadTemplate("/templates/notify/firstStartNotification.html")) + + @Provides + @Singleton + @Named("configNotRead") + fun getConfigNotReadNotification(loaders: Loaders) = + TemplateNotification("config-not-read-notification", loaders.loadTemplate("/templates/notify/configNotReadNotification.html")) + + @Provides + @Singleton + @Named("startup") + fun getStartupNotification(loaders: Loaders) = + TemplateNotification("startup-notification", loaders.loadTemplate("/templates/notify/startupNotification.html")) + + @Provides + @Singleton + @Named("webOfTrust") + fun getWebOfTrustNotification(loaders: Loaders) = + TemplateNotification("wot-missing-notification", loaders.loadTemplate("/templates/notify/wotMissingNotification.html")) + + @Provides + @Singleton + @Named("webOfTrustReacher") + fun getWebOfTrustReacher(webOfTrustConnector: WebOfTrustConnector): Runnable = + Runnable { webOfTrustConnector.ping() } + + @Provides + @Singleton + @Named("webOfTrustReschedule") + fun getWebOfTrustReschedule(@Named("notification") ticker: ScheduledExecutorService) = + Consumer { ticker.schedule(it, 15, SECONDS) } + + @Provides + @Singleton + @Named("soneMentioned") + fun getSoneMentionedNotification(loaders: Loaders) = + ListNotification("mention-notification", "posts", loaders.loadTemplate("/templates/notify/mentionNotification.html"), dismissable = false) + + @Provides + @Singleton + fun getSoneNotificationSupplier(loaders: Loaders): SoneInsertNotificationSupplier = + mutableMapOf() + .let { cache -> + { sone -> + cache.computeIfAbsent(sone) { + loaders.loadTemplate("/templates/notify/soneInsertNotification.html") + .let(::TemplateNotification) + .also { it["insertSone"] = sone } + } + } + } + + private inline fun bind(): AnnotatedBindingBuilder = bind(T::class.java) + private fun ScopedBindingBuilder.asSingleton() = `in`(Singleton::class.java) + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/RemotePostReplyHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/RemotePostReplyHandler.kt new file mode 100644 index 0000000..7dec7c9 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/RemotePostReplyHandler.kt @@ -0,0 +1,55 @@ +/** + * Sone - RemotePostReplyHandler.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.sone.utils.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for remote replies. + */ +class RemotePostReplyHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("newRemotePostReply") private val notification: ListNotification) { + + @Subscribe + fun newPostReplyFound(event: NewPostReplyFoundEvent) { + event.postReply.let { postReply -> + postReply.sone.isLocal.onFalse { + if (!notificationManager.hasFirstStartNotification()) { + notification.add(event.postReply) + notificationManager.addNotification(notification) + } + } + } + } + + @Subscribe + fun postReplyRemoved(event: PostReplyRemovedEvent) { + notification.remove(event.postReply) + } + + @Subscribe + fun postReplyMarkedAsKnown(event: MarkPostReplyKnownEvent) { + notification.remove(event.postReply) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneInsertHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneInsertHandler.kt new file mode 100644 index 0000000..b12acb4 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneInsertHandler.kt @@ -0,0 +1,58 @@ +/** + * Sone - SoneInsertHandler.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for all notifications concerning Sone-insert events. + */ +class SoneInsertHandler @Inject constructor(private val notificationManager: NotificationManager, private val soneNotifications: SoneInsertNotificationSupplier) { + + @Subscribe + fun soneInserting(event: SoneInsertingEvent) { + showNotification(event.sone, "inserting") + } + + @Subscribe + fun soneInserted(event: SoneInsertedEvent) { + showNotification(event.sone, "inserted", "insertDuration" to event.insertDuration / 1000) + } + + @Subscribe + fun soneInsertAborted(event: SoneInsertAbortedEvent) { + showNotification(event.sone, "insert-aborted") + } + + private fun showNotification(sone: Sone, status: String, vararg templateVariables: Pair) { + if (sone.options.isSoneInsertNotificationEnabled) { + soneNotifications(sone).let { notification -> + notification["soneStatus"] = status + templateVariables.forEach { notification[it.first] = it.second } + notificationManager.addNotification(notification) + } + } + } + +} + +typealias SoneInsertNotificationSupplier = (@JvmSuppressWildcards Sone) -> @JvmSuppressWildcards TemplateNotification diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedHandler.kt new file mode 100644 index 0000000..cb799a8 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedHandler.kt @@ -0,0 +1,65 @@ +/** + * Sone - SoneLockedHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import java.util.concurrent.* +import java.util.concurrent.atomic.* +import javax.inject.* + +/** + * Handler for [SoneLockedEvent]s and [SoneUnlockedEvent]s that can schedule notifications after + * a certain timeout. + */ +class SoneLockedHandler @Inject constructor( + private val notificationManager: NotificationManager, + @Named("soneLocked") private val notification: ListNotification, + @Named("notification") private val executor: ScheduledExecutorService) { + + private val future: AtomicReference> = AtomicReference() + + @Subscribe + fun soneLocked(soneLockedEvent: SoneLockedEvent) { + synchronized(future) { + notification.add(soneLockedEvent.sone) + future.get()?.also(this::cancelPreviousFuture) + future.set(executor.schedule(::showNotification, 5, TimeUnit.MINUTES)) + } + } + + @Subscribe + fun soneUnlocked(soneUnlockedEvent: SoneUnlockedEvent) { + synchronized(future) { + notification.remove(soneUnlockedEvent.sone) + future.get()?.also(::cancelPreviousFuture) + } + } + + private fun cancelPreviousFuture(future: ScheduledFuture<*>) { + future.cancel(true) + } + + private fun showNotification() { + notificationManager.addNotification(notification) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandler.kt new file mode 100644 index 0000000..8adea76 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneLockedOnStartupHandler.kt @@ -0,0 +1,40 @@ +/** + * Sone - SoneLockedOnStartupHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for [SoneLockedOnStartup][net.pterodactylus.sone.core.event.SoneLockedOnStartup] events + * that adds the appropriate notification to the [NotificationManager]. + */ +class SoneLockedOnStartupHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("soneLockedOnStartup") private val notification: ListNotification) { + + @Subscribe + @Suppress("UnstableApiUsage") + fun soneLockedOnStartup(soneLockedOnStartup: SoneLockedOnStartup) { + notification.add(soneLockedOnStartup.sone) + notificationManager.addNotification(notification) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneMentionedHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneMentionedHandler.kt new file mode 100644 index 0000000..a03e490 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/SoneMentionedHandler.kt @@ -0,0 +1,47 @@ +/** + * Sone - SoneMentionedHandler.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.notify.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for the [MentionOfLocalSoneFoundEvent] and + * [MentionOfLocalSoneRemovedEvent] events that add the corresponding + * notification to the notification manager. + */ +class SoneMentionedHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("soneMentioned") private val notification: ListNotification) { + + @Subscribe + fun mentionOfLocalSoneFound(event: MentionOfLocalSoneFoundEvent) { + if (!notificationManager.hasFirstStartNotification()) { + notification.add(event.post) + notificationManager.addNotification(notification) + } + } + + @Subscribe + fun mentionOfLocalSoneRemoved(event: MentionOfLocalSoneRemovedEvent) { + notification.remove(event.post) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/StartupHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/StartupHandler.kt new file mode 100644 index 0000000..3776d43 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/StartupHandler.kt @@ -0,0 +1,41 @@ +/** + * Sone - StartupHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.util.notify.* +import java.util.concurrent.* +import java.util.concurrent.TimeUnit.* +import javax.inject.* + +/** + * Handler for the [Startup] event notification. + */ +class StartupHandler @Inject constructor( + private val notificationManager: NotificationManager, + @Named("startup") private val notification: TemplateNotification, + @Named("notification") private val ticker: ScheduledExecutorService) { + + @Subscribe + fun startup(@Suppress("UNUSED_PARAMETER") startup: Startup) { + notificationManager.addNotification(notification) + ticker.schedule({ notificationManager.removeNotification(notification) }, 2, MINUTES) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/notification/WebOfTrustHandler.kt b/src/main/kotlin/net/pterodactylus/sone/web/notification/WebOfTrustHandler.kt new file mode 100644 index 0000000..900a885 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/notification/WebOfTrustHandler.kt @@ -0,0 +1,41 @@ +/** + * Sone - WebOfTrustHandler.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.notification + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.util.notify.* +import javax.inject.* + +/** + * Handler for web of trust-related notifications and the [WebOfTrustAppeared] + * and [WebOfTrustDisappeared] events. + */ +class WebOfTrustHandler @Inject constructor(private val notificationManager: NotificationManager, @Named("webOfTrust") private val notification: TemplateNotification) { + + @Subscribe + fun webOfTrustAppeared(@Suppress("UNUSED_PARAMETER") webOfTrustAppeared: WebOfTrustAppeared) { + notificationManager.removeNotification(notification) + } + + @Subscribe + fun webOfTrustDisappeared(@Suppress("UNUSED_PARAMETER") webOfTrustDisappeared: WebOfTrustDisappeared) { + notificationManager.addNotification(notification) + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetPage.kt index 2af0eca..68875d6 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetPage.kt @@ -1,5 +1,5 @@ /* - * Sone - FreenetPage.kt - Copyright © 2011–2019 David Roden + * Sone - FreenetPage.kt - Copyright © 2011–2020 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 diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt index 54c8f7f..5729b25 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt @@ -1,5 +1,5 @@ /* - * Sone - FreenetRequest.kt - Copyright © 2011–2019 David Roden + * Sone - FreenetRequest.kt - Copyright © 2011–2020 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 @@ -18,25 +18,11 @@ package net.pterodactylus.sone.web.page import freenet.clients.http.* -import freenet.clients.http.SessionManager.* -import freenet.l10n.* import freenet.support.api.* import net.pterodactylus.util.web.* import java.net.* -import java.util.UUID.* open class FreenetRequest(uri: URI, method: Method, val httpRequest: HTTPRequest, - val toadletContext: ToadletContext, - val l10n: BaseL10n, - val sessionManager: SessionManager -) : Request(uri, method) { - - val session: Session - get() = - sessionManager.useSession(toadletContext) - ?: sessionManager.createSession(randomUUID().toString(), toadletContext) - - val existingSession: Session? get() = sessionManager.useSession(toadletContext) - -} + val toadletContext: ToadletContext +) : Request(uri, method) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetTemplatePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetTemplatePage.kt index 2dcef51..3912f98 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetTemplatePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetTemplatePage.kt @@ -1,5 +1,5 @@ /* - * Sone - FreenetTemplatePage.kt - Copyright © 2010–2019 David Roden + * Sone - FreenetTemplatePage.kt - Copyright © 2010–2020 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 @@ -98,6 +98,9 @@ open class FreenetTemplatePage( /* do nothing. */ } + fun redirectTo(target: String?): Nothing = + throw RedirectException(target) + class RedirectException(val target: String?) : Exception() { override fun toString(): String = format("RedirectException{target='%s'}", target) } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt new file mode 100644 index 0000000..db7ede0 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt @@ -0,0 +1,79 @@ +/* + * Sone - PageToadlet.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.page + +import freenet.client.HighLevelSimpleClient +import freenet.clients.http.LinkEnabledCallback +import freenet.clients.http.LinkFilterExceptedToadlet +import freenet.clients.http.Toadlet +import freenet.clients.http.ToadletContext +import freenet.support.MultiValueTable +import freenet.support.api.HTTPRequest +import net.pterodactylus.sone.utils.use +import net.pterodactylus.util.web.Method +import net.pterodactylus.util.web.Page +import net.pterodactylus.util.web.Response +import java.net.URI + +/** + * [Toadlet] implementation that is wrapped around a [Page]. + */ +class PageToadlet( + highLevelSimpleClient: HighLevelSimpleClient, + val menuName: String?, + private val page: Page, + private val pathPrefix: String +) : Toadlet(highLevelSimpleClient), LinkEnabledCallback, LinkFilterExceptedToadlet { + + override fun path() = pathPrefix + page.path + + override fun handleMethodGET(uri: URI, httpRequest: HTTPRequest, toadletContext: ToadletContext) = + handleRequest(FreenetRequest(uri, Method.GET, httpRequest, toadletContext)) + + fun handleMethodPOST(uri: URI?, httpRequest: HTTPRequest?, toadletContext: ToadletContext?) = + handleRequest(FreenetRequest(uri!!, Method.POST, httpRequest!!, toadletContext!!)) + + private fun handleRequest(pageRequest: FreenetRequest) { + pageRequest.toadletContext.bucketFactory.makeBucket(-1).use { pageBucket -> + pageBucket.outputStream.use { pageBucketOutputStream -> + val pageResponse = page.handleRequest(pageRequest, Response(pageBucketOutputStream)) + // according to the javadoc, headers is allowed to return null but that’s stupid and it doesn’t do that. + val headers = pageResponse.headers.fold(MultiValueTable()) { headers, header -> + headers.apply { + header.forEach { put(header.name, it) } + } + } + with(pageResponse) { + writeReply(pageRequest.toadletContext, statusCode, contentType, statusText, headers, pageBucket) + } + } + } + } + + override fun isEnabled(toadletContext: ToadletContext) = + if (page is LinkEnabledCallback) { + page.isEnabled(toadletContext) + } else + true + + override fun isLinkExcepted(link: URI) = + page is FreenetPage && page.isLinkExcepted(link) + + override fun toString() = "${javaClass.name}[path=${path()},page=$page]" + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt index 3c84c09..61d8718 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt @@ -1,5 +1,5 @@ /* - * Sone - PageToadletFactory.kt - Copyright © 2010–2019 David Roden + * Sone - PageToadletFactory.kt - Copyright © 2010–2020 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 @@ -18,18 +18,16 @@ package net.pterodactylus.sone.web.page import freenet.client.* -import freenet.clients.http.* import net.pterodactylus.util.web.* import javax.inject.* class PageToadletFactory @Inject constructor( private val highLevelSimpleClient: HighLevelSimpleClient, - private val sessionManager: SessionManager, @Named("toadletPathPrefix") private val pathPrefix: String ) { @JvmOverloads fun createPageToadlet(page: Page, menuName: String? = null) = - PageToadlet(highLevelSimpleClient, sessionManager, menuName ?: page.menuName, page, pathPrefix) + PageToadlet(highLevelSimpleClient, menuName ?: page.menuName, page, pathPrefix) } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt index d3eed37..fcfc63d 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt @@ -1,16 +1,16 @@ package net.pterodactylus.sone.web.page import freenet.clients.http.* -import freenet.l10n.* import freenet.support.api.* import net.pterodactylus.sone.core.* import net.pterodactylus.sone.web.* import net.pterodactylus.util.web.* import java.net.* -class SoneRequest(uri: URI, method: Method, httpRequest: HTTPRequest, toadletContext: ToadletContext, l10n: BaseL10n, sessionManager: SessionManager, - val core: Core, - val webInterface: WebInterface -) : FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager) +class SoneRequest(uri: URI, method: Method, httpRequest: HTTPRequest, toadletContext: ToadletContext, + val core: Core, + val webInterface: WebInterface +) : FreenetRequest(uri, method, httpRequest, toadletContext) -fun FreenetRequest.toSoneRequest(core: Core, webInterface: WebInterface) = SoneRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager, core, webInterface) +fun FreenetRequest.toSoneRequest(core: Core, webInterface: WebInterface) = + SoneRequest(uri, method, httpRequest, toadletContext, core, webInterface) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/BookmarkPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/BookmarkPage.kt index 1dc12e4..c03a299 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/BookmarkPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/BookmarkPage.kt @@ -21,7 +21,7 @@ class BookmarkPage @Inject constructor(webInterface: WebInterface, loaders: Load soneRequest.core.getPost(postId)?.let { soneRequest.core.bookmarkPost(it) } - throw RedirectException(returnPage) + redirectTo(returnPage) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateAlbumPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateAlbumPage.kt index a00b4bf..b6ce673 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateAlbumPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateAlbumPage.kt @@ -35,10 +35,10 @@ class CreateAlbumPage @Inject constructor(webInterface: WebInterface, loaders: L setDescription(TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), description)) }.update() } catch (e: AlbumTitleMustNotBeEmpty) { - throw RedirectException("emptyAlbumTitle.html") + redirectTo("emptyAlbumTitle.html") } soneRequest.core.touchConfiguration() - throw RedirectException("imageBrowser.html?album=${album.id}") + redirectTo("imageBrowser.html?album=${album.id}") } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreatePostPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreatePostPage.kt index b47c2b1..515de60 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreatePostPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreatePostPage.kt @@ -28,8 +28,8 @@ class CreatePostPage @Inject constructor(webInterface: WebInterface, loaders: Lo } val sender = soneRequest.core.getLocalSone(soneRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone val recipient = soneRequest.core.getSone(soneRequest.httpRequest.getPartAsStringFailsafe("recipient", 43)) - soneRequest.core.createPost(sender, recipient.asOptional(), TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), text)) - throw RedirectException(returnPage) + soneRequest.core.createPost(sender, recipient, TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), text)) + redirectTo(returnPage) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateReplyPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateReplyPage.kt index 562e647..7714b77 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateReplyPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateReplyPage.kt @@ -26,10 +26,10 @@ class CreateReplyPage @Inject constructor(webInterface: WebInterface, loaders: L templateContext["errorTextEmpty"] = true return } - val post = soneRequest.core.getPost(postId) ?: throw RedirectException("noPermission.html") + val post = soneRequest.core.getPost(postId) ?: redirectTo("noPermission.html") val sender = soneRequest.core.getLocalSone(soneRequest.httpRequest.getPartAsStringFailsafe("sender", 43)) ?: currentSone soneRequest.core.createReply(sender, post, TextFilter.filter(soneRequest.httpRequest.getHeader("Host"), text)) - throw RedirectException(returnPage) + redirectTo(returnPage) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt index b2056c2..959788b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt @@ -21,7 +21,7 @@ class CreateSonePage @Inject constructor(webInterface: WebInterface, loaders: Lo private val logger = Logger.getLogger(CreateSonePage::class.java.name) override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) { - templateContext["sones"] = soneRequest.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR) + templateContext["sones"] = soneRequest.core.localSones.sortedWith(niceNameComparator) templateContext["identitiesWithoutSone"] = soneRequest.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}".toLowerCase() } if (soneRequest.isPOST) { val identity = soneRequest.httpRequest.getPartAsStringFailsafe("identity", 43) @@ -31,7 +31,7 @@ class CreateSonePage @Inject constructor(webInterface: WebInterface, loaders: Lo logger.log(Level.SEVERE, "Could not create Sone for OwnIdentity: $ownIdentity") } setCurrentSone(soneRequest.toadletContext, sone) - throw RedirectException("index.html") + redirectTo("index.html") } templateContext["errorNoIdentity"] = true } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DebugPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DebugPage.kt new file mode 100644 index 0000000..824f6ef --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DebugPage.kt @@ -0,0 +1,18 @@ +package net.pterodactylus.sone.web.pages + +import net.pterodactylus.sone.main.* +import net.pterodactylus.sone.web.* +import net.pterodactylus.sone.web.page.* +import net.pterodactylus.util.template.* +import javax.inject.* + +@ToadletPath("debug") +class DebugPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : + SoneTemplatePage(webInterface, loaders, templateRenderer) { + + override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) { + soneRequest.core.setDebug() + redirectTo("./") + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPage.kt index d55c7cb..bd86c95 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPage.kt @@ -18,18 +18,18 @@ class DeleteAlbumPage @Inject constructor(webInterface: WebInterface, loaders: L override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { - val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html") + val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: redirectTo("invalid.html") if (!album.sone.isLocal) { - throw RedirectException("noPermission.html") + redirectTo("noPermission.html") } if (soneRequest.httpRequest.getPartAsStringFailsafe("abortDelete", 4) == "true") { - throw RedirectException("imageBrowser.html?album=${album.id}") + redirectTo("imageBrowser.html?album=${album.id}") } soneRequest.core.deleteAlbum(album) - throw RedirectException(if (album.parent.isRoot) "imageBrowser.html?sone=${album.sone.id}" else "imageBrowser.html?album=${album.parent.id}") + redirectTo(if (album.parent.isRoot) "imageBrowser.html?sone=${album.sone.id}" else "imageBrowser.html?album=${album.parent.id}") } val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getParam("album")) - templateContext["album"] = album ?: throw RedirectException("invalid.html") + templateContext["album"] = album ?: redirectTo("invalid.html") } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePage.kt index a9d601c..6459bfd 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePage.kt @@ -18,19 +18,19 @@ class DeleteImagePage @Inject constructor(webInterface: WebInterface, loaders: L override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { - val image = soneRequest.core.getImage(soneRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html") + val image = soneRequest.core.getImage(soneRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: redirectTo("invalid.html") if (!image.sone.isLocal) { - throw RedirectException("noPermission.html") + redirectTo("noPermission.html") } if (soneRequest.httpRequest.isPartSet("abortDelete")) { - throw RedirectException("imageBrowser.html?image=${image.id}") + redirectTo("imageBrowser.html?image=${image.id}") } soneRequest.core.deleteImage(image) - throw RedirectException("imageBrowser.html?album=${image.album.id}") + redirectTo("imageBrowser.html?album=${image.album.id}") } - val image = soneRequest.core.getImage(soneRequest.httpRequest.getParam("image")) ?: throw RedirectException("invalid.html") + val image = soneRequest.core.getImage(soneRequest.httpRequest.getParam("image")) ?: redirectTo("invalid.html") if (!image.sone.isLocal) { - throw RedirectException("noPermission.html") + redirectTo("noPermission.html") } templateContext["image"] = image } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeletePostPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeletePostPage.kt index b3749b3..2396370 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeletePostPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeletePostPage.kt @@ -18,22 +18,22 @@ class DeletePostPage @Inject constructor(webInterface: WebInterface, loaders: Lo override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { - val post = soneRequest.core.getPost(soneRequest.httpRequest.getPartAsStringFailsafe("post", 36)) ?: throw RedirectException("noPermission.html") + val post = soneRequest.core.getPost(soneRequest.httpRequest.getPartAsStringFailsafe("post", 36)) ?: redirectTo("noPermission.html") val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256) if (!post.sone.isLocal) { - throw RedirectException("noPermission.html") + redirectTo("noPermission.html") } if (soneRequest.httpRequest.isPartSet("confirmDelete")) { soneRequest.core.deletePost(post) - throw RedirectException(returnPage) + redirectTo(returnPage) } else if (soneRequest.httpRequest.isPartSet("abortDelete")) { - throw RedirectException(returnPage) + redirectTo(returnPage) } templateContext["post"] = post templateContext["returnPage"] = returnPage return } - templateContext["post"] = soneRequest.core.getPost(soneRequest.httpRequest.getParam("post")) ?: throw RedirectException("noPermission.html") + templateContext["post"] = soneRequest.core.getPost(soneRequest.httpRequest.getParam("post")) ?: redirectTo("noPermission.html") templateContext["returnPage"] = soneRequest.httpRequest.getParam("returnPage") } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteProfileFieldPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteProfileFieldPage.kt index 0fabad9..6303b11 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteProfileFieldPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteProfileFieldPage.kt @@ -18,13 +18,13 @@ class DeleteProfileFieldPage @Inject constructor(webInterface: WebInterface, loa override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { - val field = currentSone.profile.getFieldById(soneRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html") + val field = currentSone.profile.getFieldById(soneRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: redirectTo("invalid.html") if (soneRequest.httpRequest.getPartAsStringFailsafe("confirm", 4) == "true") { currentSone.profile = currentSone.profile.apply { removeField(field) } } - throw RedirectException("editProfile.html#profile-fields") + redirectTo("editProfile.html#profile-fields") } - val field = currentSone.profile.getFieldById(soneRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html") + val field = currentSone.profile.getFieldById(soneRequest.httpRequest.getParam("field")) ?: redirectTo("invalid.html") templateContext["field"] = field } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteReplyPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteReplyPage.kt index e3f30ce..e6fbce8 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteReplyPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteReplyPage.kt @@ -19,17 +19,17 @@ class DeleteReplyPage @Inject constructor(webInterface: WebInterface, loaders: L override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { val replyId = soneRequest.httpRequest.getPartAsStringFailsafe("reply", 36) - val reply = soneRequest.core.getPostReply(replyId) ?: throw RedirectException("noPermission.html") + val reply = soneRequest.core.getPostReply(replyId) ?: redirectTo("noPermission.html") if (!reply.sone.isLocal) { - throw RedirectException("noPermission.html") + redirectTo("noPermission.html") } val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256) if (soneRequest.httpRequest.isPartSet("confirmDelete")) { soneRequest.core.deleteReply(reply) - throw RedirectException(returnPage) + redirectTo(returnPage) } if (soneRequest.httpRequest.isPartSet("abortDelete")) { - throw RedirectException(returnPage) + redirectTo(returnPage) } templateContext["reply"] = replyId templateContext["returnPage"] = returnPage diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteSonePage.kt index f65e378..3f99730 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DeleteSonePage.kt @@ -24,7 +24,7 @@ class DeleteSonePage @Inject constructor(webInterface: WebInterface, loaders: Lo if (soneRequest.httpRequest.isPartSet("deleteSone")) { soneRequest.core.deleteSone(currentSone) } - throw RedirectException("index.html") + redirectTo("index.html") } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DismissNotificationPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DismissNotificationPage.kt index 862db67..cd348e7 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DismissNotificationPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/DismissNotificationPage.kt @@ -17,7 +17,7 @@ class DismissNotificationPage @Inject constructor(webInterface: WebInterface, lo val returnPage = soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256) val notificationId = soneRequest.httpRequest.getPartAsStringFailsafe("notification", 36) soneRequest.webInterface.getNotification(notificationId).orNull()?.takeIf { it.isDismissable }?.dismiss() - throw RedirectException(returnPage) + redirectTo(returnPage) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/DistrustPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/DistrustPage.kt deleted file mode 100644 index f2115e5..0000000 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/DistrustPage.kt +++ /dev/null @@ -1,29 +0,0 @@ -package net.pterodactylus.sone.web.pages - -import net.pterodactylus.sone.data.* -import net.pterodactylus.sone.main.* -import net.pterodactylus.sone.utils.* -import net.pterodactylus.sone.web.* -import net.pterodactylus.sone.web.page.* -import net.pterodactylus.util.template.* -import javax.inject.* - -/** - * Page that lets the user distrust another Sone. This will assign a - * configurable (negative) amount of trust to an identity. - * - * @see net.pterodactylus.sone.core.Core#distrustSone(Sone, Sone) - */ -@ToadletPath("distrust.html") -class DistrustPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : - LoggedInPage("Page.Distrust.Title", webInterface, loaders, templateRenderer) { - - override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { - if (soneRequest.isPOST) { - soneRequest.core.getSone(soneRequest.httpRequest.getPartAsStringFailsafe("sone", 44)) - ?.run { soneRequest.core.distrustSone(currentSone, this) } - throw RedirectException(soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)) - } - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPage.kt index aeaf14e..4569e9a 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPage.kt @@ -18,16 +18,16 @@ class EditAlbumPage @Inject constructor(webInterface: WebInterface, loaders: Loa override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { - val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: throw RedirectException("invalid.html") - album.takeUnless { it.sone.isLocal }?.run { throw RedirectException("noPermission.html") } + val album = soneRequest.core.getAlbum(soneRequest.httpRequest.getPartAsStringFailsafe("album", 36)) ?: redirectTo("invalid.html") + album.takeUnless { it.sone.isLocal }?.run { redirectTo("noPermission.html") } if (soneRequest.httpRequest.getPartAsStringFailsafe("moveLeft", 4) == "true") { album.parent?.moveAlbumUp(album) soneRequest.core.touchConfiguration() - throw RedirectException("imageBrowser.html?album=${album.parent?.id}") + redirectTo("imageBrowser.html?album=${album.parent?.id}") } else if (soneRequest.httpRequest.getPartAsStringFailsafe("moveRight", 4) == "true") { album.parent?.moveAlbumDown(album) soneRequest.core.touchConfiguration() - throw RedirectException("imageBrowser.html?album=${album.parent?.id}") + redirectTo("imageBrowser.html?album=${album.parent?.id}") } else { try { album.modify() @@ -35,10 +35,10 @@ class EditAlbumPage @Inject constructor(webInterface: WebInterface, loaders: Loa .setDescription(soneRequest.httpRequest.getPartAsStringFailsafe("description", 1000)) .update() } catch (e: AlbumTitleMustNotBeEmpty) { - throw RedirectException("emptyAlbumTitle.html") + redirectTo("emptyAlbumTitle.html") } soneRequest.core.touchConfiguration() - throw RedirectException("imageBrowser.html?album=${album.id}") + redirectTo("imageBrowser.html?album=${album.id}") } } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditImagePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditImagePage.kt index 76f6e23..5443d28 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditImagePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditImagePage.kt @@ -19,9 +19,9 @@ class EditImagePage @Inject constructor(webInterface: WebInterface, loaders: Loa override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { - val image = soneRequest.core.getImage(soneRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: throw RedirectException("invalid.html") + val image = soneRequest.core.getImage(soneRequest.httpRequest.getPartAsStringFailsafe("image", 36)) ?: redirectTo("invalid.html") if (!image.sone.isLocal) { - throw RedirectException("noPermission.html") + redirectTo("noPermission.html") } soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256).let { returnPage -> if (soneRequest.httpRequest.getPartAsStringFailsafe("moveLeft", 4) == "true") { @@ -38,10 +38,10 @@ class EditImagePage @Inject constructor(webInterface: WebInterface, loaders: Loa .update() soneRequest.core.touchConfiguration() } catch (e: ImageTitleMustNotBeEmpty) { - throw RedirectException("emptyImageTitle.html") + redirectTo("emptyImageTitle.html") } } - throw RedirectException(returnPage) + redirectTo(returnPage) } } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfileFieldPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfileFieldPage.kt index 386f66b..be97e80 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfileFieldPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfileFieldPage.kt @@ -20,23 +20,23 @@ class EditProfileFieldPage @Inject constructor(webInterface: WebInterface, loade currentSone.profile.let { profile -> if (soneRequest.isPOST) { if (soneRequest.httpRequest.getPartAsStringFailsafe("cancel", 4) == "true") { - throw RedirectException("editProfile.html#profile-fields") + redirectTo("editProfile.html#profile-fields") } - val field = profile.getFieldById(soneRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: throw RedirectException("invalid.html") + val field = profile.getFieldById(soneRequest.httpRequest.getPartAsStringFailsafe("field", 36)) ?: redirectTo("invalid.html") soneRequest.httpRequest.getPartAsStringFailsafe("name", 256).let { name -> try { if (name != field.name) { field.name = name currentSone.profile = profile } - throw RedirectException("editProfile.html#profile-fields") + redirectTo("editProfile.html#profile-fields") } catch (e: IllegalArgumentException) { templateContext["duplicateFieldName"] = true return } } } - templateContext["field"] = profile.getFieldById(soneRequest.httpRequest.getParam("field")) ?: throw RedirectException("invalid.html") + templateContext["field"] = profile.getFieldById(soneRequest.httpRequest.getParam("field")) ?: redirectTo("invalid.html") } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfilePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfilePage.kt index 54b7efd..93393ba 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfilePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/EditProfilePage.kt @@ -43,31 +43,31 @@ class EditProfilePage @Inject constructor(webInterface: WebInterface, loaders: L } currentSone.profile = profile soneRequest.core.touchConfiguration() - throw RedirectException("editProfile.html") + redirectTo("editProfile.html") } else if (soneRequest.httpRequest.getPartAsStringFailsafe("add-field", 4) == "true") { val fieldName = soneRequest.httpRequest.getPartAsStringFailsafe("field-name", 100) try { profile.addField(fieldName) currentSone.profile = profile soneRequest.core.touchConfiguration() - throw RedirectException("editProfile.html#profile-fields") + redirectTo("editProfile.html#profile-fields") } catch (e: DuplicateField) { templateContext["fieldName"] = fieldName templateContext["duplicateFieldName"] = true } } else profile.fields.forEach { field -> if (soneRequest.httpRequest.getPartAsStringFailsafe("delete-field-${field.id}", 4) == "true") { - throw RedirectException("deleteProfileField.html?field=${field.id}") + redirectTo("deleteProfileField.html?field=${field.id}") } else if (soneRequest.httpRequest.getPartAsStringFailsafe("edit-field-${field.id}", 4) == "true") { - throw RedirectException("editProfileField.html?field=${field.id}") + redirectTo("editProfileField.html?field=${field.id}") } else if (soneRequest.httpRequest.getPartAsStringFailsafe("move-down-field-${field.id}", 4) == "true") { profile.moveFieldDown(field) currentSone.profile = profile - throw RedirectException("editProfile.html#profile-fields") + redirectTo("editProfile.html#profile-fields") } else if (soneRequest.httpRequest.getPartAsStringFailsafe("move-up-field-${field.id}", 4) == "true") { profile.moveFieldUp(field) currentSone.profile = profile - throw RedirectException("editProfile.html#profile-fields") + redirectTo("editProfile.html#profile-fields") } } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/FollowSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/FollowSonePage.kt index 244cb52..5fd06ce 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/FollowSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/FollowSonePage.kt @@ -24,7 +24,7 @@ class FollowSonePage @Inject constructor(webInterface: WebInterface, loaders: Lo soneRequest.core.followSone(currentSone, sone.first) soneRequest.core.markSoneKnown(sone.second) } - throw RedirectException(soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)) + redirectTo(soneRequest.httpRequest.getPartAsStringFailsafe("returnPage", 256)) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt index 20219d7..a0ca0d7 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt @@ -29,9 +29,7 @@ class ImageBrowserPage @Inject constructor(webInterface: WebInterface, loaders: } else if (soneRequest.parameters["mode"] == "gallery") { templateContext["galleryRequested"] = true soneRequest.core.sones - .map(Sone::getRootAlbum) - .flatMap(Album::getAlbums) - .flatMap { Album.FLATTENER.apply(it)!! } + .flatMap(Sone::allAlbums) .filterNot(Album::isEmpty) .sortedBy(Album::getTitle) .also { albums -> diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt index ae0d7d1..01d1d22 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt @@ -28,10 +28,10 @@ class KnownSonesPage @Inject constructor(webInterface: WebInterface, loaders: Lo .filterNot { soneRequest.parameters["filter"] == "not-own" && it.isLocal } .sortedWith( when (soneRequest.parameters["sort"]) { - "images" -> Sone.IMAGE_COUNT_COMPARATOR - "name" -> Sone.NICE_NAME_COMPARATOR.reversed() - "posts" -> Sone.POST_COUNT_COMPARATOR - else -> Sone.LAST_ACTIVITY_COMPARATOR + "images" -> imageCountComparator + "name" -> niceNameComparator.reversed() + "posts" -> postCountComparator + else -> lastActivityComparator }.let { comparator -> when (soneRequest.parameters["order"]) { "asc" -> comparator.reversed() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/LikePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/LikePage.kt index cc5043f..c8369e9 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/LikePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/LikePage.kt @@ -23,7 +23,7 @@ class LikePage @Inject constructor(webInterface: WebInterface, loaders: Loaders, "reply" -> currentSone.addLikedReplyId(soneRequest.parameters["reply", 36]!!) } } - throw RedirectException(soneRequest.parameters["returnPage", 256]!!) + redirectTo(soneRequest.parameters["returnPage", 256]!!) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/LockSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/LockSonePage.kt index 2a62a7d..9f6f30e 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/LockSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/LockSonePage.kt @@ -19,7 +19,7 @@ class LockSonePage @Inject constructor(webInterface: WebInterface, loaders: Load soneRequest.parameters["sone", 44]!! .let { soneRequest.core.getLocalSone(it) } ?.let { soneRequest.core.lockSone(it) } - throw RedirectException(returnPage) + redirectTo(returnPage) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt index 9e47049..6244325 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt @@ -23,10 +23,10 @@ class LoginPage @Inject constructor(webInterface: WebInterface, loaders: Loaders soneRequest.core.getLocalSone(soneId)?.let { sone -> setCurrentSone(soneRequest.toadletContext, sone) val target = soneRequest.httpRequest.getParam("target").emptyToNull ?: "index.html" - throw RedirectException(target) + redirectTo(target) } } - templateContext["sones"] = soneRequest.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR) + templateContext["sones"] = soneRequest.core.localSones.sortedWith(niceNameComparator) templateContext["identitiesWithoutSone"] = soneRequest.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}" } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/LogoutPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/LogoutPage.kt index 325d876..a7722d4 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/LogoutPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/LogoutPage.kt @@ -17,7 +17,7 @@ class LogoutPage @Inject constructor(webInterface: WebInterface, loaders: Loader override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { setCurrentSone(soneRequest.toadletContext, null) - throw RedirectException("index.html") + redirectTo("index.html") } override fun isEnabled(soneRequest: SoneRequest): Boolean = diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/MarkAsKnownPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/MarkAsKnownPage.kt index 652881d..f919318 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/MarkAsKnownPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/MarkAsKnownPage.kt @@ -22,9 +22,9 @@ class MarkAsKnownPage @Inject constructor(webInterface: WebInterface, loaders: L "sone" -> ids.mapNotNull(soneRequest.core::getSone).forEach(soneRequest.core::markSoneKnown) "post" -> ids.mapNotNull(soneRequest.core::getPost).forEach(soneRequest.core::markPostKnown) "reply" -> ids.mapNotNull(soneRequest.core::getPostReply).forEach(soneRequest.core::markReplyKnown) - else -> throw RedirectException("invalid.html") + else -> redirectTo("invalid.html") } - throw RedirectException(soneRequest.parameters["returnPage", 256]!!) + redirectTo(soneRequest.parameters["returnPage", 256]!!) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/MetricsPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/MetricsPage.kt new file mode 100644 index 0000000..837e2c8 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/MetricsPage.kt @@ -0,0 +1,19 @@ +package net.pterodactylus.sone.web.pages + +import com.codahale.metrics.* +import net.pterodactylus.sone.main.* +import net.pterodactylus.sone.web.* +import net.pterodactylus.sone.web.page.* +import net.pterodactylus.util.template.* +import javax.inject.* + +@MenuName("Metrics") +@TemplatePath("/templates/metrics.html") +@ToadletPath("metrics.html") +class MetricsPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer, private val metricsRegistry: MetricRegistry) : SoneTemplatePage(webInterface, loaders, templateRenderer, "Page.Metrics.Title") { + + override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) { + templateContext["histograms"] = metricsRegistry.histograms + } + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt index 8829030..9465a6a 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt @@ -40,9 +40,11 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade } val fullAccessRequired = "require-full-access" in soneRequest.parameters val fcpInterfaceActive = "fcp-interface-active" in soneRequest.parameters + val strictFiltering = "strict-filtering" in soneRequest.parameters soneRequest.core.preferences.newRequireFullAccess = fullAccessRequired soneRequest.core.preferences.newFcpInterfaceActive = fcpInterfaceActive + soneRequest.core.preferences.newStrictFiltering = strictFiltering val postsPerPage = soneRequest.parameters["posts-per-page"]?.toIntOrNull() val charactersPerPost = soneRequest.parameters["characters-per-post"]?.toIntOrNull() @@ -50,9 +52,6 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade val imagesPerPage = soneRequest.parameters["images-per-page"]?.toIntOrNull() val insertionDelay = soneRequest.parameters["insertion-delay"]?.toIntOrNull() val fcpFullAccessRequired = soneRequest.parameters["fcp-full-access-required"]?.toIntOrNull() - val negativeTrust = soneRequest.parameters["negative-trust"]?.toIntOrNull() - val positiveTrust = soneRequest.parameters["positive-trust"]?.toIntOrNull() - val trustComment = soneRequest.parameters["trust-comment"]?.emptyToNull if (cantSetOption { soneRequest.core.preferences.newPostsPerPage = postsPerPage }) fieldsWithErrors += "posts-per-page" if (cantSetOption { soneRequest.core.preferences.newCharactersPerPost = charactersPerPost }) fieldsWithErrors += "characters-per-post" @@ -60,13 +59,10 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade if (cantSetOption { soneRequest.core.preferences.newImagesPerPage = imagesPerPage }) fieldsWithErrors += "images-per-page" if (cantSetOption { soneRequest.core.preferences.newInsertionDelay = insertionDelay }) fieldsWithErrors += "insertion-delay" fcpFullAccessRequired?.also { if (cantSetOption { soneRequest.core.preferences.newFcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequired] }) fieldsWithErrors += "fcp-full-access-required" } - if (cantSetOption { soneRequest.core.preferences.newNegativeTrust = negativeTrust }) fieldsWithErrors += "negative-trust" - if (cantSetOption { soneRequest.core.preferences.newPositiveTrust = positiveTrust }) fieldsWithErrors += "positive-trust" - if (cantSetOption { soneRequest.core.preferences.newTrustComment = trustComment }) fieldsWithErrors += "trust-comment" if (fieldsWithErrors.isEmpty()) { soneRequest.core.touchConfiguration() - throw RedirectException("options.html") + redirectTo("options.html") } templateContext["fieldErrors"] = fieldsWithErrors } @@ -86,11 +82,9 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade templateContext["images-per-page"] = preferences.imagesPerPage templateContext["fcp-interface-active"] = preferences.fcpInterfaceActive templateContext["require-full-access"] = preferences.requireFullAccess - templateContext["negative-trust"] = preferences.negativeTrust - templateContext["positive-trust"] = preferences.positiveTrust templateContext["post-cut-off-length"] = preferences.postCutOffLength templateContext["posts-per-page"] = preferences.postsPerPage - templateContext["trust-comment"] = preferences.trustComment + templateContext["strict-filtering"] = preferences.strictFiltering } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt index 3c14604..9bbc3e5 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt @@ -58,7 +58,7 @@ class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: val postPagination = cache.get(phrases) { soneRequest.core.sones .flatMap(Sone::getPosts) - .filter { Post.FUTURE_POSTS_FILTER.apply(it) } + .filter(noFuturePost) .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache, soneRequest.core::getReplies) } }.apply { page = soneRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 } @@ -88,7 +88,7 @@ class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: private fun Post.allText(soneNameCache: (Sone) -> String, getReplies: (String) -> Collection) = (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + getReplies(id) - .filter { PostReply.FUTURE_REPLY_FILTER.apply(it) } + .filter(noFutureReply) .map { "${soneNameCache(it.sone)} ${it.text}" }.joinToString(" ", " ")).toLowerCase() private fun Iterable.indicesFor(text: String, predicate: (Phrase) -> Boolean) = @@ -125,7 +125,7 @@ class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: } } - private fun redirect(target: String): Nothing = throw RedirectException(target) + private fun redirect(target: String): Nothing = redirectTo(target) enum class Optionality { OPTIONAL, diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt index c905ed2..86131c3 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt @@ -2,6 +2,7 @@ package net.pterodactylus.sone.web.pages import freenet.clients.http.* import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.freenet.* import net.pterodactylus.sone.main.* import net.pterodactylus.sone.utils.* import net.pterodactylus.sone.web.* @@ -20,14 +21,15 @@ open class SoneTemplatePage( templateRenderer: TemplateRenderer, private val pageTitleKey: String? = null, private val requiresLogin: Boolean = false, - private val pageTitle: (FreenetRequest) -> String = { pageTitleKey?.let(webInterface.l10n::getString) ?: "" } + private val pageTitle: (FreenetRequest) -> String = { pageTitleKey?.let(webInterface.translation::translate) ?: "" } ) : FreenetTemplatePage(templateRenderer, loaders, "noPermission.html") { private val core = webInterface.core private val sessionProvider: SessionProvider = webInterface + protected val translation: Translation = webInterface.translation protected fun getCurrentSone(toadletContext: ToadletContext, createSession: Boolean = true) = - sessionProvider.getCurrentSone(toadletContext, createSession) + sessionProvider.getCurrentSone(toadletContext) protected fun setCurrentSone(toadletContext: ToadletContext, sone: Sone?) = sessionProvider.setCurrentSone(toadletContext, sone) @@ -89,7 +91,7 @@ open class SoneTemplatePage( private val String.urlEncode: String get() = URLEncoder.encode(this, "UTF-8") override fun isEnabled(toadletContext: ToadletContext) = - isEnabled(SoneRequest(toadletContext.uri, Method.GET, HTTPRequestImpl(toadletContext.uri, "GET"), toadletContext, webInterface.l10n, webInterface.sessionManager, core, webInterface)) + isEnabled(SoneRequest(toadletContext.uri, Method.GET, HTTPRequestImpl(toadletContext.uri, "GET"), toadletContext, core, webInterface)) open fun isEnabled(soneRequest: SoneRequest) = when { requiresLogin && getCurrentSone(soneRequest.toadletContext) == null -> false diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/TrustPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/TrustPage.kt deleted file mode 100644 index 9a4c02f..0000000 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/TrustPage.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.pterodactylus.sone.web.pages - -import net.pterodactylus.sone.data.* -import net.pterodactylus.sone.main.* -import net.pterodactylus.sone.utils.* -import net.pterodactylus.sone.web.* -import net.pterodactylus.sone.web.page.* -import net.pterodactylus.util.template.* -import javax.inject.* - -/** - * Page that lets the user trust another Sone. This will assign a configurable - * amount of trust to an identity. - */ -@ToadletPath("trust.html") -class TrustPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : - LoggedInPage("Page.Trust.Title", webInterface, loaders, templateRenderer) { - - override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { - if (soneRequest.isPOST) { - soneRequest.core.getSone(soneRequest.parameters["sone"]!!)?.let { sone -> - soneRequest.core.trustSone(currentSone, sone) - } - throw RedirectException(soneRequest.parameters["returnPage", 256]) - } - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnbookmarkPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnbookmarkPage.kt index 9c226d4..aa7bbf6 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnbookmarkPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnbookmarkPage.kt @@ -21,13 +21,13 @@ class UnbookmarkPage @Inject constructor(webInterface: WebInterface, loaders: Lo soneRequest.core.bookmarkedPosts .filterNot(Post::isLoaded) .forEach(soneRequest.core::unbookmarkPost) - throw RedirectException("bookmarks.html") + redirectTo("bookmarks.html") } soneRequest.isPOST -> { soneRequest.parameters["post", 36] ?.let(soneRequest.core::getPost) ?.also(soneRequest.core::unbookmarkPost) - throw RedirectException(soneRequest.parameters["returnPage", 256]) + redirectTo(soneRequest.parameters["returnPage", 256]) } } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnfollowSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnfollowSonePage.kt index 79fdef5..9dbe9f1 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnfollowSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnfollowSonePage.kt @@ -19,7 +19,7 @@ class UnfollowSonePage @Inject constructor(webInterface: WebInterface, loaders: if (soneRequest.isPOST) { soneRequest.parameters["sone"]!!.split(Regex("[ ,]+")) .forEach { soneRequest.core.unfollowSone(currentSone, it) } - throw RedirectException(soneRequest.parameters["returnPage", 256]) + redirectTo(soneRequest.parameters["returnPage", 256]) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlikePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlikePage.kt index 82147a9..46bd3ab 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlikePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlikePage.kt @@ -21,7 +21,7 @@ class UnlikePage @Inject constructor(webInterface: WebInterface, loaders: Loader "post" -> currentSone.removeLikedPostId(soneRequest.parameters["post"]!!) "reply" -> currentSone.removeLikedReplyId(soneRequest.parameters["reply"]!!) } - throw RedirectException(soneRequest.parameters["returnPage", 256]) + redirectTo(soneRequest.parameters["returnPage", 256]) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlockSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlockSonePage.kt index 2c5735b..450a466 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlockSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/UnlockSonePage.kt @@ -19,7 +19,7 @@ class UnlockSonePage @Inject constructor(webInterface: WebInterface, loaders: Lo soneRequest.parameters["sone", 44] .let(soneRequest.core::getLocalSone) ?.also(soneRequest.core::unlockSone) - throw RedirectException(soneRequest.parameters["returnPage", 256]) + redirectTo(soneRequest.parameters["returnPage", 256]) } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/UntrustPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/UntrustPage.kt deleted file mode 100644 index 7dd342e..0000000 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/UntrustPage.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.pterodactylus.sone.web.pages - -import net.pterodactylus.sone.data.* -import net.pterodactylus.sone.main.* -import net.pterodactylus.sone.utils.* -import net.pterodactylus.sone.web.* -import net.pterodactylus.sone.web.page.* -import net.pterodactylus.util.template.* -import javax.inject.* - -/** - * Page that lets the user untrust another Sone. This will remove all trust - * assignments for an identity. - */ -@ToadletPath("untrust.html") -class UntrustPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer) : - LoggedInPage("Page.Untrust.Title", webInterface, loaders, templateRenderer) { - - override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { - if (soneRequest.isPOST) { - soneRequest.parameters["sone", 44]!! - .let(soneRequest.core::getSone) - ?.also { soneRequest.core.untrustSone(currentSone, it) } - throw RedirectException(soneRequest.parameters["returnPage", 256]) - } - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/UploadImagePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/UploadImagePage.kt index de61835..0021c6b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/UploadImagePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/UploadImagePage.kt @@ -23,17 +23,17 @@ class UploadImagePage @Inject constructor(webInterface: WebInterface, loaders: L override fun handleRequest(soneRequest: SoneRequest, currentSone: Sone, templateContext: TemplateContext) { if (soneRequest.isPOST) { - val parentAlbum = soneRequest.parameters["parent"]!!.let(soneRequest.core::getAlbum) ?: throw RedirectException("noPermission.html") + val parentAlbum = soneRequest.parameters["parent"]!!.let(soneRequest.core::getAlbum) ?: redirectTo("noPermission.html") if (parentAlbum.sone != currentSone) { - throw RedirectException("noPermission.html") + redirectTo("noPermission.html") } - val title = soneRequest.parameters["title", 200].emptyToNull ?: throw RedirectException("emptyImageTitle.html") + val title = soneRequest.parameters["title", 200].emptyToNull ?: redirectTo("emptyImageTitle.html") val uploadedFile = soneRequest.httpRequest.getUploadedFile("image") val bytes = uploadedFile.data.use { it.toByteArray() } val bufferedImage = bytes.toImage() if (bufferedImage == null) { - templateContext["messages"] = soneRequest.l10n.getString("Page.UploadImage.Error.InvalidImage") + templateContext["messages"] = translation.translate("Page.UploadImage.Error.InvalidImage") return } @@ -44,7 +44,7 @@ class UploadImagePage @Inject constructor(webInterface: WebInterface, loaders: L setTitle(title) setDescription(TextFilter.filter(soneRequest.headers["Host"], soneRequest.parameters["description", 4000])) }.update() - throw RedirectException("imageBrowser.html?album=${parentAlbum.id}") + redirectTo("imageBrowser.html?album=${parentAlbum.id}") } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/ViewSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/ViewSonePage.kt index a498d24..efaee3f 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/ViewSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/ViewSonePage.kt @@ -51,7 +51,7 @@ class ViewSonePage @Inject constructor(webInterface: WebInterface, loaders: Load override fun getPageTitle(soneRequest: SoneRequest): String = soneRequest.parameters["sone"]!!.let(soneRequest.core::getSone)?.let { sone -> - "${SoneAccessor.getNiceName(sone)} - ${soneRequest.l10n.getString("Page.ViewSone.Title")}" - } ?: soneRequest.l10n.getString("Page.ViewSone.Page.TitleWithoutSone") + "${SoneAccessor.getNiceName(sone)} - ${translation.translate("Page.ViewSone.Title")}" + } ?: translation.translate("Page.ViewSone.Page.TitleWithoutSone") } diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties index 7e42167..cd42c45 100644 --- a/src/main/resources/i18n/sone.de.properties +++ b/src/main/resources/i18n/sone.de.properties @@ -26,6 +26,8 @@ Navigation.Menu.Sone.Item.Rescue.Name=Sonerettung Navigation.Menu.Sone.Item.Rescue.Tooltip=Rettet Ihre Sone Navigation.Menu.Sone.Item.About.Name=Über Sone Navigation.Menu.Sone.Item.About.Tooltip=Informationen über Sone +Navigation.Menu.Sone.Item.Metrics.Name=Metriken +Navigation.Menu.Sone.Item.Metrics.Tooltip=Von Sone gesammelte Metriken Page.About.Title=Über - Sone Page.About.Page.Title=Über @@ -66,16 +68,14 @@ Page.Options.Option.ImagesPerPage.Description=Anzahl der Bilder pro Seite. Page.Options.Option.CharactersPerPost.Description=Die Anzahl der Zeichen, die eine Nachricht enthalten muss, damit sie gekürzt angezeigt wird (-1 für „nie kürzen“). Die Anzahl der tatsächlich angezeigten Zeichen wird in der nächsten Option konfiguriert. Page.Options.Option.PostCutOffLength.Description=Die Anzahl der Zeichen, die von einer gekürzten Nachricht sichtbar sind (siehe Option hierüber). Wird ignoriert, wenn die Option hierüber deaktiviert ist, bzw. auf -1 steht. Page.Options.Option.RequireFullAccess.Description=Zugriff auf Sone für alle Rechner, die keinen vollen Zugriff haben, unterbinden. -Page.Options.Section.TrustOptions.Title=Vertrauenseinstellungen -Page.Options.Option.PositiveTrust.Description=Die Menge an positivem Vertrauen, die bei einem Klick auf den Haken unter einer Nachricht zugewiesen werden soll. -Page.Options.Option.NegativeTrust.Description=Die Menge an negativem Vertrauen, die bei einem Klick auf das rote X unter einer Nachricht zugewiesen werden soll. Dieser Wert sollte negativ sein. -Page.Options.Option.TrustComment.Description=Der Kommentar, der im Web of Trust für Ihre Zuweisung angezeigt werden soll. Page.Options.Section.FcpOptions.Title=FCP-Schnittstellenoptionen Page.Options.Option.FcpInterfaceActive.Description=Die FCP-Schnittstelle aktivieren, um anderen Plugins und Programmen den Zugriff auf Ihr Sone-Plugin zu ermöglichen. Page.Options.Option.FcpFullAccessRequired.Description=FCP-Verbindungen nur von „erlaubten Hosts“ erlauben (siehe {link}Knoten-Konfiguration, Abschnitt “FCP”{/link}). Page.Options.Option.FcpFullAccessRequired.Value.No=Nein Page.Options.Option.FcpFullAccessRequired.Value.Writing=Für Schreibzugriffe Page.Options.Option.FcpFullAccessRequired.Value.Always=Immer +Page.Options.Section.WebOfTrustOptions.Title=„Web of Trust“ Optionen +Page.Options.Option.StrictFiltering.Description=Identitäten strenger filtern. Wenn diese Option gewählt ist, werden Identitäten, die von mindestens einer Ihrer lokalen Identitäten einen negativen Vertrauenswert zugewiesen bekommen haben, komplett ignoriert; ansonsten werden Identitäten gezeigt, wenn sie von mindestens einer Ihrer Identitäten einen positiven Vertrauenswert zugewiesen bekommen. (Bitte beachten Sie, dass diese Einstellung ein paar Minuten braucht, um Wirkung zu zeigen!) Page.Options.Section.Cleaning.Title=Aufräumen Page.Options.Option.ClearOnNextRestart.Description=Setzt die Konfiguration des Sone-Plugins beim nächsten Start zurück. Vorsicht: {strong}Alle Informationen Ihrer Sones werden gelöscht{/strong}, also stellen Sie bitte sicher, dass Sie die notwendigen Sicherungen angefertigt haben! Damit diese Option aktiv wird, muss auch die folgende Option aktiviert werden. Page.Options.Option.ReallyClearOnNextRestart.Description=Diese Option muss auf „ja“ gestellt werden, wenn Sie wirklich {strong}wirklich{/strong} sämtliche Informationen des Sone-Plugins beim nächsten Start entfernen möchten. @@ -163,7 +163,7 @@ Page.EditProfileField.Page.Title=Benutzerdefiniertes Feld bearbeiten Page.EditProfileField.Text=Bitte geben Sie einen neuen Namen für dieses benutzerdefinierte Feld ein. Page.EditProfileField.Error.DuplicateFieldName=Der von Ihnen eingegebene Feldname existiert bereits. Page.EditProfileField.Button.Save=Ändern -Page.EditProfileField.Button.Reset=Aten Namen wiederherstellen +Page.EditProfileField.Button.Reset=Alten Namen wiederherstellen Page.EditProfileField.Button.Cancel=Namen nicht ändern Page.DeleteProfileField.Title=Benutzerdefiniertes Feld löschen - Sone @@ -276,12 +276,6 @@ Page.DeleteAlbum.Text.AlbumWillBeGone=Ihr Album „{title}“ wird gelöscht. M Page.DeleteAlbum.Button.Yes=Ja, Album löschen. Page.DeleteAlbum.Button.No=Nein, Album nicht löschen. -Page.Trust.Title=Sone vertrauen - Sone - -Page.Distrust.Title=Sone misstrauen - Sone - -Page.Untrust.Title=Sone nicht vertrauen - Sone - Page.MarkAsKnown.Title=Als gelesen markieren - Sone Page.Bookmark.Title=Als Favorit markieren - Sone @@ -305,7 +299,7 @@ Page.Rescue.Text.Fetching=Die Sonerettung versucht gerade, Version {0} Ihrer Son Page.Rescue.Text.Fetched=Die Sonerettung hat Version {0} Ihrer Sone herunter geladen. Bitte überprüfen Sie Ihre Nachrichten, Antworten und Ihr Profile. Bei Gefallen können Sie die Sone einfach entsperren. Page.Rescue.Text.FetchedLast=Die Sonerettung hat die letzte verfügbare Version Ihrer Sone herunter geladen. Wenn bis jetzt keine Version dabei war, die Sie wiederherstellen möchten, haben Sie jetzt kein Glück. Page.Rescue.Text.NotFetched=Die Sonerettung konnte Version {0} Ihrer Sone nicht herunter laden. Bitte versuchen Sie erneut, Version {0} herunter zu laden, oder versuchen Sie die nächstältere Version. -Page.Rescue.Label.NextEdition=Nächste Version: +Page.Rescue.Label.NextEdition=Nächste Version Page.Rescue.Button.Fetch=Version herunter laden Page.NoPermission.Title=Unberechtigter Zugriff - Sone @@ -331,6 +325,12 @@ Page.Invalid.Title=Ungültige Aktion ausgeführt - Sone Page.Invalid.Page.Title=Ungültige Aktion ausgeführt Page.Invalid.Text=Eine ungültige Aktion wurde ausgeführt, oder eine gültige Aktion hatte ungültige Parameter. Bitte kehren Sie zur {link}Hauptseite{/link} zurück und versuchen Sie Ihre Aktion erneut. Wenn der Fehler weiterhin besteht, haben Sie wahrscheinlich einen Programmierfehler gefunden. +Page.Metrics.Title=Metriken +Page.Metrics.Page.Title=Metriken +Page.Metrics.SoneInsertDuration.Title=Hochladedauer einer Sone +Page.Metrics.SoneParseDuration.Title=Parsdauer einer Sone +Page.Metrics.ConfigurationSaveDuration.Title=Konfigurationsspeicherdauer + View.Search.Button.Search=Suchen View.CreateSone.Text.WotIdentityRequired=Um eine Sone anzulegen, brauchen Sie eine Identität aus dem {link}„Web of Trust“ Plugin{/link}. @@ -358,9 +358,10 @@ View.Sone.Status.Downloading=Diese Sone wird gerade herunter geladen. View.Sone.Status.Inserting=Diese Sone wird gerade hoch geladen. View.SoneMenu.Link.AllAlbums=alle Alben +View.SoneMenu.WebOfTrustLink=„Web of Trust“ Profil View.Post.UnknownAuthor=(unbekannt) -View.Post.WebOfTrustLink=„Web of Trust“ Profil +View.Post.WebOfTrustLink=WoT-Profil View.Post.Permalink=Nachrichtenlink View.Post.PermalinkAuthor=Autorenlink View.Post.Bookmarks.PostIsBookmarked=Nachricht ist ein Favorit, klicken, um Favoritenmarkierung zu entfernen @@ -377,10 +378,6 @@ View.Post.ShowLess=weniger zeigen View.UpdateStatus.Text.ChooseSenderIdentity=Absender wählen -View.Trust.Tooltip.Trust=Dieser Sone vertrauen -View.Trust.Tooltip.Distrust=Dieser Sone misstrauen -View.Trust.Tooltip.Untrust=Dieser Sone kein Vertrauen zuweisen - View.CreateAlbum.Title=Album anlegen View.CreateAlbum.Label.Name=Name: View.CreateAlbum.Label.Description=Beschreibung: @@ -431,9 +428,6 @@ WebInterface.DefaultText.Option.PostsPerPage=Anzahl der Nachrichten pro Seite WebInterface.DefaultText.Option.ImagesPerPage=Anzahl der Bilder pro Seite WebInterface.DefaultText.Option.CharactersPerPost=Anzahl der Zeichen, die eine Nachricht haben muss, damit er gekürzt wird WebInterface.DefaultText.Option.PostCutOffLength=Anzahl der Zeichen, die von einer gekürzten Nachricht angezeigt werden -WebInterface.DefaultText.Option.PositiveTrust=Der positive Vertrauenswert -WebInterface.DefaultText.Option.NegativeTrust=Der negative Vertrauenswert -WebInterface.DefaultText.Option.TrustComment=Der Kommentar für die Vertrauenszuweisung WebInterface.Button.Comment=Antworten WebInterface.Confirmation.DeletePostButton=Ja, löschen! WebInterface.Confirmation.DeleteReplyButton=Ja, löschen! @@ -471,3 +465,4 @@ Notification.Mention.Text=Sie wurden in diesen Nachrichten erwähnt: Notification.SoneIsInserting.Text=Ihre Sone sone://{0} wird jetzt hoch geladen. Notification.SoneIsInserted.Text=Ihre Sone sone://{0} wurde in {1,number} {1,choice,0#Sekunden|1#Sekunde|1