✨ Apply strict filtering when getting identities
[Sone.git] / src / main / kotlin / net / pterodactylus / sone / freenet / wot / IdentityManagerImpl.kt
1 /*
2  * Sone - IdentityManagerImpl.kt - Copyright © 2010–2020 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 com.google.common.eventbus.EventBus
21 import com.google.common.eventbus.Subscribe
22 import com.google.inject.Inject
23 import com.google.inject.Singleton
24 import net.pterodactylus.sone.core.event.StrictFilteringActivatedEvent
25 import net.pterodactylus.sone.core.event.StrictFilteringDeactivatedEvent
26 import net.pterodactylus.util.service.AbstractService
27 import java.util.concurrent.TimeUnit.SECONDS
28 import java.util.concurrent.atomic.AtomicBoolean
29 import java.util.logging.Level
30 import java.util.logging.Logger
31 import java.util.logging.Logger.getLogger
32
33 /**
34  * The identity manager takes care of loading and storing identities, their
35  * contexts, and properties. It does so in a way that does not expose errors via
36  * exceptions but it only logs them and tries to return sensible defaults.
37  *
38  *
39  * It is also responsible for polling identities from the Web of Trust plugin
40  * and sending events to the [EventBus] when [Identity]s and
41  * [OwnIdentity]s are discovered or disappearing.
42  */
43 @Singleton
44 class IdentityManagerImpl @Inject constructor(
45                 private val eventBus: EventBus,
46                 private val webOfTrustConnector: WebOfTrustConnector,
47                 private val identityLoader: IdentityLoader
48 ) : AbstractService("Sone Identity Manager", false), IdentityManager {
49
50         private val currentOwnIdentities = mutableSetOf<OwnIdentity>()
51         private val strictFiltering = AtomicBoolean(false)
52
53         override val isConnected: Boolean
54                 get() = notThrowing { webOfTrustConnector.ping() }
55
56         override val allOwnIdentities: Set<OwnIdentity>
57                 get() = synchronized(currentOwnIdentities) {
58                         currentOwnIdentities.toSet()
59                 }
60
61         override fun serviceRun() {
62                 var oldIdentities = mapOf<OwnIdentity, Collection<Identity>>()
63
64                 while (!shouldStop()) {
65                         try {
66                                 val currentIdentities = identityLoader.loadAllIdentities().applyStrictFiltering()
67
68                                 val identityChangeEventSender = IdentityChangeEventSender(eventBus, oldIdentities)
69                                 identityChangeEventSender.detectChanges(currentIdentities)
70
71                                 oldIdentities = currentIdentities
72
73                                 synchronized(currentOwnIdentities) {
74                                         currentOwnIdentities.clear()
75                                         currentOwnIdentities.addAll(currentIdentities.keys)
76                                 }
77                         } catch (wote1: WebOfTrustException) {
78                                 logger.log(Level.WARNING, "WoT has disappeared!", wote1)
79                         } catch (e: Exception) {
80                                 logger.log(Level.SEVERE, "Uncaught exception in IdentityManager thread!", e)
81                         }
82
83                         /* wait a minute before checking again. */
84                         sleep(SECONDS.toMillis(60))
85                 }
86         }
87
88         private fun Map<OwnIdentity, Set<Identity>>.applyStrictFiltering() =
89                         if (strictFiltering.get()) {
90                                 val identitiesWithTrust = values.flatten()
91                                                 .groupBy { it.id }
92                                                 .mapValues { (_, identities) ->
93                                                         identities.reduce { accIdentity, identity ->
94                                                                 identity.trust.forEach { (ownIdentity: OwnIdentity?, trust: Trust?) ->
95                                                                         accIdentity.setTrust(ownIdentity, trust)
96                                                                 }
97                                                                 accIdentity
98                                                         }
99                                                 }
100
101                                 mapValues { (_, trustedIdentities) ->
102                                         trustedIdentities.filter { trustedIdentity ->
103                                                 identitiesWithTrust[trustedIdentity.id]!!.trust.all { it.value.hasZeroOrPositiveTrust() }
104                                         }
105                                 }
106                         } else {
107                                 this
108                         }
109
110         @Subscribe
111         fun strictFilteringActivated(event: StrictFilteringActivatedEvent) {
112                 strictFiltering.set(true)
113         }
114
115         @Subscribe
116         fun strictFilteringDeactivated(event: StrictFilteringDeactivatedEvent) {
117                 strictFiltering.set(false)
118         }
119
120 }
121
122 private val logger: Logger = getLogger(IdentityManagerImpl::class.java.name)
123
124 private fun notThrowing(action: () -> Unit): Boolean =
125                 try {
126                         action()
127                         true
128                 } catch (e: Exception) {
129                         false
130                 }
131
132 private fun Trust.hasZeroOrPositiveTrust() =
133                 if (explicit == null) {
134                         implicit == null || implicit >= 0
135                 } else {
136                         explicit >= 0
137                 }