Remove unused method.
[Sone.git] / src / main / java / net / pterodactylus / sone / freenet / wot / WebOfTrustConnector.java
1 /*
2  * Sone - WebOfTrustConnector.java - Copyright © 2010–2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.freenet.wot;
19
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.atomic.AtomicLong;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27
28 import net.pterodactylus.sone.freenet.plugin.PluginConnector;
29 import net.pterodactylus.sone.freenet.plugin.PluginException;
30 import net.pterodactylus.sone.freenet.plugin.event.ReceivedReplyEvent;
31 import net.pterodactylus.util.logging.Logging;
32 import net.pterodactylus.util.number.Numbers;
33
34 import com.google.common.collect.MapMaker;
35 import com.google.common.eventbus.Subscribe;
36 import com.google.inject.Inject;
37
38 import freenet.support.SimpleFieldSet;
39 import freenet.support.api.Bucket;
40
41 /**
42  * Connector for the Web of Trust plugin.
43  *
44  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
45  */
46 public class WebOfTrustConnector {
47
48         /** The logger. */
49         private static final Logger logger = Logging.getLogger(WebOfTrustConnector.class);
50
51         /** The name of the WoT plugin. */
52         private static final String WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust";
53
54         /** Counter for connection identifiers. */
55         private final AtomicLong counter = new AtomicLong();
56
57         /** The plugin connector. */
58         private final PluginConnector pluginConnector;
59
60         /** Map for replies. */
61         private final Map<PluginIdentifier, Reply> replies = new MapMaker().makeMap();
62
63         /**
64          * Creates a new Web of Trust connector that uses the given plugin
65          * connector.
66          *
67          * @param pluginConnector
68          *            The plugin connector
69          */
70         @Inject
71         public WebOfTrustConnector(PluginConnector pluginConnector) {
72                 this.pluginConnector = pluginConnector;
73         }
74
75         //
76         // ACTIONS
77         //
78
79         /**
80          * Stops the web of trust connector.
81          */
82         public void stop() {
83                 /* does nothing. */
84         }
85
86         /**
87          * Loads all own identities from the Web of Trust plugin.
88          *
89          * @return All own identity
90          * @throws WebOfTrustException
91          *             if the own identities can not be loaded
92          */
93         public Set<OwnIdentity> loadAllOwnIdentities() throws WebOfTrustException {
94                 Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get());
95                 SimpleFieldSet fields = reply.getFields();
96                 int ownIdentityCounter = -1;
97                 Set<OwnIdentity> ownIdentities = new HashSet<OwnIdentity>();
98                 while (true) {
99                         String id = fields.get("Identity" + ++ownIdentityCounter);
100                         if (id == null) {
101                                 break;
102                         }
103                         String requestUri = fields.get("RequestURI" + ownIdentityCounter);
104                         String insertUri = fields.get("InsertURI" + ownIdentityCounter);
105                         String nickname = fields.get("Nickname" + ownIdentityCounter);
106                         DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(id, nickname, requestUri, insertUri);
107                         ownIdentity.setContexts(parseContexts("Contexts" + ownIdentityCounter + ".", fields));
108                         ownIdentity.setProperties(parseProperties("Properties" + ownIdentityCounter + ".", fields));
109                         ownIdentities.add(ownIdentity);
110                 }
111                 return ownIdentities;
112         }
113
114         /**
115          * Loads all identities that the given identities trusts with a score of
116          * more than 0 and the (optional) given context.
117          *
118          * @param ownIdentity
119          *            The own identity
120          * @param context
121          *            The context to filter, or {@code null}
122          * @return All trusted identities
123          * @throws PluginException
124          *             if an error occured talking to the Web of Trust plugin
125          */
126         public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity, String context) throws PluginException {
127                 Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", (context == null) ? "" : context).put("WantTrustValues", "true").get());
128                 SimpleFieldSet fields = reply.getFields();
129                 Set<Identity> identities = new HashSet<Identity>();
130                 int identityCounter = -1;
131                 while (true) {
132                         String id = fields.get("Identity" + ++identityCounter);
133                         if (id == null) {
134                                 break;
135                         }
136                         String nickname = fields.get("Nickname" + identityCounter);
137                         String requestUri = fields.get("RequestURI" + identityCounter);
138                         DefaultIdentity identity = new DefaultIdentity(id, nickname, requestUri);
139                         identity.setContexts(parseContexts("Contexts" + identityCounter + ".", fields));
140                         identity.setProperties(parseProperties("Properties" + identityCounter + ".", fields));
141                         Integer trust = Numbers.safeParseInteger(fields.get("Trust" + identityCounter), null);
142                         int score = Numbers.safeParseInteger(fields.get("Score" + identityCounter), 0);
143                         int rank = Numbers.safeParseInteger(fields.get("Rank" + identityCounter), 0);
144                         identity.setTrust(ownIdentity, new Trust(trust, score, rank));
145                         identities.add(identity);
146                 }
147                 return identities;
148         }
149
150         /**
151          * Adds the given context to the given identity.
152          *
153          * @param ownIdentity
154          *            The identity to add the context to
155          * @param context
156          *            The context to add
157          * @throws PluginException
158          *             if an error occured talking to the Web of Trust plugin
159          */
160         public void addContext(OwnIdentity ownIdentity, String context) throws PluginException {
161                 performRequest(SimpleFieldSetConstructor.create().put("Message", "AddContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
162         }
163
164         /**
165          * Removes the given context from the given identity.
166          *
167          * @param ownIdentity
168          *            The identity to remove the context from
169          * @param context
170          *            The context to remove
171          * @throws PluginException
172          *             if an error occured talking to the Web of Trust plugin
173          */
174         public void removeContext(OwnIdentity ownIdentity, String context) throws PluginException {
175                 performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
176         }
177
178         /**
179          * Returns the value of the property with the given name.
180          *
181          * @param identity
182          *            The identity whose properties to check
183          * @param name
184          *            The name of the property to return
185          * @return The value of the property, or {@code null} if there is no value
186          * @throws PluginException
187          *             if an error occured talking to the Web of Trust plugin
188          */
189         public String getProperty(Identity identity, String name) throws PluginException {
190                 Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetProperty").put("Identity", identity.getId()).put("Property", name).get());
191                 return reply.getFields().get("Property");
192         }
193
194         /**
195          * Sets the property with the given name to the given value.
196          *
197          * @param ownIdentity
198          *            The identity to set the property on
199          * @param name
200          *            The name of the property to set
201          * @param value
202          *            The value to set
203          * @throws PluginException
204          *             if an error occured talking to the Web of Trust plugin
205          */
206         public void setProperty(OwnIdentity ownIdentity, String name, String value) throws PluginException {
207                 performRequest(SimpleFieldSetConstructor.create().put("Message", "SetProperty").put("Identity", ownIdentity.getId()).put("Property", name).put("Value", value).get());
208         }
209
210         /**
211          * Removes the property with the given name.
212          *
213          * @param ownIdentity
214          *            The identity to remove the property from
215          * @param name
216          *            The name of the property to remove
217          * @throws PluginException
218          *             if an error occured talking to the Web of Trust plugin
219          */
220         public void removeProperty(OwnIdentity ownIdentity, String name) throws PluginException {
221                 performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveProperty").put("Identity", ownIdentity.getId()).put("Property", name).get());
222         }
223
224         /**
225          * Returns the trust for the given identity assigned to it by the given own
226          * identity.
227          *
228          * @param ownIdentity
229          *            The own identity
230          * @param identity
231          *            The identity to get the trust for
232          * @return The trust for the given identity
233          * @throws PluginException
234          *             if an error occured talking to the Web of Trust plugin
235          */
236         public Trust getTrust(OwnIdentity ownIdentity, Identity identity) throws PluginException {
237                 Reply getTrustReply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentity").put("Truster", ownIdentity.getId()).put("Identity", identity.getId()).get());
238                 String trust = getTrustReply.getFields().get("Trust");
239                 String score = getTrustReply.getFields().get("Score");
240                 String rank = getTrustReply.getFields().get("Rank");
241                 Integer explicit = null;
242                 Integer implicit = null;
243                 Integer distance = null;
244                 try {
245                         explicit = Integer.valueOf(trust);
246                 } catch (NumberFormatException nfe1) {
247                         /* ignore. */
248                 }
249                 try {
250                         implicit = Integer.valueOf(score);
251                         distance = Integer.valueOf(rank);
252                 } catch (NumberFormatException nfe1) {
253                         /* ignore. */
254                 }
255                 return new Trust(explicit, implicit, distance);
256         }
257
258         /**
259          * Sets the trust for the given identity.
260          *
261          * @param ownIdentity
262          *            The trusting identity
263          * @param identity
264          *            The trusted identity
265          * @param trust
266          *            The amount of trust (-100 thru 100)
267          * @param comment
268          *            The comment or explanation of the trust value
269          * @throws PluginException
270          *             if an error occured talking to the Web of Trust plugin
271          */
272         public void setTrust(OwnIdentity ownIdentity, Identity identity, int trust, String comment) throws PluginException {
273                 performRequest(SimpleFieldSetConstructor.create().put("Message", "SetTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).put("Value", String.valueOf(trust)).put("Comment", comment).get());
274         }
275
276         /**
277          * Removes any trust assignment of the given own identity for the given
278          * identity.
279          *
280          * @param ownIdentity
281          *            The own identity
282          * @param identity
283          *            The identity to remove all trust for
284          * @throws WebOfTrustException
285          *             if an error occurs
286          */
287         public void removeTrust(OwnIdentity ownIdentity, Identity identity) throws WebOfTrustException {
288                 performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).get());
289         }
290
291         /**
292          * Pings the Web of Trust plugin. If the plugin can not be reached, a
293          * {@link PluginException} is thrown.
294          *
295          * @throws PluginException
296          *             if the plugin is not loaded
297          */
298         public void ping() throws PluginException {
299                 performRequest(SimpleFieldSetConstructor.create().put("Message", "Ping").get());
300         }
301
302         //
303         // PRIVATE ACTIONS
304         //
305
306         /**
307          * Parses the contexts from the given fields.
308          *
309          * @param prefix
310          *            The prefix to use to access the contexts
311          * @param fields
312          *            The fields to parse the contexts from
313          * @return The parsed contexts
314          */
315         private static Set<String> parseContexts(String prefix, SimpleFieldSet fields) {
316                 Set<String> contexts = new HashSet<String>();
317                 int contextCounter = -1;
318                 while (true) {
319                         String context = fields.get(prefix + "Context" + ++contextCounter);
320                         if (context == null) {
321                                 break;
322                         }
323                         contexts.add(context);
324                 }
325                 return contexts;
326         }
327
328         /**
329          * Parses the properties from the given fields.
330          *
331          * @param prefix
332          *            The prefix to use to access the properties
333          * @param fields
334          *            The fields to parse the properties from
335          * @return The parsed properties
336          */
337         private static Map<String, String> parseProperties(String prefix, SimpleFieldSet fields) {
338                 Map<String, String> properties = new HashMap<String, String>();
339                 int propertiesCounter = -1;
340                 while (true) {
341                         String propertyName = fields.get(prefix + "Property" + ++propertiesCounter + ".Name");
342                         if (propertyName == null) {
343                                 break;
344                         }
345                         String propertyValue = fields.get(prefix + "Property" + propertiesCounter + ".Value");
346                         properties.put(propertyName, propertyValue);
347                 }
348                 return properties;
349         }
350
351         /**
352          * Sends a request containing the given fields and waits for the target
353          * message.
354          *
355          * @param fields
356          *            The fields of the message
357          * @return The reply message
358          * @throws PluginException
359          *             if the request could not be sent
360          */
361         private Reply performRequest(SimpleFieldSet fields) throws PluginException {
362                 return performRequest(fields, null);
363         }
364
365         /**
366          * Sends a request containing the given fields and waits for the target
367          * message.
368          *
369          * @param fields
370          *            The fields of the message
371          * @param data
372          *            The payload of the message
373          * @return The reply message
374          * @throws PluginException
375          *             if the request could not be sent
376          */
377         private Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException {
378                 String identifier = "FCP-Command-" + System.currentTimeMillis() + "-" + counter.getAndIncrement();
379                 Reply reply = new Reply();
380                 PluginIdentifier pluginIdentifier = new PluginIdentifier(WOT_PLUGIN_NAME, identifier);
381                 replies.put(pluginIdentifier, reply);
382
383                 logger.log(Level.FINE, String.format("Sending FCP Request: %s", fields.get("Message")));
384                 synchronized (reply) {
385                         try {
386                                 pluginConnector.sendRequest(WOT_PLUGIN_NAME, identifier, fields, data);
387                                 while (reply.getFields() == null) {
388                                         try {
389                                                 reply.wait();
390                                         } catch (InterruptedException ie1) {
391                                                 logger.log(Level.WARNING, String.format("Got interrupted while waiting for reply on %s.", fields.get("Message")), ie1);
392                                         }
393                                 }
394                         } finally {
395                                 replies.remove(pluginIdentifier);
396                         }
397                 }
398                 logger.log(Level.FINEST, String.format("Received FCP Response for %s: %s", fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null));
399                 if ((reply.getFields() == null) || "Error".equals(reply.getFields().get("Message"))) {
400                         throw new PluginException("Could not perform request for " + fields.get("Message"));
401                 }
402                 return reply;
403         }
404
405         /**
406          * Notifies the connector that a plugin reply was received.
407          *
408          * @param receivedReplyEvent
409          *            The event
410          */
411         @Subscribe
412         public void receivedReply(ReceivedReplyEvent receivedReplyEvent) {
413                 PluginIdentifier pluginIdentifier = new PluginIdentifier(receivedReplyEvent.pluginName(), receivedReplyEvent.identifier());
414                 Reply reply = replies.remove(pluginIdentifier);
415                 if (reply == null) {
416                         return;
417                 }
418                 logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", receivedReplyEvent.fieldSet().get("Message")));
419                 synchronized (reply) {
420                         reply.setFields(receivedReplyEvent.fieldSet());
421                         reply.setData(receivedReplyEvent.data());
422                         reply.notify();
423                 }
424         }
425
426         /**
427          * Container for the data of the reply from a plugin.
428          *
429          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
430          */
431         private static class Reply {
432
433                 /** The fields of the reply. */
434                 private SimpleFieldSet fields;
435
436                 /** The payload of the reply. */
437                 private Bucket data;
438
439                 /** Empty constructor. */
440                 public Reply() {
441                         /* do nothing. */
442                 }
443
444                 /**
445                  * Returns the fields of the reply.
446                  *
447                  * @return The fields of the reply
448                  */
449                 public SimpleFieldSet getFields() {
450                         return fields;
451                 }
452
453                 /**
454                  * Sets the fields of the reply.
455                  *
456                  * @param fields
457                  *            The fields of the reply
458                  */
459                 public void setFields(SimpleFieldSet fields) {
460                         this.fields = fields;
461                 }
462
463                 /**
464                  * Returns the payload of the reply.
465                  *
466                  * @return The payload of the reply (may be {@code null})
467                  */
468                 @SuppressWarnings("unused")
469                 public Bucket getData() {
470                         return data;
471                 }
472
473                 /**
474                  * Sets the payload of the reply.
475                  *
476                  * @param data
477                  *            The payload of the reply (may be {@code null})
478                  */
479                 public void setData(Bucket data) {
480                         this.data = data;
481                 }
482
483         }
484
485         /**
486          * Helper method to create {@link SimpleFieldSet}s with terser code.
487          *
488          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
489          */
490         private static class SimpleFieldSetConstructor {
491
492                 /** The field set being created. */
493                 private SimpleFieldSet simpleFieldSet;
494
495                 /**
496                  * Creates a new simple field set constructor.
497                  *
498                  * @param shortLived
499                  *            {@code true} if the resulting simple field set should be
500                  *            short-lived, {@code false} otherwise
501                  */
502                 private SimpleFieldSetConstructor(boolean shortLived) {
503                         simpleFieldSet = new SimpleFieldSet(shortLived);
504                 }
505
506                 //
507                 // ACCESSORS
508                 //
509
510                 /**
511                  * Returns the created simple field set.
512                  *
513                  * @return The created simple field set
514                  */
515                 public SimpleFieldSet get() {
516                         return simpleFieldSet;
517                 }
518
519                 /**
520                  * Sets the field with the given name to the given value.
521                  *
522                  * @param name
523                  *            The name of the fleld
524                  * @param value
525                  *            The value of the field
526                  * @return This constructor (for method chaining)
527                  */
528                 public SimpleFieldSetConstructor put(String name, String value) {
529                         simpleFieldSet.putOverwrite(name, value);
530                         return this;
531                 }
532
533                 //
534                 // ACTIONS
535                 //
536
537                 /**
538                  * Creates a new simple field set constructor.
539                  *
540                  * @return The created simple field set constructor
541                  */
542                 public static SimpleFieldSetConstructor create() {
543                         return create(true);
544                 }
545
546                 /**
547                  * Creates a new simple field set constructor.
548                  *
549                  * @param shortLived
550                  *            {@code true} if the resulting simple field set should be
551                  *            short-lived, {@code false} otherwise
552                  * @return The created simple field set constructor
553                  */
554                 public static SimpleFieldSetConstructor create(boolean shortLived) {
555                         SimpleFieldSetConstructor simpleFieldSetConstructor = new SimpleFieldSetConstructor(shortLived);
556                         return simpleFieldSetConstructor;
557                 }
558
559         }
560
561         /**
562          * Container for identifying plugins. Plugins are identified by their plugin
563          * name and their unique identifier.
564          *
565          * @author <a href="mailto:d.roden@xplosion.de">David Roden</a>
566          */
567         private static class PluginIdentifier {
568
569                 /** The plugin name. */
570                 private final String pluginName;
571
572                 /** The plugin identifier. */
573                 private final String identifier;
574
575                 /**
576                  * Creates a new plugin identifier.
577                  *
578                  * @param pluginName
579                  *            The name of the plugin
580                  * @param identifier
581                  *            The identifier of the plugin
582                  */
583                 public PluginIdentifier(String pluginName, String identifier) {
584                         this.pluginName = pluginName;
585                         this.identifier = identifier;
586                 }
587
588                 //
589                 // OBJECT METHODS
590                 //
591
592                 /**
593                  * {@inheritDoc}
594                  */
595                 @Override
596                 public int hashCode() {
597                         return pluginName.hashCode() ^ identifier.hashCode();
598                 }
599
600                 /**
601                  * {@inheritDoc}
602                  */
603                 @Override
604                 public boolean equals(Object object) {
605                         if (!(object instanceof PluginIdentifier)) {
606                                 return false;
607                         }
608                         PluginIdentifier pluginIdentifier = (PluginIdentifier) object;
609                         return pluginName.equals(pluginIdentifier.pluginName) && identifier.equals(pluginIdentifier.identifier);
610                 }
611
612         }
613
614 }