Compare commits
33 Commits
muwire-0.4
...
muwire-0.4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5711979272 | ||
![]() |
9a5e2b1fa3 | ||
![]() |
a89b423dfc | ||
![]() |
79e8438941 | ||
![]() |
19c2c46491 | ||
![]() |
78f1d54b69 | ||
![]() |
9461649ed4 | ||
![]() |
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 |
@@ -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.
|
||||||
|
|
||||||
|
5
TODO.md
5
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,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.1")
|
core = new Core(props, home, "0.4.5")
|
||||||
} 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.1")
|
core = new Core(props, home, "0.4.5")
|
||||||
} 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 = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}\\?]"
|
||||||
}
|
}
|
||||||
|
@@ -39,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
|
||||||
@@ -84,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
|
||||||
|
|
||||||
@@ -93,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()
|
||||||
@@ -102,9 +129,13 @@ public class Core {
|
|||||||
log.info("launching embedded router")
|
log.info("launching embedded router")
|
||||||
Properties routerProps = new Properties()
|
Properties routerProps = new Properties()
|
||||||
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
||||||
|
routerProps.setProperty("router.excludePeerCaps", "KLM")
|
||||||
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("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()
|
||||||
@@ -122,25 +153,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
|
||||||
@@ -223,7 +235,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)
|
||||||
@@ -247,7 +261,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)
|
||||||
@@ -339,7 +353,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.4.1")
|
Core core = new Core(props, home, "0.4.5")
|
||||||
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",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))
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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() {
|
||||||
|
@@ -120,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)
|
||||||
|
@@ -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() {
|
||||||
|
@@ -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,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.4.1
|
version = 0.4.5
|
||||||
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,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()
|
||||||
|
}
|
||||||
|
}
|
@@ -14,12 +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.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
|
||||||
@@ -33,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
|
||||||
|
|
||||||
@@ -206,8 +210,11 @@ 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() {
|
void stopWatchingDirectory() {
|
||||||
|
@@ -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)
|
||||||
|
@@ -49,6 +49,7 @@ class Ready extends AbstractLifecycleHandler {
|
|||||||
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.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()
|
||||||
|
}
|
||||||
|
}
|
@@ -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
|
||||||
|
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:6))
|
||||||
|
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,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
|
||||||
|
|
||||||
@@ -71,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) {
|
||||||
@@ -361,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) {
|
||||||
@@ -425,13 +432,21 @@ class MainFrameView {
|
|||||||
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)
|
||||||
|
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!')
|
||||||
|
}
|
||||||
|
}
|
@@ -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