Compare commits
25 Commits
muwire-0.4
...
muwire-0.4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 |
@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
|
||||
|
||||
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
||||
|
||||
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.13 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||
|
||||
### Building
|
||||
|
||||
@@ -21,6 +21,8 @@ If you want to run the unit tests, type
|
||||
|
||||
Some of the UI tests will fail because they haven't been written yet :-/
|
||||
|
||||
If you want to build binary bundles for Windows and Mac that do not depend on Java or I2P, see the https://github.com/zlatinb/muwire-pkg project
|
||||
|
||||
### 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.
|
||||
@@ -29,8 +31,6 @@ If you have an I2P router running on the same machine that is all you need to do
|
||||
|
||||
[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
|
||||
|
||||
```
|
||||
|
5
TODO.md
5
TODO.md
@@ -12,10 +12,6 @@ This reduces query traffic by not sending last hop queries to peers that definit
|
||||
|
||||
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.
|
||||
|
||||
Basically any non-gui non-cli user interface
|
||||
@@ -29,3 +25,4 @@ To enable parsing of metadata from known file types and the user editing it or a
|
||||
* Wrapper of some kind for in-place upgrades
|
||||
* Download file sequentially
|
||||
* Multiple-selection download, Ctrl-A
|
||||
* Automatic adjustment of number of I2P tunnels
|
||||
|
@@ -2,7 +2,7 @@ subprojects {
|
||||
apply plugin: 'groovy'
|
||||
|
||||
dependencies {
|
||||
compile 'net.i2p:i2p:0.9.41'
|
||||
compile 'net.i2p:i2p:0.9.42'
|
||||
compile 'org.codehaus.groovy:groovy-all:2.4.15'
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@ class Cli {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.4.11")
|
||||
core = new Core(props, home, "0.4.14")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.4.11")
|
||||
core = new Core(props, home, "0.4.14")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -2,9 +2,9 @@ apply plugin : 'application'
|
||||
mainClassName = 'com.muwire.core.Core'
|
||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||
dependencies {
|
||||
compile 'net.i2p:router:0.9.41'
|
||||
compile 'net.i2p.client:mstreaming:0.9.41'
|
||||
compile 'net.i2p.client:streaming:0.9.41'
|
||||
compile 'net.i2p:router:0.9.42'
|
||||
compile 'net.i2p.client:mstreaming:0.9.42'
|
||||
compile 'net.i2p.client:streaming:0.9.42'
|
||||
|
||||
testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2'
|
||||
testCompile 'junit:junit:4.12'
|
||||
|
@@ -361,7 +361,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.4.11")
|
||||
Core core = new Core(props, home, "0.4.14")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -11,6 +11,7 @@ class MuWireSettings {
|
||||
|
||||
final boolean isLeaf
|
||||
boolean allowUntrusted
|
||||
boolean searchExtraHop
|
||||
boolean allowTrustLists
|
||||
int trustListInterval
|
||||
Set<Persona> trustSubscriptions
|
||||
@@ -24,7 +25,7 @@ class MuWireSettings {
|
||||
boolean shareDownloadedFiles
|
||||
Set<String> watchedDirectories
|
||||
float downloadSequentialRatio
|
||||
int hostClearInterval
|
||||
int hostClearInterval, hostHopelessInterval, hostRejectInterval
|
||||
int meshExpiration
|
||||
boolean embeddedRouter
|
||||
int inBw, outBw
|
||||
@@ -38,19 +39,22 @@ class MuWireSettings {
|
||||
MuWireSettings(Properties props) {
|
||||
isLeaf = Boolean.valueOf(props.get("leaf","false"))
|
||||
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
|
||||
searchExtraHop = Boolean.valueOf(props.getProperty("searchExtraHop","false"))
|
||||
allowTrustLists = Boolean.valueOf(props.getProperty("allowTrustLists","true"))
|
||||
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
|
||||
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
|
||||
nickname = props.getProperty("nickname","MuWireUser")
|
||||
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
||||
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"))
|
||||
autoDownloadUpdate = Boolean.parseBoolean(props.getProperty("autoDownloadUpdate","true"))
|
||||
updateType = props.getProperty("updateType","jar")
|
||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||
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"))
|
||||
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
|
||||
inBw = Integer.valueOf(props.getProperty("inBw","256"))
|
||||
@@ -74,6 +78,7 @@ class MuWireSettings {
|
||||
Properties props = new Properties()
|
||||
props.setProperty("leaf", isLeaf.toString())
|
||||
props.setProperty("allowUntrusted", allowUntrusted.toString())
|
||||
props.setProperty("searchExtraHop", String.valueOf(searchExtraHop))
|
||||
props.setProperty("allowTrustLists", String.valueOf(allowTrustLists))
|
||||
props.setProperty("trustListInterval", String.valueOf(trustListInterval))
|
||||
props.setProperty("crawlerResponse", crawlerResponse.toString())
|
||||
@@ -86,6 +91,8 @@ class MuWireSettings {
|
||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
||||
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("embeddedRouter", String.valueOf(embeddedRouter))
|
||||
props.setProperty("inBw", String.valueOf(inBw))
|
||||
|
@@ -31,7 +31,7 @@ class ConnectionEstablisher {
|
||||
final HostCache hostCache
|
||||
|
||||
final Timer timer
|
||||
final ExecutorService executor
|
||||
final ExecutorService executor, closer
|
||||
|
||||
final Set inProgress = new ConcurrentHashSet()
|
||||
|
||||
@@ -51,6 +51,8 @@ class ConnectionEstablisher {
|
||||
rv.setName("connector-${System.currentTimeMillis()}")
|
||||
rv
|
||||
} as ThreadFactory)
|
||||
|
||||
closer = Executors.newSingleThreadExecutor()
|
||||
}
|
||||
|
||||
void start() {
|
||||
@@ -60,6 +62,7 @@ class ConnectionEstablisher {
|
||||
void stop() {
|
||||
timer.cancel()
|
||||
executor.shutdownNow()
|
||||
closer.shutdown()
|
||||
}
|
||||
|
||||
private void connectIfNeeded() {
|
||||
@@ -120,8 +123,10 @@ class ConnectionEstablisher {
|
||||
}
|
||||
|
||||
private void fail(Endpoint endpoint) {
|
||||
endpoint.close()
|
||||
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
|
||||
closer.execute {
|
||||
endpoint.close()
|
||||
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
|
||||
} as Runnable
|
||||
}
|
||||
|
||||
private void readK(Endpoint e) {
|
||||
@@ -175,7 +180,7 @@ class ConnectionEstablisher {
|
||||
log.log(Level.WARNING,"Problem parsing post-rejection payload",ignore)
|
||||
} finally {
|
||||
// the end
|
||||
e.close()
|
||||
closer.execute({e.close()} as Runnable)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -74,7 +74,7 @@ public class DownloadManager {
|
||||
destinations.addAll(e.sources)
|
||||
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,
|
||||
infohash, pieceSize, connector, destinations,
|
||||
@@ -122,8 +122,12 @@ public class DownloadManager {
|
||||
byte [] root = Base64.decode(json.hashRoot)
|
||||
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,
|
||||
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 nPieces = (int)(length / pieceSize)
|
||||
if (length % pieceSize != 0)
|
||||
nPieces++
|
||||
Mesh mesh = meshManager.getOrCreate(infoHash, nPieces)
|
||||
Mesh mesh = meshManager.getOrCreate(infoHash, nPieces, sequential)
|
||||
mesh.pieces
|
||||
}
|
||||
|
||||
@@ -188,6 +192,9 @@ public class DownloadManager {
|
||||
json.hashRoot = Base64.encode(infoHash.getRoot())
|
||||
|
||||
json.paused = downloader.paused
|
||||
|
||||
json.sequential = downloader.pieces.ratio == 0f
|
||||
|
||||
writer.println(JsonOutput.toJson(json))
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ class Pieces {
|
||||
done = new BitSet(nPieces)
|
||||
claimed = new BitSet(nPieces)
|
||||
}
|
||||
|
||||
|
||||
synchronized int[] claim() {
|
||||
int claimedCardinality = claimed.cardinality()
|
||||
if (claimedCardinality == nPieces) {
|
||||
@@ -30,7 +30,7 @@ class Pieces {
|
||||
}
|
||||
|
||||
// if fuller than ratio just do sequential
|
||||
if ( (1.0f * claimedCardinality) / nPieces > ratio) {
|
||||
if ( (1.0f * claimedCardinality) / nPieces >= ratio) {
|
||||
int rv = claimed.nextClearBit(0)
|
||||
claimed.set(rv)
|
||||
return [rv, partials.getOrDefault(rv, 0), 0]
|
||||
@@ -59,7 +59,8 @@ class Pieces {
|
||||
return [rv, partials.getOrDefault(rv, 0), 1]
|
||||
}
|
||||
List<Integer> toList = availableCopy.toList()
|
||||
Collections.shuffle(toList)
|
||||
if (ratio > 0f)
|
||||
Collections.shuffle(toList)
|
||||
int rv = toList[0]
|
||||
claimed.set(rv)
|
||||
[rv, partials.getOrDefault(rv, 0), 0]
|
||||
|
@@ -10,4 +10,5 @@ class UIDownloadEvent extends Event {
|
||||
UIResultEvent[] result
|
||||
Set<Destination> sources
|
||||
File target
|
||||
boolean sequential
|
||||
}
|
||||
|
@@ -6,8 +6,12 @@ class CacheServers {
|
||||
|
||||
private static final int TO_GIVE = 3
|
||||
private static Set<Destination> CACHES = [
|
||||
// zlatinb
|
||||
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() {
|
||||
|
@@ -7,21 +7,35 @@ class Host {
|
||||
private static final int MAX_FAILURES = 3
|
||||
|
||||
final Destination destination
|
||||
private final int clearInterval
|
||||
private final int clearInterval, hopelessInterval, rejectionInterval
|
||||
int failures,successes
|
||||
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.clearInterval = clearInterval
|
||||
this.hopelessInterval = hopelessInterval
|
||||
this.rejectionInterval = rejectionInterval
|
||||
}
|
||||
|
||||
synchronized void onConnect() {
|
||||
|
||||
private void connectSuccessful() {
|
||||
failures = 0
|
||||
successes++
|
||||
lastAttempt = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
synchronized void onConnect() {
|
||||
connectSuccessful()
|
||||
lastSuccessfulAttempt = lastAttempt
|
||||
}
|
||||
|
||||
synchronized void onReject() {
|
||||
connectSuccessful()
|
||||
lastRejection = lastAttempt;
|
||||
}
|
||||
|
||||
synchronized void onFailure() {
|
||||
failures++
|
||||
successes = 0
|
||||
@@ -40,7 +54,17 @@ class Host {
|
||||
failures = 0
|
||||
}
|
||||
|
||||
synchronized void canTryAgain() {
|
||||
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
|
||||
synchronized boolean canTryAgain() {
|
||||
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()
|
||||
return
|
||||
}
|
||||
Host host = new Host(e.destination, settings.hostClearInterval)
|
||||
Host host = new Host(e.destination, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||
if (allowHost(host)) {
|
||||
hosts.put(e.destination, host)
|
||||
}
|
||||
@@ -64,15 +64,17 @@ class HostCache extends Service {
|
||||
Destination dest = e.endpoint.destination
|
||||
Host host = hosts.get(dest)
|
||||
if (host == null) {
|
||||
host = new Host(dest, settings.hostClearInterval)
|
||||
host = new Host(dest, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||
hosts.put(dest, host)
|
||||
}
|
||||
|
||||
switch(e.status) {
|
||||
case ConnectionAttemptStatus.SUCCESSFUL:
|
||||
case ConnectionAttemptStatus.REJECTED:
|
||||
host.onConnect()
|
||||
break
|
||||
case ConnectionAttemptStatus.REJECTED:
|
||||
host.onReject()
|
||||
break
|
||||
case ConnectionAttemptStatus.FAILED:
|
||||
host.onFailure()
|
||||
break
|
||||
@@ -82,6 +84,10 @@ class HostCache extends Service {
|
||||
List<Destination> getHosts(int n) {
|
||||
List<Destination> rv = new ArrayList<>(hosts.keySet())
|
||||
rv.retainAll {allowHost(hosts[it])}
|
||||
rv.removeAll {
|
||||
def h = hosts[it];
|
||||
(h.isFailed() && !h.canTryAgain()) || h.isRecentlyRejected()
|
||||
}
|
||||
if (rv.size() <= n)
|
||||
return rv
|
||||
Collections.shuffle(rv)
|
||||
@@ -106,12 +112,16 @@ class HostCache extends Service {
|
||||
storage.eachLine {
|
||||
def entry = slurper.parseText(it)
|
||||
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.successes = Integer.valueOf(String.valueOf(entry.successes))
|
||||
if (entry.lastAttempt != null)
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -120,8 +130,6 @@ class HostCache extends Service {
|
||||
}
|
||||
|
||||
private boolean allowHost(Host host) {
|
||||
if (host.isFailed() && !host.canTryAgain())
|
||||
return false
|
||||
if (host.destination == myself)
|
||||
return false
|
||||
TrustLevel trust = trustService.getLevel(host.destination)
|
||||
@@ -140,12 +148,14 @@ class HostCache extends Service {
|
||||
storage.delete()
|
||||
storage.withPrintWriter { writer ->
|
||||
hosts.each { dest, host ->
|
||||
if (allowHost(host)) {
|
||||
if (allowHost(host) && !host.isHopeless()) {
|
||||
def map = [:]
|
||||
map.destination = dest.toBase64()
|
||||
map.failures = host.failures
|
||||
map.successes = host.successes
|
||||
map.lastAttempt = host.lastAttempt
|
||||
map.lastSuccessfulAttempt = host.lastSuccessfulAttempt
|
||||
map.lastRejection = host.lastRejection
|
||||
def json = JsonOutput.toJson(map)
|
||||
writer.println json
|
||||
}
|
||||
|
@@ -33,11 +33,12 @@ class MeshManager {
|
||||
meshes.get(infoHash)
|
||||
}
|
||||
|
||||
Mesh getOrCreate(InfoHash infoHash, int nPieces) {
|
||||
Mesh getOrCreate(InfoHash infoHash, int nPieces, boolean sequential) {
|
||||
synchronized(meshes) {
|
||||
if (meshes.containsKey(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)) {
|
||||
for (int i = 0; i < nPieces; i++)
|
||||
pieces.markDownloaded(i)
|
||||
|
@@ -3,5 +3,5 @@ package com.muwire.core.update
|
||||
import net.i2p.data.Destination
|
||||
|
||||
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
|
||||
} else {
|
||||
SharedFile sharedFile = sharedFiles.iterator().next();
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces)
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces, false)
|
||||
file = sharedFile.file
|
||||
pieceSize = sharedFile.pieceSize
|
||||
}
|
||||
@@ -217,7 +217,7 @@ public class UploadManager {
|
||||
pieceSize = downloader.pieceSizePow2
|
||||
} else {
|
||||
SharedFile sharedFile = sharedFiles.iterator().next();
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces)
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces, false)
|
||||
file = sharedFile.file
|
||||
pieceSize = sharedFile.pieceSize
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.4.11
|
||||
version = 0.4.14
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
|
@@ -86,7 +86,8 @@ class MainFrameController {
|
||||
terms.each { if (it.length() > 0) nonEmpty << it }
|
||||
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,
|
||||
originator : core.me))
|
||||
}
|
||||
|
@@ -96,6 +96,10 @@ class OptionsController {
|
||||
model.onlyTrusted = onlyTrusted
|
||||
settings.setAllowUntrusted(!onlyTrusted)
|
||||
|
||||
boolean searchExtraHop = view.searchExtraHopCheckbox.model.isSelected()
|
||||
model.searchExtraHop = searchExtraHop
|
||||
settings.searchExtraHop = searchExtraHop
|
||||
|
||||
boolean trustLists = view.allowTrustListsCheckbox.model.isSelected()
|
||||
model.trustLists = trustLists
|
||||
settings.allowTrustLists = trustLists
|
||||
|
@@ -46,7 +46,8 @@ class SearchTabController {
|
||||
def resultsBucket = model.hashBucket[result.infohash]
|
||||
def sources = model.sourcesBucket[result.infohash]
|
||||
|
||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources, target : file))
|
||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources,
|
||||
target : file, sequential : view.sequentialDownloadCheckbox.model.isSelected()))
|
||||
mvcGroup.parentGroup.view.showDownloadsWindow.call()
|
||||
}
|
||||
|
||||
@@ -67,4 +68,13 @@ class SearchTabController {
|
||||
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())
|
||||
|
||||
System.getProperties().setProperty("awt.useSystemAAFontSettings", "true")
|
||||
System.getProperties().setProperty("awt.useSystemAAFontSettings", "gasp")
|
||||
|
||||
def guiPropsFile = new File(home, "gui.properties")
|
||||
UISettings uiSettings
|
||||
@@ -86,6 +86,8 @@ class Initialize extends AbstractLifecycleHandler {
|
||||
}
|
||||
} else {
|
||||
LookAndFeel chosen = lookAndFeel('system', 'gtk')
|
||||
if (chosen == null)
|
||||
chosen = lookAndFeel('metal')
|
||||
uiSettings.lnf = chosen.getID()
|
||||
log.info("ended up applying $chosen.name")
|
||||
}
|
||||
|
@@ -191,7 +191,7 @@ class MainFrameModel {
|
||||
return
|
||||
int retryInterval = core.muOptions.downloadRetryInterval
|
||||
if (retryInterval > 0) {
|
||||
retryInterval *= 60000
|
||||
retryInterval *= 1000
|
||||
long now = System.currentTimeMillis()
|
||||
if (now - lastRetryTime > retryInterval) {
|
||||
lastRetryTime = now
|
||||
@@ -207,7 +207,7 @@ class MainFrameModel {
|
||||
|
||||
}
|
||||
}
|
||||
}, 60000, 60000)
|
||||
}, 1000, 1000)
|
||||
|
||||
runInsideUIAsync {
|
||||
trusted.addAll(core.trustService.good.values())
|
||||
|
@@ -38,6 +38,7 @@ class OptionsModel {
|
||||
|
||||
// trust options
|
||||
@Observable boolean onlyTrusted
|
||||
@Observable boolean searchExtraHop
|
||||
@Observable boolean trustLists
|
||||
@Observable String trustListInterval
|
||||
|
||||
@@ -73,6 +74,7 @@ class OptionsModel {
|
||||
}
|
||||
|
||||
onlyTrusted = !settings.allowUntrusted()
|
||||
searchExtraHop = settings.searchExtraHop
|
||||
trustLists = settings.allowTrustLists
|
||||
trustListInterval = String.valueOf(settings.trustListInterval)
|
||||
}
|
||||
|
@@ -55,6 +55,7 @@ class OptionsView {
|
||||
def outBwField
|
||||
|
||||
def allowUntrustedCheckbox
|
||||
def searchExtraHopCheckbox
|
||||
def allowTrustListsCheckbox
|
||||
def trustListIntervalField
|
||||
|
||||
@@ -70,7 +71,7 @@ class OptionsView {
|
||||
gridBagLayout()
|
||||
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))
|
||||
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))
|
||||
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
|
||||
@@ -138,11 +139,13 @@ class OptionsView {
|
||||
gridBagLayout()
|
||||
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 0))
|
||||
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 0))
|
||||
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 1))
|
||||
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 1))
|
||||
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:2))
|
||||
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:2))
|
||||
label(text : "hours", constraints : gbc(gridx: 2, gridy:2))
|
||||
label(text : "Search extra hop", constraints : gbc(gridx:0, gridy:1))
|
||||
searchExtraHopCheckbox = checkBox(selected : bind {model.searchExtraHop}, constraints : gbc(gridx: 1, gridy : 1))
|
||||
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 2))
|
||||
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, 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.Color
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.Toolkit
|
||||
import java.awt.datatransfer.StringSelection
|
||||
import java.awt.event.MouseAdapter
|
||||
@@ -43,11 +45,13 @@ class SearchTabView {
|
||||
def lastSendersSortEvent
|
||||
def resultsTable
|
||||
def lastSortEvent
|
||||
def sequentialDownloadCheckbox
|
||||
|
||||
void initUI() {
|
||||
builder.with {
|
||||
def resultsTable
|
||||
def sendersTable
|
||||
def sequentialDownloadCheckbox
|
||||
def pane = panel {
|
||||
gridLayout(rows :1, cols : 1)
|
||||
splitPane(orientation: JSplitPane.VERTICAL_SPLIT, continuousLayout : true, dividerLocation: 300 ) {
|
||||
@@ -66,6 +70,7 @@ class SearchTabView {
|
||||
}
|
||||
panel(constraints : BorderLayout.SOUTH) {
|
||||
button(text : "Trust", enabled: bind {model.trustButtonsEnabled }, trustAction)
|
||||
button(text : "Neutral", enabled: bind {model.trustButtonsEnabled}, neutralAction)
|
||||
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
||||
}
|
||||
}
|
||||
@@ -82,7 +87,17 @@ class SearchTabView {
|
||||
}
|
||||
}
|
||||
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,6 +109,7 @@ class SearchTabView {
|
||||
|
||||
this.resultsTable = resultsTable
|
||||
this.sendersTable = sendersTable
|
||||
this.sequentialDownloadCheckbox = sequentialDownloadCheckbox
|
||||
|
||||
def selectionModel = resultsTable.getSelectionModel()
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
|
@@ -9,6 +9,7 @@ import net.i2p.client.I2PSession
|
||||
import net.i2p.client.I2PSessionMuxedListener
|
||||
import net.i2p.client.datagram.I2PDatagramDissector
|
||||
import net.i2p.client.datagram.I2PDatagramMaker
|
||||
import net.i2p.crypto.SigType
|
||||
|
||||
|
||||
@Log
|
||||
@@ -28,7 +29,7 @@ class UpdateServer {
|
||||
def session
|
||||
if (!keyFile.exists()) {
|
||||
def os = new FileOutputStream(keyFile);
|
||||
myDest = i2pClient.createDestination(os)
|
||||
myDest = i2pClient.createDestination(os, SigType.EdDSA_SHA512_Ed25519)
|
||||
os.close()
|
||||
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"
|
||||
|
Reference in New Issue
Block a user