X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fsone%2Fcore%2FCore.java;h=f7b56a1022a952130bb51b4de661a5185f0efc9a;hb=4a2fdf3b0b3dfcfc761c83797018237ec4c82a4b;hp=3f2111a6e32dc49b5c142505e7863f7cfdb9e632;hpb=9dafc7dd9ac7da59e0b27581ea360f69e6097531;p=Sone.git diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 3f2111a..f7b56a1 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -40,6 +40,8 @@ import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.sone.freenet.wot.IdentityListener; import net.pterodactylus.sone.freenet.wot.IdentityManager; import net.pterodactylus.sone.freenet.wot.OwnIdentity; +import net.pterodactylus.sone.freenet.wot.Trust; +import net.pterodactylus.sone.freenet.wot.WebOfTrustException; import net.pterodactylus.sone.main.SonePlugin; import net.pterodactylus.util.config.Configuration; import net.pterodactylus.util.config.ConfigurationException; @@ -86,6 +88,9 @@ public class Core implements IdentityListener { /** The configuration. */ private Configuration configuration; + /** Whether we’re currently saving the configuration. */ + private boolean storingConfiguration = false; + /** The identity manager. */ private final IdentityManager identityManager; @@ -144,6 +149,9 @@ public class Core implements IdentityListener { /** All known replies. */ private Set knownReplies = new HashSet(); + /** Trusted identities, sorted by own identities. */ + private Map> trustedIdentities = Collections.synchronizedMap(new HashMap>()); + /** * Creates a new core. * @@ -500,6 +508,19 @@ public class Core implements IdentityListener { } /** + * Returns whether the target Sone is trusted by the origin Sone. + * + * @param origin + * The origin Sone + * @param target + * The target Sone + * @return {@code true} if the target Sone is trusted by the origin Sone + */ + public boolean isSoneTrusted(Sone origin, Sone target) { + return trustedIdentities.containsKey(origin) && trustedIdentities.get(origin.getIdentity()).contains(target); + } + + /** * Returns the post with the given ID. * * @param postId @@ -541,27 +562,63 @@ public class Core implements IdentityListener { * otherwise */ public boolean isNewPost(String postId) { + return isNewPost(postId, true); + } + + /** + * Returns whether the given post ID is new. If {@code markAsKnown} is + * {@code true} then after this method returns the post ID is marked a known + * post ID. + * + * @param postId + * The post ID + * @param markAsKnown + * {@code true} to mark the post ID as known, {@code false} to + * not to mark it as known + * @return {@code true} if the post is considered to be new, {@code false} + * otherwise + */ + public boolean isNewPost(String postId, boolean markAsKnown) { synchronized (newPosts) { - boolean isNew = !knownPosts.contains(postId) && newPosts.remove(postId); - knownPosts.add(postId); - if (isNew) { - coreListenerManager.fireMarkPostKnown(getPost(postId)); + boolean isNew = !knownPosts.contains(postId) && newPosts.contains(postId); + if (markAsKnown) { + Post post = getPost(postId, false); + if (post != null) { + markPostKnown(post); + } } return isNew; } } /** - * Returns the reply with the given ID. + * Returns the reply with the given ID. If there is no reply with the given + * ID yet, a new one is created. * * @param replyId * The ID of the reply to get - * @return The reply, or {@code null} if there is no such reply + * @return The reply */ public Reply getReply(String replyId) { + return getReply(replyId, true); + } + + /** + * Returns the reply with the given ID. If there is no reply with the given + * ID yet, a new one is created, unless {@code create} is false in which + * case {@code null} is returned. + * + * @param replyId + * The ID of the reply to get + * @param create + * {@code true} to always return a {@link Reply}, {@code false} + * to return {@code null} if no reply can be found + * @return The reply, or {@code null} if there is no such reply + */ + public Reply getReply(String replyId, boolean create) { synchronized (replies) { Reply reply = replies.get(replyId); - if (reply == null) { + if (create && (reply == null)) { reply = new Reply(replyId); replies.put(replyId, reply); } @@ -599,11 +656,28 @@ public class Core implements IdentityListener { * otherwise */ public boolean isNewReply(String replyId) { + return isNewReply(replyId, true); + } + + /** + * Returns whether the reply with the given ID is new. + * + * @param replyId + * The ID of the reply to check + * @param markAsKnown + * {@code true} to mark the reply as known, {@code false} to not + * to mark it as known + * @return {@code true} if the reply is considered to be new, {@code false} + * otherwise + */ + public boolean isNewReply(String replyId, boolean markAsKnown) { synchronized (newReplies) { - boolean isNew = !knownReplies.contains(replyId) && newReplies.remove(replyId); - knownReplies.add(replyId); - if (isNew) { - coreListenerManager.fireMarkReplyKnown(getReply(replyId)); + boolean isNew = !knownReplies.contains(replyId) && newReplies.contains(replyId); + if (markAsKnown) { + Reply reply = getReply(replyId, false); + if (reply != null) { + markReplyKnown(reply); + } } return isNew; } @@ -657,7 +731,9 @@ public class Core implements IdentityListener { */ public void lockSone(Sone sone) { synchronized (lockedSones) { - lockedSones.add(sone); + if (lockedSones.add(sone)) { + coreListenerManager.fireSoneLocked(sone); + } } } @@ -670,7 +746,9 @@ public class Core implements IdentityListener { */ public void unlockSone(Sone sone) { synchronized (lockedSones) { - lockedSones.remove(sone); + if (lockedSones.remove(sone)) { + coreListenerManager.fireSoneUnlocked(sone); + } } } @@ -765,7 +843,12 @@ public class Core implements IdentityListener { * @return The created Sone */ public Sone createSone(OwnIdentity ownIdentity) { - identityManager.addContext(ownIdentity, "Sone"); + try { + ownIdentity.addContext("Sone"); + } catch (WebOfTrustException wote1) { + logger.log(Level.SEVERE, "Could not add “Sone” context to own identity: " + ownIdentity, wote1); + return null; + } Sone sone = addLocalSone(ownIdentity); return sone; } @@ -815,6 +898,28 @@ public class Core implements IdentityListener { } /** + * Retrieves the trust relationship from the origin to the target. + * + * @param origin + * The origin of the trust tree + * @param target + * The target of the trust + * @return The trust relationship + */ + public Trust getTrust(Sone origin, Sone target) { + if (!isLocalSone(origin)) { + logger.log(Level.WARNING, "Tried to get trust from remote Sone: %s", origin); + return null; + } + try { + return target.getIdentity().getTrust((OwnIdentity) origin.getIdentity()); + } catch (WebOfTrustException wote1) { + logger.log(Level.WARNING, "Could not get trust for Sone: " + target, wote1); + return null; + } + } + + /** * Updates the stores Sone with the given Sone. * * @param sone @@ -832,6 +937,9 @@ public class Core implements IdentityListener { if (!soneRescueMode) { for (Post post : storedSone.getPosts()) { posts.remove(post.getId()); + if (!sone.getPosts().contains(post)) { + coreListenerManager.firePostRemoved(post); + } } } synchronized (newPosts) { @@ -849,6 +957,9 @@ public class Core implements IdentityListener { if (!soneRescueMode) { for (Reply reply : storedSone.getReplies()) { replies.remove(reply.getId()); + if (!sone.getReplies().contains(reply)) { + coreListenerManager.fireReplyRemoved(reply); + } } } synchronized (newReplies) { @@ -913,8 +1024,12 @@ public class Core implements IdentityListener { localSones.remove(sone.getId()); soneInserters.remove(sone).stop(); } - identityManager.removeContext((OwnIdentity) sone.getIdentity(), "Sone"); - identityManager.removeProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition"); + try { + ((OwnIdentity) sone.getIdentity()).removeContext("Sone"); + ((OwnIdentity) sone.getIdentity()).removeProperty("Sone.LatestEdition"); + } catch (WebOfTrustException wote1) { + logger.log(Level.WARNING, "Could not remove context and properties from Sone: " + sone, wote1); + } try { configuration.getLongValue("Sone/" + sone.getId() + "/Time").setValue(null); } catch (ConfigurationException ce1) { @@ -961,13 +1076,18 @@ public class Core implements IdentityListener { if (postId == null) { break; } + String postRecipientId = configuration.getStringValue(postPrefix + "/Recipient").getValue(null); long postTime = configuration.getLongValue(postPrefix + "/Time").getValue((long) 0); String postText = configuration.getStringValue(postPrefix + "/Text").getValue(null); if ((postTime == 0) || (postText == null)) { logger.log(Level.WARNING, "Invalid post found, aborting load!"); return; } - posts.add(getPost(postId).setSone(sone).setTime(postTime).setText(postText)); + Post post = getPost(postId).setSone(sone).setTime(postTime).setText(postText); + if ((postRecipientId != null) && (postRecipientId.length() == 43)) { + post.setRecipient(getSone(postRecipientId)); + } + posts.add(post); } /* load replies. */ @@ -1053,7 +1173,7 @@ public class Core implements IdentityListener { * @param sone * The Sone to save */ - public void saveSone(Sone sone) { + public synchronized void saveSone(Sone sone) { if (!isLocalSone(sone)) { logger.log(Level.FINE, "Tried to save non-local Sone: %s", sone); return; @@ -1064,8 +1184,9 @@ public class Core implements IdentityListener { } logger.log(Level.INFO, "Saving Sone: %s", sone); - identityManager.setProperty((OwnIdentity) sone.getIdentity(), "Sone.LatestEdition", String.valueOf(sone.getLatestEdition())); try { + ((OwnIdentity) sone.getIdentity()).setProperty("Sone.LatestEdition", String.valueOf(sone.getLatestEdition())); + /* save Sone into configuration. */ String sonePrefix = "Sone/" + sone.getId(); configuration.getLongValue(sonePrefix + "/Time").setValue(sone.getTime()); @@ -1085,6 +1206,7 @@ public class Core implements IdentityListener { for (Post post : sone.getPosts()) { String postPrefix = sonePrefix + "/Posts/" + postCounter++; configuration.getStringValue(postPrefix + "/ID").setValue(post.getId()); + configuration.getStringValue(postPrefix + "/Recipient").setValue((post.getRecipient() != null) ? post.getRecipient().getId() : null); configuration.getLongValue(postPrefix + "/Time").setValue(post.getTime()); configuration.getStringValue(postPrefix + "/Text").setValue(post.getText()); } @@ -1122,9 +1244,12 @@ public class Core implements IdentityListener { } configuration.getStringValue(sonePrefix + "/Friends/" + friendCounter + "/ID").setValue(null); + configuration.save(); logger.log(Level.INFO, "Sone %s saved.", sone); } catch (ConfigurationException ce1) { logger.log(Level.WARNING, "Could not save Sone: " + sone, ce1); + } catch (WebOfTrustException wote1) { + logger.log(Level.WARNING, "Could not set WoT property for Sone: " + sone, wote1); } } @@ -1153,11 +1278,48 @@ public class Core implements IdentityListener { * @return The created post */ public Post createPost(Sone sone, long time, String text) { + return createPost(sone, null, time, text); + } + + /** + * Creates a new post. + * + * @param sone + * The Sone that creates the post + * @param recipient + * The recipient Sone, or {@code null} if this post does not have + * a recipient + * @param text + * The text of the post + * @return The created post + */ + public Post createPost(Sone sone, Sone recipient, String text) { + return createPost(sone, recipient, System.currentTimeMillis(), text); + } + + /** + * Creates a new post. + * + * @param sone + * The Sone that creates the post + * @param recipient + * The recipient Sone, or {@code null} if this post does not have + * a recipient + * @param time + * The time of the post + * @param text + * The text of the post + * @return The created post + */ + public Post createPost(Sone sone, Sone recipient, long time, String text) { if (!isLocalSone(sone)) { logger.log(Level.FINE, "Tried to create post for non-local Sone: %s", sone); return null; } Post post = new Post(sone, time, text); + if (recipient != null) { + post.setRecipient(recipient); + } synchronized (posts) { posts.put(post.getId(), post); } @@ -1188,21 +1350,6 @@ public class Core implements IdentityListener { } /** - * Creates a new reply. - * - * @param sone - * The Sone that creates the reply - * @param post - * The post that this reply refers to - * @param text - * The text of the reply - * @return The created reply - */ - public Reply createReply(Sone sone, Post post, String text) { - return createReply(sone, post, System.currentTimeMillis(), text); - } - - /** * Marks the given post as known, if it is currently a new post (according * to {@link #isNewPost(String)}). * @@ -1214,6 +1361,7 @@ public class Core implements IdentityListener { if (newPosts.remove(post.getId())) { knownPosts.add(post.getId()); coreListenerManager.fireMarkPostKnown(post); + saveConfiguration(); } } } @@ -1225,6 +1373,21 @@ public class Core implements IdentityListener { * The Sone that creates the reply * @param post * The post that this reply refers to + * @param text + * The text of the reply + * @return The created reply + */ + public Reply createReply(Sone sone, Post post, String text) { + return createReply(sone, post, System.currentTimeMillis(), text); + } + + /** + * Creates a new reply. + * + * @param sone + * The Sone that creates the reply + * @param post + * The post that this reply refers to * @param time * The time of the reply * @param text @@ -1279,6 +1442,7 @@ public class Core implements IdentityListener { if (newReplies.remove(reply.getId())) { knownReplies.add(reply.getId()); coreListenerManager.fireMarkReplyKnown(reply); + saveConfiguration(); } } } @@ -1299,6 +1463,7 @@ public class Core implements IdentityListener { soneInserter.stop(); } } + soneDownloader.stop(); saveConfiguration(); stopped = true; } @@ -1307,6 +1472,14 @@ public class Core implements IdentityListener { * Saves the current options. */ public void saveConfiguration() { + synchronized (configuration) { + if (storingConfiguration) { + logger.log(Level.FINE, "Already storing configuration…"); + return; + } + storingConfiguration = true; + } + /* store the options first. */ try { configuration.getIntValue("Option/InsertionDelay").setValue(options.getIntegerOption("InsertionDelay").getReal()); @@ -1346,6 +1519,10 @@ public class Core implements IdentityListener { } catch (ConfigurationException ce1) { logger.log(Level.SEVERE, "Could not store configuration!", ce1); + } finally { + synchronized (configuration) { + storingConfiguration = false; + } } } @@ -1451,6 +1628,7 @@ public class Core implements IdentityListener { public void ownIdentityAdded(OwnIdentity ownIdentity) { logger.log(Level.FINEST, "Adding OwnIdentity: " + ownIdentity); if (ownIdentity.hasContext("Sone")) { + trustedIdentities.put(ownIdentity, Collections.synchronizedSet(new HashSet())); addLocalSone(ownIdentity); } } @@ -1461,14 +1639,16 @@ public class Core implements IdentityListener { @Override public void ownIdentityRemoved(OwnIdentity ownIdentity) { logger.log(Level.FINEST, "Removing OwnIdentity: " + ownIdentity); + trustedIdentities.remove(ownIdentity); } /** * {@inheritDoc} */ @Override - public void identityAdded(Identity identity) { + public void identityAdded(OwnIdentity ownIdentity, Identity identity) { logger.log(Level.FINEST, "Adding Identity: " + identity); + trustedIdentities.get(ownIdentity).add(identity); addRemoteSone(identity); } @@ -1476,7 +1656,7 @@ public class Core implements IdentityListener { * {@inheritDoc} */ @Override - public void identityUpdated(final Identity identity) { + public void identityUpdated(OwnIdentity ownIdentity, final Identity identity) { new Thread(new Runnable() { @Override @@ -1492,8 +1672,8 @@ public class Core implements IdentityListener { * {@inheritDoc} */ @Override - public void identityRemoved(Identity identity) { - /* TODO */ + public void identityRemoved(OwnIdentity ownIdentity, Identity identity) { + trustedIdentities.get(ownIdentity).remove(identity); } }