Compare commits
72 Commits
muwire-0.4
...
muwire-0.4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4f948c1b9e | ||
![]() |
2b68c24f9c | ||
![]() |
bcdf0422db | ||
![]() |
f6434b478d | ||
![]() |
e979fdd26f | ||
![]() |
e6bfcaaab9 | ||
![]() |
9780108e8a | ||
![]() |
697c7d2d6d | ||
![]() |
887d10c8bf | ||
![]() |
ef6b8fe458 | ||
![]() |
20ab55d763 | ||
![]() |
eda58c9e0d | ||
![]() |
fb42fc0e35 | ||
![]() |
35cabc47ad | ||
![]() |
5be97d0404 | ||
![]() |
82b0fa253c | ||
![]() |
011a4d5766 | ||
![]() |
5cd1ca88c1 | ||
![]() |
44c880d911 | ||
![]() |
14857cb5ad | ||
![]() |
7daf981f1a | ||
![]() |
b99bc0ea32 | ||
![]() |
1ccf6fbdfa | ||
![]() |
5711979272 | ||
![]() |
9a5e2b1fa3 | ||
![]() |
cafc5f582e | ||
![]() |
a89b423dfc | ||
![]() |
79e8438941 | ||
![]() |
19c2c46491 | ||
![]() |
78f1d54b69 | ||
![]() |
9461649ed4 | ||
![]() |
8573ab2850 | ||
![]() |
8b3d752727 | ||
![]() |
7c54bd8966 | ||
![]() |
5d0fcb7027 | ||
![]() |
3ec9654d3c | ||
![]() |
7c8d64b462 | ||
![]() |
31e30e3d31 | ||
![]() |
8caf6e99b0 | ||
![]() |
624155debd | ||
![]() |
4468a262ae | ||
![]() |
1780901cb0 | ||
![]() |
d830d9261f | ||
![]() |
f5e1833a48 | ||
![]() |
9feb2a3c8f | ||
![]() |
b27665f5dd | ||
![]() |
4465aa4134 | ||
![]() |
ad766ac748 | ||
![]() |
d9e7d67d86 | ||
![]() |
3fefbc94b3 | ||
![]() |
21034209a5 | ||
![]() |
7c04c0f83c | ||
![]() |
f5293d65dd | ||
![]() |
8191bf6066 | ||
![]() |
29b6bfd463 | ||
![]() |
2f3d23bc34 | ||
![]() |
98dd80c4b8 | ||
![]() |
d9edb2e128 | ||
![]() |
de04b40b86 | ||
![]() |
7206a3d926 | ||
![]() |
98b98d8938 | ||
![]() |
294b8fcc2f | ||
![]() |
32f601a1b1 | ||
![]() |
8e3a398080 | ||
![]() |
720b9688b4 | ||
![]() |
e3066161c5 | ||
![]() |
a9aa3a524f | ||
![]() |
92848e818a | ||
![]() |
a7aa3008c0 | ||
![]() |
485325e824 | ||
![]() |
0df2a0e039 | ||
![]() |
fb7b4466c2 |
33
README.md
33
README.md
@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
|
|||||||
|
|
||||||
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
||||||
|
|
||||||
The current stable release - 0.2.5 is avaiable for download at http://muwire.com. You can find technical documentation in the "doc" folder.
|
The current stable release - 0.4.0 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
@@ -23,34 +23,9 @@ Some of the UI tests will fail because they haven't been written yet :-/
|
|||||||
|
|
||||||
### Running
|
### Running
|
||||||
|
|
||||||
You need to have an I2P router up and running on the same machine. After you build the application, look inside "gui/build/distributions". Untar/unzip one of the "shadow" files and then run the jar contained inside by typing "java -jar MuWire-x.y.z.jar" in a terminal or command prompt. If you use a custom I2CP host and port, create a file $HOME/.MuWire/i2p.properties and put "i2cp.tcp.host=<host>" and "i2cp.tcp.port=<port>" in there.
|
After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt.
|
||||||
|
|
||||||
The first time you run MuWire it will ask you to select a nickname. This nickname will be displayed with search results, so that others can verify the file was shared by you. It is best to leave MuWire running all the time, just like I2P.
|
If you have an I2P router running on the same machine that is all you need to do. If you use a custom I2CP host and port, create a file `$HOME/.MuWire/i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there.
|
||||||
|
|
||||||
|
If you do not have an I2P router, pass the following switch to the Java process: `-DembeddedRouter=true`. This will launch MuWire's embedded router. Be aware that this causes startup to take a lot longer.
|
||||||
|
|
||||||
### Known bugs and limitations
|
|
||||||
|
|
||||||
* Many UI features you would expect are not there yet
|
|
||||||
|
|
||||||
### Quick FAQ
|
|
||||||
|
|
||||||
* why is MuWire slow ?
|
|
||||||
|
|
||||||
- too few sources you're downloading from
|
|
||||||
- you can increase the number of tunnels by using more tunnels via Options->I2P Inbound/Outbound Quantity
|
|
||||||
the default is 4 and you could raise up to as high as 16 ( Caution !!!!)
|
|
||||||
|
|
||||||
* my search is not returning (enough) results !
|
|
||||||
|
|
||||||
- search is keyword or hash based
|
|
||||||
- keywords and hash(es) are NOT regexed or wildcarded so they have to be complete
|
|
||||||
so searching for 'musi' will not return results with 'music' - you have to search for 'music'
|
|
||||||
- ALL keywords have to match
|
|
||||||
- only use space for keyword separation
|
|
||||||
- if you already have the file in question it is not displayed ( can be changed via Options )
|
|
||||||
|
|
||||||
* what's this right click -> 'Copy hash to clipboard' for ?
|
|
||||||
|
|
||||||
- if you have a specific file you wish to share or download you can use the hash as a unique identifier
|
|
||||||
to make sure you have exactly the right file.
|
|
||||||
- you can share this hash with others to ensure they are getting the right file
|
|
||||||
|
9
TODO.md
9
TODO.md
@@ -12,18 +12,10 @@ This reduces query traffic by not sending last hop queries to peers that definit
|
|||||||
|
|
||||||
This helps with scalability
|
This helps with scalability
|
||||||
|
|
||||||
##### Trust List Sharing
|
|
||||||
|
|
||||||
For helping users make better decisions whom to trust
|
|
||||||
|
|
||||||
##### Content Control Panel
|
##### Content Control Panel
|
||||||
|
|
||||||
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
|
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
|
||||||
|
|
||||||
##### Packaging With JRE, Embedded Router
|
|
||||||
|
|
||||||
For ease of deployment for new users, and so that users do not need to run a separate I2P router
|
|
||||||
|
|
||||||
##### Web UI, REST Interface, etc.
|
##### Web UI, REST Interface, etc.
|
||||||
|
|
||||||
Basically any non-gui non-cli user interface
|
Basically any non-gui non-cli user interface
|
||||||
@@ -36,5 +28,4 @@ To enable parsing of metadata from known file types and the user editing it or a
|
|||||||
|
|
||||||
* Wrapper of some kind for in-place upgrades
|
* Wrapper of some kind for in-place upgrades
|
||||||
* Download file sequentially
|
* Download file sequentially
|
||||||
* Unsharing of files
|
|
||||||
* Multiple-selection download, Ctrl-A
|
* Multiple-selection download, Ctrl-A
|
||||||
|
@@ -35,7 +35,7 @@ class Cli {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.4.0")
|
core = new Core(props, home, "0.4.6")
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
bad.printStackTrace(System.out)
|
bad.printStackTrace(System.out)
|
||||||
println "Failed to initialize core, exiting"
|
println "Failed to initialize core, exiting"
|
||||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.4.0")
|
core = new Core(props, home, "0.4.6")
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
bad.printStackTrace(System.out)
|
bad.printStackTrace(System.out)
|
||||||
println "Failed to initialize core, exiting"
|
println "Failed to initialize core, exiting"
|
||||||
|
@@ -2,6 +2,7 @@ apply plugin : 'application'
|
|||||||
mainClassName = 'com.muwire.core.Core'
|
mainClassName = 'com.muwire.core.Core'
|
||||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||||
dependencies {
|
dependencies {
|
||||||
|
compile 'net.i2p:router:0.9.40'
|
||||||
compile 'net.i2p.client:mstreaming:0.9.40'
|
compile 'net.i2p.client:mstreaming:0.9.40'
|
||||||
compile 'net.i2p.client:streaming:0.9.40'
|
compile 'net.i2p.client:streaming:0.9.40'
|
||||||
|
|
||||||
|
@@ -9,5 +9,5 @@ class Constants {
|
|||||||
public static final int MAX_HEADER_SIZE = 0x1 << 14
|
public static final int MAX_HEADER_SIZE = 0x1 << 14
|
||||||
public static final int MAX_HEADERS = 16
|
public static final int MAX_HEADERS = 16
|
||||||
|
|
||||||
public static final String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}]"
|
public static final String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}\\?]"
|
||||||
}
|
}
|
||||||
|
@@ -28,6 +28,7 @@ import com.muwire.core.files.FileUnsharedEvent
|
|||||||
import com.muwire.core.files.HasherService
|
import com.muwire.core.files.HasherService
|
||||||
import com.muwire.core.files.PersisterService
|
import com.muwire.core.files.PersisterService
|
||||||
import com.muwire.core.files.AllFilesLoadedEvent
|
import com.muwire.core.files.AllFilesLoadedEvent
|
||||||
|
import com.muwire.core.files.DirectoryUnsharedEvent
|
||||||
import com.muwire.core.files.DirectoryWatcher
|
import com.muwire.core.files.DirectoryWatcher
|
||||||
import com.muwire.core.hostcache.CacheClient
|
import com.muwire.core.hostcache.CacheClient
|
||||||
import com.muwire.core.hostcache.HostCache
|
import com.muwire.core.hostcache.HostCache
|
||||||
@@ -38,8 +39,11 @@ import com.muwire.core.search.ResultsEvent
|
|||||||
import com.muwire.core.search.ResultsSender
|
import com.muwire.core.search.ResultsSender
|
||||||
import com.muwire.core.search.SearchEvent
|
import com.muwire.core.search.SearchEvent
|
||||||
import com.muwire.core.search.SearchManager
|
import com.muwire.core.search.SearchManager
|
||||||
|
import com.muwire.core.search.UIResultBatchEvent
|
||||||
import com.muwire.core.trust.TrustEvent
|
import com.muwire.core.trust.TrustEvent
|
||||||
import com.muwire.core.trust.TrustService
|
import com.muwire.core.trust.TrustService
|
||||||
|
import com.muwire.core.trust.TrustSubscriber
|
||||||
|
import com.muwire.core.trust.TrustSubscriptionEvent
|
||||||
import com.muwire.core.update.UpdateClient
|
import com.muwire.core.update.UpdateClient
|
||||||
import com.muwire.core.upload.UploadManager
|
import com.muwire.core.upload.UploadManager
|
||||||
import com.muwire.core.util.MuWireLogManager
|
import com.muwire.core.util.MuWireLogManager
|
||||||
@@ -59,6 +63,9 @@ import net.i2p.data.PrivateKey
|
|||||||
import net.i2p.data.Signature
|
import net.i2p.data.Signature
|
||||||
import net.i2p.data.SigningPrivateKey
|
import net.i2p.data.SigningPrivateKey
|
||||||
|
|
||||||
|
import net.i2p.router.Router
|
||||||
|
import net.i2p.router.RouterContext
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
public class Core {
|
public class Core {
|
||||||
|
|
||||||
@@ -69,6 +76,7 @@ public class Core {
|
|||||||
final MuWireSettings muOptions
|
final MuWireSettings muOptions
|
||||||
|
|
||||||
private final TrustService trustService
|
private final TrustService trustService
|
||||||
|
private final TrustSubscriber trustSubscriber
|
||||||
private final PersisterService persisterService
|
private final PersisterService persisterService
|
||||||
private final HostCache hostCache
|
private final HostCache hostCache
|
||||||
private final ConnectionManager connectionManager
|
private final ConnectionManager connectionManager
|
||||||
@@ -80,15 +88,63 @@ public class Core {
|
|||||||
private final DownloadManager downloadManager
|
private final DownloadManager downloadManager
|
||||||
private final DirectoryWatcher directoryWatcher
|
private final DirectoryWatcher directoryWatcher
|
||||||
final FileManager fileManager
|
final FileManager fileManager
|
||||||
|
final UploadManager uploadManager
|
||||||
|
|
||||||
|
private final Router router
|
||||||
|
|
||||||
final AtomicBoolean shutdown = new AtomicBoolean()
|
final AtomicBoolean shutdown = new AtomicBoolean()
|
||||||
|
|
||||||
public Core(MuWireSettings props, File home, String myVersion) {
|
public Core(MuWireSettings props, File home, String myVersion) {
|
||||||
this.home = home
|
this.home = home
|
||||||
this.muOptions = props
|
this.muOptions = props
|
||||||
log.info "Initializing I2P context"
|
|
||||||
I2PAppContext.getGlobalContext().logManager()
|
i2pOptions = new Properties()
|
||||||
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
|
def i2pOptionsFile = new File(home,"i2p.properties")
|
||||||
|
if (i2pOptionsFile.exists()) {
|
||||||
|
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
|
||||||
|
|
||||||
|
if (!i2pOptions.containsKey("inbound.nickname"))
|
||||||
|
i2pOptions["inbound.nickname"] = "MuWire"
|
||||||
|
if (!i2pOptions.containsKey("outbound.nickname"))
|
||||||
|
i2pOptions["outbound.nickname"] = "MuWire"
|
||||||
|
} else {
|
||||||
|
i2pOptions["inbound.nickname"] = "MuWire"
|
||||||
|
i2pOptions["outbound.nickname"] = "MuWire"
|
||||||
|
i2pOptions["inbound.length"] = "3"
|
||||||
|
i2pOptions["inbound.quantity"] = "4"
|
||||||
|
i2pOptions["outbound.length"] = "3"
|
||||||
|
i2pOptions["outbound.quantity"] = "4"
|
||||||
|
i2pOptions["i2cp.tcp.host"] = "127.0.0.1"
|
||||||
|
i2pOptions["i2cp.tcp.port"] = "7654"
|
||||||
|
Random r = new Random()
|
||||||
|
int port = r.nextInt(60000) + 4000
|
||||||
|
i2pOptions["i2np.ntcp.port"] = String.valueOf(port)
|
||||||
|
i2pOptions["i2np.udp.port"] = String.valueOf(port)
|
||||||
|
i2pOptionsFile.withOutputStream { i2pOptions.store(it, "") }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!props.embeddedRouter) {
|
||||||
|
log.info "Initializing I2P context"
|
||||||
|
I2PAppContext.getGlobalContext().logManager()
|
||||||
|
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
|
||||||
|
router = null
|
||||||
|
} else {
|
||||||
|
log.info("launching embedded router")
|
||||||
|
Properties routerProps = new Properties()
|
||||||
|
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
||||||
|
routerProps.setProperty("router.excludePeerCaps", "KLM")
|
||||||
|
routerProps.setProperty("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
|
||||||
|
routerProps.setProperty("i2np.outboundKBytesPerSecond", String.valueOf(props.outBw))
|
||||||
|
routerProps.setProperty("i2cp.disableInterface", "true")
|
||||||
|
routerProps.setProperty("i2np.ntcp.port", i2pOptions["i2np.ntcp.port"])
|
||||||
|
routerProps.setProperty("i2np.udp.port", i2pOptions["i2np.udp.port"])
|
||||||
|
routerProps.setProperty("i2np.udp.internalPort", i2pOptions["i2np.udp.port"])
|
||||||
|
router = new Router(routerProps)
|
||||||
|
I2PAppContext.getGlobalContext().metaClass = new RouterContextMetaClass()
|
||||||
|
router.runRouter()
|
||||||
|
while(!router.isRunning())
|
||||||
|
Thread.sleep(100)
|
||||||
|
}
|
||||||
|
|
||||||
log.info("initializing I2P socket manager")
|
log.info("initializing I2P socket manager")
|
||||||
def i2pClient = new I2PClientFactory().createClient()
|
def i2pClient = new I2PClientFactory().createClient()
|
||||||
@@ -100,25 +156,6 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i2pOptions = new Properties()
|
|
||||||
def i2pOptionsFile = new File(home,"i2p.properties")
|
|
||||||
if (i2pOptionsFile.exists()) {
|
|
||||||
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
|
|
||||||
|
|
||||||
if (!i2pOptions.containsKey("inbound.nickname"))
|
|
||||||
i2pOptions["inbound.nickname"] = "MuWire"
|
|
||||||
if (!i2pOptions.containsKey("outbound.nickname"))
|
|
||||||
i2pOptions["outbound.nickname"] = "MuWire"
|
|
||||||
} else {
|
|
||||||
i2pOptions["inbound.nickname"] = "MuWire"
|
|
||||||
i2pOptions["outbound.nickname"] = "MuWire"
|
|
||||||
i2pOptions["inbound.length"] = "3"
|
|
||||||
i2pOptions["inbound.quantity"] = "4"
|
|
||||||
i2pOptions["outbound.length"] = "3"
|
|
||||||
i2pOptions["outbound.quantity"] = "4"
|
|
||||||
i2pOptions["i2cp.tcp.host"] = "127.0.0.1"
|
|
||||||
i2pOptions["i2cp.tcp.port"] = "7654"
|
|
||||||
}
|
|
||||||
|
|
||||||
// options like tunnel length and quantity
|
// options like tunnel length and quantity
|
||||||
I2PSession i2pSession
|
I2PSession i2pSession
|
||||||
@@ -172,6 +209,7 @@ public class Core {
|
|||||||
eventBus.register(FileDownloadedEvent.class, fileManager)
|
eventBus.register(FileDownloadedEvent.class, fileManager)
|
||||||
eventBus.register(FileUnsharedEvent.class, fileManager)
|
eventBus.register(FileUnsharedEvent.class, fileManager)
|
||||||
eventBus.register(SearchEvent.class, fileManager)
|
eventBus.register(SearchEvent.class, fileManager)
|
||||||
|
eventBus.register(DirectoryUnsharedEvent.class, fileManager)
|
||||||
|
|
||||||
log.info("initializing mesh manager")
|
log.info("initializing mesh manager")
|
||||||
MeshManager meshManager = new MeshManager(fileManager, home, props)
|
MeshManager meshManager = new MeshManager(fileManager, home, props)
|
||||||
@@ -200,7 +238,9 @@ public class Core {
|
|||||||
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
|
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
|
||||||
|
|
||||||
log.info("initializing update client")
|
log.info("initializing update client")
|
||||||
updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props)
|
updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props, fileManager, me)
|
||||||
|
eventBus.register(FileDownloadedEvent.class, updateClient)
|
||||||
|
eventBus.register(UIResultBatchEvent.class, updateClient)
|
||||||
|
|
||||||
log.info("initializing connector")
|
log.info("initializing connector")
|
||||||
I2PConnector i2pConnector = new I2PConnector(socketManager)
|
I2PConnector i2pConnector = new I2PConnector(socketManager)
|
||||||
@@ -224,7 +264,7 @@ public class Core {
|
|||||||
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
|
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
|
||||||
|
|
||||||
log.info("initializing upload manager")
|
log.info("initializing upload manager")
|
||||||
UploadManager uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
|
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
|
||||||
|
|
||||||
log.info("initializing connection establisher")
|
log.info("initializing connection establisher")
|
||||||
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
|
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
|
||||||
@@ -238,10 +278,16 @@ public class Core {
|
|||||||
directoryWatcher = new DirectoryWatcher(eventBus, fileManager)
|
directoryWatcher = new DirectoryWatcher(eventBus, fileManager)
|
||||||
eventBus.register(FileSharedEvent.class, directoryWatcher)
|
eventBus.register(FileSharedEvent.class, directoryWatcher)
|
||||||
eventBus.register(AllFilesLoadedEvent.class, directoryWatcher)
|
eventBus.register(AllFilesLoadedEvent.class, directoryWatcher)
|
||||||
|
eventBus.register(DirectoryUnsharedEvent.class, directoryWatcher)
|
||||||
|
|
||||||
log.info("initializing hasher service")
|
log.info("initializing hasher service")
|
||||||
hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
|
hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
|
||||||
eventBus.register(FileSharedEvent.class, hasherService)
|
eventBus.register(FileSharedEvent.class, hasherService)
|
||||||
|
|
||||||
|
log.info("initializing trust subscriber")
|
||||||
|
trustSubscriber = new TrustSubscriber(eventBus, i2pConnector, props)
|
||||||
|
eventBus.register(UILoadedEvent.class, trustSubscriber)
|
||||||
|
eventBus.register(TrustSubscriptionEvent.class, trustSubscriber)
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startServices() {
|
public void startServices() {
|
||||||
@@ -262,6 +308,8 @@ public class Core {
|
|||||||
log.info("already shutting down")
|
log.info("already shutting down")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.info("shutting down trust subscriber")
|
||||||
|
trustSubscriber.stop()
|
||||||
log.info("shutting down download manageer")
|
log.info("shutting down download manageer")
|
||||||
downloadManager.shutdown()
|
downloadManager.shutdown()
|
||||||
log.info("shutting down connection acceeptor")
|
log.info("shutting down connection acceeptor")
|
||||||
@@ -272,8 +320,25 @@ public class Core {
|
|||||||
directoryWatcher.stop()
|
directoryWatcher.stop()
|
||||||
log.info("shutting down connection manager")
|
log.info("shutting down connection manager")
|
||||||
connectionManager.shutdown()
|
connectionManager.shutdown()
|
||||||
|
if (router != null) {
|
||||||
|
log.info("shutting down embedded router")
|
||||||
|
router.shutdown(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class RouterContextMetaClass extends DelegatingMetaClass {
|
||||||
|
private final Object logManager = new MuWireLogManager()
|
||||||
|
RouterContextMetaClass() {
|
||||||
|
super(RouterContext.class)
|
||||||
|
}
|
||||||
|
|
||||||
|
Object invokeMethod(Object object, String name, Object[] args) {
|
||||||
|
if (name == "logManager")
|
||||||
|
return logManager
|
||||||
|
super.invokeMethod(object, name, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static main(args) {
|
static main(args) {
|
||||||
def home = System.getProperty("user.home") + File.separator + ".MuWire"
|
def home = System.getProperty("user.home") + File.separator + ".MuWire"
|
||||||
home = new File(home)
|
home = new File(home)
|
||||||
@@ -298,7 +363,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.4.0")
|
Core core = new Core(props, home, "0.4.6")
|
||||||
core.startServices()
|
core.startServices()
|
||||||
|
|
||||||
// ... at the end, sleep or execute script
|
// ... at the end, sleep or execute script
|
||||||
|
@@ -11,8 +11,13 @@ class MuWireSettings {
|
|||||||
|
|
||||||
final boolean isLeaf
|
final boolean isLeaf
|
||||||
boolean allowUntrusted
|
boolean allowUntrusted
|
||||||
|
boolean allowTrustLists
|
||||||
|
int trustListInterval
|
||||||
|
Set<Persona> trustSubscriptions
|
||||||
int downloadRetryInterval
|
int downloadRetryInterval
|
||||||
int updateCheckInterval
|
int updateCheckInterval
|
||||||
|
boolean autoDownloadUpdate
|
||||||
|
String updateType
|
||||||
String nickname
|
String nickname
|
||||||
File downloadLocation
|
File downloadLocation
|
||||||
CrawlerResponse crawlerResponse
|
CrawlerResponse crawlerResponse
|
||||||
@@ -21,6 +26,8 @@ class MuWireSettings {
|
|||||||
float downloadSequentialRatio
|
float downloadSequentialRatio
|
||||||
int hostClearInterval
|
int hostClearInterval
|
||||||
int meshExpiration
|
int meshExpiration
|
||||||
|
boolean embeddedRouter
|
||||||
|
int inBw, outBw
|
||||||
|
|
||||||
MuWireSettings() {
|
MuWireSettings() {
|
||||||
this(new Properties())
|
this(new Properties())
|
||||||
@@ -28,17 +35,24 @@ class MuWireSettings {
|
|||||||
|
|
||||||
MuWireSettings(Properties props) {
|
MuWireSettings(Properties props) {
|
||||||
isLeaf = Boolean.valueOf(props.get("leaf","false"))
|
isLeaf = Boolean.valueOf(props.get("leaf","false"))
|
||||||
allowUntrusted = Boolean.valueOf(props.get("allowUntrusted","true"))
|
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
|
||||||
|
allowTrustLists = Boolean.valueOf(props.getProperty("allowTrustLists","true"))
|
||||||
|
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
|
||||||
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
|
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
|
||||||
nickname = props.getProperty("nickname","MuWireUser")
|
nickname = props.getProperty("nickname","MuWireUser")
|
||||||
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
||||||
System.getProperty("user.home")))
|
System.getProperty("user.home")))
|
||||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
|
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
|
||||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
|
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
|
||||||
|
autoDownloadUpdate = Boolean.parseBoolean(props.getProperty("autoDownloadUpdate","true"))
|
||||||
|
updateType = props.getProperty("updateType","jar")
|
||||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||||
downloadSequentialRatio = Float.valueOf(props.getProperty("downloadSequentialRatio","0.8"))
|
downloadSequentialRatio = Float.valueOf(props.getProperty("downloadSequentialRatio","0.8"))
|
||||||
hostClearInterval = Integer.valueOf(props.getProperty("hostClearInterval","60"))
|
hostClearInterval = Integer.valueOf(props.getProperty("hostClearInterval","60"))
|
||||||
meshExpiration = Integer.valueOf(props.getProperty("meshExpiration","60"))
|
meshExpiration = Integer.valueOf(props.getProperty("meshExpiration","60"))
|
||||||
|
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
|
||||||
|
inBw = Integer.valueOf(props.getProperty("inBw","256"))
|
||||||
|
outBw = Integer.valueOf(props.getProperty("outBw","128"))
|
||||||
|
|
||||||
watchedDirectories = new HashSet<>()
|
watchedDirectories = new HashSet<>()
|
||||||
if (props.containsKey("watchedDirectories")) {
|
if (props.containsKey("watchedDirectories")) {
|
||||||
@@ -46,21 +60,34 @@ class MuWireSettings {
|
|||||||
encoded.each { watchedDirectories << DataUtil.readi18nString(Base64.decode(it)) }
|
encoded.each { watchedDirectories << DataUtil.readi18nString(Base64.decode(it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trustSubscriptions = new HashSet<>()
|
||||||
|
if (props.containsKey("trustSubscriptions")) {
|
||||||
|
props.getProperty("trustSubscriptions").split(",").each {
|
||||||
|
trustSubscriptions.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(OutputStream out) throws IOException {
|
void write(OutputStream out) throws IOException {
|
||||||
Properties props = new Properties()
|
Properties props = new Properties()
|
||||||
props.setProperty("leaf", isLeaf.toString())
|
props.setProperty("leaf", isLeaf.toString())
|
||||||
props.setProperty("allowUntrusted", allowUntrusted.toString())
|
props.setProperty("allowUntrusted", allowUntrusted.toString())
|
||||||
|
props.setProperty("allowTrustLists", String.valueOf(allowTrustLists))
|
||||||
|
props.setProperty("trustListInterval", String.valueOf(trustListInterval))
|
||||||
props.setProperty("crawlerResponse", crawlerResponse.toString())
|
props.setProperty("crawlerResponse", crawlerResponse.toString())
|
||||||
props.setProperty("nickname", nickname)
|
props.setProperty("nickname", nickname)
|
||||||
props.setProperty("downloadLocation", downloadLocation.getAbsolutePath())
|
props.setProperty("downloadLocation", downloadLocation.getAbsolutePath())
|
||||||
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
||||||
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
||||||
|
props.setProperty("autoDownloadUpdate", String.valueOf(autoDownloadUpdate))
|
||||||
|
props.setProperty("updateType",String.valueOf(updateType))
|
||||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||||
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
||||||
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
||||||
props.setProperty("meshExpiration", String.valueOf(meshExpiration))
|
props.setProperty("meshExpiration", String.valueOf(meshExpiration))
|
||||||
|
props.setProperty("embeddedRouter", String.valueOf(embeddedRouter))
|
||||||
|
props.setProperty("inBw", String.valueOf(inBw))
|
||||||
|
props.setProperty("outBw", String.valueOf(outBw))
|
||||||
|
|
||||||
if (!watchedDirectories.isEmpty()) {
|
if (!watchedDirectories.isEmpty()) {
|
||||||
String encoded = watchedDirectories.stream().
|
String encoded = watchedDirectories.stream().
|
||||||
@@ -69,6 +96,13 @@ class MuWireSettings {
|
|||||||
props.setProperty("watchedDirectories", encoded)
|
props.setProperty("watchedDirectories", encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!trustSubscriptions.isEmpty()) {
|
||||||
|
String encoded = trustSubscriptions.stream().
|
||||||
|
map({it.toBase64()}).
|
||||||
|
collect(Collectors.joining(","))
|
||||||
|
props.setProperty("trustSubscriptions", encoded)
|
||||||
|
}
|
||||||
|
|
||||||
props.store(out, "")
|
props.store(out, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -82,4 +82,13 @@ public class Persona {
|
|||||||
Persona other = (Persona)o
|
Persona other = (Persona)o
|
||||||
name.equals(other.name) && destination.equals(other.destination)
|
name.equals(other.name) && destination.equals(other.destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String []args) {
|
||||||
|
if (args.length != 1) {
|
||||||
|
println "This utility decodes a bas64-encoded persona"
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
Persona p = new Persona(new ByteArrayInputStream(Base64.decode(args[0])))
|
||||||
|
println p.getHumanReadableName()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,9 @@ import net.i2p.data.Destination
|
|||||||
|
|
||||||
@Log
|
@Log
|
||||||
abstract class Connection implements Closeable {
|
abstract class Connection implements Closeable {
|
||||||
|
|
||||||
|
private static final int SEARCHES = 10
|
||||||
|
private static final long INTERVAL = 1000
|
||||||
|
|
||||||
final EventBus eventBus
|
final EventBus eventBus
|
||||||
final Endpoint endpoint
|
final Endpoint endpoint
|
||||||
@@ -32,6 +35,7 @@ abstract class Connection implements Closeable {
|
|||||||
private final AtomicBoolean running = new AtomicBoolean()
|
private final AtomicBoolean running = new AtomicBoolean()
|
||||||
private final BlockingQueue messages = new LinkedBlockingQueue()
|
private final BlockingQueue messages = new LinkedBlockingQueue()
|
||||||
private final Thread reader, writer
|
private final Thread reader, writer
|
||||||
|
private final LinkedList<Long> searchTimestamps = new LinkedList<>()
|
||||||
|
|
||||||
protected final String name
|
protected final String name
|
||||||
|
|
||||||
@@ -156,7 +160,25 @@ abstract class Connection implements Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean throttleSearch() {
|
||||||
|
final long now = System.currentTimeMillis()
|
||||||
|
if (searchTimestamps.size() < SEARCHES) {
|
||||||
|
searchTimestamps.addLast(now)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Long oldest = searchTimestamps.getFirst()
|
||||||
|
if (now - oldest.longValue() < INTERVAL)
|
||||||
|
return true
|
||||||
|
searchTimestamps.addLast(now)
|
||||||
|
searchTimestamps.removeFirst()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
protected void handleSearch(def search) {
|
protected void handleSearch(def search) {
|
||||||
|
if (throttleSearch()) {
|
||||||
|
log.info("dropping excessive search")
|
||||||
|
return
|
||||||
|
}
|
||||||
UUID uuid = UUID.fromString(search.uuid)
|
UUID uuid = UUID.fromString(search.uuid)
|
||||||
byte [] infohash = null
|
byte [] infohash = null
|
||||||
if (search.infohash != null) {
|
if (search.infohash != null) {
|
||||||
|
@@ -14,6 +14,7 @@ import com.muwire.core.hostcache.HostCache
|
|||||||
import com.muwire.core.trust.TrustLevel
|
import com.muwire.core.trust.TrustLevel
|
||||||
import com.muwire.core.trust.TrustService
|
import com.muwire.core.trust.TrustService
|
||||||
import com.muwire.core.upload.UploadManager
|
import com.muwire.core.upload.UploadManager
|
||||||
|
import com.muwire.core.util.DataUtil
|
||||||
import com.muwire.core.search.InvalidSearchResultException
|
import com.muwire.core.search.InvalidSearchResultException
|
||||||
import com.muwire.core.search.ResultsParser
|
import com.muwire.core.search.ResultsParser
|
||||||
import com.muwire.core.search.SearchManager
|
import com.muwire.core.search.SearchManager
|
||||||
@@ -124,6 +125,9 @@ class ConnectionAcceptor {
|
|||||||
break
|
break
|
||||||
case (byte)'P':
|
case (byte)'P':
|
||||||
processPOST(e)
|
processPOST(e)
|
||||||
|
break
|
||||||
|
case (byte)'T':
|
||||||
|
processTRUST(e)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw new Exception("Invalid read $read")
|
throw new Exception("Invalid read $read")
|
||||||
@@ -242,5 +246,44 @@ class ConnectionAcceptor {
|
|||||||
e.close()
|
e.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processTRUST(Endpoint e) {
|
||||||
|
byte[] RUST = new byte[6]
|
||||||
|
DataInputStream dis = new DataInputStream(e.getInputStream())
|
||||||
|
dis.readFully(RUST)
|
||||||
|
if (RUST != "RUST\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||||
|
throw new IOException("Invalid TRUST connection")
|
||||||
|
String header
|
||||||
|
while ((header = DataUtil.readTillRN(dis)) != ""); // ignore headers for now
|
||||||
|
|
||||||
|
OutputStream os = e.getOutputStream()
|
||||||
|
if (!settings.allowTrustLists) {
|
||||||
|
os.write("403 Not Allowed\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||||
|
os.flush()
|
||||||
|
e.close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
os.write("200 OK\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||||
|
List<Persona> good = new ArrayList<>(trustService.good.values())
|
||||||
|
int size = Math.min(Short.MAX_VALUE * 2, good.size())
|
||||||
|
good = good.subList(0, size)
|
||||||
|
DataOutputStream dos = new DataOutputStream(os)
|
||||||
|
dos.writeShort(size)
|
||||||
|
good.each {
|
||||||
|
it.write(dos)
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Persona> bad = new ArrayList<>(trustService.bad.values())
|
||||||
|
size = Math.min(Short.MAX_VALUE * 2, bad.size())
|
||||||
|
bad = bad.subList(0, size)
|
||||||
|
dos.writeShort(size)
|
||||||
|
bad.each {
|
||||||
|
it.write(dos)
|
||||||
|
}
|
||||||
|
|
||||||
|
dos.flush()
|
||||||
|
e.close()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,8 +25,6 @@ import java.util.logging.Level
|
|||||||
@Log
|
@Log
|
||||||
class DownloadSession {
|
class DownloadSession {
|
||||||
|
|
||||||
private static int SAMPLES = 10
|
|
||||||
|
|
||||||
private final EventBus eventBus
|
private final EventBus eventBus
|
||||||
private final String meB64
|
private final String meB64
|
||||||
private final Pieces pieces
|
private final Pieces pieces
|
||||||
@@ -37,9 +35,9 @@ class DownloadSession {
|
|||||||
private final long fileLength
|
private final long fileLength
|
||||||
private final Set<Integer> available
|
private final Set<Integer> available
|
||||||
private final MessageDigest digest
|
private final MessageDigest digest
|
||||||
|
|
||||||
private final LinkedList<Long> timestamps = new LinkedList<>()
|
private long lastSpeedRead = System.currentTimeMillis()
|
||||||
private final LinkedList<Integer> reads = new LinkedList<>()
|
private long dataSinceLastRead
|
||||||
|
|
||||||
private ByteBuffer mapped
|
private ByteBuffer mapped
|
||||||
|
|
||||||
@@ -186,18 +184,13 @@ class DownloadSession {
|
|||||||
throw new IOException()
|
throw new IOException()
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
mapped.put(tmp, 0, read)
|
mapped.put(tmp, 0, read)
|
||||||
|
dataSinceLastRead += read
|
||||||
if (timestamps.size() == SAMPLES) {
|
|
||||||
timestamps.removeFirst()
|
|
||||||
reads.removeFirst()
|
|
||||||
}
|
|
||||||
timestamps.addLast(System.currentTimeMillis())
|
|
||||||
reads.addLast(read)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped.clear()
|
mapped.clear()
|
||||||
digest.update(mapped)
|
digest.update(mapped)
|
||||||
|
DataUtil.tryUnmap(mapped)
|
||||||
byte [] hash = digest.digest()
|
byte [] hash = digest.digest()
|
||||||
byte [] expected = new byte[32]
|
byte [] expected = new byte[32]
|
||||||
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
|
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
|
||||||
@@ -222,24 +215,11 @@ class DownloadSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized int speed() {
|
synchronized int speed() {
|
||||||
if (timestamps.size() < SAMPLES)
|
|
||||||
return 0
|
|
||||||
int totalRead = 0
|
|
||||||
int idx = 0
|
|
||||||
final long now = System.currentTimeMillis()
|
final long now = System.currentTimeMillis()
|
||||||
|
long interval = Math.max(1000, now - lastSpeedRead)
|
||||||
while(idx < SAMPLES && timestamps.get(idx) < now - 1000)
|
lastSpeedRead = now;
|
||||||
idx++
|
int rv = (int) (dataSinceLastRead * 1000.0 / interval)
|
||||||
if (idx == SAMPLES)
|
dataSinceLastRead = 0
|
||||||
return 0
|
rv
|
||||||
if (idx == SAMPLES - 1)
|
|
||||||
return reads[idx]
|
|
||||||
|
|
||||||
long interval = timestamps.last - timestamps[idx]
|
|
||||||
if (interval == 0)
|
|
||||||
interval = 1
|
|
||||||
for (int i = idx; i < SAMPLES; i++)
|
|
||||||
totalRead += reads[idx]
|
|
||||||
(int)(totalRead * 1000.0 / interval)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import com.muwire.core.connection.Endpoint
|
|||||||
import java.nio.file.AtomicMoveNotSupportedException
|
import java.nio.file.AtomicMoveNotSupportedException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
import java.time.Instant
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@@ -58,6 +59,11 @@ public class Downloader {
|
|||||||
private final AtomicBoolean eventFired = new AtomicBoolean()
|
private final AtomicBoolean eventFired = new AtomicBoolean()
|
||||||
private boolean piecesFileClosed
|
private boolean piecesFileClosed
|
||||||
|
|
||||||
|
private ArrayList speedArr = new ArrayList<Integer>()
|
||||||
|
private int speedPos = 0
|
||||||
|
private int speedAvg = 0
|
||||||
|
private long timestamp = Instant.now().toEpochMilli()
|
||||||
|
|
||||||
public Downloader(EventBus eventBus, DownloadManager downloadManager,
|
public Downloader(EventBus eventBus, DownloadManager downloadManager,
|
||||||
Persona me, File file, long length, InfoHash infoHash,
|
Persona me, File file, long length, InfoHash infoHash,
|
||||||
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
|
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
|
||||||
@@ -76,6 +82,10 @@ public class Downloader {
|
|||||||
this.pieceSize = 1 << pieceSizePow2
|
this.pieceSize = 1 << pieceSizePow2
|
||||||
this.pieces = pieces
|
this.pieces = pieces
|
||||||
this.nPieces = pieces.nPieces
|
this.nPieces = pieces.nPieces
|
||||||
|
|
||||||
|
// default size suitable for an average of 5 seconds / 5 elements / 5 interval units
|
||||||
|
// it's easily adjustable by resizing the size of speedArr
|
||||||
|
this.speedArr = [ 0, 0, 0, 0, 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized InfoHash getInfoHash() {
|
public synchronized InfoHash getInfoHash() {
|
||||||
@@ -124,14 +134,35 @@ public class Downloader {
|
|||||||
|
|
||||||
|
|
||||||
public int speed() {
|
public int speed() {
|
||||||
int total = 0
|
int currSpeed = 0
|
||||||
if (getCurrentState() == DownloadState.DOWNLOADING) {
|
if (getCurrentState() == DownloadState.DOWNLOADING) {
|
||||||
activeWorkers.values().each {
|
activeWorkers.values().each {
|
||||||
if (it.currentState == WorkerState.DOWNLOADING)
|
if (it.currentState == WorkerState.DOWNLOADING)
|
||||||
total += it.speed()
|
currSpeed += it.speed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
total
|
|
||||||
|
// normalize to speedArr.size
|
||||||
|
currSpeed /= speedArr.size()
|
||||||
|
|
||||||
|
// compute new speedAvg and update speedArr
|
||||||
|
if ( speedArr[speedPos] > speedAvg ) {
|
||||||
|
speedAvg = 0
|
||||||
|
} else {
|
||||||
|
speedAvg -= speedArr[speedPos]
|
||||||
|
}
|
||||||
|
speedAvg += currSpeed
|
||||||
|
speedArr[speedPos] = currSpeed
|
||||||
|
// this might be necessary due to rounding errors
|
||||||
|
if (speedAvg < 0)
|
||||||
|
speedAvg = 0
|
||||||
|
|
||||||
|
// rolling index over the speedArr
|
||||||
|
speedPos++
|
||||||
|
if (speedPos >= speedArr.size())
|
||||||
|
speedPos=0
|
||||||
|
|
||||||
|
speedAvg
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadState getCurrentState() {
|
public DownloadState getCurrentState() {
|
||||||
|
@@ -0,0 +1,7 @@
|
|||||||
|
package com.muwire.core.files
|
||||||
|
|
||||||
|
import com.muwire.core.Event
|
||||||
|
|
||||||
|
class DirectoryUnsharedEvent extends Event {
|
||||||
|
File directory
|
||||||
|
}
|
@@ -35,6 +35,7 @@ class DirectoryWatcher {
|
|||||||
private final FileManager fileManager
|
private final FileManager fileManager
|
||||||
private final Thread watcherThread, publisherThread
|
private final Thread watcherThread, publisherThread
|
||||||
private final Map<File, Long> waitingFiles = new ConcurrentHashMap<>()
|
private final Map<File, Long> waitingFiles = new ConcurrentHashMap<>()
|
||||||
|
private final Map<File, WatchKey> watchedDirectories = new ConcurrentHashMap<>()
|
||||||
private WatchService watchService
|
private WatchService watchService
|
||||||
private volatile boolean shutdown
|
private volatile boolean shutdown
|
||||||
|
|
||||||
@@ -64,9 +65,15 @@ class DirectoryWatcher {
|
|||||||
if (!e.file.isDirectory())
|
if (!e.file.isDirectory())
|
||||||
return
|
return
|
||||||
Path path = e.file.getCanonicalFile().toPath()
|
Path path = e.file.getCanonicalFile().toPath()
|
||||||
path.register(watchService, kinds)
|
WatchKey wk = path.register(watchService, kinds)
|
||||||
|
watchedDirectories.put(e.file, wk)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onDirectoryUnsharedEvent(DirectoryUnsharedEvent e) {
|
||||||
|
WatchKey wk = watchedDirectories.remove(e.directory)
|
||||||
|
wk?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
private void watch() {
|
private void watch() {
|
||||||
try {
|
try {
|
||||||
@@ -113,7 +120,7 @@ class DirectoryWatcher {
|
|||||||
|
|
||||||
private static File join(Path parent, Path path) {
|
private static File join(Path parent, Path path) {
|
||||||
File parentFile = parent.toFile().getCanonicalFile()
|
File parentFile = parent.toFile().getCanonicalFile()
|
||||||
new File(parentFile, path.toFile().getName())
|
new File(parentFile, path.toFile().getName()).getCanonicalFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publish() {
|
private void publish() {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package com.muwire.core.files
|
package com.muwire.core.files
|
||||||
|
|
||||||
import com.muwire.core.InfoHash
|
import com.muwire.core.InfoHash
|
||||||
|
import com.muwire.core.util.DataUtil
|
||||||
|
|
||||||
import net.i2p.data.Base64
|
import net.i2p.data.Base64
|
||||||
|
|
||||||
@@ -18,6 +19,8 @@ class FileHasher {
|
|||||||
/**
|
/**
|
||||||
* @param size of the file to be shared
|
* @param size of the file to be shared
|
||||||
* @return the size of each piece in power of 2
|
* @return the size of each piece in power of 2
|
||||||
|
* piece size is minimum 128 KBytees and maximum 16 MBytes in power of 2 steps (2^17 - 2^24)
|
||||||
|
* there can be up to 8192 pieces maximum per file
|
||||||
*/
|
*/
|
||||||
static int getPieceSize(long size) {
|
static int getPieceSize(long size) {
|
||||||
if (size <= 0x1 << 30)
|
if (size <= 0x1 << 30)
|
||||||
@@ -57,6 +60,7 @@ class FileHasher {
|
|||||||
for (int i = 0; i < numPieces - 1; i++) {
|
for (int i = 0; i < numPieces - 1; i++) {
|
||||||
buf = raf.getChannel().map(MapMode.READ_ONLY, ((long)size) * i, size)
|
buf = raf.getChannel().map(MapMode.READ_ONLY, ((long)size) * i, size)
|
||||||
digest.update buf
|
digest.update buf
|
||||||
|
DataUtil.tryUnmap(buf)
|
||||||
output.write(digest.digest(), 0, 32)
|
output.write(digest.digest(), 0, 32)
|
||||||
}
|
}
|
||||||
def lastPieceLength = length - (numPieces - 1) * ((long)size)
|
def lastPieceLength = length - (numPieces - 1) * ((long)size)
|
||||||
|
@@ -135,4 +135,16 @@ class FileManager {
|
|||||||
}
|
}
|
||||||
rv
|
rv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onDirectoryUnsharedEvent(DirectoryUnsharedEvent e) {
|
||||||
|
e.directory.listFiles().each {
|
||||||
|
if (it.isDirectory())
|
||||||
|
eventBus.publish(new DirectoryUnsharedEvent(directory : it))
|
||||||
|
else {
|
||||||
|
SharedFile sf = fileToSharedFile.get(it)
|
||||||
|
if (sf != null)
|
||||||
|
eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ class HasherService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onFileSharedEvent(FileSharedEvent evt) {
|
void onFileSharedEvent(FileSharedEvent evt) {
|
||||||
if (fileManager.fileToSharedFile.containsKey(evt.file))
|
if (fileManager.fileToSharedFile.containsKey(evt.file.getCanonicalFile()))
|
||||||
return
|
return
|
||||||
executor.execute( { -> process(evt.file) } as Runnable)
|
executor.execute( { -> process(evt.file) } as Runnable)
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,8 @@ class CacheServers {
|
|||||||
|
|
||||||
private static final int TO_GIVE = 3
|
private static final int TO_GIVE = 3
|
||||||
private static Set<Destination> CACHES = [
|
private static Set<Destination> CACHES = [
|
||||||
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA")
|
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA"),
|
||||||
|
new Destination("JC63wJNOqSJmymkj4~UJWywBTvDGikKMoYP0HX2Wz9c5l3otXSkwnxWAFL4cKr~Ygh3BNNi2t93vuLIiI1W8AsE42kR~PwRx~Y-WvIHXR6KUejRmOp-n8WidtjKg9k4aDy428uSOedqXDxys5mpoeQXwDsv1CoPTTwnmb1GWFy~oTGIsCguCl~aJWGnqiKarPO3GJQ~ev-NbvAQzUfC3HeP1e6pdI5CGGjExahTCID5UjpJw8GaDXWlGmYWWH303Xu4x-vAHQy1dJLsOBCn8dZravsn5BKJk~j0POUon45CCx-~NYtaPe0Itt9cMdD2ciC76Rep1D0X0sm1SjlSs8sZ52KmF3oaLZ6OzgI9QLMIyBUrfi41sK5I0qTuUVBAkvW1xr~L-20dYJ9TrbOaOb2-vDIfKaxVi6xQOuhgQDiSBhd3qv2m0xGu-BM9DQYfNA0FdMjnZmqjmji9RMavzQSsVFIbQGLbrLepiEFlb7TseCK5UtRp8TxnG7L4gbYevBQAEAAcAAA==")
|
||||||
]
|
]
|
||||||
|
|
||||||
static List<Destination> getCacheServers() {
|
static List<Destination> getCacheServers() {
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
package com.muwire.core.trust
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
|
import com.muwire.core.Persona
|
||||||
|
|
||||||
|
import net.i2p.util.ConcurrentHashSet
|
||||||
|
|
||||||
|
class RemoteTrustList {
|
||||||
|
public enum Status { NEW, UPDATING, UPDATED, UPDATE_FAILED }
|
||||||
|
|
||||||
|
private final Persona persona
|
||||||
|
private final Set<Persona> good, bad
|
||||||
|
volatile long timestamp
|
||||||
|
volatile boolean forceUpdate
|
||||||
|
Status status = Status.NEW
|
||||||
|
|
||||||
|
RemoteTrustList(Persona persona) {
|
||||||
|
this.persona = persona
|
||||||
|
good = new ConcurrentHashSet<>()
|
||||||
|
bad = new ConcurrentHashSet<>()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof RemoteTrustList))
|
||||||
|
return false
|
||||||
|
RemoteTrustList other = (RemoteTrustList)o
|
||||||
|
persona == other.persona
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,161 @@
|
|||||||
|
package com.muwire.core.trust
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
import com.muwire.core.EventBus
|
||||||
|
import com.muwire.core.MuWireSettings
|
||||||
|
import com.muwire.core.Persona
|
||||||
|
import com.muwire.core.UILoadedEvent
|
||||||
|
import com.muwire.core.connection.Endpoint
|
||||||
|
import com.muwire.core.connection.I2PConnector
|
||||||
|
import com.muwire.core.util.DataUtil
|
||||||
|
|
||||||
|
import groovy.util.logging.Log
|
||||||
|
import net.i2p.data.Destination
|
||||||
|
|
||||||
|
@Log
|
||||||
|
class TrustSubscriber {
|
||||||
|
private final EventBus eventBus
|
||||||
|
private final I2PConnector i2pConnector
|
||||||
|
private final MuWireSettings settings
|
||||||
|
|
||||||
|
private final Map<Destination, RemoteTrustList> remoteTrustLists = new ConcurrentHashMap<>()
|
||||||
|
|
||||||
|
private final Object waitLock = new Object()
|
||||||
|
private volatile boolean shutdown
|
||||||
|
private volatile Thread thread
|
||||||
|
private final ExecutorService updateThreads = Executors.newCachedThreadPool()
|
||||||
|
|
||||||
|
TrustSubscriber(EventBus eventBus, I2PConnector i2pConnector, MuWireSettings settings) {
|
||||||
|
this.eventBus = eventBus
|
||||||
|
this.i2pConnector = i2pConnector
|
||||||
|
this.settings = settings
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUILoadedEvent(UILoadedEvent e) {
|
||||||
|
thread = new Thread({checkLoop()} as Runnable, "trust-subscriber")
|
||||||
|
thread.setDaemon(true)
|
||||||
|
thread.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
shutdown = true
|
||||||
|
thread?.interrupt()
|
||||||
|
updateThreads.shutdownNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTrustSubscriptionEvent(TrustSubscriptionEvent e) {
|
||||||
|
if (!e.subscribe) {
|
||||||
|
remoteTrustLists.remove(e.persona.destination)
|
||||||
|
} else {
|
||||||
|
RemoteTrustList trustList = remoteTrustLists.putIfAbsent(e.persona.destination, new RemoteTrustList(e.persona))
|
||||||
|
trustList?.forceUpdate = true
|
||||||
|
synchronized(waitLock) {
|
||||||
|
waitLock.notify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLoop() {
|
||||||
|
try {
|
||||||
|
while(!shutdown) {
|
||||||
|
synchronized(waitLock) {
|
||||||
|
waitLock.wait(60 * 1000)
|
||||||
|
}
|
||||||
|
final long now = System.currentTimeMillis()
|
||||||
|
remoteTrustLists.values().each { trustList ->
|
||||||
|
if (trustList.status == RemoteTrustList.Status.UPDATING)
|
||||||
|
return
|
||||||
|
if (!trustList.forceUpdate &&
|
||||||
|
now - trustList.timestamp < settings.trustListInterval * 60 * 60 * 1000)
|
||||||
|
return
|
||||||
|
trustList.forceUpdate = false
|
||||||
|
updateThreads.submit(new UpdateJob(trustList))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
if (!shutdown)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UpdateJob implements Runnable {
|
||||||
|
|
||||||
|
private final RemoteTrustList trustList
|
||||||
|
|
||||||
|
UpdateJob(RemoteTrustList trustList) {
|
||||||
|
this.trustList = trustList
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
trustList.status = RemoteTrustList.Status.UPDATING
|
||||||
|
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
|
||||||
|
if (check(trustList, System.currentTimeMillis()))
|
||||||
|
trustList.status = RemoteTrustList.Status.UPDATED
|
||||||
|
else
|
||||||
|
trustList.status = RemoteTrustList.Status.UPDATE_FAILED
|
||||||
|
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean check(RemoteTrustList trustList, long now) {
|
||||||
|
log.info("fetching trust list from ${trustList.persona.getHumanReadableName()}")
|
||||||
|
Endpoint endpoint = null
|
||||||
|
try {
|
||||||
|
endpoint = i2pConnector.connect(trustList.persona.destination)
|
||||||
|
OutputStream os = endpoint.getOutputStream()
|
||||||
|
InputStream is = endpoint.getInputStream()
|
||||||
|
os.write("TRUST\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||||
|
os.flush()
|
||||||
|
|
||||||
|
String codeString = DataUtil.readTillRN(is)
|
||||||
|
int space = codeString.indexOf(' ')
|
||||||
|
if (space > 0)
|
||||||
|
codeString = codeString.substring(0,space)
|
||||||
|
int code = Integer.parseInt(codeString.trim())
|
||||||
|
|
||||||
|
if (code != 200) {
|
||||||
|
log.info("couldn't fetch trust list, code $code")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// swallow any headers
|
||||||
|
String header
|
||||||
|
while (( header = DataUtil.readTillRN(is)) != "");
|
||||||
|
|
||||||
|
DataInputStream dis = new DataInputStream(is)
|
||||||
|
|
||||||
|
Set<Persona> good = new HashSet<>()
|
||||||
|
int nGood = dis.readUnsignedShort()
|
||||||
|
for (int i = 0; i < nGood; i++) {
|
||||||
|
Persona p = new Persona(dis)
|
||||||
|
good.add(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Persona> bad = new HashSet<>()
|
||||||
|
int nBad = dis.readUnsignedShort()
|
||||||
|
for (int i = 0; i < nBad; i++) {
|
||||||
|
Persona p = new Persona(dis)
|
||||||
|
bad.add(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
trustList.timestamp = now
|
||||||
|
trustList.good.clear()
|
||||||
|
trustList.good.addAll(good)
|
||||||
|
trustList.bad.clear()
|
||||||
|
trustList.bad.addAll(bad)
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.log(Level.WARNING,"exception fetching trust list from ${trustList.persona.getHumanReadableName()}",e)
|
||||||
|
return false
|
||||||
|
} finally {
|
||||||
|
endpoint?.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,9 @@
|
|||||||
|
package com.muwire.core.trust
|
||||||
|
|
||||||
|
import com.muwire.core.Event
|
||||||
|
import com.muwire.core.Persona
|
||||||
|
|
||||||
|
class TrustSubscriptionEvent extends Event {
|
||||||
|
Persona persona
|
||||||
|
boolean subscribe
|
||||||
|
}
|
@@ -0,0 +1,7 @@
|
|||||||
|
package com.muwire.core.trust
|
||||||
|
|
||||||
|
import com.muwire.core.Event
|
||||||
|
|
||||||
|
class TrustSubscriptionUpdatedEvent extends Event {
|
||||||
|
RemoteTrustList trustList
|
||||||
|
}
|
@@ -3,7 +3,15 @@ package com.muwire.core.update
|
|||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
|
||||||
import com.muwire.core.EventBus
|
import com.muwire.core.EventBus
|
||||||
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.MuWireSettings
|
import com.muwire.core.MuWireSettings
|
||||||
|
import com.muwire.core.Persona
|
||||||
|
import com.muwire.core.download.UIDownloadEvent
|
||||||
|
import com.muwire.core.files.FileDownloadedEvent
|
||||||
|
import com.muwire.core.files.FileManager
|
||||||
|
import com.muwire.core.search.QueryEvent
|
||||||
|
import com.muwire.core.search.SearchEvent
|
||||||
|
import com.muwire.core.search.UIResultBatchEvent
|
||||||
|
|
||||||
import groovy.json.JsonOutput
|
import groovy.json.JsonOutput
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
@@ -13,6 +21,7 @@ import net.i2p.client.I2PSessionMuxedListener
|
|||||||
import net.i2p.client.SendMessageOptions
|
import net.i2p.client.SendMessageOptions
|
||||||
import net.i2p.client.datagram.I2PDatagramDissector
|
import net.i2p.client.datagram.I2PDatagramDissector
|
||||||
import net.i2p.client.datagram.I2PDatagramMaker
|
import net.i2p.client.datagram.I2PDatagramMaker
|
||||||
|
import net.i2p.data.Base64
|
||||||
import net.i2p.util.VersionComparator
|
import net.i2p.util.VersionComparator
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
@@ -21,16 +30,24 @@ class UpdateClient {
|
|||||||
final I2PSession session
|
final I2PSession session
|
||||||
final String myVersion
|
final String myVersion
|
||||||
final MuWireSettings settings
|
final MuWireSettings settings
|
||||||
|
final FileManager fileManager
|
||||||
|
final Persona me
|
||||||
|
|
||||||
private final Timer timer
|
private final Timer timer
|
||||||
|
|
||||||
private long lastUpdateCheckTime
|
private long lastUpdateCheckTime
|
||||||
|
|
||||||
UpdateClient(EventBus eventBus, I2PSession session, String myVersion, MuWireSettings settings) {
|
private volatile InfoHash updateInfoHash
|
||||||
|
private volatile String version, signer
|
||||||
|
private volatile boolean updateDownloading
|
||||||
|
|
||||||
|
UpdateClient(EventBus eventBus, I2PSession session, String myVersion, MuWireSettings settings, FileManager fileManager, Persona me) {
|
||||||
this.eventBus = eventBus
|
this.eventBus = eventBus
|
||||||
this.session = session
|
this.session = session
|
||||||
this.myVersion = myVersion
|
this.myVersion = myVersion
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
|
this.fileManager = fileManager
|
||||||
|
this.me = me
|
||||||
timer = new Timer("update-client",true)
|
timer = new Timer("update-client",true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +60,24 @@ class UpdateClient {
|
|||||||
timer.cancel()
|
timer.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onUIResultBatchEvent(UIResultBatchEvent results) {
|
||||||
|
if (results.results[0].infohash != updateInfoHash)
|
||||||
|
return
|
||||||
|
if (updateDownloading)
|
||||||
|
return
|
||||||
|
updateDownloading = true
|
||||||
|
def file = new File(settings.downloadLocation, results.results[0].name)
|
||||||
|
def downloadEvent = new UIDownloadEvent(result: results.results[0], sources : results.results[0].sources, target : file)
|
||||||
|
eventBus.publish(downloadEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
||||||
|
if (e.downloadedFile.infoHash != updateInfoHash)
|
||||||
|
return
|
||||||
|
updateDownloading = false
|
||||||
|
eventBus.publish(new UpdateDownloadedEvent(version : version, signer : signer))
|
||||||
|
}
|
||||||
|
|
||||||
private void checkUpdate() {
|
private void checkUpdate() {
|
||||||
final long now = System.currentTimeMillis()
|
final long now = System.currentTimeMillis()
|
||||||
if (lastUpdateCheckTime > 0) {
|
if (lastUpdateCheckTime > 0) {
|
||||||
@@ -106,8 +141,32 @@ class UpdateClient {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("new version $payload.version available, publishing event")
|
String infoHash
|
||||||
eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : payload.infoHash))
|
if (settings.updateType == "jar") {
|
||||||
|
infoHash = payload.infoHash
|
||||||
|
} else
|
||||||
|
infoHash = payload[settings.updateType]
|
||||||
|
|
||||||
|
|
||||||
|
if (!settings.autoDownloadUpdate) {
|
||||||
|
log.info("new version $payload.version available, publishing event")
|
||||||
|
eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : infoHash))
|
||||||
|
} else {
|
||||||
|
log.info("new version $payload.version available")
|
||||||
|
updateInfoHash = new InfoHash(Base64.decode(infoHash))
|
||||||
|
if (fileManager.rootToFiles.containsKey(updateInfoHash))
|
||||||
|
eventBus.publish(new UpdateDownloadedEvent(version : payload.version, signer : payload.signer))
|
||||||
|
else {
|
||||||
|
updateDownloading = false
|
||||||
|
version = payload.version
|
||||||
|
signer = payload.signer
|
||||||
|
log.info("starting search for new version hash $payload.infoHash")
|
||||||
|
def searchEvent = new SearchEvent(searchHash : updateInfoHash.getRoot(), uuid : UUID.randomUUID(), oobInfohash : true)
|
||||||
|
def queryEvent = new QueryEvent(searchEvent : searchEvent, firstHop : true, replyTo : me.destination,
|
||||||
|
receivedOn : me.destination, originator : me)
|
||||||
|
eventBus.publish(queryEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.log(Level.WARNING,"Invalid datagram",e)
|
log.log(Level.WARNING,"Invalid datagram",e)
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
package com.muwire.core.update
|
||||||
|
|
||||||
|
import com.muwire.core.Event
|
||||||
|
|
||||||
|
class UpdateDownloadedEvent extends Event {
|
||||||
|
String version
|
||||||
|
String signer
|
||||||
|
}
|
@@ -56,7 +56,7 @@ class ContentUploader extends Uploader {
|
|||||||
writeMesh(request.downloader)
|
writeMesh(request.downloader)
|
||||||
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
|
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||||
|
|
||||||
FileChannel channel
|
FileChannel channel = null
|
||||||
try {
|
try {
|
||||||
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ))
|
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ))
|
||||||
mapped = channel.map(FileChannel.MapMode.READ_ONLY, range.start, range.end - range.start + 1)
|
mapped = channel.map(FileChannel.MapMode.READ_ONLY, range.start, range.end - range.start + 1)
|
||||||
@@ -72,6 +72,10 @@ class ContentUploader extends Uploader {
|
|||||||
} finally {
|
} finally {
|
||||||
try {channel?.close() } catch (IOException ignored) {}
|
try {channel?.close() } catch (IOException ignored) {}
|
||||||
endpoint.getOutputStream().flush()
|
endpoint.getOutputStream().flush()
|
||||||
|
synchronized(this) {
|
||||||
|
DataUtil.tryUnmap(mapped)
|
||||||
|
mapped = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
package com.muwire.core.util
|
package com.muwire.core.util
|
||||||
|
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
import java.nio.ByteBuffer
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
@@ -115,4 +118,39 @@ class DataUtil {
|
|||||||
e = e.getCause()
|
e = e.getCause()
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void tryUnmap(ByteBuffer cb) {
|
||||||
|
if (cb==null || !cb.isDirect()) return;
|
||||||
|
// we could use this type cast and call functions without reflection code,
|
||||||
|
// but static import from sun.* package is risky for non-SUN virtual machine.
|
||||||
|
//try { ((sun.nio.ch.DirectBuffer)cb).cleaner().clean(); } catch (Exception ex) { }
|
||||||
|
|
||||||
|
// JavaSpecVer: 1.6, 1.7, 1.8, 9, 10
|
||||||
|
boolean isOldJDK = System.getProperty("java.specification.version","99").startsWith("1.");
|
||||||
|
try {
|
||||||
|
if (isOldJDK) {
|
||||||
|
Method cleaner = cb.getClass().getMethod("cleaner");
|
||||||
|
cleaner.setAccessible(true);
|
||||||
|
Method clean = Class.forName("sun.misc.Cleaner").getMethod("clean");
|
||||||
|
clean.setAccessible(true);
|
||||||
|
clean.invoke(cleaner.invoke(cb));
|
||||||
|
} else {
|
||||||
|
Class unsafeClass;
|
||||||
|
try {
|
||||||
|
unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||||
|
} catch(Exception ex) {
|
||||||
|
// jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,
|
||||||
|
// but that method should be added if sun.misc.Unsafe is removed.
|
||||||
|
unsafeClass = Class.forName("jdk.internal.misc.Unsafe");
|
||||||
|
}
|
||||||
|
Method clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class);
|
||||||
|
clean.setAccessible(true);
|
||||||
|
Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||||
|
theUnsafeField.setAccessible(true);
|
||||||
|
Object theUnsafe = theUnsafeField.get(null);
|
||||||
|
clean.invoke(theUnsafe, cb);
|
||||||
|
}
|
||||||
|
} catch(Exception ex) { }
|
||||||
|
cb = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package com.muwire.core;
|
package com.muwire.core;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@@ -9,7 +10,8 @@ public class DownloadedFile extends SharedFile {
|
|||||||
|
|
||||||
private final Set<Destination> sources;
|
private final Set<Destination> sources;
|
||||||
|
|
||||||
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources) {
|
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources)
|
||||||
|
throws IOException {
|
||||||
super(file, infoHash, pieceSize);
|
super(file, infoHash, pieceSize);
|
||||||
this.sources = sources;
|
this.sources = sources;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package com.muwire.core;
|
package com.muwire.core;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class SharedFile {
|
public class SharedFile {
|
||||||
|
|
||||||
@@ -8,10 +9,15 @@ public class SharedFile {
|
|||||||
private final InfoHash infoHash;
|
private final InfoHash infoHash;
|
||||||
private final int pieceSize;
|
private final int pieceSize;
|
||||||
|
|
||||||
public SharedFile(File file, InfoHash infoHash, int pieceSize) {
|
private final String cachedPath;
|
||||||
|
private final long cachedLength;
|
||||||
|
|
||||||
|
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.infoHash = infoHash;
|
this.infoHash = infoHash;
|
||||||
this.pieceSize = pieceSize;
|
this.pieceSize = pieceSize;
|
||||||
|
this.cachedPath = file.getAbsolutePath();
|
||||||
|
this.cachedLength = file.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getFile() {
|
public File getFile() {
|
||||||
@@ -35,6 +41,14 @@ public class SharedFile {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getCachedPath() {
|
||||||
|
return cachedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCachedLength() {
|
||||||
|
return cachedLength;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return file.hashCode() ^ infoHash.hashCode();
|
return file.hashCode() ^ infoHash.hashCode();
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.4.0
|
version = 0.4.6
|
||||||
groovyVersion = 2.4.15
|
groovyVersion = 2.4.15
|
||||||
slf4jVersion = 1.7.25
|
slf4jVersion = 1.7.25
|
||||||
spockVersion = 1.1-groovy-2.4
|
spockVersion = 1.1-groovy-2.4
|
||||||
|
@@ -26,4 +26,19 @@ mvcGroups {
|
|||||||
view = 'com.muwire.gui.OptionsView'
|
view = 'com.muwire.gui.OptionsView'
|
||||||
controller = 'com.muwire.gui.OptionsController'
|
controller = 'com.muwire.gui.OptionsController'
|
||||||
}
|
}
|
||||||
|
"mu-wire-status" {
|
||||||
|
model = 'com.muwire.gui.MuWireStatusModel'
|
||||||
|
view = 'com.muwire.gui.MuWireStatusView'
|
||||||
|
controller = 'com.muwire.gui.MuWireStatusController'
|
||||||
|
}
|
||||||
|
'i-2-p-status' {
|
||||||
|
model = 'com.muwire.gui.I2PStatusModel'
|
||||||
|
view = 'com.muwire.gui.I2PStatusView'
|
||||||
|
controller = 'com.muwire.gui.I2PStatusController'
|
||||||
|
}
|
||||||
|
'trust-list' {
|
||||||
|
model = 'com.muwire.gui.TrustListModel'
|
||||||
|
view = 'com.muwire.gui.TrustListView'
|
||||||
|
controller = 'com.muwire.gui.TrustListController'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,41 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonController
|
||||||
|
import griffon.core.controller.ControllerAction
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
import net.i2p.router.Router
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
import com.muwire.core.Core
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonController)
|
||||||
|
class I2PStatusController {
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
I2PStatusModel model
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
I2PStatusView view
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void refresh() {
|
||||||
|
Core core = application.context.get("core")
|
||||||
|
Router router = core.router
|
||||||
|
model.networkStatus = router._context.commSystem().status.toStatusString()
|
||||||
|
model.floodfill = router._context.netDb().floodfillEnabled()
|
||||||
|
model.ntcpConnections = router._context.commSystem().getTransports()["NTCP"].countPeers()
|
||||||
|
model.ssuConnections = router._context.commSystem().getTransports()["SSU"].countPeers()
|
||||||
|
model.participatingTunnels = router._context.tunnelManager().getParticipatingCount()
|
||||||
|
model.activePeers = router._context.profileOrganizer().countActivePeers()
|
||||||
|
model.receiveBps = router._context.bandwidthLimiter().getReceiveBps15s()
|
||||||
|
model.sendBps = router._context.bandwidthLimiter().getSendBps15s()
|
||||||
|
model.participatingBW = router._context.bandwidthLimiter().getCurrentParticipatingBandwidth()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void close() {
|
||||||
|
view.dialog.setVisible(false)
|
||||||
|
mvcGroup.destroy()
|
||||||
|
}
|
||||||
|
}
|
@@ -11,18 +11,25 @@ import net.i2p.data.Base64
|
|||||||
|
|
||||||
import javax.annotation.Nonnull
|
import javax.annotation.Nonnull
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.swing.JTable
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
|
import com.muwire.core.Persona
|
||||||
|
import com.muwire.core.SharedFile
|
||||||
import com.muwire.core.download.DownloadStartedEvent
|
import com.muwire.core.download.DownloadStartedEvent
|
||||||
import com.muwire.core.download.UIDownloadCancelledEvent
|
import com.muwire.core.download.UIDownloadCancelledEvent
|
||||||
import com.muwire.core.download.UIDownloadEvent
|
import com.muwire.core.download.UIDownloadEvent
|
||||||
import com.muwire.core.download.UIDownloadPausedEvent
|
import com.muwire.core.download.UIDownloadPausedEvent
|
||||||
import com.muwire.core.download.UIDownloadResumedEvent
|
import com.muwire.core.download.UIDownloadResumedEvent
|
||||||
|
import com.muwire.core.files.DirectoryUnsharedEvent
|
||||||
|
import com.muwire.core.files.FileUnsharedEvent
|
||||||
import com.muwire.core.search.QueryEvent
|
import com.muwire.core.search.QueryEvent
|
||||||
import com.muwire.core.search.SearchEvent
|
import com.muwire.core.search.SearchEvent
|
||||||
|
import com.muwire.core.trust.RemoteTrustList
|
||||||
import com.muwire.core.trust.TrustEvent
|
import com.muwire.core.trust.TrustEvent
|
||||||
import com.muwire.core.trust.TrustLevel
|
import com.muwire.core.trust.TrustLevel
|
||||||
|
import com.muwire.core.trust.TrustSubscriptionEvent
|
||||||
|
|
||||||
@ArtifactProviderFor(GriffonController)
|
@ArtifactProviderFor(GriffonController)
|
||||||
class MainFrameController {
|
class MainFrameController {
|
||||||
@@ -32,6 +39,8 @@ class MainFrameController {
|
|||||||
|
|
||||||
@MVCMember @Nonnull
|
@MVCMember @Nonnull
|
||||||
MainFrameModel model
|
MainFrameModel model
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
MainFrameView view
|
||||||
|
|
||||||
private volatile Core core
|
private volatile Core core
|
||||||
|
|
||||||
@@ -179,34 +188,115 @@ class MainFrameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void markTrust(String tableName, TrustLevel level, def list) {
|
private void markTrust(String tableName, TrustLevel level, def list) {
|
||||||
int row = builder.getVariable(tableName).getSelectedRow()
|
int row = view.getSelectedTrustTablesRow(tableName)
|
||||||
if (row < 0)
|
if (row < 0)
|
||||||
return
|
return
|
||||||
|
builder.getVariable(tableName).model.fireTableDataChanged()
|
||||||
core.eventBus.publish(new TrustEvent(persona : list[row], level : level))
|
core.eventBus.publish(new TrustEvent(persona : list[row], level : level))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markTrusted() {
|
void markTrusted() {
|
||||||
markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted)
|
markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted)
|
||||||
|
model.markTrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markNeutralFromDistrusted() {
|
void markNeutralFromDistrusted() {
|
||||||
markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted)
|
markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted)
|
||||||
|
model.markTrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markDistrusted() {
|
void markDistrusted() {
|
||||||
markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted)
|
markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted)
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markNeutralFromTrusted() {
|
void markNeutralFromTrusted() {
|
||||||
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
void unshareSelectedFiles() {
|
@ControllerAction
|
||||||
println "unsharing selected files"
|
void subscribe() {
|
||||||
|
int row = view.getSelectedTrustTablesRow("trusted-table")
|
||||||
|
if (row < 0)
|
||||||
|
return
|
||||||
|
Persona p = model.trusted[row]
|
||||||
|
core.muOptions.trustSubscriptions.add(p)
|
||||||
|
saveMuWireSettings()
|
||||||
|
core.eventBus.publish(new TrustSubscriptionEvent(persona : p, subscribe : true))
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void review() {
|
||||||
|
RemoteTrustList list = getSelectedTrustList()
|
||||||
|
if (list == null)
|
||||||
|
return
|
||||||
|
Map<String,Object> env = new HashMap<>()
|
||||||
|
env["trustList"] = list
|
||||||
|
env["trustService"] = core.trustService
|
||||||
|
env["eventBus"] = core.eventBus
|
||||||
|
mvcGroup.createMVCGroup("trust-list", env)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void update() {
|
||||||
|
RemoteTrustList list = getSelectedTrustList()
|
||||||
|
if (list == null)
|
||||||
|
return
|
||||||
|
core.eventBus.publish(new TrustSubscriptionEvent(persona : list.persona, subscribe : true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void unsubscribe() {
|
||||||
|
RemoteTrustList list = getSelectedTrustList()
|
||||||
|
if (list == null)
|
||||||
|
return
|
||||||
|
core.muOptions.trustSubscriptions.remove(list.persona)
|
||||||
|
saveMuWireSettings()
|
||||||
|
model.subscriptions.remove(list)
|
||||||
|
JTable table = builder.getVariable("subscription-table")
|
||||||
|
table.model.fireTableDataChanged()
|
||||||
|
core.eventBus.publish(new TrustSubscriptionEvent(persona : list.persona, subscribe : false))
|
||||||
|
}
|
||||||
|
|
||||||
|
private RemoteTrustList getSelectedTrustList() {
|
||||||
|
int row = view.getSelectedTrustTablesRow("subscription-table")
|
||||||
|
if (row < 0)
|
||||||
|
return null
|
||||||
|
model.subscriptions[row]
|
||||||
|
}
|
||||||
|
|
||||||
|
void unshareSelectedFile() {
|
||||||
|
SharedFile sf = view.selectedSharedFile()
|
||||||
|
if (sf == null)
|
||||||
|
return
|
||||||
|
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopWatchingDirectory() {
|
||||||
|
String directory = mvcGroup.view.getSelectedWatchedDirectory()
|
||||||
|
if (directory == null)
|
||||||
|
return
|
||||||
|
core.muOptions.watchedDirectories.remove(directory)
|
||||||
|
saveMuWireSettings()
|
||||||
|
core.eventBus.publish(new DirectoryUnsharedEvent(directory : new File(directory)))
|
||||||
|
|
||||||
|
model.watched.remove(directory)
|
||||||
|
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveMuWireSettings() {
|
void saveMuWireSettings() {
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonController
|
||||||
|
import griffon.core.controller.ControllerAction
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
import com.muwire.core.Core
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonController)
|
||||||
|
class MuWireStatusController {
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
MuWireStatusModel model
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
MuWireStatusView view
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void refresh() {
|
||||||
|
Core core = application.context.get("core")
|
||||||
|
|
||||||
|
int incoming = 0
|
||||||
|
int outgoing = 0
|
||||||
|
core.connectionManager.getConnections().each {
|
||||||
|
if (it.incoming)
|
||||||
|
incoming++
|
||||||
|
else
|
||||||
|
outgoing++
|
||||||
|
}
|
||||||
|
model.incomingConnections = incoming
|
||||||
|
model.outgoingConnections = outgoing
|
||||||
|
|
||||||
|
model.knownHosts = core.hostCache.hosts.size()
|
||||||
|
|
||||||
|
model.sharedFiles = core.fileManager.fileToSharedFile.size()
|
||||||
|
|
||||||
|
model.downloads = core.downloadManager.downloaders.size()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void close() {
|
||||||
|
view.dialog.setVisible(false)
|
||||||
|
mvcGroup.destroy()
|
||||||
|
}
|
||||||
|
}
|
@@ -4,10 +4,15 @@ import griffon.core.artifact.GriffonController
|
|||||||
import griffon.core.controller.ControllerAction
|
import griffon.core.controller.ControllerAction
|
||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
import groovy.util.logging.Log
|
||||||
|
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
import javax.annotation.Nonnull
|
import javax.annotation.Nonnull
|
||||||
import javax.swing.JFileChooser
|
import javax.swing.JFileChooser
|
||||||
|
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
|
import com.muwire.core.MuWireSettings
|
||||||
|
|
||||||
@ArtifactProviderFor(GriffonController)
|
@ArtifactProviderFor(GriffonController)
|
||||||
class OptionsController {
|
class OptionsController {
|
||||||
@@ -20,6 +25,7 @@ class OptionsController {
|
|||||||
void save() {
|
void save() {
|
||||||
String text
|
String text
|
||||||
Core core = application.context.get("core")
|
Core core = application.context.get("core")
|
||||||
|
MuWireSettings settings = application.context.get("muwire-settings")
|
||||||
|
|
||||||
def i2pProps = core.i2pOptions
|
def i2pProps = core.i2pOptions
|
||||||
|
|
||||||
@@ -39,6 +45,17 @@ class OptionsController {
|
|||||||
model.outboundLength = text
|
model.outboundLength = text
|
||||||
i2pProps["outbound.length"] = text
|
i2pProps["outbound.length"] = text
|
||||||
|
|
||||||
|
if (settings.embeddedRouter) {
|
||||||
|
text = view.i2pNTCPPortField.text
|
||||||
|
model.i2pNTCPPort = text
|
||||||
|
i2pProps["i2np.ntcp.port"] = text
|
||||||
|
|
||||||
|
text = view.i2pUDPPortField.text
|
||||||
|
model.i2pUDPPort = text
|
||||||
|
i2pProps["i2np.udp.port"] = text
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
File i2pSettingsFile = new File(core.home, "i2p.properties")
|
File i2pSettingsFile = new File(core.home, "i2p.properties")
|
||||||
i2pSettingsFile.withOutputStream {
|
i2pSettingsFile.withOutputStream {
|
||||||
i2pProps.store(it,"")
|
i2pProps.store(it,"")
|
||||||
@@ -47,16 +64,16 @@ class OptionsController {
|
|||||||
text = view.retryField.text
|
text = view.retryField.text
|
||||||
model.downloadRetryInterval = text
|
model.downloadRetryInterval = text
|
||||||
|
|
||||||
def settings = application.context.get("muwire-settings")
|
|
||||||
settings.downloadRetryInterval = Integer.valueOf(text)
|
settings.downloadRetryInterval = Integer.valueOf(text)
|
||||||
|
|
||||||
text = view.updateField.text
|
text = view.updateField.text
|
||||||
model.updateCheckInterval = text
|
model.updateCheckInterval = text
|
||||||
settings.updateCheckInterval = Integer.valueOf(text)
|
settings.updateCheckInterval = Integer.valueOf(text)
|
||||||
|
|
||||||
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
|
boolean autoDownloadUpdate = view.autoDownloadUpdateCheckbox.model.isSelected()
|
||||||
model.onlyTrusted = onlyTrusted
|
model.autoDownloadUpdate = autoDownloadUpdate
|
||||||
settings.setAllowUntrusted(!onlyTrusted)
|
settings.autoDownloadUpdate = autoDownloadUpdate
|
||||||
|
|
||||||
|
|
||||||
boolean shareDownloaded = view.shareDownloadedCheckbox.model.isSelected()
|
boolean shareDownloaded = view.shareDownloadedCheckbox.model.isSelected()
|
||||||
model.shareDownloadedFiles = shareDownloaded
|
model.shareDownloadedFiles = shareDownloaded
|
||||||
@@ -64,7 +81,29 @@ class OptionsController {
|
|||||||
|
|
||||||
String downloadLocation = model.downloadLocation
|
String downloadLocation = model.downloadLocation
|
||||||
settings.downloadLocation = new File(downloadLocation)
|
settings.downloadLocation = new File(downloadLocation)
|
||||||
|
|
||||||
|
if (settings.embeddedRouter) {
|
||||||
|
text = view.inBwField.text
|
||||||
|
model.inBw = text
|
||||||
|
settings.inBw = Integer.valueOf(text)
|
||||||
|
text = view.outBwField.text
|
||||||
|
model.outBw = text
|
||||||
|
settings.outBw = Integer.valueOf(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
|
||||||
|
model.onlyTrusted = onlyTrusted
|
||||||
|
settings.setAllowUntrusted(!onlyTrusted)
|
||||||
|
|
||||||
|
boolean trustLists = view.allowTrustListsCheckbox.model.isSelected()
|
||||||
|
model.trustLists = trustLists
|
||||||
|
settings.allowTrustLists = trustLists
|
||||||
|
|
||||||
|
String trustListInterval = view.trustListIntervalField.text
|
||||||
|
model.trustListInterval = trustListInterval
|
||||||
|
settings.trustListInterval = Integer.parseInt(trustListInterval)
|
||||||
|
|
||||||
File settingsFile = new File(core.home, "MuWire.properties")
|
File settingsFile = new File(core.home, "MuWire.properties")
|
||||||
settingsFile.withOutputStream {
|
settingsFile.withOutputStream {
|
||||||
settings.write(it)
|
settings.write(it)
|
||||||
|
@@ -0,0 +1,62 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonController
|
||||||
|
import griffon.core.controller.ControllerAction
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
import com.muwire.core.EventBus
|
||||||
|
import com.muwire.core.Persona
|
||||||
|
import com.muwire.core.trust.TrustEvent
|
||||||
|
import com.muwire.core.trust.TrustLevel
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonController)
|
||||||
|
class TrustListController {
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
TrustListModel model
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
TrustListView view
|
||||||
|
|
||||||
|
EventBus eventBus
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void trustFromTrusted() {
|
||||||
|
int selectedRow = view.getSelectedRow("trusted-table")
|
||||||
|
if (selectedRow < 0)
|
||||||
|
return
|
||||||
|
Persona p = model.trusted[selectedRow]
|
||||||
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
|
||||||
|
view.fireUpdate("trusted-table")
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void trustFromDistrusted() {
|
||||||
|
int selectedRow = view.getSelectedRow("distrusted-table")
|
||||||
|
if (selectedRow < 0)
|
||||||
|
return
|
||||||
|
Persona p = model.distrusted[selectedRow]
|
||||||
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
|
||||||
|
view.fireUpdate("distrusted-table")
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void distrustFromTrusted() {
|
||||||
|
int selectedRow = view.getSelectedRow("trusted-table")
|
||||||
|
if (selectedRow < 0)
|
||||||
|
return
|
||||||
|
Persona p = model.trusted[selectedRow]
|
||||||
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
|
||||||
|
view.fireUpdate("trusted-table")
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void distrustFromDistrusted() {
|
||||||
|
int selectedRow = view.getSelectedRow("distrusted-table")
|
||||||
|
if (selectedRow < 0)
|
||||||
|
return
|
||||||
|
Persona p = model.distrusted[selectedRow]
|
||||||
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
|
||||||
|
view.fireUpdate("distrusted-table")
|
||||||
|
}
|
||||||
|
}
|
@@ -48,6 +48,8 @@ class Ready extends AbstractLifecycleHandler {
|
|||||||
} else {
|
} else {
|
||||||
log.info("creating new properties")
|
log.info("creating new properties")
|
||||||
props = new MuWireSettings()
|
props = new MuWireSettings()
|
||||||
|
props.embeddedRouter = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
|
||||||
|
props.updateType = System.getProperty("updateType","jar")
|
||||||
def nickname
|
def nickname
|
||||||
while (true) {
|
while (true) {
|
||||||
nickname = JOptionPane.showInputDialog(null,
|
nickname = JOptionPane.showInputDialog(null,
|
||||||
|
28
gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy
Normal file
28
gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonModel
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.transform.Observable
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonModel)
|
||||||
|
class I2PStatusModel {
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
I2PStatusController controller
|
||||||
|
|
||||||
|
@Observable int ntcpConnections
|
||||||
|
@Observable int ssuConnections
|
||||||
|
@Observable String networkStatus
|
||||||
|
@Observable boolean floodfill
|
||||||
|
@Observable int participatingTunnels
|
||||||
|
@Observable int activePeers
|
||||||
|
@Observable int receiveBps
|
||||||
|
@Observable int sendBps
|
||||||
|
@Observable int participatingBW
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
|
controller.refresh()
|
||||||
|
}
|
||||||
|
}
|
@@ -28,7 +28,10 @@ import com.muwire.core.search.UIResultBatchEvent
|
|||||||
import com.muwire.core.search.UIResultEvent
|
import com.muwire.core.search.UIResultEvent
|
||||||
import com.muwire.core.trust.TrustEvent
|
import com.muwire.core.trust.TrustEvent
|
||||||
import com.muwire.core.trust.TrustService
|
import com.muwire.core.trust.TrustService
|
||||||
|
import com.muwire.core.trust.TrustSubscriptionEvent
|
||||||
|
import com.muwire.core.trust.TrustSubscriptionUpdatedEvent
|
||||||
import com.muwire.core.update.UpdateAvailableEvent
|
import com.muwire.core.update.UpdateAvailableEvent
|
||||||
|
import com.muwire.core.update.UpdateDownloadedEvent
|
||||||
import com.muwire.core.upload.UploadEvent
|
import com.muwire.core.upload.UploadEvent
|
||||||
import com.muwire.core.upload.UploadFinishedEvent
|
import com.muwire.core.upload.UploadFinishedEvent
|
||||||
|
|
||||||
@@ -52,6 +55,7 @@ class MainFrameModel {
|
|||||||
MainFrameController controller
|
MainFrameController controller
|
||||||
@Inject @Nonnull GriffonApplication application
|
@Inject @Nonnull GriffonApplication application
|
||||||
@Observable boolean coreInitialized = false
|
@Observable boolean coreInitialized = false
|
||||||
|
@Observable boolean routerPresent
|
||||||
|
|
||||||
def results = new ConcurrentHashMap<>()
|
def results = new ConcurrentHashMap<>()
|
||||||
def downloads = []
|
def downloads = []
|
||||||
@@ -62,6 +66,7 @@ class MainFrameModel {
|
|||||||
def searches = new LinkedList()
|
def searches = new LinkedList()
|
||||||
def trusted = []
|
def trusted = []
|
||||||
def distrusted = []
|
def distrusted = []
|
||||||
|
def subscriptions = []
|
||||||
|
|
||||||
@Observable int connections
|
@Observable int connections
|
||||||
@Observable String me
|
@Observable String me
|
||||||
@@ -71,12 +76,20 @@ class MainFrameModel {
|
|||||||
@Observable boolean retryButtonEnabled
|
@Observable boolean retryButtonEnabled
|
||||||
@Observable boolean pauseButtonEnabled
|
@Observable boolean pauseButtonEnabled
|
||||||
@Observable String resumeButtonText
|
@Observable String resumeButtonText
|
||||||
|
@Observable boolean subscribeButtonEnabled
|
||||||
|
@Observable boolean markNeutralFromTrustedButtonEnabled
|
||||||
|
@Observable boolean markDistrustedButtonEnabled
|
||||||
|
@Observable boolean markNeutralFromDistrustedButtonEnabled
|
||||||
|
@Observable boolean markTrustedButtonEnabled
|
||||||
|
@Observable boolean reviewButtonEnabled
|
||||||
|
@Observable boolean updateButtonEnabled
|
||||||
|
@Observable boolean unsubscribeButtonEnabled
|
||||||
|
|
||||||
private final Set<InfoHash> infoHashes = new HashSet<>()
|
private final Set<InfoHash> infoHashes = new HashSet<>()
|
||||||
|
|
||||||
private final Set<InfoHash> downloadInfoHashes = new HashSet<>()
|
private final Set<InfoHash> downloadInfoHashes = new HashSet<>()
|
||||||
|
|
||||||
volatile Core core
|
@Observable volatile Core core
|
||||||
|
|
||||||
private long lastRetryTime = System.currentTimeMillis()
|
private long lastRetryTime = System.currentTimeMillis()
|
||||||
|
|
||||||
@@ -124,6 +137,7 @@ class MainFrameModel {
|
|||||||
application.addPropertyChangeListener("core", {e ->
|
application.addPropertyChangeListener("core", {e ->
|
||||||
coreInitialized = (e.getNewValue() != null)
|
coreInitialized = (e.getNewValue() != null)
|
||||||
core = e.getNewValue()
|
core = e.getNewValue()
|
||||||
|
routerPresent = core.router != null
|
||||||
me = core.me.getHumanReadableName()
|
me = core.me.getHumanReadableName()
|
||||||
core.eventBus.register(UIResultEvent.class, this)
|
core.eventBus.register(UIResultEvent.class, this)
|
||||||
core.eventBus.register(UIResultBatchEvent.class, this)
|
core.eventBus.register(UIResultBatchEvent.class, this)
|
||||||
@@ -141,6 +155,8 @@ class MainFrameModel {
|
|||||||
core.eventBus.register(FileUnsharedEvent.class, this)
|
core.eventBus.register(FileUnsharedEvent.class, this)
|
||||||
core.eventBus.register(RouterDisconnectedEvent.class, this)
|
core.eventBus.register(RouterDisconnectedEvent.class, this)
|
||||||
core.eventBus.register(AllFilesLoadedEvent.class, this)
|
core.eventBus.register(AllFilesLoadedEvent.class, this)
|
||||||
|
core.eventBus.register(UpdateDownloadedEvent.class, this)
|
||||||
|
core.eventBus.register(TrustSubscriptionUpdatedEvent.class, this)
|
||||||
|
|
||||||
timer.schedule({
|
timer.schedule({
|
||||||
if (core.shutdown.get())
|
if (core.shutdown.get())
|
||||||
@@ -169,7 +185,6 @@ class MainFrameModel {
|
|||||||
trusted.addAll(core.trustService.good.values())
|
trusted.addAll(core.trustService.good.values())
|
||||||
distrusted.addAll(core.trustService.bad.values())
|
distrusted.addAll(core.trustService.bad.values())
|
||||||
|
|
||||||
|
|
||||||
resumeButtonText = "Retry"
|
resumeButtonText = "Retry"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -181,8 +196,20 @@ class MainFrameModel {
|
|||||||
watched.addAll(core.muOptions.watchedDirectories)
|
watched.addAll(core.muOptions.watchedDirectories)
|
||||||
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
||||||
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
|
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
|
||||||
|
|
||||||
|
core.muOptions.trustSubscriptions.each {
|
||||||
|
core.eventBus.publish(new TrustSubscriptionEvent(persona : it, subscribe : true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onUpdateDownloadedEvent(UpdateDownloadedEvent e) {
|
||||||
|
runInsideUIAsync {
|
||||||
|
JOptionPane.showMessageDialog(null, "MuWire $e.version has been downloaded. You can update now",
|
||||||
|
"Update Downloaded", JOptionPane.INFORMATION_MESSAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onUIResultEvent(UIResultEvent e) {
|
void onUIResultEvent(UIResultEvent e) {
|
||||||
MVCGroup resultsGroup = results.get(e.uuid)
|
MVCGroup resultsGroup = results.get(e.uuid)
|
||||||
resultsGroup?.model.handleResult(e)
|
resultsGroup?.model.handleResult(e)
|
||||||
@@ -190,7 +217,7 @@ class MainFrameModel {
|
|||||||
|
|
||||||
void onUIResultBatchEvent(UIResultBatchEvent e) {
|
void onUIResultBatchEvent(UIResultBatchEvent e) {
|
||||||
MVCGroup resultsGroup = results.get(e.uuid)
|
MVCGroup resultsGroup = results.get(e.uuid)
|
||||||
resultsGroup?.model.handleResultBatch(e.results)
|
resultsGroup?.model?.handleResultBatch(e.results)
|
||||||
}
|
}
|
||||||
|
|
||||||
void onDownloadStartedEvent(DownloadStartedEvent e) {
|
void onDownloadStartedEvent(DownloadStartedEvent e) {
|
||||||
@@ -304,6 +331,14 @@ class MainFrameModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onTrustSubscriptionUpdatedEvent(TrustSubscriptionUpdatedEvent e) {
|
||||||
|
runInsideUIAsync {
|
||||||
|
if (!subscriptions.contains(e.trustList))
|
||||||
|
subscriptions << e.trustList
|
||||||
|
updateTablePreservingSelection("subscription-table")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onQueryEvent(QueryEvent e) {
|
void onQueryEvent(QueryEvent e) {
|
||||||
if (e.replyTo == core.me.destination)
|
if (e.replyTo == core.me.destination)
|
||||||
return
|
return
|
||||||
|
@@ -0,0 +1,25 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonModel
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.transform.Observable
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonModel)
|
||||||
|
class MuWireStatusModel {
|
||||||
|
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
MuWireStatusController controller
|
||||||
|
|
||||||
|
@Observable int incomingConnections
|
||||||
|
@Observable int outgoingConnections
|
||||||
|
@Observable int knownHosts
|
||||||
|
@Observable int sharedFiles
|
||||||
|
@Observable int downloads
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
|
controller.refresh()
|
||||||
|
}
|
||||||
|
}
|
@@ -11,7 +11,7 @@ import griffon.metadata.ArtifactProviderFor
|
|||||||
class OptionsModel {
|
class OptionsModel {
|
||||||
@Observable String downloadRetryInterval
|
@Observable String downloadRetryInterval
|
||||||
@Observable String updateCheckInterval
|
@Observable String updateCheckInterval
|
||||||
@Observable boolean onlyTrusted
|
@Observable boolean autoDownloadUpdate
|
||||||
@Observable boolean shareDownloadedFiles
|
@Observable boolean shareDownloadedFiles
|
||||||
@Observable String downloadLocation
|
@Observable String downloadLocation
|
||||||
|
|
||||||
@@ -20,6 +20,8 @@ class OptionsModel {
|
|||||||
@Observable String inboundQuantity
|
@Observable String inboundQuantity
|
||||||
@Observable String outboundLength
|
@Observable String outboundLength
|
||||||
@Observable String outboundQuantity
|
@Observable String outboundQuantity
|
||||||
|
@Observable String i2pUDPPort
|
||||||
|
@Observable String i2pNTCPPort
|
||||||
|
|
||||||
// gui options
|
// gui options
|
||||||
@Observable boolean showMonitor
|
@Observable boolean showMonitor
|
||||||
@@ -30,11 +32,21 @@ class OptionsModel {
|
|||||||
@Observable boolean excludeLocalResult
|
@Observable boolean excludeLocalResult
|
||||||
@Observable boolean showSearchHashes
|
@Observable boolean showSearchHashes
|
||||||
|
|
||||||
|
// bw options
|
||||||
|
@Observable String inBw
|
||||||
|
@Observable String outBw
|
||||||
|
|
||||||
|
// trust options
|
||||||
|
@Observable boolean onlyTrusted
|
||||||
|
@Observable boolean trustLists
|
||||||
|
@Observable String trustListInterval
|
||||||
|
|
||||||
|
|
||||||
void mvcGroupInit(Map<String, String> args) {
|
void mvcGroupInit(Map<String, String> args) {
|
||||||
MuWireSettings settings = application.context.get("muwire-settings")
|
MuWireSettings settings = application.context.get("muwire-settings")
|
||||||
downloadRetryInterval = settings.downloadRetryInterval
|
downloadRetryInterval = settings.downloadRetryInterval
|
||||||
updateCheckInterval = settings.updateCheckInterval
|
updateCheckInterval = settings.updateCheckInterval
|
||||||
onlyTrusted = !settings.allowUntrusted()
|
autoDownloadUpdate = settings.autoDownloadUpdate
|
||||||
shareDownloadedFiles = settings.shareDownloadedFiles
|
shareDownloadedFiles = settings.shareDownloadedFiles
|
||||||
downloadLocation = settings.downloadLocation.getAbsolutePath()
|
downloadLocation = settings.downloadLocation.getAbsolutePath()
|
||||||
|
|
||||||
@@ -43,6 +55,8 @@ class OptionsModel {
|
|||||||
inboundQuantity = core.i2pOptions["inbound.quantity"]
|
inboundQuantity = core.i2pOptions["inbound.quantity"]
|
||||||
outboundLength = core.i2pOptions["outbound.length"]
|
outboundLength = core.i2pOptions["outbound.length"]
|
||||||
outboundQuantity = core.i2pOptions["outbound.quantity"]
|
outboundQuantity = core.i2pOptions["outbound.quantity"]
|
||||||
|
i2pUDPPort = core.i2pOptions["i2np.udp.port"]
|
||||||
|
i2pNTCPPort = core.i2pOptions["i2np.ntcp.port"]
|
||||||
|
|
||||||
UISettings uiSettings = application.context.get("ui-settings")
|
UISettings uiSettings = application.context.get("ui-settings")
|
||||||
showMonitor = uiSettings.showMonitor
|
showMonitor = uiSettings.showMonitor
|
||||||
@@ -52,5 +66,14 @@ class OptionsModel {
|
|||||||
clearFinishedDownloads = uiSettings.clearFinishedDownloads
|
clearFinishedDownloads = uiSettings.clearFinishedDownloads
|
||||||
excludeLocalResult = uiSettings.excludeLocalResult
|
excludeLocalResult = uiSettings.excludeLocalResult
|
||||||
showSearchHashes = uiSettings.showSearchHashes
|
showSearchHashes = uiSettings.showSearchHashes
|
||||||
|
|
||||||
|
if (core.router != null) {
|
||||||
|
inBw = String.valueOf(settings.inBw)
|
||||||
|
outBw = String.valueOf(settings.outBw)
|
||||||
|
}
|
||||||
|
|
||||||
|
onlyTrusted = !settings.allowUntrusted()
|
||||||
|
trustLists = settings.allowTrustLists
|
||||||
|
trustListInterval = String.valueOf(settings.trustListInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
22
gui/griffon-app/models/com/muwire/gui/TrustListModel.groovy
Normal file
22
gui/griffon-app/models/com/muwire/gui/TrustListModel.groovy
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import com.muwire.core.trust.RemoteTrustList
|
||||||
|
import com.muwire.core.trust.TrustService
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonModel
|
||||||
|
import griffon.transform.Observable
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonModel)
|
||||||
|
class TrustListModel {
|
||||||
|
RemoteTrustList trustList
|
||||||
|
TrustService trustService
|
||||||
|
|
||||||
|
def trusted
|
||||||
|
def distrusted
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
|
trusted = new ArrayList<>(trustList.good)
|
||||||
|
distrusted = new ArrayList<>(trustList.bad)
|
||||||
|
}
|
||||||
|
}
|
80
gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy
Normal file
80
gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonView
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
|
import javax.swing.JDialog
|
||||||
|
import javax.swing.JPanel
|
||||||
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.event.WindowAdapter
|
||||||
|
import java.awt.event.WindowEvent
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonView)
|
||||||
|
class I2PStatusView {
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
FactoryBuilderSupport builder
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
I2PStatusModel model
|
||||||
|
|
||||||
|
def mainFrame
|
||||||
|
def dialog
|
||||||
|
def panel
|
||||||
|
def buttonsPanel
|
||||||
|
|
||||||
|
void initUI() {
|
||||||
|
mainFrame = application.windowManager.findWindow("main-frame")
|
||||||
|
|
||||||
|
dialog = new JDialog(mainFrame, "I2P Status", true)
|
||||||
|
|
||||||
|
panel = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
label(text : "Network status", constraints : gbc(gridx:0, gridy:0))
|
||||||
|
label(text : bind {model.networkStatus}, constraints : gbc(gridx: 1, gridy:0))
|
||||||
|
label(text: "Floodfill", constraints : gbc(gridx: 0, gridy : 1))
|
||||||
|
label(text : bind {model.floodfill}, constraints : gbc(gridx:1, gridy:1))
|
||||||
|
label(text : "NTCP Connections", constraints : gbc(gridx:0, gridy:2))
|
||||||
|
label(text : bind {model.ntcpConnections}, constraints : gbc(gridx: 1, gridy:2))
|
||||||
|
label(text : "SSU Connections", constraints : gbc(gridx:0, gridy:3))
|
||||||
|
label(text : bind {model.ssuConnections}, constraints : gbc(gridx: 1, gridy:3))
|
||||||
|
label(text : "Participating Tunnels", constraints : gbc(gridx:0, gridy:4))
|
||||||
|
label(text : bind {model.participatingTunnels}, constraints : gbc(gridx: 1, gridy:4))
|
||||||
|
label(text : "Participating Bandwidth", constraints : gbc(gridx:0, gridy:5))
|
||||||
|
label(text : bind {model.participatingBW}, constraints : gbc(gridx: 1, gridy:5))
|
||||||
|
label(text : "Active Peers", constraints : gbc(gridx:0, gridy:6))
|
||||||
|
label(text : bind {model.activePeers}, constraints : gbc(gridx: 1, gridy:6))
|
||||||
|
label(text : "Receive Bps (15 seconds)", constraints : gbc(gridx:0, gridy:7))
|
||||||
|
label(text : bind {model.receiveBps}, constraints : gbc(gridx: 1, gridy:7))
|
||||||
|
label(text : "Send Bps (15 seconds)", constraints : gbc(gridx:0, gridy:8))
|
||||||
|
label(text : bind {model.sendBps}, constraints : gbc(gridx: 1, gridy:8))
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonsPanel = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
button(text : "Refresh", constraints: gbc(gridx: 0, gridy: 0), refreshAction)
|
||||||
|
button(text : "Close", constraints : gbc(gridx : 1, gridy :0), closeAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
|
JPanel statusPanel = new JPanel()
|
||||||
|
statusPanel.setLayout(new BorderLayout())
|
||||||
|
statusPanel.add(panel, BorderLayout.CENTER)
|
||||||
|
statusPanel.add(buttonsPanel, BorderLayout.SOUTH)
|
||||||
|
|
||||||
|
dialog.getContentPane().add(statusPanel)
|
||||||
|
dialog.pack()
|
||||||
|
dialog.setLocationRelativeTo(mainFrame)
|
||||||
|
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
||||||
|
dialog.addWindowListener(new WindowAdapter() {
|
||||||
|
public void windowClosed(WindowEvent e) {
|
||||||
|
mvcGroup.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
@@ -22,8 +22,10 @@ import javax.swing.border.Border
|
|||||||
import javax.swing.table.DefaultTableCellRenderer
|
import javax.swing.table.DefaultTableCellRenderer
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
|
import com.muwire.core.MuWireSettings
|
||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
import com.muwire.core.files.FileSharedEvent
|
import com.muwire.core.files.FileSharedEvent
|
||||||
|
import com.muwire.core.trust.RemoteTrustList
|
||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
import java.awt.CardLayout
|
import java.awt.CardLayout
|
||||||
@@ -52,6 +54,8 @@ class MainFrameView {
|
|||||||
def downloadsTable
|
def downloadsTable
|
||||||
def lastDownloadSortEvent
|
def lastDownloadSortEvent
|
||||||
def lastSharedSortEvent
|
def lastSharedSortEvent
|
||||||
|
def lastWatchedSortEvent
|
||||||
|
def trustTablesSortEvents = [:]
|
||||||
|
|
||||||
void initUI() {
|
void initUI() {
|
||||||
UISettings settings = application.context.get("ui-settings")
|
UISettings settings = application.context.get("ui-settings")
|
||||||
@@ -70,6 +74,11 @@ class MainFrameView {
|
|||||||
menu (text : "Options") {
|
menu (text : "Options") {
|
||||||
menuItem("Configuration", actionPerformed : {mvcGroup.createMVCGroup("Options")})
|
menuItem("Configuration", actionPerformed : {mvcGroup.createMVCGroup("Options")})
|
||||||
}
|
}
|
||||||
|
menu (text : "Status") {
|
||||||
|
menuItem("MuWire", actionPerformed : {mvcGroup.createMVCGroup("mu-wire-status")})
|
||||||
|
MuWireSettings muSettings = application.context.get("muwire-settings")
|
||||||
|
menuItem("I2P", enabled : bind {model.routerPresent}, actionPerformed: {mvcGroup.createMVCGroup("i-2-p-status")})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
borderLayout()
|
borderLayout()
|
||||||
panel (border: etchedBorder(), constraints : BorderLayout.NORTH) {
|
panel (border: etchedBorder(), constraints : BorderLayout.NORTH) {
|
||||||
@@ -167,8 +176,8 @@ class MainFrameView {
|
|||||||
scrollPane(constraints : BorderLayout.CENTER) {
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
table(id : "shared-files-table", autoCreateRowSorter: true) {
|
table(id : "shared-files-table", autoCreateRowSorter: true) {
|
||||||
tableModel(list : model.shared) {
|
tableModel(list : model.shared) {
|
||||||
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.file.getAbsolutePath()})
|
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()})
|
||||||
closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.file.length() })
|
closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.getCachedLength() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,35 +253,66 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel(constraints : "trust window") {
|
panel(constraints : "trust window") {
|
||||||
gridLayout(rows: 1, cols :2)
|
gridLayout(rows : 2, cols : 1)
|
||||||
panel (border : etchedBorder()){
|
panel {
|
||||||
borderLayout()
|
gridLayout(rows: 1, cols :2)
|
||||||
scrollPane(constraints : BorderLayout.CENTER) {
|
panel (border : etchedBorder()){
|
||||||
table(id : "trusted-table", autoCreateRowSorter : true) {
|
borderLayout()
|
||||||
tableModel(list : model.trusted) {
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
closureColumn(header : "Trusted Users", type : String, read : { it.getHumanReadableName() } )
|
table(id : "trusted-table", autoCreateRowSorter : true) {
|
||||||
|
tableModel(list : model.trusted) {
|
||||||
|
closureColumn(header : "Trusted Users", type : String, read : { it.getHumanReadableName() } )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
panel (constraints : BorderLayout.SOUTH) {
|
||||||
|
gridBagLayout()
|
||||||
|
button(text : "Subscribe", enabled : bind {model.subscribeButtonEnabled}, constraints : gbc(gridx: 0, gridy : 0), subscribeAction)
|
||||||
|
button(text : "Mark Neutral", enabled : bind {model.markNeutralFromTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy: 0), markNeutralFromTrustedAction)
|
||||||
|
button(text : "Mark Distrusted", enabled : bind {model.markDistrustedButtonEnabled}, constraints : gbc(gridx: 2, gridy:0), markDistrustedAction)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
panel (constraints : BorderLayout.EAST) {
|
panel (border : etchedBorder()){
|
||||||
gridBagLayout()
|
borderLayout()
|
||||||
button(text : "Mark Neutral", constraints : gbc(gridx: 0, gridy: 0), markNeutralFromTrustedAction)
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
button(text : "Mark Distrusted", constraints : gbc(gridx: 0, gridy:1), markDistrustedAction)
|
table(id : "distrusted-table", autoCreateRowSorter : true) {
|
||||||
|
tableModel(list : model.distrusted) {
|
||||||
|
closureColumn(header: "Distrusted Users", type : String, read : { it.getHumanReadableName() } )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel(constraints : BorderLayout.SOUTH) {
|
||||||
|
gridBagLayout()
|
||||||
|
button(text: "Mark Neutral", enabled : bind {model.markNeutralFromDistrustedButtonEnabled}, constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction)
|
||||||
|
button(text: "Mark Trusted", enabled : bind {model.markTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy : 0), markTrustedAction)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel (border : etchedBorder()){
|
panel {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
|
panel (constraints : BorderLayout.NORTH){
|
||||||
|
label(text : "Trust List Subscriptions")
|
||||||
|
}
|
||||||
scrollPane(constraints : BorderLayout.CENTER) {
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
table(id : "distrusted-table", autoCreateRowSorter : true) {
|
table(id : "subscription-table", autoCreateRowSorter : true) {
|
||||||
tableModel(list : model.distrusted) {
|
tableModel(list : model.subscriptions) {
|
||||||
closureColumn(header: "Distrusted Users", type : String, read : { it.getHumanReadableName() } )
|
closureColumn(header : "Name", preferredWidth: 200, type: String, read : {it.persona.getHumanReadableName()})
|
||||||
|
closureColumn(header : "Trusted", preferredWidth : 20, type: Integer, read : {it.good.size()})
|
||||||
|
closureColumn(header : "Distrusted", preferredWidth: 20, type: Integer, read : {it.bad.size()})
|
||||||
|
closureColumn(header : "Status", preferredWidth: 30, type: String, read : {it.status.toString()})
|
||||||
|
closureColumn(header : "Last Updated", preferredWidth: 200, type : String, read : {
|
||||||
|
if (it.timestamp == 0)
|
||||||
|
return "Never"
|
||||||
|
else
|
||||||
|
return String.valueOf(new Date(it.timestamp))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel(constraints : BorderLayout.WEST) {
|
panel(constraints : BorderLayout.SOUTH) {
|
||||||
gridBagLayout()
|
button(text : "Review", enabled : bind {model.reviewButtonEnabled}, reviewAction)
|
||||||
button(text: "Mark Neutral", constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction)
|
button(text : "Update", enabled : bind {model.updateButtonEnabled}, updateAction)
|
||||||
button(text: "Mark Trusted", constraints : gbc(gridx: 0, gridy : 1), markTrustedAction)
|
button(text : "Unsubscribe", enabled : bind {model.unsubscribeButtonEnabled}, unsubscribeAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,11 +400,12 @@ class MainFrameView {
|
|||||||
sharedFilesTable.rowSorter.setSortsOnUpdates(true)
|
sharedFilesTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
|
||||||
JPopupMenu sharedFilesMenu = new JPopupMenu()
|
JPopupMenu sharedFilesMenu = new JPopupMenu()
|
||||||
// JMenuItem unshareSelectedFiles = new JMenuItem("Unshare selected files")
|
|
||||||
// unshareSelectedFiles.addActionListener({mvcGroup.controller.unshareSelectedFiles()})
|
|
||||||
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
|
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
|
||||||
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard(sharedFilesTable)})
|
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()})
|
||||||
sharedFilesMenu.add(copyHashToClipboard)
|
sharedFilesMenu.add(copyHashToClipboard)
|
||||||
|
JMenuItem unshareSelectedFiles = new JMenuItem("Unshare selected files")
|
||||||
|
unshareSelectedFiles.addActionListener({mvcGroup.controller.unshareSelectedFile()})
|
||||||
|
sharedFilesMenu.add(unshareSelectedFiles)
|
||||||
sharedFilesTable.addMouseListener(new MouseAdapter() {
|
sharedFilesTable.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseReleased(MouseEvent e) {
|
public void mouseReleased(MouseEvent e) {
|
||||||
@@ -397,19 +438,121 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// watched directories table
|
||||||
|
def watchedTable = builder.getVariable("watched-directories-table")
|
||||||
|
watchedTable.rowSorter.addRowSorterListener({evt -> lastWatchedSortEvent = evt})
|
||||||
|
watchedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
JPopupMenu watchedMenu = new JPopupMenu()
|
||||||
|
JMenuItem stopWatching = new JMenuItem("Stop sharing")
|
||||||
|
stopWatching.addActionListener({mvcGroup.controller.stopWatchingDirectory()})
|
||||||
|
watchedMenu.add(stopWatching)
|
||||||
|
watchedTable.addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
if (e.isPopupTrigger())
|
||||||
|
showPopupMenu(watchedMenu, e)
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
if (e.isPopupTrigger())
|
||||||
|
showPopupMenu(watchedMenu, e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// subscription table
|
||||||
|
def subscriptionTable = builder.getVariable("subscription-table")
|
||||||
|
subscriptionTable.setDefaultRenderer(Integer.class, centerRenderer)
|
||||||
|
subscriptionTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["subscription-table"] = evt})
|
||||||
|
subscriptionTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
selectionModel = subscriptionTable.getSelectionModel()
|
||||||
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
selectionModel.addListSelectionListener({
|
||||||
|
int selectedRow = getSelectedTrustTablesRow("subscription-table")
|
||||||
|
if (selectedRow < 0) {
|
||||||
|
model.reviewButtonEnabled = false
|
||||||
|
model.updateButtonEnabled = false
|
||||||
|
model.unsubscribeButtonEnabled = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
def trustList = model.subscriptions[selectedRow]
|
||||||
|
if (trustList == null)
|
||||||
|
return
|
||||||
|
switch(trustList.status) {
|
||||||
|
case RemoteTrustList.Status.NEW:
|
||||||
|
case RemoteTrustList.Status.UPDATING:
|
||||||
|
model.reviewButtonEnabled = false
|
||||||
|
model.updateButtonEnabled = false
|
||||||
|
model.unsubscribeButtonEnabled = false
|
||||||
|
break
|
||||||
|
case RemoteTrustList.Status.UPDATED:
|
||||||
|
model.reviewButtonEnabled = true
|
||||||
|
model.updateButtonEnabled = true
|
||||||
|
model.unsubscribeButtonEnabled = true
|
||||||
|
break
|
||||||
|
case RemoteTrustList.Status.UPDATE_FAILED:
|
||||||
|
model.reviewButtonEnabled = false
|
||||||
|
model.updateButtonEnabled = true
|
||||||
|
model.unsubscribeButtonEnabled = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// trusted table
|
||||||
|
def trustedTable = builder.getVariable("trusted-table")
|
||||||
|
trustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["trusted-table"] = evt})
|
||||||
|
trustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
selectionModel = trustedTable.getSelectionModel()
|
||||||
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
selectionModel.addListSelectionListener({
|
||||||
|
int selectedRow = getSelectedTrustTablesRow("trusted-table")
|
||||||
|
if (selectedRow < 0) {
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
|
} else {
|
||||||
|
model.subscribeButtonEnabled = true
|
||||||
|
model.markDistrustedButtonEnabled = true
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// distrusted table
|
||||||
|
def distrustedTable = builder.getVariable("distrusted-table")
|
||||||
|
distrustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["distrusted-table"] = evt})
|
||||||
|
distrustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
selectionModel = distrustedTable.getSelectionModel()
|
||||||
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
selectionModel.addListSelectionListener({
|
||||||
|
int selectedRow = getSelectedTrustTablesRow("distrusted-table")
|
||||||
|
if (selectedRow < 0) {
|
||||||
|
model.markTrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = false
|
||||||
|
} else {
|
||||||
|
model.markTrustedButtonEnabled = true
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void showPopupMenu(JPopupMenu menu, MouseEvent event) {
|
private static void showPopupMenu(JPopupMenu menu, MouseEvent event) {
|
||||||
menu.show(event.getComponent(), event.getX(), event.getY())
|
menu.show(event.getComponent(), event.getX(), event.getY())
|
||||||
}
|
}
|
||||||
|
|
||||||
def copyHashToClipboard(JTable sharedFilesTable) {
|
def selectedSharedFile() {
|
||||||
|
def sharedFilesTable = builder.getVariable("shared-files-table")
|
||||||
int selected = sharedFilesTable.getSelectedRow()
|
int selected = sharedFilesTable.getSelectedRow()
|
||||||
if (selected < 0)
|
if (selected < 0)
|
||||||
return
|
return null
|
||||||
if (lastSharedSortEvent != null)
|
if (lastSharedSortEvent != null)
|
||||||
selected = sharedFilesTable.rowSorter.convertRowIndexToModel(selected)
|
selected = sharedFilesTable.rowSorter.convertRowIndexToModel(selected)
|
||||||
String root = Base64.encode(model.shared[selected].infoHash.getRoot())
|
model.shared[selected]
|
||||||
|
}
|
||||||
|
|
||||||
|
def copyHashToClipboard() {
|
||||||
|
def selected = selectedSharedFile()
|
||||||
|
if (selected == null)
|
||||||
|
return
|
||||||
|
String root = Base64.encode(selected.infoHash.getRoot())
|
||||||
StringSelection selection = new StringSelection(root)
|
StringSelection selection = new StringSelection(root)
|
||||||
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
clipboard.setContents(selection, null)
|
clipboard.setContents(selection, null)
|
||||||
@@ -543,4 +686,24 @@ class MainFrameView {
|
|||||||
model.core.eventBus.publish(new FileSharedEvent(file : f))
|
model.core.eventBus.publish(new FileSharedEvent(file : f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String getSelectedWatchedDirectory() {
|
||||||
|
def watchedTable = builder.getVariable("watched-directories-table")
|
||||||
|
int selectedRow = watchedTable.getSelectedRow()
|
||||||
|
if (selectedRow < 0)
|
||||||
|
return null
|
||||||
|
if (lastWatchedSortEvent != null)
|
||||||
|
selectedRow = watchedTable.rowSorter.convertRowIndexToModel(selectedRow)
|
||||||
|
model.watched[selectedRow]
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSelectedTrustTablesRow(String tableName) {
|
||||||
|
def table = builder.getVariable(tableName)
|
||||||
|
int selectedRow = table.getSelectedRow()
|
||||||
|
if (selectedRow < 0)
|
||||||
|
return -1
|
||||||
|
if (trustTablesSortEvents.get(tableName) != null)
|
||||||
|
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
|
||||||
|
selectedRow
|
||||||
|
}
|
||||||
}
|
}
|
73
gui/griffon-app/views/com/muwire/gui/MuWireStatusView.groovy
Normal file
73
gui/griffon-app/views/com/muwire/gui/MuWireStatusView.groovy
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonView
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
|
import javax.swing.JDialog
|
||||||
|
import javax.swing.JPanel
|
||||||
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
|
import com.muwire.core.Core
|
||||||
|
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.event.WindowAdapter
|
||||||
|
import java.awt.event.WindowEvent
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonView)
|
||||||
|
class MuWireStatusView {
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
FactoryBuilderSupport builder
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
MuWireStatusModel model
|
||||||
|
|
||||||
|
def mainFrame
|
||||||
|
def dialog
|
||||||
|
def panel
|
||||||
|
def buttonsPanel
|
||||||
|
|
||||||
|
void initUI() {
|
||||||
|
mainFrame = application.windowManager.findWindow("main-frame")
|
||||||
|
|
||||||
|
dialog = new JDialog(mainFrame, "MuWire Status", true)
|
||||||
|
|
||||||
|
panel = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
label(text : "Incoming connections", constraints : gbc(gridx:0, gridy:0))
|
||||||
|
label(text : bind {model.incomingConnections}, constraints : gbc(gridx:1, gridy:0))
|
||||||
|
label(text : "Outgoing connections", constraints : gbc(gridx:0, gridy:1))
|
||||||
|
label(text : bind {model.outgoingConnections}, constraints : gbc(gridx:1, gridy:1))
|
||||||
|
label(text : "Known hosts", constraints : gbc(gridx:0, gridy:2))
|
||||||
|
label(text : bind {model.knownHosts}, constraints : gbc(gridx:1, gridy:2))
|
||||||
|
label(text : "Shared files", constraints : gbc(gridx:0, gridy:3))
|
||||||
|
label(text : bind {model.sharedFiles}, constraints : gbc(gridx:1, gridy:3))
|
||||||
|
label(text : "Downloads", constraints : gbc(gridx:0, gridy:4))
|
||||||
|
label(text : bind {model.downloads}, constraints : gbc(gridx:1, gridy:4))
|
||||||
|
}
|
||||||
|
buttonsPanel = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
button(text : "Refresh", constraints: gbc(gridx: 0, gridy: 0), refreshAction)
|
||||||
|
button(text : "Close", constraints : gbc(gridx : 1, gridy :0), closeAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
|
JPanel statusPanel = new JPanel()
|
||||||
|
statusPanel.setLayout(new BorderLayout())
|
||||||
|
statusPanel.add(panel, BorderLayout.CENTER)
|
||||||
|
statusPanel.add(buttonsPanel, BorderLayout.SOUTH)
|
||||||
|
|
||||||
|
dialog.getContentPane().add(statusPanel)
|
||||||
|
dialog.pack()
|
||||||
|
dialog.setLocationRelativeTo(mainFrame)
|
||||||
|
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
||||||
|
dialog.addWindowListener(new WindowAdapter() {
|
||||||
|
public void windowClosed(WindowEvent e) {
|
||||||
|
mvcGroup.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,8 @@ import javax.swing.JPanel
|
|||||||
import javax.swing.JTabbedPane
|
import javax.swing.JTabbedPane
|
||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
|
import com.muwire.core.Core
|
||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
import java.awt.event.WindowAdapter
|
import java.awt.event.WindowAdapter
|
||||||
import java.awt.event.WindowEvent
|
import java.awt.event.WindowEvent
|
||||||
@@ -26,16 +28,20 @@ class OptionsView {
|
|||||||
def p
|
def p
|
||||||
def i
|
def i
|
||||||
def u
|
def u
|
||||||
|
def bandwidth
|
||||||
|
def trust
|
||||||
|
|
||||||
def retryField
|
def retryField
|
||||||
def updateField
|
def updateField
|
||||||
def allowUntrustedCheckbox
|
def autoDownloadUpdateCheckbox
|
||||||
def shareDownloadedCheckbox
|
def shareDownloadedCheckbox
|
||||||
|
|
||||||
def inboundLengthField
|
def inboundLengthField
|
||||||
def inboundQuantityField
|
def inboundQuantityField
|
||||||
def outboundLengthField
|
def outboundLengthField
|
||||||
def outboundQuantityField
|
def outboundQuantityField
|
||||||
|
def i2pUDPPortField
|
||||||
|
def i2pNTCPPortField
|
||||||
|
|
||||||
def lnfField
|
def lnfField
|
||||||
def monitorCheckbox
|
def monitorCheckbox
|
||||||
@@ -44,6 +50,13 @@ class OptionsView {
|
|||||||
def clearFinishedDownloadsCheckbox
|
def clearFinishedDownloadsCheckbox
|
||||||
def excludeLocalResultCheckbox
|
def excludeLocalResultCheckbox
|
||||||
def showSearchHashesCheckbox
|
def showSearchHashesCheckbox
|
||||||
|
|
||||||
|
def inBwField
|
||||||
|
def outBwField
|
||||||
|
|
||||||
|
def allowUntrustedCheckbox
|
||||||
|
def allowTrustListsCheckbox
|
||||||
|
def trustListIntervalField
|
||||||
|
|
||||||
def buttonsPanel
|
def buttonsPanel
|
||||||
|
|
||||||
@@ -62,10 +75,10 @@ class OptionsView {
|
|||||||
label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1))
|
label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1))
|
||||||
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
|
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
|
||||||
label(text : "hours", constraints : gbc(gridx: 2, gridy : 1))
|
label(text : "hours", constraints : gbc(gridx: 2, gridy : 1))
|
||||||
|
|
||||||
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 2))
|
|
||||||
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 2))
|
|
||||||
|
|
||||||
|
label(text : "Download updates automatically", constraints: gbc(gridx :0, gridy : 2))
|
||||||
|
autoDownloadUpdateCheckbox = checkBox(selected : bind {model.autoDownloadUpdate}, constraints : gbc(gridx:1, gridy : 2))
|
||||||
|
|
||||||
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
|
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
|
||||||
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3))
|
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3))
|
||||||
|
|
||||||
@@ -85,6 +98,15 @@ class OptionsView {
|
|||||||
outboundLengthField = textField(text : bind {model.outboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:3))
|
outboundLengthField = textField(text : bind {model.outboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:3))
|
||||||
label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4))
|
label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4))
|
||||||
outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:4))
|
outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:4))
|
||||||
|
|
||||||
|
Core core = application.context.get("core")
|
||||||
|
if (core.router != null) {
|
||||||
|
label(text : "TCP Port", constraints : gbc(gridx :0, gridy: 5))
|
||||||
|
i2pNTCPPortField = textField(text : bind {model.i2pNTCPPort}, columns : 4, constraints : gbc(gridx:1, gridy:5))
|
||||||
|
label(text : "UDP Port", constraints : gbc(gridx :0, gridy: 6))
|
||||||
|
i2pUDPPortField = textField(text : bind {model.i2pUDPPort}, columns : 4, constraints : gbc(gridx:1, gridy:6))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
u = builder.panel {
|
u = builder.panel {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
@@ -104,6 +126,26 @@ class OptionsView {
|
|||||||
// label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
|
// label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
|
||||||
// showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
|
// showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
|
||||||
}
|
}
|
||||||
|
bandwidth = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
|
||||||
|
label(text : "Inbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 1))
|
||||||
|
inBwField = textField(text : bind {model.inBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 1))
|
||||||
|
label(text : "Outbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 2))
|
||||||
|
outBwField = textField(text : bind {model.outBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 2))
|
||||||
|
}
|
||||||
|
trust = builder.panel {
|
||||||
|
gridBagLayout()
|
||||||
|
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 0))
|
||||||
|
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 0))
|
||||||
|
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 1))
|
||||||
|
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 1))
|
||||||
|
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:2))
|
||||||
|
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:2))
|
||||||
|
label(text : "hours", constraints : gbc(gridx: 2, gridy:2))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
buttonsPanel = builder.panel {
|
buttonsPanel = builder.panel {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
||||||
@@ -116,6 +158,11 @@ class OptionsView {
|
|||||||
tabbedPane.addTab("MuWire", p)
|
tabbedPane.addTab("MuWire", p)
|
||||||
tabbedPane.addTab("I2P", i)
|
tabbedPane.addTab("I2P", i)
|
||||||
tabbedPane.addTab("GUI", u)
|
tabbedPane.addTab("GUI", u)
|
||||||
|
Core core = application.context.get("core")
|
||||||
|
if (core.router != null) {
|
||||||
|
tabbedPane.addTab("Bandwidth", bandwidth)
|
||||||
|
}
|
||||||
|
tabbedPane.addTab("Trust", trust)
|
||||||
|
|
||||||
JPanel panel = new JPanel()
|
JPanel panel = new JPanel()
|
||||||
panel.setLayout(new BorderLayout())
|
panel.setLayout(new BorderLayout())
|
||||||
|
120
gui/griffon-app/views/com/muwire/gui/TrustListView.groovy
Normal file
120
gui/griffon-app/views/com/muwire/gui/TrustListView.groovy
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.artifact.GriffonView
|
||||||
|
import griffon.inject.MVCMember
|
||||||
|
import griffon.metadata.ArtifactProviderFor
|
||||||
|
|
||||||
|
import javax.swing.JDialog
|
||||||
|
import javax.swing.ListSelectionModel
|
||||||
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.event.WindowAdapter
|
||||||
|
import java.awt.event.WindowEvent
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull
|
||||||
|
|
||||||
|
@ArtifactProviderFor(GriffonView)
|
||||||
|
class TrustListView {
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
FactoryBuilderSupport builder
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
TrustListModel model
|
||||||
|
|
||||||
|
def dialog
|
||||||
|
def mainFrame
|
||||||
|
def mainPanel
|
||||||
|
|
||||||
|
def sortEvents = [:]
|
||||||
|
|
||||||
|
void initUI() {
|
||||||
|
mainFrame = application.windowManager.findWindow("main-frame")
|
||||||
|
dialog = new JDialog(mainFrame, model.trustList.persona.getHumanReadableName(), true)
|
||||||
|
mainPanel = builder.panel {
|
||||||
|
borderLayout()
|
||||||
|
panel(constraints : BorderLayout.NORTH) {
|
||||||
|
borderLayout()
|
||||||
|
panel (constraints : BorderLayout.NORTH) {
|
||||||
|
label(text: "Trust List of "+model.trustList.persona.getHumanReadableName())
|
||||||
|
}
|
||||||
|
panel (constraints: BorderLayout.SOUTH) {
|
||||||
|
label(text : "Last updated "+ new Date(model.trustList.timestamp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel(constraints : BorderLayout.CENTER) {
|
||||||
|
gridLayout(rows : 1, cols : 2)
|
||||||
|
panel {
|
||||||
|
borderLayout()
|
||||||
|
scrollPane (constraints : BorderLayout.CENTER){
|
||||||
|
table(id : "trusted-table", autoCreateRowSorter : true) {
|
||||||
|
tableModel(list : model.trusted) {
|
||||||
|
closureColumn(header: "Trusted Users", type : String, read : {it.getHumanReadableName()})
|
||||||
|
closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.destination).toString()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel (constraints : BorderLayout.SOUTH) {
|
||||||
|
gridBagLayout()
|
||||||
|
button(text : "Trust", constraints : gbc(gridx : 0, gridy : 0), trustFromTrustedAction)
|
||||||
|
button(text : "Distrust", constraints : gbc(gridx : 1, gridy : 0), distrustFromTrustedAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel {
|
||||||
|
borderLayout()
|
||||||
|
scrollPane (constraints : BorderLayout.CENTER ){
|
||||||
|
table(id : "distrusted-table", autoCreateRowSorter : true) {
|
||||||
|
tableModel(list : model.distrusted) {
|
||||||
|
closureColumn(header: "Distrusted Users", type : String, read : {it.getHumanReadableName()})
|
||||||
|
closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.destination).toString()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel(constraints : BorderLayout.SOUTH) {
|
||||||
|
gridBagLayout()
|
||||||
|
button(text : "Trust", constraints : gbc(gridx : 0, gridy : 0), trustFromDistrustedAction)
|
||||||
|
button(text : "Distrust", constraints : gbc(gridx : 1, gridy : 0), distrustFromDistrustedAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
|
|
||||||
|
def trustedTable = builder.getVariable("trusted-table")
|
||||||
|
trustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["trusted-table"] = evt})
|
||||||
|
trustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
trustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
|
||||||
|
def distrustedTable = builder.getVariable("distrusted-table")
|
||||||
|
distrustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["distrusted-table"] = evt})
|
||||||
|
distrustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
distrustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
|
||||||
|
dialog.getContentPane().add(mainPanel)
|
||||||
|
dialog.pack()
|
||||||
|
dialog.setLocationRelativeTo(mainFrame)
|
||||||
|
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
||||||
|
dialog.addWindowListener(new WindowAdapter() {
|
||||||
|
public void windowClosed(WindowEvent e) {
|
||||||
|
mvcGroup.destroy()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSelectedRow(String tableName) {
|
||||||
|
def table = builder.getVariable(tableName)
|
||||||
|
int selectedRow = table.getSelectedRow()
|
||||||
|
if (selectedRow < 0)
|
||||||
|
return -1
|
||||||
|
if (sortEvents.get(tableName) != null)
|
||||||
|
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
|
||||||
|
selectedRow
|
||||||
|
}
|
||||||
|
|
||||||
|
void fireUpdate(String tableName) {
|
||||||
|
def table = builder.getVariable(tableName)
|
||||||
|
table.model.fireTableDataChanged()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.test.GriffonFestRule
|
||||||
|
import org.fest.swing.fixture.FrameFixture
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail
|
||||||
|
|
||||||
|
class I2PStatusIntegrationTest {
|
||||||
|
static {
|
||||||
|
System.setProperty('griffon.swing.edt.violations.check', 'true')
|
||||||
|
System.setProperty('griffon.swing.edt.hang.monitor', 'true')
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final GriffonFestRule fest = new GriffonFestRule()
|
||||||
|
|
||||||
|
private FrameFixture window
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void smokeTest() {
|
||||||
|
fail('Not implemented yet!')
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.test.GriffonFestRule
|
||||||
|
import org.fest.swing.fixture.FrameFixture
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail
|
||||||
|
|
||||||
|
class MuWireStatusIntegrationTest {
|
||||||
|
static {
|
||||||
|
System.setProperty('griffon.swing.edt.violations.check', 'true')
|
||||||
|
System.setProperty('griffon.swing.edt.hang.monitor', 'true')
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final GriffonFestRule fest = new GriffonFestRule()
|
||||||
|
|
||||||
|
private FrameFixture window
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void smokeTest() {
|
||||||
|
fail('Not implemented yet!')
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.test.GriffonFestRule
|
||||||
|
import org.fest.swing.fixture.FrameFixture
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail
|
||||||
|
|
||||||
|
class TrustListIntegrationTest {
|
||||||
|
static {
|
||||||
|
System.setProperty('griffon.swing.edt.violations.check', 'true')
|
||||||
|
System.setProperty('griffon.swing.edt.hang.monitor', 'true')
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final GriffonFestRule fest = new GriffonFestRule()
|
||||||
|
|
||||||
|
private FrameFixture window
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void smokeTest() {
|
||||||
|
fail('Not implemented yet!')
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.test.GriffonUnitRule
|
||||||
|
import griffon.core.test.TestFor
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail
|
||||||
|
|
||||||
|
@TestFor(I2PStatusController)
|
||||||
|
class I2PStatusControllerTest {
|
||||||
|
private I2PStatusController controller
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void smokeTest() {
|
||||||
|
fail('Not yet implemented!')
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.test.GriffonUnitRule
|
||||||
|
import griffon.core.test.TestFor
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail
|
||||||
|
|
||||||
|
@TestFor(MuWireStatusController)
|
||||||
|
class MuWireStatusControllerTest {
|
||||||
|
private MuWireStatusController controller
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void smokeTest() {
|
||||||
|
fail('Not yet implemented!')
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.muwire.gui
|
||||||
|
|
||||||
|
import griffon.core.test.GriffonUnitRule
|
||||||
|
import griffon.core.test.TestFor
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail
|
||||||
|
|
||||||
|
@TestFor(TrustListController)
|
||||||
|
class TrustListControllerTest {
|
||||||
|
private TrustListController controller
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void smokeTest() {
|
||||||
|
fail('Not yet implemented!')
|
||||||
|
}
|
||||||
|
}
|
@@ -11,6 +11,7 @@ import net.i2p.client.I2PSession
|
|||||||
import net.i2p.client.I2PSessionMuxedListener
|
import net.i2p.client.I2PSessionMuxedListener
|
||||||
import net.i2p.client.datagram.I2PDatagramDissector
|
import net.i2p.client.datagram.I2PDatagramDissector
|
||||||
import net.i2p.client.datagram.I2PDatagramMaker
|
import net.i2p.client.datagram.I2PDatagramMaker
|
||||||
|
import net.i2p.crypto.SigType
|
||||||
import net.i2p.util.SystemVersion
|
import net.i2p.util.SystemVersion
|
||||||
import net.i2p.data.*
|
import net.i2p.data.*
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ public class HostCache {
|
|||||||
def session
|
def session
|
||||||
if (!keyfile.exists()) {
|
if (!keyfile.exists()) {
|
||||||
def os = new FileOutputStream(keyfile);
|
def os = new FileOutputStream(keyfile);
|
||||||
myDest = i2pClient.createDestination(os)
|
myDest = i2pClient.createDestination(os, SigType.EdDSA_SHA512_Ed25519)
|
||||||
os.close()
|
os.close()
|
||||||
println "No key.dat file was found, so creating a new destination."
|
println "No key.dat file was found, so creating a new destination."
|
||||||
println "This is the destination you want to give out for your new HostCache"
|
println "This is the destination you want to give out for your new HostCache"
|
||||||
|
Reference in New Issue
Block a user