Compare commits
43 Commits
muwire-0.4
...
muwire-0.4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
337605dc0f | ||
![]() |
14bdfa6b2e | ||
![]() |
ed3f9da773 | ||
![]() |
251080d08f | ||
![]() |
f530ab999d | ||
![]() |
4133384e48 | ||
![]() |
600fc98868 | ||
![]() |
129eeb3b88 | ||
![]() |
20b51b78a0 | ||
![]() |
33fe755b60 | ||
![]() |
8b0668a134 | ||
![]() |
730d2202fd | ||
![]() |
69906a986d | ||
![]() |
5bc8fa8633 | ||
![]() |
7de7c9d8f3 | ||
![]() |
e943f6019d | ||
![]() |
2eec7bec5b | ||
![]() |
c36110cf76 | ||
![]() |
abe28517bc | ||
![]() |
15bc4c064d | ||
![]() |
91d771944b | ||
![]() |
e09c456a13 | ||
![]() |
d9c1067226 | ||
![]() |
eda3e7ad3a | ||
![]() |
e9798c7eaa | ||
![]() |
66bb4eef5b | ||
![]() |
55f260b3f4 | ||
![]() |
32d4c3965e | ||
![]() |
de1534d837 | ||
![]() |
7b58e8a88a | ||
![]() |
8a03b89985 | ||
![]() |
1d97374857 | ||
![]() |
549e8c2d98 | ||
![]() |
b54d24db0d | ||
![]() |
fa12e84345 | ||
![]() |
6430ff2691 | ||
![]() |
591313c81c | ||
![]() |
ce7b6a0c65 | ||
![]() |
5c4d4c4580 | ||
![]() |
4cb864ff9f | ||
![]() |
417675ad07 | ||
![]() |
9513e5ba3c | ||
![]() |
85610cf169 |
10
README.md
10
README.md
@@ -4,11 +4,11 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
|
|||||||
|
|
||||||
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
||||||
|
|
||||||
The current stable release - 0.4.6 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
The current stable release - 0.4.14 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
You need JRE 8 or newer. After installing that and setting up the appropriate paths, just type
|
You need JDK 8 or newer. After installing that and setting up the appropriate paths, just type
|
||||||
|
|
||||||
```
|
```
|
||||||
./gradlew clean assemble
|
./gradlew clean assemble
|
||||||
@@ -19,18 +19,16 @@ If you want to run the unit tests, type
|
|||||||
./gradlew clean build
|
./gradlew clean build
|
||||||
```
|
```
|
||||||
|
|
||||||
Some of the UI tests will fail because they haven't been written yet :-/
|
If you want to build binary bundles that do not depend on Java or I2P, see the https://github.com/zlatinb/muwire-pkg project
|
||||||
|
|
||||||
### Running
|
### Running
|
||||||
|
|
||||||
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.
|
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 gui-x.y.z.jar` in a terminal or command prompt.
|
||||||
|
|
||||||
If you have an I2P router running on the same machine that is all you need to do. If you use a custom I2CP host and port, create a file `i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there. On Windows that file should go into `%HOME%\AppData\Roaming\MuWire`, on Mac into `$HOME/Library/Application Support/MuWire` and on Linux `$HOME/.MuWire`
|
If you have an I2P router running on the same machine that is all you need to do. If you use a custom I2CP host and port, create a file `i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there. On Windows that file should go into `%HOME%\AppData\Roaming\MuWire`, on Mac into `$HOME/Library/Application Support/MuWire` and on Linux `$HOME/.MuWire`
|
||||||
|
|
||||||
[Default I2CP port]\: `7654`
|
[Default I2CP port]\: `7654`
|
||||||
|
|
||||||
If you do not have an I2P router, pass the following switch to the Java process: `-DembeddedRouter=true`. This will launch MuWire's embedded router. Be aware that this causes startup to take a lot longer.
|
|
||||||
|
|
||||||
### GPG Fingerprint
|
### GPG Fingerprint
|
||||||
|
|
||||||
```
|
```
|
||||||
|
7
TODO.md
7
TODO.md
@@ -12,10 +12,6 @@ This reduces query traffic by not sending last hop queries to peers that definit
|
|||||||
|
|
||||||
This helps with scalability
|
This helps with scalability
|
||||||
|
|
||||||
##### Content Control Panel
|
|
||||||
|
|
||||||
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
|
|
||||||
|
|
||||||
##### 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
|
||||||
@@ -27,5 +23,4 @@ To enable parsing of metadata from known file types and the user editing it or a
|
|||||||
### Small Items
|
### Small Items
|
||||||
|
|
||||||
* Wrapper of some kind for in-place upgrades
|
* Wrapper of some kind for in-place upgrades
|
||||||
* Download file sequentially
|
* Automatic adjustment of number of I2P tunnels
|
||||||
* Multiple-selection download, Ctrl-A
|
|
||||||
|
@@ -2,7 +2,7 @@ subprojects {
|
|||||||
apply plugin: 'groovy'
|
apply plugin: 'groovy'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'net.i2p:i2p:0.9.41'
|
compile 'net.i2p:i2p:0.9.42'
|
||||||
compile 'org.codehaus.groovy:groovy-all:2.4.15'
|
compile 'org.codehaus.groovy:groovy-all:2.4.15'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -35,7 +35,7 @@ class Cli {
|
|||||||
|
|
||||||
Core core
|
Core core
|
||||||
try {
|
try {
|
||||||
core = new Core(props, home, "0.4.11")
|
core = new Core(props, home, "0.4.15")
|
||||||
} 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.11")
|
core = new Core(props, home, "0.4.15")
|
||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
bad.printStackTrace(System.out)
|
bad.printStackTrace(System.out)
|
||||||
println "Failed to initialize core, exiting"
|
println "Failed to initialize core, exiting"
|
||||||
|
@@ -2,9 +2,9 @@ apply plugin : 'application'
|
|||||||
mainClassName = 'com.muwire.core.Core'
|
mainClassName = 'com.muwire.core.Core'
|
||||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'net.i2p:router:0.9.41'
|
compile 'net.i2p:router:0.9.42'
|
||||||
compile 'net.i2p.client:mstreaming:0.9.41'
|
compile 'net.i2p.client:mstreaming:0.9.42'
|
||||||
compile 'net.i2p.client:streaming:0.9.41'
|
compile 'net.i2p.client:streaming:0.9.42'
|
||||||
|
|
||||||
testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2'
|
testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2'
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
package com.muwire.core
|
|
||||||
|
|
||||||
import net.i2p.crypto.SigType
|
|
||||||
|
|
||||||
class Constants {
|
|
||||||
public static final byte PERSONA_VERSION = (byte)1
|
|
||||||
public static final SigType SIG_TYPE = SigType.EdDSA_SHA512_Ed25519
|
|
||||||
|
|
||||||
public static final int MAX_HEADER_SIZE = 0x1 << 14
|
|
||||||
public static final int MAX_HEADERS = 16
|
|
||||||
|
|
||||||
public static final String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}\\?]"
|
|
||||||
}
|
|
@@ -135,6 +135,7 @@ public class Core {
|
|||||||
} else {
|
} else {
|
||||||
log.info("launching embedded router")
|
log.info("launching embedded router")
|
||||||
Properties routerProps = new Properties()
|
Properties routerProps = new Properties()
|
||||||
|
routerProps.setProperty("i2p.dir.base", home.getAbsolutePath())
|
||||||
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
|
||||||
routerProps.setProperty("router.excludePeerCaps", "KLM")
|
routerProps.setProperty("router.excludePeerCaps", "KLM")
|
||||||
routerProps.setProperty("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
|
routerProps.setProperty("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
|
||||||
@@ -361,7 +362,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.4.11")
|
Core core = new Core(props, home, "0.4.15")
|
||||||
core.startServices()
|
core.startServices()
|
||||||
|
|
||||||
// ... at the end, sleep or execute script
|
// ... at the end, sleep or execute script
|
||||||
|
@@ -11,6 +11,7 @@ class MuWireSettings {
|
|||||||
|
|
||||||
final boolean isLeaf
|
final boolean isLeaf
|
||||||
boolean allowUntrusted
|
boolean allowUntrusted
|
||||||
|
boolean searchExtraHop
|
||||||
boolean allowTrustLists
|
boolean allowTrustLists
|
||||||
int trustListInterval
|
int trustListInterval
|
||||||
Set<Persona> trustSubscriptions
|
Set<Persona> trustSubscriptions
|
||||||
@@ -24,7 +25,7 @@ class MuWireSettings {
|
|||||||
boolean shareDownloadedFiles
|
boolean shareDownloadedFiles
|
||||||
Set<String> watchedDirectories
|
Set<String> watchedDirectories
|
||||||
float downloadSequentialRatio
|
float downloadSequentialRatio
|
||||||
int hostClearInterval
|
int hostClearInterval, hostHopelessInterval, hostRejectInterval
|
||||||
int meshExpiration
|
int meshExpiration
|
||||||
boolean embeddedRouter
|
boolean embeddedRouter
|
||||||
int inBw, outBw
|
int inBw, outBw
|
||||||
@@ -38,19 +39,22 @@ class MuWireSettings {
|
|||||||
MuWireSettings(Properties props) {
|
MuWireSettings(Properties props) {
|
||||||
isLeaf = Boolean.valueOf(props.get("leaf","false"))
|
isLeaf = Boolean.valueOf(props.get("leaf","false"))
|
||||||
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
|
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
|
||||||
|
searchExtraHop = Boolean.valueOf(props.getProperty("searchExtraHop","false"))
|
||||||
allowTrustLists = Boolean.valueOf(props.getProperty("allowTrustLists","true"))
|
allowTrustLists = Boolean.valueOf(props.getProperty("allowTrustLists","true"))
|
||||||
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
|
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
|
||||||
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
|
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
|
||||||
nickname = props.getProperty("nickname","MuWireUser")
|
nickname = props.getProperty("nickname","MuWireUser")
|
||||||
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
||||||
System.getProperty("user.home")))
|
System.getProperty("user.home")))
|
||||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
|
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","60"))
|
||||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
|
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
|
||||||
autoDownloadUpdate = Boolean.parseBoolean(props.getProperty("autoDownloadUpdate","true"))
|
autoDownloadUpdate = Boolean.parseBoolean(props.getProperty("autoDownloadUpdate","true"))
|
||||||
updateType = props.getProperty("updateType","jar")
|
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","15"))
|
||||||
|
hostHopelessInterval = Integer.valueOf(props.getProperty("hostHopelessInterval", "1440"))
|
||||||
|
hostRejectInterval = Integer.valueOf(props.getProperty("hostRejectInterval", "1"))
|
||||||
meshExpiration = Integer.valueOf(props.getProperty("meshExpiration","60"))
|
meshExpiration = Integer.valueOf(props.getProperty("meshExpiration","60"))
|
||||||
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
|
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
|
||||||
inBw = Integer.valueOf(props.getProperty("inBw","256"))
|
inBw = Integer.valueOf(props.getProperty("inBw","256"))
|
||||||
@@ -74,6 +78,7 @@ class MuWireSettings {
|
|||||||
Properties props = new Properties()
|
Properties props = new Properties()
|
||||||
props.setProperty("leaf", isLeaf.toString())
|
props.setProperty("leaf", isLeaf.toString())
|
||||||
props.setProperty("allowUntrusted", allowUntrusted.toString())
|
props.setProperty("allowUntrusted", allowUntrusted.toString())
|
||||||
|
props.setProperty("searchExtraHop", String.valueOf(searchExtraHop))
|
||||||
props.setProperty("allowTrustLists", String.valueOf(allowTrustLists))
|
props.setProperty("allowTrustLists", String.valueOf(allowTrustLists))
|
||||||
props.setProperty("trustListInterval", String.valueOf(trustListInterval))
|
props.setProperty("trustListInterval", String.valueOf(trustListInterval))
|
||||||
props.setProperty("crawlerResponse", crawlerResponse.toString())
|
props.setProperty("crawlerResponse", crawlerResponse.toString())
|
||||||
@@ -86,6 +91,8 @@ class MuWireSettings {
|
|||||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||||
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
||||||
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
||||||
|
props.setProperty("hostHopelessInterval", String.valueOf(hostHopelessInterval))
|
||||||
|
props.setProperty("hostRejectInterval", String.valueOf(hostRejectInterval))
|
||||||
props.setProperty("meshExpiration", String.valueOf(meshExpiration))
|
props.setProperty("meshExpiration", String.valueOf(meshExpiration))
|
||||||
props.setProperty("embeddedRouter", String.valueOf(embeddedRouter))
|
props.setProperty("embeddedRouter", String.valueOf(embeddedRouter))
|
||||||
props.setProperty("inBw", String.valueOf(inBw))
|
props.setProperty("inBw", String.valueOf(inBw))
|
||||||
|
7
core/src/main/groovy/com/muwire/core/SplitPattern.groovy
Normal file
7
core/src/main/groovy/com/muwire/core/SplitPattern.groovy
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package com.muwire.core
|
||||||
|
|
||||||
|
class SplitPattern {
|
||||||
|
|
||||||
|
public static final String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}\\?]";
|
||||||
|
|
||||||
|
}
|
@@ -31,7 +31,7 @@ class ConnectionEstablisher {
|
|||||||
final HostCache hostCache
|
final HostCache hostCache
|
||||||
|
|
||||||
final Timer timer
|
final Timer timer
|
||||||
final ExecutorService executor
|
final ExecutorService executor, closer
|
||||||
|
|
||||||
final Set inProgress = new ConcurrentHashSet()
|
final Set inProgress = new ConcurrentHashSet()
|
||||||
|
|
||||||
@@ -51,6 +51,8 @@ class ConnectionEstablisher {
|
|||||||
rv.setName("connector-${System.currentTimeMillis()}")
|
rv.setName("connector-${System.currentTimeMillis()}")
|
||||||
rv
|
rv
|
||||||
} as ThreadFactory)
|
} as ThreadFactory)
|
||||||
|
|
||||||
|
closer = Executors.newSingleThreadExecutor()
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
@@ -60,6 +62,7 @@ class ConnectionEstablisher {
|
|||||||
void stop() {
|
void stop() {
|
||||||
timer.cancel()
|
timer.cancel()
|
||||||
executor.shutdownNow()
|
executor.shutdownNow()
|
||||||
|
closer.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectIfNeeded() {
|
private void connectIfNeeded() {
|
||||||
@@ -120,8 +123,10 @@ class ConnectionEstablisher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void fail(Endpoint endpoint) {
|
private void fail(Endpoint endpoint) {
|
||||||
endpoint.close()
|
closer.execute {
|
||||||
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
|
endpoint.close()
|
||||||
|
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
|
||||||
|
} as Runnable
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readK(Endpoint e) {
|
private void readK(Endpoint e) {
|
||||||
@@ -175,7 +180,7 @@ class ConnectionEstablisher {
|
|||||||
log.log(Level.WARNING,"Problem parsing post-rejection payload",ignore)
|
log.log(Level.WARNING,"Problem parsing post-rejection payload",ignore)
|
||||||
} finally {
|
} finally {
|
||||||
// the end
|
// the end
|
||||||
e.close()
|
closer.execute({e.close()} as Runnable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -74,7 +74,7 @@ public class DownloadManager {
|
|||||||
destinations.addAll(e.sources)
|
destinations.addAll(e.sources)
|
||||||
destinations.remove(me.destination)
|
destinations.remove(me.destination)
|
||||||
|
|
||||||
Pieces pieces = getPieces(infohash, size, pieceSize)
|
Pieces pieces = getPieces(infohash, size, pieceSize, e.sequential)
|
||||||
|
|
||||||
def downloader = new Downloader(eventBus, this, me, e.target, size,
|
def downloader = new Downloader(eventBus, this, me, e.target, size,
|
||||||
infohash, pieceSize, connector, destinations,
|
infohash, pieceSize, connector, destinations,
|
||||||
@@ -122,8 +122,12 @@ public class DownloadManager {
|
|||||||
byte [] root = Base64.decode(json.hashRoot)
|
byte [] root = Base64.decode(json.hashRoot)
|
||||||
infoHash = new InfoHash(root)
|
infoHash = new InfoHash(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean sequential = false
|
||||||
|
if (json.sequential != null)
|
||||||
|
sequential = json.sequential
|
||||||
|
|
||||||
Pieces pieces = getPieces(infoHash, (long)json.length, json.pieceSizePow2)
|
Pieces pieces = getPieces(infoHash, (long)json.length, json.pieceSizePow2, sequential)
|
||||||
|
|
||||||
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
||||||
infoHash, json.pieceSizePow2, connector, destinations, incompletes, pieces)
|
infoHash, json.pieceSizePow2, connector, destinations, incompletes, pieces)
|
||||||
@@ -137,12 +141,12 @@ public class DownloadManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pieces getPieces(InfoHash infoHash, long length, int pieceSizePow2) {
|
private Pieces getPieces(InfoHash infoHash, long length, int pieceSizePow2, boolean sequential) {
|
||||||
int pieceSize = 0x1 << pieceSizePow2
|
int pieceSize = 0x1 << pieceSizePow2
|
||||||
int nPieces = (int)(length / pieceSize)
|
int nPieces = (int)(length / pieceSize)
|
||||||
if (length % pieceSize != 0)
|
if (length % pieceSize != 0)
|
||||||
nPieces++
|
nPieces++
|
||||||
Mesh mesh = meshManager.getOrCreate(infoHash, nPieces)
|
Mesh mesh = meshManager.getOrCreate(infoHash, nPieces, sequential)
|
||||||
mesh.pieces
|
mesh.pieces
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +192,9 @@ public class DownloadManager {
|
|||||||
json.hashRoot = Base64.encode(infoHash.getRoot())
|
json.hashRoot = Base64.encode(infoHash.getRoot())
|
||||||
|
|
||||||
json.paused = downloader.paused
|
json.paused = downloader.paused
|
||||||
|
|
||||||
|
json.sequential = downloader.pieces.ratio == 0f
|
||||||
|
|
||||||
writer.println(JsonOutput.toJson(json))
|
writer.println(JsonOutput.toJson(json))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,7 @@ class Pieces {
|
|||||||
done = new BitSet(nPieces)
|
done = new BitSet(nPieces)
|
||||||
claimed = new BitSet(nPieces)
|
claimed = new BitSet(nPieces)
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized int[] claim() {
|
synchronized int[] claim() {
|
||||||
int claimedCardinality = claimed.cardinality()
|
int claimedCardinality = claimed.cardinality()
|
||||||
if (claimedCardinality == nPieces) {
|
if (claimedCardinality == nPieces) {
|
||||||
@@ -30,7 +30,7 @@ class Pieces {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if fuller than ratio just do sequential
|
// if fuller than ratio just do sequential
|
||||||
if ( (1.0f * claimedCardinality) / nPieces > ratio) {
|
if ( (1.0f * claimedCardinality) / nPieces >= ratio) {
|
||||||
int rv = claimed.nextClearBit(0)
|
int rv = claimed.nextClearBit(0)
|
||||||
claimed.set(rv)
|
claimed.set(rv)
|
||||||
return [rv, partials.getOrDefault(rv, 0), 0]
|
return [rv, partials.getOrDefault(rv, 0), 0]
|
||||||
@@ -59,7 +59,8 @@ class Pieces {
|
|||||||
return [rv, partials.getOrDefault(rv, 0), 1]
|
return [rv, partials.getOrDefault(rv, 0), 1]
|
||||||
}
|
}
|
||||||
List<Integer> toList = availableCopy.toList()
|
List<Integer> toList = availableCopy.toList()
|
||||||
Collections.shuffle(toList)
|
if (ratio > 0f)
|
||||||
|
Collections.shuffle(toList)
|
||||||
int rv = toList[0]
|
int rv = toList[0]
|
||||||
claimed.set(rv)
|
claimed.set(rv)
|
||||||
[rv, partials.getOrDefault(rv, 0), 0]
|
[rv, partials.getOrDefault(rv, 0), 0]
|
||||||
|
@@ -10,4 +10,5 @@ class UIDownloadEvent extends Event {
|
|||||||
UIResultEvent[] result
|
UIResultEvent[] result
|
||||||
Set<Destination> sources
|
Set<Destination> sources
|
||||||
File target
|
File target
|
||||||
|
boolean sequential
|
||||||
}
|
}
|
||||||
|
@@ -46,7 +46,10 @@ class PersisterService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
|
Thread.currentThread().setPriority(Thread.MIN_PRIORITY)
|
||||||
|
|
||||||
if (location.exists() && location.isFile()) {
|
if (location.exists() && location.isFile()) {
|
||||||
|
int loaded = 0
|
||||||
def slurper = new JsonSlurper()
|
def slurper = new JsonSlurper()
|
||||||
try {
|
try {
|
||||||
location.eachLine {
|
location.eachLine {
|
||||||
@@ -56,6 +59,9 @@ class PersisterService extends Service {
|
|||||||
if (event != null) {
|
if (event != null) {
|
||||||
log.fine("loaded file $event.loadedFile.file")
|
log.fine("loaded file $event.loadedFile.file")
|
||||||
listener.publish event
|
listener.publish event
|
||||||
|
loaded++
|
||||||
|
if (loaded % 10 == 0)
|
||||||
|
Thread.sleep(20)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,17 +142,12 @@ class PersisterService extends Service {
|
|||||||
|
|
||||||
private def toJson(File f, SharedFile sf) {
|
private def toJson(File f, SharedFile sf) {
|
||||||
def json = [:]
|
def json = [:]
|
||||||
json.file = Base64.encode DataUtil.encodei18nString(f.toString())
|
json.file = sf.getB64EncodedFileName()
|
||||||
json.length = sf.getCachedLength()
|
json.length = sf.getCachedLength()
|
||||||
InfoHash ih = sf.getInfoHash()
|
InfoHash ih = sf.getInfoHash()
|
||||||
json.infoHash = Base64.encode ih.getRoot()
|
json.infoHash = sf.getB64EncodedHashRoot()
|
||||||
json.pieceSize = sf.getPieceSize()
|
json.pieceSize = sf.getPieceSize()
|
||||||
byte [] tmp = new byte [32]
|
json.hashList = sf.getB64EncodedHashList()
|
||||||
json.hashList = []
|
|
||||||
for (int i = 0;i < ih.getHashList().length / 32; i++) {
|
|
||||||
System.arraycopy(ih.getHashList(), i * 32, tmp, 0, 32)
|
|
||||||
json.hashList.add Base64.encode(tmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sf instanceof DownloadedFile) {
|
if (sf instanceof DownloadedFile) {
|
||||||
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
|
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
|
||||||
|
@@ -6,8 +6,12 @@ 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 = [
|
||||||
|
// zlatinb
|
||||||
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==")
|
// sNL
|
||||||
|
new Destination("JC63wJNOqSJmymkj4~UJWywBTvDGikKMoYP0HX2Wz9c5l3otXSkwnxWAFL4cKr~Ygh3BNNi2t93vuLIiI1W8AsE42kR~PwRx~Y-WvIHXR6KUejRmOp-n8WidtjKg9k4aDy428uSOedqXDxys5mpoeQXwDsv1CoPTTwnmb1GWFy~oTGIsCguCl~aJWGnqiKarPO3GJQ~ev-NbvAQzUfC3HeP1e6pdI5CGGjExahTCID5UjpJw8GaDXWlGmYWWH303Xu4x-vAHQy1dJLsOBCn8dZravsn5BKJk~j0POUon45CCx-~NYtaPe0Itt9cMdD2ciC76Rep1D0X0sm1SjlSs8sZ52KmF3oaLZ6OzgI9QLMIyBUrfi41sK5I0qTuUVBAkvW1xr~L-20dYJ9TrbOaOb2-vDIfKaxVi6xQOuhgQDiSBhd3qv2m0xGu-BM9DQYfNA0FdMjnZmqjmji9RMavzQSsVFIbQGLbrLepiEFlb7TseCK5UtRp8TxnG7L4gbYevBQAEAAcAAA=="),
|
||||||
|
// dark_trion
|
||||||
|
new Destination("Gec9L29FVcQvYDgpcYuEYdltJn06PPoOWAcAM8Af-gDm~ehlrJcwlLXXs0hidq~yP2A0X7QcDi6i6shAfuEofTchxGJl8LRNqj9lio7WnB7cIixXWL~uCkD7Np5LMX0~akNX34oOb9RcBYVT2U5rFGJmJ7OtBv~IBkGeLhsMrqaCjahd0jdBO~QJ-t82ZKZhh044d24~JEfF9zSJxdBoCdAcXzryGNy7sYtFVDFsPKJudAxSW-UsSQiGw2~k-TxyF0r-iAt1IdzfNu8Lu0WPqLdhDYJWcPldx2PR5uJorI~zo~z3I5RX3NwzarlbD4nEP5s65ahPSfVCEkzmaJUBgP8DvBqlFaX89K4nGRYc7jkEjJ8cX4L6YPXUpTPWcfKkW259WdQY3YFh6x7rzijrGZewpczOLCrt-bZRYgDrUibmZxKZmNhy~lQu4gYVVjkz1i4tL~DWlhIc4y0x2vItwkYLArPPi~ejTnt-~Lhb7oPMXRcWa3UrwGKpFvGZY4NXBQAEAAcAAA==")
|
||||||
]
|
]
|
||||||
|
|
||||||
static List<Destination> getCacheServers() {
|
static List<Destination> getCacheServers() {
|
||||||
|
@@ -7,21 +7,35 @@ class Host {
|
|||||||
private static final int MAX_FAILURES = 3
|
private static final int MAX_FAILURES = 3
|
||||||
|
|
||||||
final Destination destination
|
final Destination destination
|
||||||
private final int clearInterval
|
private final int clearInterval, hopelessInterval, rejectionInterval
|
||||||
int failures,successes
|
int failures,successes
|
||||||
long lastAttempt
|
long lastAttempt
|
||||||
|
long lastSuccessfulAttempt
|
||||||
|
long lastRejection
|
||||||
|
|
||||||
public Host(Destination destination, int clearInterval) {
|
public Host(Destination destination, int clearInterval, int hopelessInterval, int rejectionInterval) {
|
||||||
this.destination = destination
|
this.destination = destination
|
||||||
this.clearInterval = clearInterval
|
this.clearInterval = clearInterval
|
||||||
|
this.hopelessInterval = hopelessInterval
|
||||||
|
this.rejectionInterval = rejectionInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void onConnect() {
|
private void connectSuccessful() {
|
||||||
failures = 0
|
failures = 0
|
||||||
successes++
|
successes++
|
||||||
lastAttempt = System.currentTimeMillis()
|
lastAttempt = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized void onConnect() {
|
||||||
|
connectSuccessful()
|
||||||
|
lastSuccessfulAttempt = lastAttempt
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void onReject() {
|
||||||
|
connectSuccessful()
|
||||||
|
lastRejection = lastAttempt;
|
||||||
|
}
|
||||||
|
|
||||||
synchronized void onFailure() {
|
synchronized void onFailure() {
|
||||||
failures++
|
failures++
|
||||||
successes = 0
|
successes = 0
|
||||||
@@ -40,7 +54,17 @@ class Host {
|
|||||||
failures = 0
|
failures = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void canTryAgain() {
|
synchronized boolean canTryAgain() {
|
||||||
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
|
lastSuccessfulAttempt > 0 &&
|
||||||
|
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized boolean isHopeless() {
|
||||||
|
isFailed() &&
|
||||||
|
System.currentTimeMillis() - lastSuccessfulAttempt > (hopelessInterval * 60 * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized boolean isRecentlyRejected() {
|
||||||
|
System.currentTimeMillis() - lastRejection < (rejectionInterval * 60 * 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,7 +52,7 @@ class HostCache extends Service {
|
|||||||
hosts.get(e.destination).clearFailures()
|
hosts.get(e.destination).clearFailures()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Host host = new Host(e.destination, settings.hostClearInterval)
|
Host host = new Host(e.destination, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||||
if (allowHost(host)) {
|
if (allowHost(host)) {
|
||||||
hosts.put(e.destination, host)
|
hosts.put(e.destination, host)
|
||||||
}
|
}
|
||||||
@@ -64,15 +64,17 @@ class HostCache extends Service {
|
|||||||
Destination dest = e.endpoint.destination
|
Destination dest = e.endpoint.destination
|
||||||
Host host = hosts.get(dest)
|
Host host = hosts.get(dest)
|
||||||
if (host == null) {
|
if (host == null) {
|
||||||
host = new Host(dest, settings.hostClearInterval)
|
host = new Host(dest, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||||
hosts.put(dest, host)
|
hosts.put(dest, host)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(e.status) {
|
switch(e.status) {
|
||||||
case ConnectionAttemptStatus.SUCCESSFUL:
|
case ConnectionAttemptStatus.SUCCESSFUL:
|
||||||
case ConnectionAttemptStatus.REJECTED:
|
|
||||||
host.onConnect()
|
host.onConnect()
|
||||||
break
|
break
|
||||||
|
case ConnectionAttemptStatus.REJECTED:
|
||||||
|
host.onReject()
|
||||||
|
break
|
||||||
case ConnectionAttemptStatus.FAILED:
|
case ConnectionAttemptStatus.FAILED:
|
||||||
host.onFailure()
|
host.onFailure()
|
||||||
break
|
break
|
||||||
@@ -82,6 +84,10 @@ class HostCache extends Service {
|
|||||||
List<Destination> getHosts(int n) {
|
List<Destination> getHosts(int n) {
|
||||||
List<Destination> rv = new ArrayList<>(hosts.keySet())
|
List<Destination> rv = new ArrayList<>(hosts.keySet())
|
||||||
rv.retainAll {allowHost(hosts[it])}
|
rv.retainAll {allowHost(hosts[it])}
|
||||||
|
rv.removeAll {
|
||||||
|
def h = hosts[it];
|
||||||
|
(h.isFailed() && !h.canTryAgain()) || h.isRecentlyRejected()
|
||||||
|
}
|
||||||
if (rv.size() <= n)
|
if (rv.size() <= n)
|
||||||
return rv
|
return rv
|
||||||
Collections.shuffle(rv)
|
Collections.shuffle(rv)
|
||||||
@@ -106,12 +112,16 @@ class HostCache extends Service {
|
|||||||
storage.eachLine {
|
storage.eachLine {
|
||||||
def entry = slurper.parseText(it)
|
def entry = slurper.parseText(it)
|
||||||
Destination dest = new Destination(entry.destination)
|
Destination dest = new Destination(entry.destination)
|
||||||
Host host = new Host(dest, settings.hostClearInterval)
|
Host host = new Host(dest, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||||
host.failures = Integer.valueOf(String.valueOf(entry.failures))
|
host.failures = Integer.valueOf(String.valueOf(entry.failures))
|
||||||
host.successes = Integer.valueOf(String.valueOf(entry.successes))
|
host.successes = Integer.valueOf(String.valueOf(entry.successes))
|
||||||
if (entry.lastAttempt != null)
|
if (entry.lastAttempt != null)
|
||||||
host.lastAttempt = entry.lastAttempt
|
host.lastAttempt = entry.lastAttempt
|
||||||
if (allowHost(host))
|
if (entry.lastSuccessfulAttempt != null)
|
||||||
|
host.lastSuccessfulAttempt = entry.lastSuccessfulAttempt
|
||||||
|
if (entry.lastRejection != null)
|
||||||
|
host.lastRejection = entry.lastRejection
|
||||||
|
if (allowHost(host))
|
||||||
hosts.put(dest, host)
|
hosts.put(dest, host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,8 +130,6 @@ class HostCache extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean allowHost(Host host) {
|
private boolean allowHost(Host host) {
|
||||||
if (host.isFailed() && !host.canTryAgain())
|
|
||||||
return false
|
|
||||||
if (host.destination == myself)
|
if (host.destination == myself)
|
||||||
return false
|
return false
|
||||||
TrustLevel trust = trustService.getLevel(host.destination)
|
TrustLevel trust = trustService.getLevel(host.destination)
|
||||||
@@ -140,12 +148,14 @@ class HostCache extends Service {
|
|||||||
storage.delete()
|
storage.delete()
|
||||||
storage.withPrintWriter { writer ->
|
storage.withPrintWriter { writer ->
|
||||||
hosts.each { dest, host ->
|
hosts.each { dest, host ->
|
||||||
if (allowHost(host)) {
|
if (allowHost(host) && !host.isHopeless()) {
|
||||||
def map = [:]
|
def map = [:]
|
||||||
map.destination = dest.toBase64()
|
map.destination = dest.toBase64()
|
||||||
map.failures = host.failures
|
map.failures = host.failures
|
||||||
map.successes = host.successes
|
map.successes = host.successes
|
||||||
map.lastAttempt = host.lastAttempt
|
map.lastAttempt = host.lastAttempt
|
||||||
|
map.lastSuccessfulAttempt = host.lastSuccessfulAttempt
|
||||||
|
map.lastRejection = host.lastRejection
|
||||||
def json = JsonOutput.toJson(map)
|
def json = JsonOutput.toJson(map)
|
||||||
writer.println json
|
writer.println json
|
||||||
}
|
}
|
||||||
|
@@ -33,11 +33,12 @@ class MeshManager {
|
|||||||
meshes.get(infoHash)
|
meshes.get(infoHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh getOrCreate(InfoHash infoHash, int nPieces) {
|
Mesh getOrCreate(InfoHash infoHash, int nPieces, boolean sequential) {
|
||||||
synchronized(meshes) {
|
synchronized(meshes) {
|
||||||
if (meshes.containsKey(infoHash))
|
if (meshes.containsKey(infoHash))
|
||||||
return meshes.get(infoHash)
|
return meshes.get(infoHash)
|
||||||
Pieces pieces = new Pieces(nPieces, settings.downloadSequentialRatio)
|
float ratio = sequential ? 0f : settings.downloadSequentialRatio
|
||||||
|
Pieces pieces = new Pieces(nPieces, ratio)
|
||||||
if (fileManager.rootToFiles.containsKey(infoHash)) {
|
if (fileManager.rootToFiles.containsKey(infoHash)) {
|
||||||
for (int i = 0; i < nPieces; i++)
|
for (int i = 0; i < nPieces; i++)
|
||||||
pieces.markDownloaded(i)
|
pieces.markDownloaded(i)
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
package com.muwire.core.search
|
package com.muwire.core.search
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.SplitPattern
|
||||||
|
|
||||||
class SearchIndex {
|
class SearchIndex {
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ class SearchIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String[] split(String source) {
|
private static String[] split(String source) {
|
||||||
source = source.replaceAll(Constants.SPLIT_PATTERN, " ").toLowerCase()
|
source = source.replaceAll(SplitPattern.SPLIT_PATTERN, " ").toLowerCase()
|
||||||
String [] split = source.split(" ")
|
String [] split = source.split(" ")
|
||||||
def rv = []
|
def rv = []
|
||||||
split.each { if (it.length() > 0) rv << it }
|
split.each { if (it.length() > 0) rv << it }
|
||||||
|
@@ -3,5 +3,5 @@ package com.muwire.core.update
|
|||||||
import net.i2p.data.Destination
|
import net.i2p.data.Destination
|
||||||
|
|
||||||
class UpdateServers {
|
class UpdateServers {
|
||||||
static final Destination UPDATE_SERVER = new Destination("pSWieSRB3czCl3Zz4WpKp4Z8tjv-05zbogRDS7SEnKcSdWOupVwjzQ92GsgQh1VqgoSRk1F8dpZOnHxxz5HFy9D7ri0uFdkMyXdSKoB7IgkkvCfTAyEmeaPwSYnurF3Zk7u286E7YG2rZkQZgJ77tow7ZS0mxFB7Z0Ti-VkZ9~GeGePW~howwNm4iSQACZA0DyTpI8iv5j4I0itPCQRgaGziob~Vfvjk49nd8N4jtaDGo9cEcafikVzQ2OgBgYWL6LRbrrItwuGqsDvITUHWaElUYIDhRQYUq8gYiUA6rwAJputfhFU0J7lIxFR9vVY7YzRvcFckfr0DNI4VQVVlPnRPkUxQa--BlldMaCIppWugjgKLwqiSiHywKpSMlBWgY2z1ry4ueEBo1WEP-mEf88wRk4cFQBCKtctCQnIG2GsnATqTl-VGUAsuzeNWZiFSwXiTy~gQ094yWx-K06fFZUDt4CMiLZVhGlixiInD~34FCRC9LVMtFcqiFB2M-Ql2AAAA")
|
static final Destination UPDATE_SERVER = new Destination("VJYAiCPZHNLraWvLkeRLxRiT4PHAqNqRO1nH240r7u1noBw8Pa~-lJOhKR7CccPkEN8ejSi4H6XjqKYLC8BKLVLeOgnAbedUVx81MV7DETPDdPEGV4RVu6YDFri7-tJOeqauGHxtlXT44YWuR69xKrTG3u4~iTWgxKnlBDht9Q3aVpSPFD2KqEizfVxolqXI0zmAZ2xMi8jfl0oe4GbgHrD9hR2FYj6yKfdqcUgHVobY4kDdJt-u31QqwWdsQMEj8Y3tR2XcNaITEVPiAjoKgBrYwB4jddWPNaT4XdHz76d9p9Iqes7dhOKq3OKpk6kg-bfIKiEOiA1mY49fn5h8pNShTqV7QBhh4CE4EDT3Szl~WsLdrlHUKJufSi7erEMh3coF7HORpF1wah2Xw7q470t~b8dKGKi7N7xQsqhGruDm66PH9oE9Kt9WBVBq2zORdPRtRM61I7EnrwDlbOkL0y~XpvQ3JKUQKdBQ3QsOJt8CHlhHHXMMbvqhntR61RSDBQAEAAcAAA==")
|
||||||
}
|
}
|
||||||
|
@@ -92,7 +92,7 @@ public class UploadManager {
|
|||||||
pieceSize = downloader.pieceSizePow2
|
pieceSize = downloader.pieceSizePow2
|
||||||
} else {
|
} else {
|
||||||
SharedFile sharedFile = sharedFiles.iterator().next();
|
SharedFile sharedFile = sharedFiles.iterator().next();
|
||||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces)
|
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces, false)
|
||||||
file = sharedFile.file
|
file = sharedFile.file
|
||||||
pieceSize = sharedFile.pieceSize
|
pieceSize = sharedFile.pieceSize
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,7 @@ public class UploadManager {
|
|||||||
pieceSize = downloader.pieceSizePow2
|
pieceSize = downloader.pieceSizePow2
|
||||||
} else {
|
} else {
|
||||||
SharedFile sharedFile = sharedFiles.iterator().next();
|
SharedFile sharedFile = sharedFiles.iterator().next();
|
||||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces)
|
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces, false)
|
||||||
file = sharedFile.file
|
file = sharedFile.file
|
||||||
pieceSize = sharedFile.pieceSize
|
pieceSize = sharedFile.pieceSize
|
||||||
}
|
}
|
||||||
|
11
core/src/main/java/com/muwire/core/Constants.java
Normal file
11
core/src/main/java/com/muwire/core/Constants.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package com.muwire.core;
|
||||||
|
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
|
|
||||||
|
public class Constants {
|
||||||
|
public static final byte PERSONA_VERSION = (byte)1;
|
||||||
|
public static final SigType SIG_TYPE = SigType.EdDSA_SHA512_Ed25519;
|
||||||
|
|
||||||
|
public static final int MAX_HEADER_SIZE = 0x1 << 14;
|
||||||
|
public static final int MAX_HEADERS = 16;
|
||||||
|
}
|
@@ -2,6 +2,12 @@ package com.muwire.core;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.muwire.core.util.DataUtil;
|
||||||
|
|
||||||
|
import net.i2p.data.Base64;
|
||||||
|
|
||||||
public class SharedFile {
|
public class SharedFile {
|
||||||
|
|
||||||
@@ -11,6 +17,10 @@ public class SharedFile {
|
|||||||
|
|
||||||
private final String cachedPath;
|
private final String cachedPath;
|
||||||
private final long cachedLength;
|
private final long cachedLength;
|
||||||
|
|
||||||
|
private final String b64EncodedFileName;
|
||||||
|
private final String b64EncodedHashRoot;
|
||||||
|
private final List<String> b64EncodedHashList;
|
||||||
|
|
||||||
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
|
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
@@ -18,6 +28,16 @@ public class SharedFile {
|
|||||||
this.pieceSize = pieceSize;
|
this.pieceSize = pieceSize;
|
||||||
this.cachedPath = file.getAbsolutePath();
|
this.cachedPath = file.getAbsolutePath();
|
||||||
this.cachedLength = file.length();
|
this.cachedLength = file.length();
|
||||||
|
this.b64EncodedFileName = Base64.encode(DataUtil.encodei18nString(file.toString()));
|
||||||
|
this.b64EncodedHashRoot = Base64.encode(infoHash.getRoot());
|
||||||
|
|
||||||
|
List<String> b64List = new ArrayList<String>();
|
||||||
|
byte[] tmp = new byte[32];
|
||||||
|
for (int i = 0; i < infoHash.getHashList().length / 32; i++) {
|
||||||
|
System.arraycopy(infoHash.getHashList(), i * 32, tmp, 0, 32);
|
||||||
|
b64List.add(Base64.encode(tmp));
|
||||||
|
}
|
||||||
|
this.b64EncodedHashList = b64List;
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getFile() {
|
public File getFile() {
|
||||||
@@ -40,6 +60,18 @@ public class SharedFile {
|
|||||||
rv++;
|
rv++;
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getB64EncodedFileName() {
|
||||||
|
return b64EncodedFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getB64EncodedHashRoot() {
|
||||||
|
return b64EncodedHashRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getB64EncodedHashList() {
|
||||||
|
return b64EncodedHashList;
|
||||||
|
}
|
||||||
|
|
||||||
public String getCachedPath() {
|
public String getCachedPath() {
|
||||||
return cachedPath;
|
return cachedPath;
|
||||||
|
@@ -1,122 +1,134 @@
|
|||||||
package com.muwire.core.util
|
package com.muwire.core.util;
|
||||||
|
|
||||||
import java.lang.reflect.Field
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.lang.reflect.Method
|
import java.io.DataOutputStream;
|
||||||
import java.nio.ByteBuffer
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants;
|
||||||
|
|
||||||
import net.i2p.data.Base64
|
import net.i2p.data.Base64;
|
||||||
|
|
||||||
class DataUtil {
|
public class DataUtil {
|
||||||
|
|
||||||
private final static int MAX_SHORT = (0x1 << 16) - 1
|
private final static int MAX_SHORT = (0x1 << 16) - 1;
|
||||||
|
|
||||||
static void writeUnsignedShort(int value, OutputStream os) {
|
static void writeUnsignedShort(int value, OutputStream os) throws IOException {
|
||||||
if (value > MAX_SHORT || value < 0)
|
if (value > MAX_SHORT || value < 0)
|
||||||
throw new IllegalArgumentException("$value invalid")
|
throw new IllegalArgumentException("$value invalid");
|
||||||
|
|
||||||
byte lsb = (byte) (value & 0xFF)
|
byte lsb = (byte) (value & 0xFF);
|
||||||
byte msb = (byte) (value >> 8)
|
byte msb = (byte) (value >> 8);
|
||||||
|
|
||||||
os.write(msb)
|
os.write(msb);
|
||||||
os.write(lsb)
|
os.write(lsb);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static int MAX_HEADER = 0x7FFFFF
|
private final static int MAX_HEADER = 0x7FFFFF;
|
||||||
|
|
||||||
static void packHeader(int length, byte [] header) {
|
static void packHeader(int length, byte [] header) {
|
||||||
if (header.length != 3)
|
if (header.length != 3)
|
||||||
throw new IllegalArgumentException("header length $header.length")
|
throw new IllegalArgumentException("header length $header.length");
|
||||||
if (length < 0 || length > MAX_HEADER)
|
if (length < 0 || length > MAX_HEADER)
|
||||||
throw new IllegalArgumentException("length $length")
|
throw new IllegalArgumentException("length $length");
|
||||||
|
|
||||||
header[2] = (byte) (length & 0xFF)
|
header[2] = (byte) (length & 0xFF);
|
||||||
header[1] = (byte) ((length >> 8) & 0xFF)
|
header[1] = (byte) ((length >> 8) & 0xFF);
|
||||||
header[0] = (byte) ((length >> 16) & 0x7F)
|
header[0] = (byte) ((length >> 16) & 0x7F);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int readLength(byte [] header) {
|
static int readLength(byte [] header) {
|
||||||
if (header.length != 3)
|
if (header.length != 3)
|
||||||
throw new IllegalArgumentException("header length $header.length")
|
throw new IllegalArgumentException("header length $header.length");
|
||||||
|
|
||||||
return (((int)(header[0] & 0x7F)) << 16) |
|
return (((int)(header[0] & 0x7F)) << 16) |
|
||||||
(((int)(header[1] & 0xFF) << 8)) |
|
(((int)(header[1] & 0xFF) << 8)) |
|
||||||
((int)header[2] & 0xFF)
|
((int)header[2] & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String readi18nString(byte [] encoded) {
|
static String readi18nString(byte [] encoded) {
|
||||||
if (encoded.length < 2)
|
if (encoded.length < 2)
|
||||||
throw new IllegalArgumentException("encoding too short $encoded.length")
|
throw new IllegalArgumentException("encoding too short $encoded.length");
|
||||||
int length = ((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF)
|
int length = ((encoded[0] & 0xFF) << 8) | (encoded[1] & 0xFF);
|
||||||
if (encoded.length != length + 2)
|
if (encoded.length != length + 2)
|
||||||
throw new IllegalArgumentException("encoding doesn't match length, expected $length found $encoded.length")
|
throw new IllegalArgumentException("encoding doesn't match length, expected $length found $encoded.length");
|
||||||
byte [] string = new byte[length]
|
byte [] string = new byte[length];
|
||||||
System.arraycopy(encoded, 2, string, 0, length)
|
System.arraycopy(encoded, 2, string, 0, length);
|
||||||
new String(string, StandardCharsets.UTF_8)
|
return new String(string, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte[] encodei18nString(String string) {
|
public static byte[] encodei18nString(String string) {
|
||||||
byte [] utf8 = string.getBytes(StandardCharsets.UTF_8)
|
byte [] utf8 = string.getBytes(StandardCharsets.UTF_8);
|
||||||
if (utf8.length > Short.MAX_VALUE)
|
if (utf8.length > Short.MAX_VALUE)
|
||||||
throw new IllegalArgumentException("String in utf8 too long $utf8.length")
|
throw new IllegalArgumentException("String in utf8 too long $utf8.length");
|
||||||
def baos = new ByteArrayOutputStream()
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
def daos = new DataOutputStream(baos)
|
DataOutputStream daos = new DataOutputStream(baos);
|
||||||
daos.writeShort((short) utf8.length)
|
try {
|
||||||
daos.write(utf8)
|
daos.writeShort((short) utf8.length);
|
||||||
daos.close()
|
daos.write(utf8);
|
||||||
baos.toByteArray()
|
daos.close();
|
||||||
|
} catch (IOException impossible) {
|
||||||
|
throw new IllegalStateException(impossible);
|
||||||
|
}
|
||||||
|
return baos.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String readTillRN(InputStream is) {
|
public static String readTillRN(InputStream is) throws IOException {
|
||||||
def baos = new ByteArrayOutputStream()
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
while(baos.size() < (Constants.MAX_HEADER_SIZE)) {
|
while(baos.size() < (Constants.MAX_HEADER_SIZE)) {
|
||||||
byte read = is.read()
|
int read = is.read();
|
||||||
if (read == -1)
|
if (read == -1)
|
||||||
throw new IOException()
|
throw new IOException();
|
||||||
if (read == '\r') {
|
if (read == '\r') {
|
||||||
if (is.read() != '\n')
|
if (is.read() != '\n')
|
||||||
throw new IOException("invalid header")
|
throw new IOException("invalid header");
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
baos.write(read)
|
baos.write(read);
|
||||||
}
|
}
|
||||||
new String(baos.toByteArray(), StandardCharsets.US_ASCII)
|
return new String(baos.toByteArray(), StandardCharsets.US_ASCII);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String encodeXHave(List<Integer> pieces, int totalPieces) {
|
public static String encodeXHave(List<Integer> pieces, int totalPieces) {
|
||||||
int bytes = totalPieces / 8
|
int bytes = totalPieces / 8;
|
||||||
if (totalPieces % 8 != 0)
|
if (totalPieces % 8 != 0)
|
||||||
bytes++
|
bytes++;
|
||||||
byte[] raw = new byte[bytes]
|
byte[] raw = new byte[bytes];
|
||||||
pieces.each {
|
for (int it : pieces) {
|
||||||
int byteIdx = it / 8
|
int byteIdx = it / 8;
|
||||||
int offset = it % 8
|
int offset = it % 8;
|
||||||
int mask = 0x80 >>> offset
|
int mask = 0x80 >>> offset;
|
||||||
raw[byteIdx] |= mask
|
raw[byteIdx] |= mask;
|
||||||
}
|
}
|
||||||
Base64.encode(raw)
|
return Base64.encode(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> decodeXHave(String xHave) {
|
public static List<Integer> decodeXHave(String xHave) {
|
||||||
byte [] availablePieces = Base64.decode(xHave)
|
byte [] availablePieces = Base64.decode(xHave);
|
||||||
List<Integer> available = new ArrayList<>()
|
List<Integer> available = new ArrayList<>();
|
||||||
availablePieces.eachWithIndex {b, i ->
|
for (int i = 0; i < availablePieces.length; i ++) {
|
||||||
|
byte b = availablePieces[i];
|
||||||
for (int j = 0; j < 8 ; j++) {
|
for (int j = 0; j < 8 ; j++) {
|
||||||
byte mask = 0x80 >>> j
|
byte mask = (byte) (0x80 >>> j);
|
||||||
if ((b & mask) == mask) {
|
if ((b & mask) == mask) {
|
||||||
available.add(i * 8 + j)
|
available.add(i * 8 + j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
available
|
return available;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Exception findRoot(Exception e) {
|
public static Throwable findRoot(Throwable e) {
|
||||||
while(e.getCause() != null)
|
while(e.getCause() != null)
|
||||||
e = e.getCause()
|
e = e.getCause();
|
||||||
e
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void tryUnmap(ByteBuffer cb) {
|
public static void tryUnmap(ByteBuffer cb) {
|
@@ -4,6 +4,7 @@ import static org.junit.Assert.fail
|
|||||||
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
import com.muwire.core.EventBus
|
import com.muwire.core.EventBus
|
||||||
@@ -180,10 +181,11 @@ class DownloadSessionTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Ignore // this needs to be rewritten with stealing in mind
|
||||||
public void testSmallFileClaimed() {
|
public void testSmallFileClaimed() {
|
||||||
initSession(20, [0])
|
initSession(20, [0])
|
||||||
long now = System.currentTimeMillis()
|
long now = System.currentTimeMillis()
|
||||||
downloadThread.join(100)
|
downloadThread.join(150)
|
||||||
assert 100 >= (System.currentTimeMillis() - now)
|
assert 100 >= (System.currentTimeMillis() - now)
|
||||||
assert !performed
|
assert !performed
|
||||||
assert available.isEmpty()
|
assert available.isEmpty()
|
||||||
|
@@ -16,7 +16,7 @@ class PiecesTest {
|
|||||||
public void testSinglePiece() {
|
public void testSinglePiece() {
|
||||||
pieces = new Pieces(1)
|
pieces = new Pieces(1)
|
||||||
assert !pieces.isComplete()
|
assert !pieces.isComplete()
|
||||||
assert pieces.claim() == 0
|
assert pieces.claim() == [0,0,0]
|
||||||
pieces.markDownloaded(0)
|
pieces.markDownloaded(0)
|
||||||
assert pieces.isComplete()
|
assert pieces.isComplete()
|
||||||
}
|
}
|
||||||
@@ -25,28 +25,28 @@ class PiecesTest {
|
|||||||
public void testTwoPieces() {
|
public void testTwoPieces() {
|
||||||
pieces = new Pieces(2)
|
pieces = new Pieces(2)
|
||||||
assert !pieces.isComplete()
|
assert !pieces.isComplete()
|
||||||
int piece = pieces.claim()
|
int[] piece = pieces.claim()
|
||||||
assert piece == 0 || piece == 1
|
assert piece[0] == 0 || piece[0] == 1
|
||||||
pieces.markDownloaded(piece)
|
pieces.markDownloaded(piece[0])
|
||||||
assert !pieces.isComplete()
|
assert !pieces.isComplete()
|
||||||
int piece2 = pieces.claim()
|
int[] piece2 = pieces.claim()
|
||||||
assert piece != piece2
|
assert piece[0] != piece2[0]
|
||||||
pieces.markDownloaded(piece2)
|
pieces.markDownloaded(piece2[0])
|
||||||
assert pieces.isComplete()
|
assert pieces.isComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClaimAvailable() {
|
public void testClaimAvailable() {
|
||||||
pieces = new Pieces(2)
|
pieces = new Pieces(2)
|
||||||
int claimed = pieces.claim([0].toSet())
|
int[] claimed = pieces.claim([0].toSet())
|
||||||
assert claimed == 0
|
assert claimed == [0,0,0]
|
||||||
assert -1 == pieces.claim([0].toSet())
|
assert [0,0,1] == pieces.claim([0].toSet())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClaimNoneAvailable() {
|
public void testClaimNoneAvailable() {
|
||||||
pieces = new Pieces(20)
|
pieces = new Pieces(20)
|
||||||
int claimed = pieces.claim()
|
int[] claimed = pieces.claim()
|
||||||
assert -1 == pieces.claim([claimed].toSet())
|
assert [0,0,0] == pieces.claim(claimed.toSet())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -72,6 +72,9 @@ class HostCacheTest {
|
|||||||
TrustLevel.NEUTRAL
|
TrustLevel.NEUTRAL
|
||||||
}
|
}
|
||||||
settingsMock.ignore.allowUntrusted { true }
|
settingsMock.ignore.allowUntrusted { true }
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
|
|
||||||
@@ -91,6 +94,10 @@ class HostCacheTest {
|
|||||||
TrustLevel.DISTRUSTED
|
TrustLevel.DISTRUSTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
|
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
@@ -104,6 +111,9 @@ class HostCacheTest {
|
|||||||
TrustLevel.NEUTRAL
|
TrustLevel.NEUTRAL
|
||||||
}
|
}
|
||||||
settingsMock.ignore.allowUntrusted { false }
|
settingsMock.ignore.allowUntrusted { false }
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
|
|
||||||
@@ -123,6 +133,9 @@ class HostCacheTest {
|
|||||||
}
|
}
|
||||||
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
|
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
|
||||||
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
|
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
@@ -139,7 +152,15 @@ class HostCacheTest {
|
|||||||
assert d == destinations.dest1
|
assert d == destinations.dest1
|
||||||
TrustLevel.TRUSTED
|
TrustLevel.TRUSTED
|
||||||
}
|
}
|
||||||
|
trustMock.demand.getLevel { d ->
|
||||||
|
assert d == destinations.dest1
|
||||||
|
TrustLevel.TRUSTED
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsMock.ignore.getHostClearInterval { 100 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
|
|
||||||
@@ -158,6 +179,10 @@ class HostCacheTest {
|
|||||||
TrustLevel.TRUSTED
|
TrustLevel.TRUSTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
|
|
||||||
@@ -183,6 +208,10 @@ class HostCacheTest {
|
|||||||
TrustLevel.TRUSTED
|
TrustLevel.TRUSTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
|
|
||||||
@@ -214,6 +243,10 @@ class HostCacheTest {
|
|||||||
TrustLevel.TRUSTED
|
TrustLevel.TRUSTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
@@ -229,6 +262,11 @@ class HostCacheTest {
|
|||||||
assert d == destinations.dest1
|
assert d == destinations.dest1
|
||||||
TrustLevel.TRUSTED
|
TrustLevel.TRUSTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
|
||||||
Thread.sleep(150)
|
Thread.sleep(150)
|
||||||
@@ -260,6 +298,10 @@ class HostCacheTest {
|
|||||||
TrustLevel.TRUSTED
|
TrustLevel.TRUSTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsMock.ignore.getHostClearInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostHopelessInterval { 0 }
|
||||||
|
settingsMock.ignore.getHostRejectInterval { 0 }
|
||||||
|
|
||||||
initMocks()
|
initMocks()
|
||||||
def rv = cache.getHosts(5)
|
def rv = cache.getHosts(5)
|
||||||
assert rv.size() == 1
|
assert rv.size() == 1
|
||||||
|
@@ -9,6 +9,9 @@ import org.junit.Test
|
|||||||
|
|
||||||
import com.muwire.core.InfoHash
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.connection.Endpoint
|
import com.muwire.core.connection.Endpoint
|
||||||
|
import com.muwire.core.download.Pieces
|
||||||
|
import com.muwire.core.files.FileHasher
|
||||||
|
import com.muwire.core.mesh.Mesh
|
||||||
|
|
||||||
class UploaderTest {
|
class UploaderTest {
|
||||||
|
|
||||||
@@ -52,7 +55,13 @@ class UploaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startUpload() {
|
private void startUpload() {
|
||||||
uploader = new ContentUploader(file, request, endpoint)
|
def hasher = new FileHasher()
|
||||||
|
InfoHash infoHash = hasher.hashFile(file)
|
||||||
|
Pieces pieces = new Pieces(FileHasher.getPieceSize(file.length()))
|
||||||
|
for (int i = 0; i < pieces.nPieces; i++)
|
||||||
|
pieces.markDownloaded(i)
|
||||||
|
Mesh mesh = new Mesh(infoHash, pieces)
|
||||||
|
uploader = new ContentUploader(file, request, endpoint, mesh, FileHasher.getPieceSize(file.length()))
|
||||||
uploadThread = new Thread(uploader.respond() as Runnable)
|
uploadThread = new Thread(uploader.respond() as Runnable)
|
||||||
uploadThread.setDaemon(true)
|
uploadThread.setDaemon(true)
|
||||||
uploadThread.start()
|
uploadThread.start()
|
||||||
@@ -81,6 +90,7 @@ class UploaderTest {
|
|||||||
startUpload()
|
startUpload()
|
||||||
assert "200 OK" == readUntilRN()
|
assert "200 OK" == readUntilRN()
|
||||||
assert "Content-Range: 0-19" == readUntilRN()
|
assert "Content-Range: 0-19" == readUntilRN()
|
||||||
|
assert readUntilRN().startsWith("X-Have")
|
||||||
assert "" == readUntilRN()
|
assert "" == readUntilRN()
|
||||||
|
|
||||||
byte [] data = new byte[20]
|
byte [] data = new byte[20]
|
||||||
@@ -96,6 +106,7 @@ class UploaderTest {
|
|||||||
startUpload()
|
startUpload()
|
||||||
assert "200 OK" == readUntilRN()
|
assert "200 OK" == readUntilRN()
|
||||||
assert "Content-Range: 5-15" == readUntilRN()
|
assert "Content-Range: 5-15" == readUntilRN()
|
||||||
|
assert readUntilRN().startsWith("X-Have")
|
||||||
assert "" == readUntilRN()
|
assert "" == readUntilRN()
|
||||||
|
|
||||||
byte [] data = new byte[11]
|
byte [] data = new byte[11]
|
||||||
@@ -111,6 +122,7 @@ class UploaderTest {
|
|||||||
request = new ContentRequest(range : new Range(0,20))
|
request = new ContentRequest(range : new Range(0,20))
|
||||||
startUpload()
|
startUpload()
|
||||||
assert "416 Range Not Satisfiable" == readUntilRN()
|
assert "416 Range Not Satisfiable" == readUntilRN()
|
||||||
|
assert readUntilRN().startsWith("X-Have")
|
||||||
assert "" == readUntilRN()
|
assert "" == readUntilRN()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +135,7 @@ class UploaderTest {
|
|||||||
readUntilRN()
|
readUntilRN()
|
||||||
readUntilRN()
|
readUntilRN()
|
||||||
readUntilRN()
|
readUntilRN()
|
||||||
|
readUntilRN()
|
||||||
|
|
||||||
byte [] data = new byte[length]
|
byte [] data = new byte[length]
|
||||||
DataInputStream dis = new DataInputStream(is)
|
DataInputStream dis = new DataInputStream(is)
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.4.11
|
version = 0.4.15
|
||||||
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
|
||||||
|
@@ -44,9 +44,9 @@ mainClassName = 'com.muwire.gui.Launcher'
|
|||||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||||
|
|
||||||
apply from: 'gradle/publishing.gradle'
|
apply from: 'gradle/publishing.gradle'
|
||||||
apply from: 'gradle/code-coverage.gradle'
|
// apply from: 'gradle/code-coverage.gradle'
|
||||||
apply from: 'gradle/code-quality.gradle'
|
// apply from: 'gradle/code-quality.gradle'
|
||||||
apply from: 'gradle/integration-test.gradle'
|
// apply from: 'gradle/integration-test.gradle'
|
||||||
// apply from: 'gradle/package.gradle'
|
// apply from: 'gradle/package.gradle'
|
||||||
apply from: 'gradle/docs.gradle'
|
apply from: 'gradle/docs.gradle'
|
||||||
apply plugin: 'com.github.johnrengelman.shadow'
|
apply plugin: 'com.github.johnrengelman.shadow'
|
||||||
@@ -119,6 +119,7 @@ if (hasProperty('debugRun') && ((project.debugRun as boolean))) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
task jacocoRootMerge(type: org.gradle.testing.jacoco.tasks.JacocoMerge, dependsOn: [test, jacocoTestReport, jacocoIntegrationTestReport]) {
|
task jacocoRootMerge(type: org.gradle.testing.jacoco.tasks.JacocoMerge, dependsOn: [test, jacocoTestReport, jacocoIntegrationTestReport]) {
|
||||||
executionData = files(jacocoTestReport.executionData, jacocoIntegrationTestReport.executionData)
|
executionData = files(jacocoTestReport.executionData, jacocoIntegrationTestReport.executionData)
|
||||||
destinationFile = file("${buildDir}/jacoco/root.exec")
|
destinationFile = file("${buildDir}/jacoco/root.exec")
|
||||||
@@ -138,4 +139,5 @@ task jacocoRootReport(dependsOn: jacocoRootMerge, type: JacocoReport) {
|
|||||||
xml.destination = file("${buildDir}/reports/jacoco/root/root.xml")
|
xml.destination = file("${buildDir}/reports/jacoco/root/root.xml")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@@ -68,6 +68,16 @@ class ContentPanelController {
|
|||||||
model.refresh()
|
model.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void clearHits() {
|
||||||
|
int selectedRule = view.getSelectedRule()
|
||||||
|
if (selectedRule < 0)
|
||||||
|
return
|
||||||
|
Matcher matcher = model.rules[selectedRule]
|
||||||
|
matcher.matches.clear()
|
||||||
|
model.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void trust() {
|
void trust() {
|
||||||
int selectedHit = view.getSelectedHit()
|
int selectedHit = view.getSelectedHit()
|
||||||
|
@@ -13,10 +13,10 @@ import javax.annotation.Nonnull
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.swing.JTable
|
import javax.swing.JTable
|
||||||
|
|
||||||
import com.muwire.core.Constants
|
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
import com.muwire.core.Persona
|
import com.muwire.core.Persona
|
||||||
import com.muwire.core.SharedFile
|
import com.muwire.core.SharedFile
|
||||||
|
import com.muwire.core.SplitPattern
|
||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
import com.muwire.core.download.DownloadStartedEvent
|
import com.muwire.core.download.DownloadStartedEvent
|
||||||
import com.muwire.core.download.UIDownloadCancelledEvent
|
import com.muwire.core.download.UIDownloadCancelledEvent
|
||||||
@@ -80,13 +80,14 @@ class MainFrameController {
|
|||||||
searchEvent = new SearchEvent(searchHash : root, uuid : uuid, oobInfohash: true)
|
searchEvent = new SearchEvent(searchHash : root, uuid : uuid, oobInfohash: true)
|
||||||
} else {
|
} else {
|
||||||
// this can be improved a lot
|
// this can be improved a lot
|
||||||
def replaced = search.toLowerCase().trim().replaceAll(Constants.SPLIT_PATTERN, " ")
|
def replaced = search.toLowerCase().trim().replaceAll(SplitPattern.SPLIT_PATTERN, " ")
|
||||||
def terms = replaced.split(" ")
|
def terms = replaced.split(" ")
|
||||||
def nonEmpty = []
|
def nonEmpty = []
|
||||||
terms.each { if (it.length() > 0) nonEmpty << it }
|
terms.each { if (it.length() > 0) nonEmpty << it }
|
||||||
searchEvent = new SearchEvent(searchTerms : nonEmpty, uuid : uuid, oobInfohash: true)
|
searchEvent = new SearchEvent(searchTerms : nonEmpty, uuid : uuid, oobInfohash: true)
|
||||||
}
|
}
|
||||||
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
|
boolean firstHop = core.muOptions.allowUntrusted || core.muOptions.searchExtraHop
|
||||||
|
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : firstHop,
|
||||||
replyTo: core.me.destination, receivedOn: core.me.destination,
|
replyTo: core.me.destination, receivedOn: core.me.destination,
|
||||||
originator : core.me))
|
originator : core.me))
|
||||||
}
|
}
|
||||||
@@ -269,10 +270,12 @@ class MainFrameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void unshareSelectedFile() {
|
void unshareSelectedFile() {
|
||||||
SharedFile sf = view.selectedSharedFile()
|
def sf = view.selectedSharedFiles()
|
||||||
if (sf == null)
|
if (sf == null)
|
||||||
return
|
return
|
||||||
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
|
sf.each {
|
||||||
|
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : it))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopWatchingDirectory() {
|
void stopWatchingDirectory() {
|
||||||
|
@@ -96,6 +96,10 @@ class OptionsController {
|
|||||||
model.onlyTrusted = onlyTrusted
|
model.onlyTrusted = onlyTrusted
|
||||||
settings.setAllowUntrusted(!onlyTrusted)
|
settings.setAllowUntrusted(!onlyTrusted)
|
||||||
|
|
||||||
|
boolean searchExtraHop = view.searchExtraHopCheckbox.model.isSelected()
|
||||||
|
model.searchExtraHop = searchExtraHop
|
||||||
|
settings.searchExtraHop = searchExtraHop
|
||||||
|
|
||||||
boolean trustLists = view.allowTrustListsCheckbox.model.isSelected()
|
boolean trustLists = view.allowTrustListsCheckbox.model.isSelected()
|
||||||
model.trustLists = trustLists
|
model.trustLists = trustLists
|
||||||
settings.allowTrustLists = trustLists
|
settings.allowTrustLists = trustLists
|
||||||
|
@@ -8,63 +8,81 @@ import javax.annotation.Nonnull
|
|||||||
|
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
import com.muwire.core.download.UIDownloadEvent
|
import com.muwire.core.download.UIDownloadEvent
|
||||||
|
import com.muwire.core.search.UIResultEvent
|
||||||
import com.muwire.core.trust.TrustEvent
|
import com.muwire.core.trust.TrustEvent
|
||||||
import com.muwire.core.trust.TrustLevel
|
import com.muwire.core.trust.TrustLevel
|
||||||
|
|
||||||
@ArtifactProviderFor(GriffonController)
|
@ArtifactProviderFor(GriffonController)
|
||||||
class SearchTabController {
|
class SearchTabController {
|
||||||
|
|
||||||
@MVCMember @Nonnull
|
@MVCMember @Nonnull
|
||||||
SearchTabModel model
|
SearchTabModel model
|
||||||
@MVCMember @Nonnull
|
@MVCMember @Nonnull
|
||||||
SearchTabView view
|
SearchTabView view
|
||||||
|
|
||||||
Core core
|
Core core
|
||||||
|
|
||||||
private def selectedResult() {
|
private def selectedResults() {
|
||||||
int row = view.resultsTable.getSelectedRow()
|
int[] rows = view.resultsTable.getSelectedRows()
|
||||||
if (row == -1)
|
if (rows.length == 0)
|
||||||
return null
|
return null
|
||||||
def sortEvt = view.lastSortEvent
|
def sortEvt = view.lastSortEvent
|
||||||
if (sortEvt != null) {
|
if (sortEvt != null) {
|
||||||
row = view.resultsTable.rowSorter.convertRowIndexToModel(row)
|
for (int i = 0; i < rows.length; i++) {
|
||||||
|
rows[i] = view.resultsTable.rowSorter.convertRowIndexToModel(rows[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<UIResultEvent> results = new ArrayList<>()
|
||||||
|
rows.each { results.add(model.results[it]) }
|
||||||
|
results
|
||||||
}
|
}
|
||||||
model.results[row]
|
|
||||||
}
|
|
||||||
|
|
||||||
@ControllerAction
|
|
||||||
void download() {
|
|
||||||
def result = selectedResult()
|
|
||||||
if (result == null)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (!mvcGroup.parentGroup.model.canDownload(result.infohash))
|
@ControllerAction
|
||||||
return
|
void download() {
|
||||||
|
def results = selectedResults()
|
||||||
|
if (results == null)
|
||||||
|
return
|
||||||
|
|
||||||
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
|
results.removeAll {
|
||||||
|
!mvcGroup.parentGroup.model.canDownload(it.infohash)
|
||||||
|
}
|
||||||
|
|
||||||
def resultsBucket = model.hashBucket[result.infohash]
|
results.each { result ->
|
||||||
def sources = model.sourcesBucket[result.infohash]
|
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
|
||||||
|
|
||||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources, target : file))
|
def resultsBucket = model.hashBucket[result.infohash]
|
||||||
mvcGroup.parentGroup.view.showDownloadsWindow.call()
|
def sources = model.sourcesBucket[result.infohash]
|
||||||
}
|
|
||||||
|
|
||||||
@ControllerAction
|
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources,
|
||||||
void trust() {
|
target : file, sequential : view.sequentialDownloadCheckbox.model.isSelected()))
|
||||||
int row = view.selectedSenderRow()
|
}
|
||||||
if (row < 0)
|
mvcGroup.parentGroup.view.showDownloadsWindow.call()
|
||||||
return
|
}
|
||||||
def sender = model.senders[row]
|
|
||||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.TRUSTED))
|
|
||||||
}
|
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void distrust() {
|
void trust() {
|
||||||
int row = view.selectedSenderRow()
|
int row = view.selectedSenderRow()
|
||||||
if (row < 0)
|
if (row < 0)
|
||||||
return
|
return
|
||||||
def sender = model.senders[row]
|
def sender = model.senders[row]
|
||||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.DISTRUSTED))
|
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.TRUSTED))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ControllerAction
|
||||||
|
void distrust() {
|
||||||
|
int row = view.selectedSenderRow()
|
||||||
|
if (row < 0)
|
||||||
|
return
|
||||||
|
def sender = model.senders[row]
|
||||||
|
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.DISTRUSTED))
|
||||||
|
}
|
||||||
|
|
||||||
|
@ControllerAction
|
||||||
|
void neutral() {
|
||||||
|
int row = view.selectedSenderRow()
|
||||||
|
if (row < 0)
|
||||||
|
return
|
||||||
|
def sender = model.senders[row]
|
||||||
|
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.NEUTRAL))
|
||||||
|
}
|
||||||
|
}
|
@@ -43,7 +43,7 @@ class Initialize extends AbstractLifecycleHandler {
|
|||||||
|
|
||||||
application.context.put("muwire-home", home.getAbsolutePath())
|
application.context.put("muwire-home", home.getAbsolutePath())
|
||||||
|
|
||||||
System.getProperties().setProperty("awt.useSystemAAFontSettings", "true")
|
System.getProperties().setProperty("awt.useSystemAAFontSettings", "gasp")
|
||||||
|
|
||||||
def guiPropsFile = new File(home, "gui.properties")
|
def guiPropsFile = new File(home, "gui.properties")
|
||||||
UISettings uiSettings
|
UISettings uiSettings
|
||||||
@@ -86,6 +86,8 @@ class Initialize extends AbstractLifecycleHandler {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LookAndFeel chosen = lookAndFeel('system', 'gtk')
|
LookAndFeel chosen = lookAndFeel('system', 'gtk')
|
||||||
|
if (chosen == null)
|
||||||
|
chosen = lookAndFeel('metal')
|
||||||
uiSettings.lnf = chosen.getID()
|
uiSettings.lnf = chosen.getID()
|
||||||
log.info("ended up applying $chosen.name")
|
log.info("ended up applying $chosen.name")
|
||||||
}
|
}
|
||||||
|
@@ -40,11 +40,15 @@ class ContentPanelModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void refresh() {
|
void refresh() {
|
||||||
|
int selectedRule = view.getSelectedRule()
|
||||||
rules.clear()
|
rules.clear()
|
||||||
rules.addAll(contentManager.matchers)
|
rules.addAll(contentManager.matchers)
|
||||||
hits.clear()
|
hits.clear()
|
||||||
view.rulesTable.model.fireTableDataChanged()
|
view.rulesTable.model.fireTableDataChanged()
|
||||||
view.hitsTable.model.fireTableDataChanged()
|
view.hitsTable.model.fireTableDataChanged()
|
||||||
|
if (selectedRule >= 0) {
|
||||||
|
view.rulesTable.selectionModel.setSelectionInterval(selectedRule,selectedRule)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onContentControlEvent(ContentControlEvent e) {
|
void onContentControlEvent(ContentControlEvent e) {
|
||||||
|
@@ -191,7 +191,7 @@ class MainFrameModel {
|
|||||||
return
|
return
|
||||||
int retryInterval = core.muOptions.downloadRetryInterval
|
int retryInterval = core.muOptions.downloadRetryInterval
|
||||||
if (retryInterval > 0) {
|
if (retryInterval > 0) {
|
||||||
retryInterval *= 60000
|
retryInterval *= 1000
|
||||||
long now = System.currentTimeMillis()
|
long now = System.currentTimeMillis()
|
||||||
if (now - lastRetryTime > retryInterval) {
|
if (now - lastRetryTime > retryInterval) {
|
||||||
lastRetryTime = now
|
lastRetryTime = now
|
||||||
@@ -207,7 +207,7 @@ class MainFrameModel {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 60000, 60000)
|
}, 1000, 1000)
|
||||||
|
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
trusted.addAll(core.trustService.good.values())
|
trusted.addAll(core.trustService.good.values())
|
||||||
|
@@ -38,6 +38,7 @@ class OptionsModel {
|
|||||||
|
|
||||||
// trust options
|
// trust options
|
||||||
@Observable boolean onlyTrusted
|
@Observable boolean onlyTrusted
|
||||||
|
@Observable boolean searchExtraHop
|
||||||
@Observable boolean trustLists
|
@Observable boolean trustLists
|
||||||
@Observable String trustListInterval
|
@Observable String trustListInterval
|
||||||
|
|
||||||
@@ -73,6 +74,7 @@ class OptionsModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onlyTrusted = !settings.allowUntrusted()
|
onlyTrusted = !settings.allowUntrusted()
|
||||||
|
searchExtraHop = settings.searchExtraHop
|
||||||
trustLists = settings.allowTrustLists
|
trustLists = settings.allowTrustLists
|
||||||
trustListInterval = String.valueOf(settings.trustListInterval)
|
trustListInterval = String.valueOf(settings.trustListInterval)
|
||||||
}
|
}
|
||||||
|
@@ -84,6 +84,7 @@ class ContentPanelView {
|
|||||||
}
|
}
|
||||||
panel (constraints : BorderLayout.SOUTH) {
|
panel (constraints : BorderLayout.SOUTH) {
|
||||||
button(text : "Refresh", refreshAction)
|
button(text : "Refresh", refreshAction)
|
||||||
|
button(text : "Clear Hits", clearHitsAction)
|
||||||
button(text : "Trust", enabled : bind {model.trustButtonsEnabled}, trustAction)
|
button(text : "Trust", enabled : bind {model.trustButtonsEnabled}, trustAction)
|
||||||
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ import javax.swing.table.DefaultTableCellRenderer
|
|||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
import com.muwire.core.MuWireSettings
|
import com.muwire.core.MuWireSettings
|
||||||
|
import com.muwire.core.SharedFile
|
||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
import com.muwire.core.files.FileSharedEvent
|
import com.muwire.core.files.FileSharedEvent
|
||||||
import com.muwire.core.trust.RemoteTrustList
|
import com.muwire.core.trust.RemoteTrustList
|
||||||
@@ -615,22 +616,36 @@ class MainFrameView {
|
|||||||
menu.show(event.getComponent(), event.getX(), event.getY())
|
menu.show(event.getComponent(), event.getX(), event.getY())
|
||||||
}
|
}
|
||||||
|
|
||||||
def selectedSharedFile() {
|
def selectedSharedFiles() {
|
||||||
def sharedFilesTable = builder.getVariable("shared-files-table")
|
def sharedFilesTable = builder.getVariable("shared-files-table")
|
||||||
int selected = sharedFilesTable.getSelectedRow()
|
int[] selected = sharedFilesTable.getSelectedRows()
|
||||||
if (selected < 0)
|
if (selected.length == 0)
|
||||||
return null
|
return null
|
||||||
if (lastSharedSortEvent != null)
|
List<SharedFile> rv = new ArrayList<>()
|
||||||
selected = sharedFilesTable.rowSorter.convertRowIndexToModel(selected)
|
if (lastSharedSortEvent != null) {
|
||||||
model.shared[selected]
|
for (int i = 0; i < selected.length; i ++) {
|
||||||
|
selected[i] = sharedFilesTable.rowSorter.convertRowIndexToModel(selected[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selected.each {
|
||||||
|
rv.add(model.shared[it])
|
||||||
|
}
|
||||||
|
rv
|
||||||
}
|
}
|
||||||
|
|
||||||
def copyHashToClipboard() {
|
def copyHashToClipboard() {
|
||||||
def selected = selectedSharedFile()
|
def selectedFiles = selectedSharedFiles()
|
||||||
if (selected == null)
|
if (selectedFiles == null)
|
||||||
return
|
return
|
||||||
String root = Base64.encode(selected.infoHash.getRoot())
|
String roots = ""
|
||||||
StringSelection selection = new StringSelection(root)
|
for (Iterator<SharedFile> iterator = selectedFiles.iterator(); iterator.hasNext(); ) {
|
||||||
|
SharedFile selected = iterator.next()
|
||||||
|
String root = Base64.encode(selected.infoHash.getRoot())
|
||||||
|
roots += root
|
||||||
|
if (iterator.hasNext())
|
||||||
|
roots += "\n"
|
||||||
|
}
|
||||||
|
StringSelection selection = new StringSelection(roots)
|
||||||
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||||
clipboard.setContents(selection, null)
|
clipboard.setContents(selection, null)
|
||||||
}
|
}
|
||||||
@@ -772,9 +787,12 @@ class MainFrameView {
|
|||||||
chooser.setFileHidingEnabled(false)
|
chooser.setFileHidingEnabled(false)
|
||||||
chooser.setDialogTitle("Select file to share")
|
chooser.setDialogTitle("Select file to share")
|
||||||
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY)
|
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY)
|
||||||
|
chooser.setMultiSelectionEnabled(true)
|
||||||
int rv = chooser.showOpenDialog(null)
|
int rv = chooser.showOpenDialog(null)
|
||||||
if (rv == JFileChooser.APPROVE_OPTION) {
|
if (rv == JFileChooser.APPROVE_OPTION) {
|
||||||
model.core.eventBus.publish(new FileSharedEvent(file : chooser.getSelectedFile()))
|
chooser.getSelectedFiles().each {
|
||||||
|
model.core.eventBus.publish(new FileSharedEvent(file : it))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,14 +801,16 @@ class MainFrameView {
|
|||||||
chooser.setFileHidingEnabled(false)
|
chooser.setFileHidingEnabled(false)
|
||||||
chooser.setDialogTitle("Select directory to watch")
|
chooser.setDialogTitle("Select directory to watch")
|
||||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
|
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
|
||||||
|
chooser.setMultiSelectionEnabled(true)
|
||||||
int rv = chooser.showOpenDialog(null)
|
int rv = chooser.showOpenDialog(null)
|
||||||
if (rv == JFileChooser.APPROVE_OPTION) {
|
if (rv == JFileChooser.APPROVE_OPTION) {
|
||||||
File f = chooser.getSelectedFile()
|
chooser.getSelectedFiles().each { f ->
|
||||||
model.watched << f.getAbsolutePath()
|
model.watched << f.getAbsolutePath()
|
||||||
application.context.get("muwire-settings").watchedDirectories << f.getAbsolutePath()
|
application.context.get("muwire-settings").watchedDirectories << f.getAbsolutePath()
|
||||||
mvcGroup.controller.saveMuWireSettings()
|
mvcGroup.controller.saveMuWireSettings()
|
||||||
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
|
||||||
model.core.eventBus.publish(new FileSharedEvent(file : f))
|
model.core.eventBus.publish(new FileSharedEvent(file : f))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -55,6 +55,7 @@ class OptionsView {
|
|||||||
def outBwField
|
def outBwField
|
||||||
|
|
||||||
def allowUntrustedCheckbox
|
def allowUntrustedCheckbox
|
||||||
|
def searchExtraHopCheckbox
|
||||||
def allowTrustListsCheckbox
|
def allowTrustListsCheckbox
|
||||||
def trustListIntervalField
|
def trustListIntervalField
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ class OptionsView {
|
|||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
label(text : "Retry failed downloads every", constraints : gbc(gridx: 0, gridy: 0))
|
label(text : "Retry failed downloads every", constraints : gbc(gridx: 0, gridy: 0))
|
||||||
retryField = textField(text : bind { model.downloadRetryInterval }, columns : 2, constraints : gbc(gridx: 1, gridy: 0))
|
retryField = textField(text : bind { model.downloadRetryInterval }, columns : 2, constraints : gbc(gridx: 1, gridy: 0))
|
||||||
label(text : "minutes", constraints : gbc(gridx : 2, gridy: 0))
|
label(text : "seconds", constraints : gbc(gridx : 2, gridy: 0))
|
||||||
|
|
||||||
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))
|
||||||
@@ -138,11 +139,13 @@ class OptionsView {
|
|||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 0))
|
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 0))
|
||||||
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 0))
|
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 0))
|
||||||
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 1))
|
label(text : "Search extra hop", constraints : gbc(gridx:0, gridy:1))
|
||||||
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 1))
|
searchExtraHopCheckbox = checkBox(selected : bind {model.searchExtraHop}, constraints : gbc(gridx: 1, gridy : 1))
|
||||||
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:2))
|
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 2))
|
||||||
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:2))
|
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 2))
|
||||||
label(text : "hours", constraints : gbc(gridx: 2, gridy:2))
|
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:3))
|
||||||
|
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:3))
|
||||||
|
label(text : "hours", constraints : gbc(gridx: 2, gridy:3))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -22,6 +22,8 @@ import com.muwire.core.util.DataUtil
|
|||||||
|
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
|
import java.awt.FlowLayout
|
||||||
|
import java.awt.GridBagConstraints
|
||||||
import java.awt.Toolkit
|
import java.awt.Toolkit
|
||||||
import java.awt.datatransfer.StringSelection
|
import java.awt.datatransfer.StringSelection
|
||||||
import java.awt.event.MouseAdapter
|
import java.awt.event.MouseAdapter
|
||||||
@@ -43,11 +45,13 @@ class SearchTabView {
|
|||||||
def lastSendersSortEvent
|
def lastSendersSortEvent
|
||||||
def resultsTable
|
def resultsTable
|
||||||
def lastSortEvent
|
def lastSortEvent
|
||||||
|
def sequentialDownloadCheckbox
|
||||||
|
|
||||||
void initUI() {
|
void initUI() {
|
||||||
builder.with {
|
builder.with {
|
||||||
def resultsTable
|
def resultsTable
|
||||||
def sendersTable
|
def sendersTable
|
||||||
|
def sequentialDownloadCheckbox
|
||||||
def pane = panel {
|
def pane = panel {
|
||||||
gridLayout(rows :1, cols : 1)
|
gridLayout(rows :1, cols : 1)
|
||||||
splitPane(orientation: JSplitPane.VERTICAL_SPLIT, continuousLayout : true, dividerLocation: 300 ) {
|
splitPane(orientation: JSplitPane.VERTICAL_SPLIT, continuousLayout : true, dividerLocation: 300 ) {
|
||||||
@@ -66,6 +70,7 @@ class SearchTabView {
|
|||||||
}
|
}
|
||||||
panel(constraints : BorderLayout.SOUTH) {
|
panel(constraints : BorderLayout.SOUTH) {
|
||||||
button(text : "Trust", enabled: bind {model.trustButtonsEnabled }, trustAction)
|
button(text : "Trust", enabled: bind {model.trustButtonsEnabled }, trustAction)
|
||||||
|
button(text : "Neutral", enabled: bind {model.trustButtonsEnabled}, neutralAction)
|
||||||
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +87,17 @@ class SearchTabView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel(constraints : BorderLayout.SOUTH) {
|
panel(constraints : BorderLayout.SOUTH) {
|
||||||
button(text : "Download", enabled : bind {model.downloadActionEnabled}, downloadAction)
|
gridLayout(rows: 1, cols: 3)
|
||||||
|
panel()
|
||||||
|
panel {
|
||||||
|
button(text : "Download", enabled : bind {model.downloadActionEnabled}, downloadAction)
|
||||||
|
}
|
||||||
|
panel {
|
||||||
|
gridBagLayout()
|
||||||
|
panel (constraints : gbc(gridx : 0, gridy : 0, weightx : 100))
|
||||||
|
sequentialDownloadCheckbox = checkBox(constraints : gbc(gridx : 1, gridy: 0, weightx : 0),selected : false, enabled : bind {model.downloadActionEnabled})
|
||||||
|
label(constraints: gbc(gridx: 2, gridy: 0, weightx : 0),text : "Download sequentially")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,18 +109,26 @@ class SearchTabView {
|
|||||||
|
|
||||||
this.resultsTable = resultsTable
|
this.resultsTable = resultsTable
|
||||||
this.sendersTable = sendersTable
|
this.sendersTable = sendersTable
|
||||||
|
this.sequentialDownloadCheckbox = sequentialDownloadCheckbox
|
||||||
|
|
||||||
def selectionModel = resultsTable.getSelectionModel()
|
def selectionModel = resultsTable.getSelectionModel()
|
||||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
|
||||||
selectionModel.addListSelectionListener( {
|
selectionModel.addListSelectionListener( {
|
||||||
int row = resultsTable.getSelectedRow()
|
int[] rows = resultsTable.getSelectedRows()
|
||||||
if (row < 0) {
|
if (rows.length == 0) {
|
||||||
model.downloadActionEnabled = false
|
model.downloadActionEnabled = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (lastSortEvent != null)
|
if (lastSortEvent != null) {
|
||||||
row = resultsTable.rowSorter.convertRowIndexToModel(row)
|
for (int i = 0; i < rows.length; i ++) {
|
||||||
model.downloadActionEnabled = mvcGroup.parentGroup.model.canDownload(model.results[row].infohash)
|
rows[i] = resultsTable.rowSorter.convertRowIndexToModel(rows[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean downloadActionEnabled = true
|
||||||
|
rows.each {
|
||||||
|
downloadActionEnabled &= mvcGroup.parentGroup.model.canDownload(model.results[it].infohash)
|
||||||
|
}
|
||||||
|
model.downloadActionEnabled = downloadActionEnabled
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,21 +213,28 @@ class SearchTabView {
|
|||||||
|
|
||||||
def showPopupMenu(MouseEvent e) {
|
def showPopupMenu(MouseEvent e) {
|
||||||
JPopupMenu menu = new JPopupMenu()
|
JPopupMenu menu = new JPopupMenu()
|
||||||
|
boolean showMenu = false
|
||||||
if (model.downloadActionEnabled) {
|
if (model.downloadActionEnabled) {
|
||||||
JMenuItem download = new JMenuItem("Download")
|
JMenuItem download = new JMenuItem("Download")
|
||||||
download.addActionListener({mvcGroup.controller.download()})
|
download.addActionListener({mvcGroup.controller.download()})
|
||||||
menu.add(download)
|
menu.add(download)
|
||||||
|
showMenu = true
|
||||||
}
|
}
|
||||||
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
|
if (resultsTable.getSelectedRows().length == 1) {
|
||||||
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()})
|
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
|
||||||
menu.add(copyHashToClipboard)
|
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()})
|
||||||
menu.show(e.getComponent(), e.getX(), e.getY())
|
menu.add(copyHashToClipboard)
|
||||||
|
showMenu = true
|
||||||
|
}
|
||||||
|
if (showMenu)
|
||||||
|
menu.show(e.getComponent(), e.getX(), e.getY())
|
||||||
}
|
}
|
||||||
|
|
||||||
def copyHashToClipboard() {
|
def copyHashToClipboard() {
|
||||||
int selected = resultsTable.getSelectedRow()
|
int[] selectedRows = resultsTable.getSelectedRows()
|
||||||
if (selected < 0)
|
if (selectedRows.length != 1)
|
||||||
return
|
return
|
||||||
|
int selected = selectedRows[0]
|
||||||
if (lastSortEvent != null)
|
if (lastSortEvent != null)
|
||||||
selected = resultsTable.rowSorter.convertRowIndexToModel(selected)
|
selected = resultsTable.rowSorter.convertRowIndexToModel(selected)
|
||||||
String hash = Base64.encode(model.results[selected].infohash.getRoot())
|
String hash = Base64.encode(model.results[selected].infohash.getRoot())
|
||||||
|
@@ -1,21 +0,0 @@
|
|||||||
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(ContentPanelController)
|
|
||||||
class ContentPanelControllerTest {
|
|
||||||
private ContentPanelController controller
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void smokeTest() {
|
|
||||||
fail('Not yet implemented!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
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(EventListController)
|
|
||||||
class EventListControllerTest {
|
|
||||||
private EventListController controller
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void smokeTest() {
|
|
||||||
fail('Not yet implemented!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
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!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
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(MainFrameController)
|
|
||||||
class MainFrameControllerTest {
|
|
||||||
private MainFrameController controller
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void smokeTest() {
|
|
||||||
fail('Not yet implemented!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
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!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
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(OptionsController)
|
|
||||||
class OptionsControllerTest {
|
|
||||||
private OptionsController controller
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void smokeTest() {
|
|
||||||
fail('Not yet implemented!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
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(SearchTabController)
|
|
||||||
class SearchTabControllerTest {
|
|
||||||
private SearchTabController controller
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void smokeTest() {
|
|
||||||
fail('Not yet implemented!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
package com.muwire.gui
|
|
||||||
|
|
||||||
import griffon.core.test.GriffonUnitRule
|
|
||||||
import griffon.core.test.TestFor
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
import static org.junit.Assert.fail
|
|
||||||
|
|
||||||
@TestFor(TrustListController)
|
|
||||||
class TrustListControllerTest {
|
|
||||||
private TrustListController controller
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final GriffonUnitRule griffon = new GriffonUnitRule()
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void smokeTest() {
|
|
||||||
fail('Not yet implemented!')
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +1,8 @@
|
|||||||
apply plugin : 'application'
|
apply plugin : 'application'
|
||||||
mainClassName = 'com.muwire.hostcache.HostCache'
|
mainClassName = 'com.muwire.hostcache.HostCache'
|
||||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2'
|
||||||
|
testCompile 'junit:junit:4.12'
|
||||||
|
}
|
||||||
|
@@ -9,6 +9,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
|
||||||
|
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
@@ -28,7 +29,7 @@ class UpdateServer {
|
|||||||
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()
|
||||||
log.info "No key.dat file was found, so creating a new destination."
|
log.info "No key.dat file was found, so creating a new destination."
|
||||||
log.info "This is the destination you want to give out for your new UpdateServer"
|
log.info "This is the destination you want to give out for your new UpdateServer"
|
||||||
|
Reference in New Issue
Block a user