Compare commits
26 Commits
embedded-r
...
fix-double
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -23,7 +23,7 @@ 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.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
|||||||
6
TODO.md
6
TODO.md
@@ -20,10 +20,6 @@ For helping users make better decisions whom to trust
|
|||||||
|
|
||||||
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 +32,5 @@ 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
|
* Unsharing of files (half done)
|
||||||
* 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.4")
|
||||||
} 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.4")
|
||||||
} 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"
|
||||||
|
|||||||
@@ -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,6 +39,7 @@ 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.update.UpdateClient
|
import com.muwire.core.update.UpdateClient
|
||||||
@@ -83,6 +85,7 @@ 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
|
private final Router router
|
||||||
|
|
||||||
@@ -92,6 +95,31 @@ public class Core {
|
|||||||
this.home = home
|
this.home = home
|
||||||
this.muOptions = props
|
this.muOptions = props
|
||||||
|
|
||||||
|
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"
|
||||||
|
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) {
|
if (!props.embeddedRouter) {
|
||||||
log.info "Initializing I2P context"
|
log.info "Initializing I2P context"
|
||||||
I2PAppContext.getGlobalContext().logManager()
|
I2PAppContext.getGlobalContext().logManager()
|
||||||
@@ -103,6 +131,10 @@ public class Core {
|
|||||||
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
||||||
routerProps.setProperty("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
|
routerProps.setProperty("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
|
||||||
routerProps.setProperty("i2np.outboundKBytesPerSecond", String.valueOf(props.outBw))
|
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)
|
router = new Router(routerProps)
|
||||||
I2PAppContext.getGlobalContext().metaClass = new RouterContextMetaClass()
|
I2PAppContext.getGlobalContext().metaClass = new RouterContextMetaClass()
|
||||||
router.runRouter()
|
router.runRouter()
|
||||||
@@ -120,25 +152,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
|
||||||
@@ -192,6 +205,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)
|
||||||
@@ -220,7 +234,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)
|
||||||
@@ -244,7 +260,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)
|
||||||
@@ -258,6 +274,7 @@ 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)
|
||||||
@@ -335,7 +352,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.4.0")
|
Core core = new Core(props, home, "0.4.4")
|
||||||
core.startServices()
|
core.startServices()
|
||||||
|
|
||||||
// ... at the end, sleep or execute script
|
// ... at the end, sleep or execute script
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ class MuWireSettings {
|
|||||||
boolean allowUntrusted
|
boolean allowUntrusted
|
||||||
int downloadRetryInterval
|
int downloadRetryInterval
|
||||||
int updateCheckInterval
|
int updateCheckInterval
|
||||||
|
boolean autoDownloadUpdate
|
||||||
|
String updateType
|
||||||
String nickname
|
String nickname
|
||||||
File downloadLocation
|
File downloadLocation
|
||||||
CrawlerResponse crawlerResponse
|
CrawlerResponse crawlerResponse
|
||||||
@@ -37,6 +39,8 @@ class MuWireSettings {
|
|||||||
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"))
|
||||||
@@ -62,6 +66,8 @@ class MuWireSettings {
|
|||||||
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",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))
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ class DownloadSession {
|
|||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.4
|
||||||
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,14 @@ 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'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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.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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,11 +14,14 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
|
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.TrustEvent
|
import com.muwire.core.trust.TrustEvent
|
||||||
@@ -32,6 +35,8 @@ class MainFrameController {
|
|||||||
|
|
||||||
@MVCMember @Nonnull
|
@MVCMember @Nonnull
|
||||||
MainFrameModel model
|
MainFrameModel model
|
||||||
|
@MVCMember @Nonnull
|
||||||
|
MainFrameView view
|
||||||
|
|
||||||
private volatile Core core
|
private volatile Core core
|
||||||
|
|
||||||
@@ -205,8 +210,23 @@ class MainFrameController {
|
|||||||
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
||||||
}
|
}
|
||||||
|
|
||||||
void unshareSelectedFiles() {
|
void unshareSelectedFile() {
|
||||||
println "unsharing selected files"
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,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
|
||||||
|
|
||||||
@@ -44,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,"")
|
||||||
@@ -52,13 +64,16 @@ class OptionsController {
|
|||||||
text = view.retryField.text
|
text = view.retryField.text
|
||||||
model.downloadRetryInterval = text
|
model.downloadRetryInterval = text
|
||||||
|
|
||||||
MuWireSettings 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 autoDownloadUpdate = view.autoDownloadUpdateCheckbox.model.isSelected()
|
||||||
|
model.autoDownloadUpdate = autoDownloadUpdate
|
||||||
|
settings.autoDownloadUpdate = autoDownloadUpdate
|
||||||
|
|
||||||
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
|
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
|
||||||
model.onlyTrusted = onlyTrusted
|
model.onlyTrusted = onlyTrusted
|
||||||
settings.setAllowUntrusted(!onlyTrusted)
|
settings.setAllowUntrusted(!onlyTrusted)
|
||||||
|
|||||||
@@ -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")
|
||||||
def nickname
|
def nickname
|
||||||
while (true) {
|
while (true) {
|
||||||
nickname = JOptionPane.showInputDialog(null,
|
nickname = JOptionPane.showInputDialog(null,
|
||||||
|
|||||||
27
gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy
Normal file
27
gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
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 int participatingTunnels
|
||||||
|
@Observable int activePeers
|
||||||
|
@Observable int receiveBps
|
||||||
|
@Observable int sendBps
|
||||||
|
@Observable int participatingBW
|
||||||
|
|
||||||
|
void mvcGroupInit(Map<String,String> args) {
|
||||||
|
controller.refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ 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.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 +53,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 = []
|
||||||
@@ -76,7 +78,7 @@ class MainFrameModel {
|
|||||||
|
|
||||||
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 +126,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 +144,7 @@ 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)
|
||||||
|
|
||||||
timer.schedule({
|
timer.schedule({
|
||||||
if (core.shutdown.get())
|
if (core.shutdown.get())
|
||||||
@@ -183,6 +187,14 @@ class MainFrameModel {
|
|||||||
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
|
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 +202,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) {
|
||||||
|
|||||||
@@ -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,6 +11,7 @@ import griffon.metadata.ArtifactProviderFor
|
|||||||
class OptionsModel {
|
class OptionsModel {
|
||||||
@Observable String downloadRetryInterval
|
@Observable String downloadRetryInterval
|
||||||
@Observable String updateCheckInterval
|
@Observable String updateCheckInterval
|
||||||
|
@Observable boolean autoDownloadUpdate
|
||||||
@Observable boolean onlyTrusted
|
@Observable boolean onlyTrusted
|
||||||
@Observable boolean shareDownloadedFiles
|
@Observable boolean shareDownloadedFiles
|
||||||
@Observable String downloadLocation
|
@Observable String downloadLocation
|
||||||
@@ -20,6 +21,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
|
||||||
@@ -38,6 +41,7 @@ class OptionsModel {
|
|||||||
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
|
||||||
|
autoDownloadUpdate = settings.autoDownloadUpdate
|
||||||
onlyTrusted = !settings.allowUntrusted()
|
onlyTrusted = !settings.allowUntrusted()
|
||||||
shareDownloadedFiles = settings.shareDownloadedFiles
|
shareDownloadedFiles = settings.shareDownloadedFiles
|
||||||
downloadLocation = settings.downloadLocation.getAbsolutePath()
|
downloadLocation = settings.downloadLocation.getAbsolutePath()
|
||||||
@@ -47,6 +51,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
|
||||||
|
|||||||
78
gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy
Normal file
78
gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
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 : "NTCP Connections", constraints : gbc(gridx:0, gridy:1))
|
||||||
|
label(text : bind {model.ntcpConnections}, constraints : gbc(gridx: 1, gridy:1))
|
||||||
|
label(text : "SSU Connections", constraints : gbc(gridx:0, gridy:2))
|
||||||
|
label(text : bind {model.ssuConnections}, constraints : gbc(gridx: 1, gridy:2))
|
||||||
|
label(text : "Participating Tunnels", constraints : gbc(gridx:0, gridy:3))
|
||||||
|
label(text : bind {model.participatingTunnels}, constraints : gbc(gridx: 1, gridy:3))
|
||||||
|
label(text : "Participating Bandwidth", constraints : gbc(gridx:0, gridy:4))
|
||||||
|
label(text : bind {model.participatingBW}, constraints : gbc(gridx: 1, gridy:4))
|
||||||
|
label(text : "Active Peers", constraints : gbc(gridx:0, gridy:5))
|
||||||
|
label(text : bind {model.activePeers}, constraints : gbc(gridx: 1, gridy:5))
|
||||||
|
label(text : "Receive Bps (15 seconds)", constraints : gbc(gridx:0, gridy:6))
|
||||||
|
label(text : bind {model.receiveBps}, constraints : gbc(gridx: 1, gridy:6))
|
||||||
|
label(text : "Send Bps (15 seconds)", constraints : gbc(gridx:0, gridy:7))
|
||||||
|
label(text : bind {model.sendBps}, constraints : gbc(gridx: 1, gridy:7))
|
||||||
|
}
|
||||||
|
|
||||||
|
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,6 +22,7 @@ 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
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ class MainFrameView {
|
|||||||
def downloadsTable
|
def downloadsTable
|
||||||
def lastDownloadSortEvent
|
def lastDownloadSortEvent
|
||||||
def lastSharedSortEvent
|
def lastSharedSortEvent
|
||||||
|
def lastWatchedSortEvent
|
||||||
|
|
||||||
void initUI() {
|
void initUI() {
|
||||||
UISettings settings = application.context.get("ui-settings")
|
UISettings settings = application.context.get("ui-settings")
|
||||||
@@ -70,6 +72,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 +174,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() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,11 +367,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 +405,48 @@ 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 +580,14 @@ 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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ class OptionsView {
|
|||||||
|
|
||||||
def retryField
|
def retryField
|
||||||
def updateField
|
def updateField
|
||||||
|
def autoDownloadUpdateCheckbox
|
||||||
def allowUntrustedCheckbox
|
def allowUntrustedCheckbox
|
||||||
def shareDownloadedCheckbox
|
def shareDownloadedCheckbox
|
||||||
|
|
||||||
@@ -39,6 +40,8 @@ class OptionsView {
|
|||||||
def inboundQuantityField
|
def inboundQuantityField
|
||||||
def outboundLengthField
|
def outboundLengthField
|
||||||
def outboundQuantityField
|
def outboundQuantityField
|
||||||
|
def i2pUDPPortField
|
||||||
|
def i2pNTCPPortField
|
||||||
|
|
||||||
def lnfField
|
def lnfField
|
||||||
def monitorCheckbox
|
def monitorCheckbox
|
||||||
@@ -69,16 +72,19 @@ 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 : "Download updates automatically", constraints: gbc(gridx :0, gridy : 2))
|
||||||
|
autoDownloadUpdateCheckbox = checkBox(selected : bind {model.autoDownloadUpdate}, constraints : gbc(gridx:1, gridy : 2))
|
||||||
|
|
||||||
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 2))
|
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 3))
|
||||||
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 2))
|
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 3))
|
||||||
|
|
||||||
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
|
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:4))
|
||||||
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3))
|
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:4))
|
||||||
|
|
||||||
label(text : "Save downloaded files to:", constraints: gbc(gridx:0, gridy:4))
|
label(text : "Save downloaded files to:", constraints: gbc(gridx:0, gridy:5))
|
||||||
button(text : "Choose", constraints : gbc(gridx : 1, gridy:4), downloadLocationAction)
|
button(text : "Choose", constraints : gbc(gridx : 1, gridy:5), downloadLocationAction)
|
||||||
label(text : bind {model.downloadLocation}, constraints: gbc(gridx:0, gridy:5, gridwidth:2))
|
label(text : bind {model.downloadLocation}, constraints: gbc(gridx:0, gridy:6, gridwidth:2))
|
||||||
|
|
||||||
}
|
}
|
||||||
i = builder.panel {
|
i = builder.panel {
|
||||||
@@ -92,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()
|
||||||
|
|||||||
@@ -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,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!')
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user