Compare commits
39 Commits
trust-list
...
partial-pi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9dc636bce | ||
|
|
3cc0574d11 | ||
|
|
20fab9b16d | ||
|
|
4015818323 | ||
|
|
f569d45c8c | ||
|
|
3773647869 | ||
|
|
29cdbf018c | ||
|
|
94bb7022eb | ||
|
|
39808302df | ||
|
|
2d22f9c39e | ||
|
|
ee8f80bab6 | ||
|
|
3e6242e583 | ||
|
|
41181616ee | ||
|
|
eb2530ca32 | ||
|
|
b5233780ef | ||
|
|
78753d7538 | ||
|
|
4740e8b4f5 | ||
|
|
ad5b00fc90 | ||
|
|
d6c6880848 | ||
|
|
4f948c1b9e | ||
|
|
2b68c24f9c | ||
|
|
bcdf0422db | ||
|
|
f6434b478d | ||
|
|
e979fdd26f | ||
|
|
e6bfcaaab9 | ||
|
|
9780108e8a | ||
|
|
697c7d2d6d | ||
|
|
887d10c8bf | ||
|
|
ef6b8fe458 | ||
|
|
20ab55d763 | ||
|
|
eda58c9e0d | ||
|
|
1ccf6fbdfa | ||
|
|
5711979272 | ||
|
|
9a5e2b1fa3 | ||
|
|
a89b423dfc | ||
|
|
79e8438941 | ||
|
|
19c2c46491 | ||
|
|
78f1d54b69 | ||
|
|
9461649ed4 |
33
README.md
33
README.md
@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
|
|||||||
|
|
||||||
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
||||||
|
|
||||||
The current stable release - 0.4.0 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
The current stable release - 0.4.6 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
@@ -23,34 +23,9 @@ Some of the UI tests will fail because they haven't been written yet :-/
|
|||||||
|
|
||||||
### Running
|
### Running
|
||||||
|
|
||||||
You need to have an I2P router up and running on the same machine. After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt. If you use a custom I2CP host and port, create a file `$HOME/.MuWire/i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there.
|
After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt.
|
||||||
|
|
||||||
The first time you run MuWire it will ask you to select a nickname. This nickname will be displayed with search results, so that others can verify the file was shared by you. It is best to leave MuWire running all the time, just like I2P.
|
If you have an I2P router running on the same machine that is all you need to do. If you use a custom I2CP host and port, create a file `$HOME/.MuWire/i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there.
|
||||||
|
|
||||||
|
If you do not have an I2P router, pass the following switch to the Java process: `-DembeddedRouter=true`. This will launch MuWire's embedded router. Be aware that this causes startup to take a lot longer.
|
||||||
|
|
||||||
### Known bugs and limitations
|
|
||||||
|
|
||||||
* Many UI features you would expect are not there yet
|
|
||||||
|
|
||||||
### Quick FAQ
|
|
||||||
|
|
||||||
* why is MuWire slow ?
|
|
||||||
|
|
||||||
- too few sources you're downloading from
|
|
||||||
- you can increase the number of tunnels by using more tunnels via Options->I2P Inbound/Outbound Quantity
|
|
||||||
the default is 4 and you could raise up to as high as 16 ( Caution !!!!)
|
|
||||||
|
|
||||||
* my search is not returning (enough) results !
|
|
||||||
|
|
||||||
- search is keyword or hash based
|
|
||||||
- keywords and hash(es) are NOT regexed or wildcarded so they have to be complete
|
|
||||||
so searching for 'musi' will not return results with 'music' - you have to search for 'music'
|
|
||||||
- ALL keywords have to match
|
|
||||||
- only use space for keyword separation
|
|
||||||
- if you already have the file in question it is not displayed ( can be changed via Options )
|
|
||||||
|
|
||||||
* what's this right click -> 'Copy hash to clipboard' for ?
|
|
||||||
|
|
||||||
- if you have a specific file you wish to share or download you can use the hash as a unique identifier
|
|
||||||
to make sure you have exactly the right file.
|
|
||||||
- you can share this hash with others to ensure they are getting the right file
|
|
||||||
|
|||||||
4
TODO.md
4
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
|
||||||
|
|
||||||
##### Trust List Sharing
|
|
||||||
|
|
||||||
For helping users make better decisions whom to trust
|
|
||||||
|
|
||||||
##### Content Control Panel
|
##### Content Control Panel
|
||||||
|
|
||||||
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
|
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ subprojects {
|
|||||||
apply plugin: 'groovy'
|
apply plugin: 'groovy'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'net.i2p:i2p:0.9.40'
|
compile 'net.i2p:i2p:0.9.41'
|
||||||
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.4")
|
core = new Core(props, home, "0.4.7")
|
||||||
} 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.4")
|
core = new Core(props, home, "0.4.7")
|
||||||
} 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.40'
|
compile 'net.i2p:router:0.9.41'
|
||||||
compile 'net.i2p.client:mstreaming:0.9.40'
|
compile 'net.i2p.client:mstreaming:0.9.41'
|
||||||
compile 'net.i2p.client:streaming:0.9.40'
|
compile 'net.i2p.client:streaming:0.9.41'
|
||||||
|
|
||||||
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'
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.muwire.core.download.UIDownloadPausedEvent
|
|||||||
import com.muwire.core.download.UIDownloadResumedEvent
|
import com.muwire.core.download.UIDownloadResumedEvent
|
||||||
import com.muwire.core.files.FileDownloadedEvent
|
import com.muwire.core.files.FileDownloadedEvent
|
||||||
import com.muwire.core.files.FileHashedEvent
|
import com.muwire.core.files.FileHashedEvent
|
||||||
|
import com.muwire.core.files.FileHashingEvent
|
||||||
import com.muwire.core.files.FileHasher
|
import com.muwire.core.files.FileHasher
|
||||||
import com.muwire.core.files.FileLoadedEvent
|
import com.muwire.core.files.FileLoadedEvent
|
||||||
import com.muwire.core.files.FileManager
|
import com.muwire.core.files.FileManager
|
||||||
@@ -140,7 +141,7 @@ public class Core {
|
|||||||
routerProps.setProperty("i2np.udp.port", i2pOptions["i2np.udp.port"])
|
routerProps.setProperty("i2np.udp.port", i2pOptions["i2np.udp.port"])
|
||||||
routerProps.setProperty("i2np.udp.internalPort", i2pOptions["i2np.udp.port"])
|
routerProps.setProperty("i2np.udp.internalPort", i2pOptions["i2np.udp.port"])
|
||||||
router = new Router(routerProps)
|
router = new Router(routerProps)
|
||||||
I2PAppContext.getGlobalContext().metaClass = new RouterContextMetaClass()
|
router.getContext().setLogManager(new MuWireLogManager())
|
||||||
router.runRouter()
|
router.runRouter()
|
||||||
while(!router.isRunning())
|
while(!router.isRunning())
|
||||||
Thread.sleep(100)
|
Thread.sleep(100)
|
||||||
@@ -318,6 +319,8 @@ public class Core {
|
|||||||
connectionEstablisher.stop()
|
connectionEstablisher.stop()
|
||||||
log.info("shutting down directory watcher")
|
log.info("shutting down directory watcher")
|
||||||
directoryWatcher.stop()
|
directoryWatcher.stop()
|
||||||
|
log.info("shutting down cache client")
|
||||||
|
cacheClient.stop()
|
||||||
log.info("shutting down connection manager")
|
log.info("shutting down connection manager")
|
||||||
connectionManager.shutdown()
|
connectionManager.shutdown()
|
||||||
if (router != null) {
|
if (router != null) {
|
||||||
@@ -326,19 +329,6 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class RouterContextMetaClass extends DelegatingMetaClass {
|
|
||||||
private final Object logManager = new MuWireLogManager()
|
|
||||||
RouterContextMetaClass() {
|
|
||||||
super(RouterContext.class)
|
|
||||||
}
|
|
||||||
|
|
||||||
Object invokeMethod(Object object, String name, Object[] args) {
|
|
||||||
if (name == "logManager")
|
|
||||||
return logManager
|
|
||||||
super.invokeMethod(object, name, args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static main(args) {
|
static main(args) {
|
||||||
def home = System.getProperty("user.home") + File.separator + ".MuWire"
|
def home = System.getProperty("user.home") + File.separator + ".MuWire"
|
||||||
home = new File(home)
|
home = new File(home)
|
||||||
@@ -363,7 +353,7 @@ public class Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Core core = new Core(props, home, "0.4.4")
|
Core core = new Core(props, home, "0.4.7")
|
||||||
core.startServices()
|
core.startServices()
|
||||||
|
|
||||||
// ... at the end, sleep or execute script
|
// ... at the end, sleep or execute script
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class MuWireSettings {
|
|||||||
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
||||||
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
||||||
props.setProperty("autoDownloadUpdate", String.valueOf(autoDownloadUpdate))
|
props.setProperty("autoDownloadUpdate", String.valueOf(autoDownloadUpdate))
|
||||||
props.setProperty("updateType",updateType)
|
props.setProperty("updateType",String.valueOf(updateType))
|
||||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||||
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
||||||
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import static com.muwire.core.util.DataUtil.readTillRN
|
|||||||
import groovy.util.logging.Log
|
import groovy.util.logging.Log
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.MappedByteBuffer
|
||||||
import java.nio.channels.FileChannel
|
import java.nio.channels.FileChannel
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@@ -25,8 +26,6 @@ import java.util.logging.Level
|
|||||||
@Log
|
@Log
|
||||||
class DownloadSession {
|
class DownloadSession {
|
||||||
|
|
||||||
private static int SAMPLES = 10
|
|
||||||
|
|
||||||
private final EventBus eventBus
|
private final EventBus eventBus
|
||||||
private final String meB64
|
private final String meB64
|
||||||
private final Pieces pieces
|
private final Pieces pieces
|
||||||
@@ -38,10 +37,10 @@ class DownloadSession {
|
|||||||
private final Set<Integer> available
|
private final Set<Integer> available
|
||||||
private final MessageDigest digest
|
private final MessageDigest digest
|
||||||
|
|
||||||
private final LinkedList<Long> timestamps = new LinkedList<>()
|
private long lastSpeedRead = System.currentTimeMillis()
|
||||||
private final LinkedList<Integer> reads = new LinkedList<>()
|
private long dataSinceLastRead
|
||||||
|
|
||||||
private ByteBuffer mapped
|
private MappedByteBuffer mapped
|
||||||
|
|
||||||
DownloadSession(EventBus eventBus, String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file,
|
DownloadSession(EventBus eventBus, String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file,
|
||||||
int pieceSize, long fileLength, Set<Integer> available) {
|
int pieceSize, long fileLength, Set<Integer> available) {
|
||||||
@@ -71,21 +70,22 @@ class DownloadSession {
|
|||||||
OutputStream os = endpoint.getOutputStream()
|
OutputStream os = endpoint.getOutputStream()
|
||||||
InputStream is = endpoint.getInputStream()
|
InputStream is = endpoint.getInputStream()
|
||||||
|
|
||||||
int piece
|
int[] pieceAndPosition
|
||||||
if (available.isEmpty())
|
if (available.isEmpty())
|
||||||
piece = pieces.claim()
|
pieceAndPosition = pieces.claim()
|
||||||
else
|
else
|
||||||
piece = pieces.claim(new HashSet<>(available))
|
pieceAndPosition = pieces.claim(new HashSet<>(available))
|
||||||
if (piece == -1)
|
if (pieceAndPosition == null)
|
||||||
return false
|
return false
|
||||||
|
int piece = pieceAndPosition[0]
|
||||||
|
int position = pieceAndPosition[1]
|
||||||
boolean unclaim = true
|
boolean unclaim = true
|
||||||
|
|
||||||
log.info("will download piece $piece")
|
log.info("will download piece $piece from position $position")
|
||||||
|
|
||||||
long start = piece * pieceSize
|
|
||||||
long end = Math.min(fileLength, start + pieceSize) - 1
|
|
||||||
long length = end - start + 1
|
|
||||||
|
|
||||||
|
long pieceStart = piece * pieceSize
|
||||||
|
long end = Math.min(fileLength, pieceStart + pieceSize) - 1
|
||||||
|
long start = pieceStart + position
|
||||||
String root = Base64.encode(infoHash.getRoot())
|
String root = Base64.encode(infoHash.getRoot())
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -174,8 +174,9 @@ class DownloadSession {
|
|||||||
FileChannel channel
|
FileChannel channel
|
||||||
try {
|
try {
|
||||||
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE,
|
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE,
|
||||||
StandardOpenOption.SPARSE, StandardOpenOption.CREATE)) // TODO: double-check, maybe CREATE_NEW
|
StandardOpenOption.SPARSE, StandardOpenOption.CREATE))
|
||||||
mapped = channel.map(FileChannel.MapMode.READ_WRITE, start, end - start + 1)
|
mapped = channel.map(FileChannel.MapMode.READ_WRITE, pieceStart, end - pieceStart + 1)
|
||||||
|
mapped.position(position)
|
||||||
|
|
||||||
byte[] tmp = new byte[0x1 << 13]
|
byte[] tmp = new byte[0x1 << 13]
|
||||||
while(mapped.hasRemaining()) {
|
while(mapped.hasRemaining()) {
|
||||||
@@ -186,26 +187,23 @@ class DownloadSession {
|
|||||||
throw new IOException()
|
throw new IOException()
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
mapped.put(tmp, 0, read)
|
mapped.put(tmp, 0, read)
|
||||||
|
dataSinceLastRead += read
|
||||||
if (timestamps.size() == SAMPLES) {
|
pieces.markPartial(piece, mapped.position())
|
||||||
timestamps.removeFirst()
|
|
||||||
reads.removeFirst()
|
|
||||||
}
|
|
||||||
timestamps.addLast(System.currentTimeMillis())
|
|
||||||
reads.addLast(read)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped.clear()
|
mapped.clear()
|
||||||
digest.update(mapped)
|
digest.update(mapped)
|
||||||
DataUtil.tryUnmap(mapped)
|
|
||||||
byte [] hash = digest.digest()
|
byte [] hash = digest.digest()
|
||||||
byte [] expected = new byte[32]
|
byte [] expected = new byte[32]
|
||||||
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
|
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
|
||||||
if (hash != expected)
|
if (hash != expected) {
|
||||||
throw new BadHashException()
|
pieces.markPartial(piece, 0)
|
||||||
|
throw new BadHashException("bad hash on piece $piece")
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try { channel?.close() } catch (IOException ignore) {}
|
try { channel?.close() } catch (IOException ignore) {}
|
||||||
|
DataUtil.tryUnmap(mapped)
|
||||||
}
|
}
|
||||||
pieces.markDownloaded(piece)
|
pieces.markDownloaded(piece)
|
||||||
unclaim = false
|
unclaim = false
|
||||||
@@ -223,24 +221,11 @@ class DownloadSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized int speed() {
|
synchronized int speed() {
|
||||||
if (timestamps.size() < SAMPLES)
|
|
||||||
return 0
|
|
||||||
int totalRead = 0
|
|
||||||
int idx = 0
|
|
||||||
final long now = System.currentTimeMillis()
|
final long now = System.currentTimeMillis()
|
||||||
|
long interval = Math.max(1000, now - lastSpeedRead)
|
||||||
while(idx < SAMPLES && timestamps.get(idx) < now - 1000)
|
lastSpeedRead = now;
|
||||||
idx++
|
int rv = (int) (dataSinceLastRead * 1000.0 / interval)
|
||||||
if (idx == SAMPLES)
|
dataSinceLastRead = 0
|
||||||
return 0
|
rv
|
||||||
if (idx == SAMPLES - 1)
|
|
||||||
return reads[idx]
|
|
||||||
|
|
||||||
long interval = timestamps.last - timestamps[idx]
|
|
||||||
if (interval == 0)
|
|
||||||
interval = 1
|
|
||||||
for (int i = idx; i < SAMPLES; i++)
|
|
||||||
totalRead += reads[idx]
|
|
||||||
(int)(totalRead * 1000.0 / interval)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.muwire.core.connection.Endpoint
|
|||||||
import java.nio.file.AtomicMoveNotSupportedException
|
import java.nio.file.AtomicMoveNotSupportedException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
import java.time.Instant
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@@ -58,6 +59,11 @@ public class Downloader {
|
|||||||
private final AtomicBoolean eventFired = new AtomicBoolean()
|
private final AtomicBoolean eventFired = new AtomicBoolean()
|
||||||
private boolean piecesFileClosed
|
private boolean piecesFileClosed
|
||||||
|
|
||||||
|
private ArrayList speedArr = new ArrayList<Integer>()
|
||||||
|
private int speedPos = 0
|
||||||
|
private int speedAvg = 0
|
||||||
|
private long timestamp = Instant.now().toEpochMilli()
|
||||||
|
|
||||||
public Downloader(EventBus eventBus, DownloadManager downloadManager,
|
public Downloader(EventBus eventBus, DownloadManager downloadManager,
|
||||||
Persona me, File file, long length, InfoHash infoHash,
|
Persona me, File file, long length, InfoHash infoHash,
|
||||||
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
|
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
|
||||||
@@ -76,6 +82,10 @@ public class Downloader {
|
|||||||
this.pieceSize = 1 << pieceSizePow2
|
this.pieceSize = 1 << pieceSizePow2
|
||||||
this.pieces = pieces
|
this.pieces = pieces
|
||||||
this.nPieces = pieces.nPieces
|
this.nPieces = pieces.nPieces
|
||||||
|
|
||||||
|
// default size suitable for an average of 5 seconds / 5 elements / 5 interval units
|
||||||
|
// it's easily adjustable by resizing the size of speedArr
|
||||||
|
this.speedArr = [ 0, 0, 0, 0, 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized InfoHash getInfoHash() {
|
public synchronized InfoHash getInfoHash() {
|
||||||
@@ -101,8 +111,14 @@ public class Downloader {
|
|||||||
if (!piecesFile.exists())
|
if (!piecesFile.exists())
|
||||||
return
|
return
|
||||||
piecesFile.eachLine {
|
piecesFile.eachLine {
|
||||||
int piece = Integer.parseInt(it)
|
String [] split = it.split(",")
|
||||||
|
int piece = Integer.parseInt(split[0])
|
||||||
|
if (split.length == 1)
|
||||||
pieces.markDownloaded(piece)
|
pieces.markDownloaded(piece)
|
||||||
|
else {
|
||||||
|
int position = Integer.parseInt(split[1])
|
||||||
|
pieces.markPartial(piece, position)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,9 +127,7 @@ public class Downloader {
|
|||||||
if (piecesFileClosed)
|
if (piecesFileClosed)
|
||||||
return
|
return
|
||||||
piecesFile.withPrintWriter { writer ->
|
piecesFile.withPrintWriter { writer ->
|
||||||
pieces.getDownloaded().each { piece ->
|
pieces.write(writer)
|
||||||
writer.println(piece)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,14 +138,35 @@ public class Downloader {
|
|||||||
|
|
||||||
|
|
||||||
public int speed() {
|
public int speed() {
|
||||||
int total = 0
|
int currSpeed = 0
|
||||||
if (getCurrentState() == DownloadState.DOWNLOADING) {
|
if (getCurrentState() == DownloadState.DOWNLOADING) {
|
||||||
activeWorkers.values().each {
|
activeWorkers.values().each {
|
||||||
if (it.currentState == WorkerState.DOWNLOADING)
|
if (it.currentState == WorkerState.DOWNLOADING)
|
||||||
total += it.speed()
|
currSpeed += it.speed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
total
|
|
||||||
|
// normalize to speedArr.size
|
||||||
|
currSpeed /= speedArr.size()
|
||||||
|
|
||||||
|
// compute new speedAvg and update speedArr
|
||||||
|
if ( speedArr[speedPos] > speedAvg ) {
|
||||||
|
speedAvg = 0
|
||||||
|
} else {
|
||||||
|
speedAvg -= speedArr[speedPos]
|
||||||
|
}
|
||||||
|
speedAvg += currSpeed
|
||||||
|
speedArr[speedPos] = currSpeed
|
||||||
|
// this might be necessary due to rounding errors
|
||||||
|
if (speedAvg < 0)
|
||||||
|
speedAvg = 0
|
||||||
|
|
||||||
|
// rolling index over the speedArr
|
||||||
|
speedPos++
|
||||||
|
if (speedPos >= speedArr.size())
|
||||||
|
speedPos=0
|
||||||
|
|
||||||
|
speedAvg
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadState getCurrentState() {
|
public DownloadState getCurrentState() {
|
||||||
@@ -272,6 +307,7 @@ public class Downloader {
|
|||||||
} catch (Exception bad) {
|
} catch (Exception bad) {
|
||||||
log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad))
|
log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad))
|
||||||
} finally {
|
} finally {
|
||||||
|
writePieces()
|
||||||
currentState = WorkerState.FINISHED
|
currentState = WorkerState.FINISHED
|
||||||
if (pieces.isComplete() && eventFired.compareAndSet(false, true)) {
|
if (pieces.isComplete() && eventFired.compareAndSet(false, true)) {
|
||||||
synchronized(piecesFile) {
|
synchronized(piecesFile) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ class Pieces {
|
|||||||
private final int nPieces
|
private final int nPieces
|
||||||
private final float ratio
|
private final float ratio
|
||||||
private final Random random = new Random()
|
private final Random random = new Random()
|
||||||
|
private final Map<Integer,Integer> partials = new HashMap<>()
|
||||||
|
|
||||||
Pieces(int nPieces) {
|
Pieces(int nPieces) {
|
||||||
this(nPieces, 1.0f)
|
this(nPieces, 1.0f)
|
||||||
@@ -17,16 +18,16 @@ class Pieces {
|
|||||||
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)
|
||||||
return -1
|
return null
|
||||||
|
|
||||||
// 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
|
return [rv, partials.getOrDefault(rv, 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
@@ -34,11 +35,11 @@ class Pieces {
|
|||||||
if (claimed.get(start))
|
if (claimed.get(start))
|
||||||
continue
|
continue
|
||||||
claimed.set(start)
|
claimed.set(start)
|
||||||
return start
|
return [start, partials.getOrDefault(start,0)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized int claim(Set<Integer> available) {
|
synchronized int[] claim(Set<Integer> available) {
|
||||||
for (int i = claimed.nextSetBit(0); i >= 0; i = claimed.nextSetBit(i+1))
|
for (int i = claimed.nextSetBit(0); i >= 0; i = claimed.nextSetBit(i+1))
|
||||||
available.remove(i)
|
available.remove(i)
|
||||||
if (available.isEmpty())
|
if (available.isEmpty())
|
||||||
@@ -47,7 +48,7 @@ class Pieces {
|
|||||||
Collections.shuffle(toList)
|
Collections.shuffle(toList)
|
||||||
int rv = toList[0]
|
int rv = toList[0]
|
||||||
claimed.set(rv)
|
claimed.set(rv)
|
||||||
rv
|
[rv, partials.getOrDefault(rv, 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized def getDownloaded() {
|
synchronized def getDownloaded() {
|
||||||
@@ -61,6 +62,11 @@ class Pieces {
|
|||||||
synchronized void markDownloaded(int piece) {
|
synchronized void markDownloaded(int piece) {
|
||||||
done.set(piece)
|
done.set(piece)
|
||||||
claimed.set(piece)
|
claimed.set(piece)
|
||||||
|
partials.remove(piece)
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void markPartial(int piece, int position) {
|
||||||
|
partials.put(piece, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void unclaim(int piece) {
|
synchronized void unclaim(int piece) {
|
||||||
@@ -82,5 +88,15 @@ class Pieces {
|
|||||||
synchronized void clearAll() {
|
synchronized void clearAll() {
|
||||||
done.clear()
|
done.clear()
|
||||||
claimed.clear()
|
claimed.clear()
|
||||||
|
partials.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void write(PrintWriter writer) {
|
||||||
|
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1)) {
|
||||||
|
writer.println(i)
|
||||||
|
}
|
||||||
|
partials.each { piece, position ->
|
||||||
|
writer.println("$piece,$position")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.muwire.core.files
|
||||||
|
|
||||||
|
import com.muwire.core.Event
|
||||||
|
import com.muwire.core.SharedFile
|
||||||
|
|
||||||
|
class FileHashingEvent extends Event {
|
||||||
|
|
||||||
|
File hashingFile
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
super.toString() + " hashingFile " + hashingFile.getAbsolutePath()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ class HasherService {
|
|||||||
} else if (f.length() > FileHasher.MAX_SIZE) {
|
} else if (f.length() > FileHasher.MAX_SIZE) {
|
||||||
eventBus.publish new FileHashedEvent(error: "$f is too large to be shared ${f.length()}")
|
eventBus.publish new FileHashedEvent(error: "$f is too large to be shared ${f.length()}")
|
||||||
} else {
|
} else {
|
||||||
|
eventBus.publish new FileHashingEvent(hashingFile: f)
|
||||||
def hash = hasher.hashFile f
|
def hash = hasher.hashFile f
|
||||||
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length())))
|
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length())))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ class CacheServers {
|
|||||||
|
|
||||||
private static final int TO_GIVE = 3
|
private static final int TO_GIVE = 3
|
||||||
private static Set<Destination> CACHES = [
|
private static Set<Destination> CACHES = [
|
||||||
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA")
|
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA"),
|
||||||
|
new Destination("JC63wJNOqSJmymkj4~UJWywBTvDGikKMoYP0HX2Wz9c5l3otXSkwnxWAFL4cKr~Ygh3BNNi2t93vuLIiI1W8AsE42kR~PwRx~Y-WvIHXR6KUejRmOp-n8WidtjKg9k4aDy428uSOedqXDxys5mpoeQXwDsv1CoPTTwnmb1GWFy~oTGIsCguCl~aJWGnqiKarPO3GJQ~ev-NbvAQzUfC3HeP1e6pdI5CGGjExahTCID5UjpJw8GaDXWlGmYWWH303Xu4x-vAHQy1dJLsOBCn8dZravsn5BKJk~j0POUon45CCx-~NYtaPe0Itt9cMdD2ciC76Rep1D0X0sm1SjlSs8sZ52KmF3oaLZ6OzgI9QLMIyBUrfi41sK5I0qTuUVBAkvW1xr~L-20dYJ9TrbOaOb2-vDIfKaxVi6xQOuhgQDiSBhd3qv2m0xGu-BM9DQYfNA0FdMjnZmqjmji9RMavzQSsVFIbQGLbrLepiEFlb7TseCK5UtRp8TxnG7L4gbYevBQAEAAcAAA==")
|
||||||
]
|
]
|
||||||
|
|
||||||
static List<Destination> getCacheServers() {
|
static List<Destination> getCacheServers() {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import com.muwire.core.Persona
|
|||||||
import net.i2p.util.ConcurrentHashSet
|
import net.i2p.util.ConcurrentHashSet
|
||||||
|
|
||||||
class RemoteTrustList {
|
class RemoteTrustList {
|
||||||
public enum Status { NEW, UPDATING, UPDATED }
|
public enum Status { NEW, UPDATING, UPDATED, UPDATE_FAILED }
|
||||||
|
|
||||||
private final Persona persona
|
private final Persona persona
|
||||||
private final Set<Persona> good, bad
|
private final Set<Persona> good, bad
|
||||||
|
|||||||
@@ -94,13 +94,15 @@ class TrustSubscriber {
|
|||||||
public void run() {
|
public void run() {
|
||||||
trustList.status = RemoteTrustList.Status.UPDATING
|
trustList.status = RemoteTrustList.Status.UPDATING
|
||||||
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
|
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
|
||||||
check(trustList, System.currentTimeMillis())
|
if (check(trustList, System.currentTimeMillis()))
|
||||||
trustList.status = RemoteTrustList.Status.UPDATED
|
trustList.status = RemoteTrustList.Status.UPDATED
|
||||||
|
else
|
||||||
|
trustList.status = RemoteTrustList.Status.UPDATE_FAILED
|
||||||
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
|
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void check(RemoteTrustList trustList, long now) {
|
private boolean check(RemoteTrustList trustList, long now) {
|
||||||
log.info("fetching trust list from ${trustList.persona.getHumanReadableName()}")
|
log.info("fetching trust list from ${trustList.persona.getHumanReadableName()}")
|
||||||
Endpoint endpoint = null
|
Endpoint endpoint = null
|
||||||
try {
|
try {
|
||||||
@@ -118,7 +120,7 @@ class TrustSubscriber {
|
|||||||
|
|
||||||
if (code != 200) {
|
if (code != 200) {
|
||||||
log.info("couldn't fetch trust list, code $code")
|
log.info("couldn't fetch trust list, code $code")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// swallow any headers
|
// swallow any headers
|
||||||
@@ -147,8 +149,10 @@ class TrustSubscriber {
|
|||||||
trustList.bad.clear()
|
trustList.bad.clear()
|
||||||
trustList.bad.addAll(bad)
|
trustList.bad.addAll(bad)
|
||||||
|
|
||||||
|
return true
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.log(Level.WARNING,"exception fetching trust list from ${trustList.persona.getHumanReadableName()}",e)
|
log.log(Level.WARNING,"exception fetching trust list from ${trustList.persona.getHumanReadableName()}",e)
|
||||||
|
return false
|
||||||
} finally {
|
} finally {
|
||||||
endpoint?.close()
|
endpoint?.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,4 +119,8 @@ class ContentUploader extends Uploader {
|
|||||||
return mesh.pieces.nPieces;
|
return mesh.pieces.nPieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTotalSize() {
|
||||||
|
return file.length();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,5 +61,8 @@ class HashListUploader extends Uploader {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTotalSize() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,5 +35,7 @@ abstract class Uploader {
|
|||||||
|
|
||||||
abstract int getDonePieces();
|
abstract int getDonePieces();
|
||||||
|
|
||||||
abstract int getTotalPieces()
|
abstract int getTotalPieces();
|
||||||
|
|
||||||
|
abstract long getTotalSize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
group = com.muwire
|
group = com.muwire
|
||||||
version = 0.4.4
|
version = 0.4.7
|
||||||
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
|
||||||
|
|||||||
@@ -191,27 +191,38 @@ class MainFrameController {
|
|||||||
int row = view.getSelectedTrustTablesRow(tableName)
|
int row = view.getSelectedTrustTablesRow(tableName)
|
||||||
if (row < 0)
|
if (row < 0)
|
||||||
return
|
return
|
||||||
|
builder.getVariable(tableName).model.fireTableDataChanged()
|
||||||
core.eventBus.publish(new TrustEvent(persona : list[row], level : level))
|
core.eventBus.publish(new TrustEvent(persona : list[row], level : level))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markTrusted() {
|
void markTrusted() {
|
||||||
markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted)
|
markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted)
|
||||||
|
model.markTrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markNeutralFromDistrusted() {
|
void markNeutralFromDistrusted() {
|
||||||
markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted)
|
markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted)
|
||||||
|
model.markTrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markDistrusted() {
|
void markDistrusted() {
|
||||||
markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted)
|
markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted)
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
void markNeutralFromTrusted() {
|
void markNeutralFromTrusted() {
|
||||||
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
@@ -223,6 +234,9 @@ class MainFrameController {
|
|||||||
core.muOptions.trustSubscriptions.add(p)
|
core.muOptions.trustSubscriptions.add(p)
|
||||||
saveMuWireSettings()
|
saveMuWireSettings()
|
||||||
core.eventBus.publish(new TrustSubscriptionEvent(persona : p, subscribe : true))
|
core.eventBus.publish(new TrustSubscriptionEvent(persona : p, subscribe : true))
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class TrustListController {
|
|||||||
return
|
return
|
||||||
Persona p = model.trusted[selectedRow]
|
Persona p = model.trusted[selectedRow]
|
||||||
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
|
||||||
|
view.fireUpdate("trusted-table")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
@@ -36,6 +37,7 @@ class TrustListController {
|
|||||||
return
|
return
|
||||||
Persona p = model.distrusted[selectedRow]
|
Persona p = model.distrusted[selectedRow]
|
||||||
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
|
||||||
|
view.fireUpdate("distrusted-table")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
@@ -45,6 +47,7 @@ class TrustListController {
|
|||||||
return
|
return
|
||||||
Persona p = model.trusted[selectedRow]
|
Persona p = model.trusted[selectedRow]
|
||||||
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
|
||||||
|
view.fireUpdate("trusted-table")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
@@ -54,5 +57,6 @@ class TrustListController {
|
|||||||
return
|
return
|
||||||
Persona p = model.distrusted[selectedRow]
|
Persona p = model.distrusted[selectedRow]
|
||||||
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
|
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
|
||||||
|
view.fireUpdate("distrusted-table")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ class Ready extends AbstractLifecycleHandler {
|
|||||||
log.info("creating new properties")
|
log.info("creating new properties")
|
||||||
props = new MuWireSettings()
|
props = new MuWireSettings()
|
||||||
props.embeddedRouter = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
|
props.embeddedRouter = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
|
||||||
props.updateType = System.getProperty("updateType")
|
props.updateType = System.getProperty("updateType","jar")
|
||||||
def nickname
|
def nickname
|
||||||
while (true) {
|
while (true) {
|
||||||
nickname = JOptionPane.showInputDialog(null,
|
nickname = JOptionPane.showInputDialog(null,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.muwire.gui
|
package com.muwire.gui
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
import javax.annotation.Nonnull
|
import javax.annotation.Nonnull
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -20,6 +22,7 @@ import com.muwire.core.download.Downloader
|
|||||||
import com.muwire.core.files.AllFilesLoadedEvent
|
import com.muwire.core.files.AllFilesLoadedEvent
|
||||||
import com.muwire.core.files.FileDownloadedEvent
|
import com.muwire.core.files.FileDownloadedEvent
|
||||||
import com.muwire.core.files.FileHashedEvent
|
import com.muwire.core.files.FileHashedEvent
|
||||||
|
import com.muwire.core.files.FileHashingEvent
|
||||||
import com.muwire.core.files.FileLoadedEvent
|
import com.muwire.core.files.FileLoadedEvent
|
||||||
import com.muwire.core.files.FileSharedEvent
|
import com.muwire.core.files.FileSharedEvent
|
||||||
import com.muwire.core.files.FileUnsharedEvent
|
import com.muwire.core.files.FileUnsharedEvent
|
||||||
@@ -70,12 +73,19 @@ class MainFrameModel {
|
|||||||
|
|
||||||
@Observable int connections
|
@Observable int connections
|
||||||
@Observable String me
|
@Observable String me
|
||||||
|
@Observable int loadedFiles
|
||||||
|
@Observable File hashingFile
|
||||||
@Observable boolean downloadActionEnabled
|
@Observable boolean downloadActionEnabled
|
||||||
@Observable boolean trustButtonsEnabled
|
@Observable boolean trustButtonsEnabled
|
||||||
@Observable boolean cancelButtonEnabled
|
@Observable boolean cancelButtonEnabled
|
||||||
@Observable boolean retryButtonEnabled
|
@Observable boolean retryButtonEnabled
|
||||||
@Observable boolean pauseButtonEnabled
|
@Observable boolean pauseButtonEnabled
|
||||||
@Observable String resumeButtonText
|
@Observable String resumeButtonText
|
||||||
|
@Observable boolean subscribeButtonEnabled
|
||||||
|
@Observable boolean markNeutralFromTrustedButtonEnabled
|
||||||
|
@Observable boolean markDistrustedButtonEnabled
|
||||||
|
@Observable boolean markNeutralFromDistrustedButtonEnabled
|
||||||
|
@Observable boolean markTrustedButtonEnabled
|
||||||
@Observable boolean reviewButtonEnabled
|
@Observable boolean reviewButtonEnabled
|
||||||
@Observable boolean updateButtonEnabled
|
@Observable boolean updateButtonEnabled
|
||||||
@Observable boolean unsubscribeButtonEnabled
|
@Observable boolean unsubscribeButtonEnabled
|
||||||
@@ -140,6 +150,7 @@ class MainFrameModel {
|
|||||||
core.eventBus.register(ConnectionEvent.class, this)
|
core.eventBus.register(ConnectionEvent.class, this)
|
||||||
core.eventBus.register(DisconnectionEvent.class, this)
|
core.eventBus.register(DisconnectionEvent.class, this)
|
||||||
core.eventBus.register(FileHashedEvent.class, this)
|
core.eventBus.register(FileHashedEvent.class, this)
|
||||||
|
core.eventBus.register(FileHashingEvent.class, this)
|
||||||
core.eventBus.register(FileLoadedEvent.class, this)
|
core.eventBus.register(FileLoadedEvent.class, this)
|
||||||
core.eventBus.register(UploadEvent.class, this)
|
core.eventBus.register(UploadEvent.class, this)
|
||||||
core.eventBus.register(UploadFinishedEvent.class, this)
|
core.eventBus.register(UploadFinishedEvent.class, this)
|
||||||
@@ -256,7 +267,17 @@ class MainFrameModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onFileHashingEvent(FileHashingEvent e) {
|
||||||
|
runInsideUIAsync {
|
||||||
|
loadedFiles = shared.size()
|
||||||
|
hashingFile = e.hashingFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void onFileHashedEvent(FileHashedEvent e) {
|
void onFileHashedEvent(FileHashedEvent e) {
|
||||||
|
runInsideUIAsync {
|
||||||
|
hashingFile = null
|
||||||
|
}
|
||||||
if (e.error != null)
|
if (e.error != null)
|
||||||
return // TODO do something
|
return // TODO do something
|
||||||
if (infoHashes.contains(e.sharedFile.infoHash))
|
if (infoHashes.contains(e.sharedFile.infoHash))
|
||||||
@@ -264,6 +285,7 @@ class MainFrameModel {
|
|||||||
infoHashes.add(e.sharedFile.infoHash)
|
infoHashes.add(e.sharedFile.infoHash)
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
shared << e.sharedFile
|
shared << e.sharedFile
|
||||||
|
loadedFiles = shared.size()
|
||||||
JTable table = builder.getVariable("shared-files-table")
|
JTable table = builder.getVariable("shared-files-table")
|
||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
@@ -275,6 +297,7 @@ class MainFrameModel {
|
|||||||
infoHashes.add(e.loadedFile.infoHash)
|
infoHashes.add(e.loadedFile.infoHash)
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
shared << e.loadedFile
|
shared << e.loadedFile
|
||||||
|
loadedFiles = shared.size()
|
||||||
JTable table = builder.getVariable("shared-files-table")
|
JTable table = builder.getVariable("shared-files-table")
|
||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
@@ -286,6 +309,7 @@ class MainFrameModel {
|
|||||||
return
|
return
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
shared.remove(e.unsharedFile)
|
shared.remove(e.unsharedFile)
|
||||||
|
loadedFiles = shared.size()
|
||||||
JTable table = builder.getVariable("shared-files-table")
|
JTable table = builder.getVariable("shared-files-table")
|
||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
@@ -355,10 +379,32 @@ class MainFrameModel {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
runInsideUIAsync {
|
runInsideUIAsync {
|
||||||
searches.addFirst(new IncomingSearch(search : search, replyTo : e.replyTo, originator : e.originator))
|
JTable table = builder.getVariable("searches-table")
|
||||||
|
|
||||||
|
Boolean searchFound = false
|
||||||
|
Iterator searchIter = searches.iterator()
|
||||||
|
while ( searchIter.hasNext() ) {
|
||||||
|
IncomingSearch searchEle = searchIter.next()
|
||||||
|
if ( searchEle.search == search
|
||||||
|
&& searchEle.originator == e.originator
|
||||||
|
&& searchEle.uuid == e.searchEvent.getUuid() ) {
|
||||||
|
searchIter.remove()
|
||||||
|
table.model.fireTableDataChanged()
|
||||||
|
searchFound = true
|
||||||
|
searchEle.count++
|
||||||
|
searchEle.timestamp = Calendar.getInstance()
|
||||||
|
searches.addFirst(searchEle)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!searchFound) {
|
||||||
|
searches.addFirst(new IncomingSearch(search, e.replyTo, e.originator, e.searchEvent.getUuid()))
|
||||||
|
}
|
||||||
|
|
||||||
while(searches.size() > 200)
|
while(searches.size() > 200)
|
||||||
searches.removeLast()
|
searches.removeLast()
|
||||||
JTable table = builder.getVariable("searches-table")
|
|
||||||
table.model.fireTableDataChanged()
|
table.model.fireTableDataChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,6 +413,18 @@ class MainFrameModel {
|
|||||||
String search
|
String search
|
||||||
Destination replyTo
|
Destination replyTo
|
||||||
Persona originator
|
Persona originator
|
||||||
|
long count
|
||||||
|
UUID uuid
|
||||||
|
Calendar timestamp
|
||||||
|
|
||||||
|
IncomingSearch( String search, Destination replyTo, Persona originator, UUID uuid ) {
|
||||||
|
this.search = search
|
||||||
|
this.replyTo = replyTo
|
||||||
|
this.originator = originator
|
||||||
|
this.uuid = uuid
|
||||||
|
this.count = 1
|
||||||
|
this.timestamp = Calendar.getInstance()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onUpdateAvailableEvent(UpdateAvailableEvent e) {
|
void onUpdateAvailableEvent(UpdateAvailableEvent e) {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class I2PStatusView {
|
|||||||
label(text : "Participating Tunnels", constraints : gbc(gridx:0, gridy:4))
|
label(text : "Participating Tunnels", constraints : gbc(gridx:0, gridy:4))
|
||||||
label(text : bind {model.participatingTunnels}, constraints : gbc(gridx: 1, gridy:4))
|
label(text : bind {model.participatingTunnels}, constraints : gbc(gridx: 1, gridy:4))
|
||||||
label(text : "Participating Bandwidth", constraints : gbc(gridx:0, gridy:5))
|
label(text : "Participating Bandwidth", constraints : gbc(gridx:0, gridy:5))
|
||||||
label(text : bind {model.participatingBW}, constraints : gbc(gridx: 1, gridy:6))
|
label(text : bind {model.participatingBW}, constraints : gbc(gridx: 1, gridy:5))
|
||||||
label(text : "Active Peers", constraints : gbc(gridx:0, gridy:6))
|
label(text : "Active Peers", constraints : gbc(gridx:0, gridy:6))
|
||||||
label(text : bind {model.activePeers}, constraints : gbc(gridx: 1, gridy:6))
|
label(text : bind {model.activePeers}, constraints : gbc(gridx: 1, gridy:6))
|
||||||
label(text : "Receive Bps (15 seconds)", constraints : gbc(gridx:0, gridy:7))
|
label(text : "Receive Bps (15 seconds)", constraints : gbc(gridx:0, gridy:7))
|
||||||
|
|||||||
@@ -129,12 +129,19 @@ class MainFrameView {
|
|||||||
scrollPane (constraints : BorderLayout.CENTER) {
|
scrollPane (constraints : BorderLayout.CENTER) {
|
||||||
downloadsTable = table(id : "downloads-table", autoCreateRowSorter : true) {
|
downloadsTable = table(id : "downloads-table", autoCreateRowSorter : true) {
|
||||||
tableModel(list: model.downloads) {
|
tableModel(list: model.downloads) {
|
||||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.downloader.file.getName()})
|
closureColumn(header: "Name", preferredWidth: 300, type: String, read : {row -> row.downloader.file.getName()})
|
||||||
closureColumn(header: "Status", preferredWidth: 50, type: String, read : {row -> row.downloader.getCurrentState().toString()})
|
closureColumn(header: "Status", preferredWidth: 50, type: String, read : {row -> row.downloader.getCurrentState().toString()})
|
||||||
closureColumn(header: "Progress", preferredWidth: 20, type: String, read: { row ->
|
closureColumn(header: "Progress", preferredWidth: 70, type: String, read: { row ->
|
||||||
int pieces = row.downloader.nPieces
|
int pieces = row.downloader.nPieces
|
||||||
int done = row.downloader.donePieces()
|
int done = row.downloader.donePieces()
|
||||||
"$done/$pieces pieces".toString()
|
int percent = -1
|
||||||
|
if ( row.downloader.nPieces != 0 ) {
|
||||||
|
percent = (done * 100) / pieces
|
||||||
|
}
|
||||||
|
long size = row.downloader.pieceSize
|
||||||
|
size *= pieces
|
||||||
|
String totalSize = DataHelper.formatSize2Decimal(size, false) + "B"
|
||||||
|
String.format("%02d", percent) + "% of ${totalSize} ($done/$pieces pcs)".toString()
|
||||||
})
|
})
|
||||||
closureColumn(header: "Sources", preferredWidth : 10, type: Integer, read : {row -> row.downloader.activeWorkers()})
|
closureColumn(header: "Sources", preferredWidth : 10, type: Integer, read : {row -> row.downloader.activeWorkers()})
|
||||||
closureColumn(header: "Speed", preferredWidth: 50, type:String, read :{row ->
|
closureColumn(header: "Speed", preferredWidth: 50, type:String, read :{row ->
|
||||||
@@ -154,12 +161,20 @@ class MainFrameView {
|
|||||||
panel (constraints: "uploads window"){
|
panel (constraints: "uploads window"){
|
||||||
gridLayout(cols : 1, rows : 2)
|
gridLayout(cols : 1, rows : 2)
|
||||||
panel {
|
panel {
|
||||||
|
borderLayout()
|
||||||
|
panel (constraints : BorderLayout.NORTH) {
|
||||||
|
label(text : bind {
|
||||||
|
if (model.hashingFile == null) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
"hashing: " + model.hashingFile.getAbsolutePath() + " (" + DataHelper.formatSize2Decimal(model.hashingFile.length(), false).toString() + "B)"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
panel (border : etchedBorder(), constraints : BorderLayout.CENTER) {
|
||||||
gridLayout(cols : 2, rows : 1)
|
gridLayout(cols : 2, rows : 1)
|
||||||
panel {
|
panel {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
panel (constraints : BorderLayout.NORTH) {
|
|
||||||
button(text : "Add directories to watch", actionPerformed : watchDirectories)
|
|
||||||
}
|
|
||||||
scrollPane (constraints : BorderLayout.CENTER) {
|
scrollPane (constraints : BorderLayout.CENTER) {
|
||||||
table(id : "watched-directories-table", autoCreateRowSorter: true) {
|
table(id : "watched-directories-table", autoCreateRowSorter: true) {
|
||||||
tableModel(list : model.watched) {
|
tableModel(list : model.watched) {
|
||||||
@@ -170,9 +185,6 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
panel {
|
panel {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
panel (constraints : BorderLayout.NORTH) {
|
|
||||||
button(text : "Share files", actionPerformed : shareFiles)
|
|
||||||
}
|
|
||||||
scrollPane(constraints : BorderLayout.CENTER) {
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
table(id : "shared-files-table", autoCreateRowSorter: true) {
|
table(id : "shared-files-table", autoCreateRowSorter: true) {
|
||||||
tableModel(list : model.shared) {
|
tableModel(list : model.shared) {
|
||||||
@@ -183,7 +195,19 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
panel (constraints : BorderLayout.SOUTH) {
|
||||||
|
gridLayout(rows:1, cols:2)
|
||||||
panel {
|
panel {
|
||||||
|
button(text : "Add directories to watch", actionPerformed : watchDirectories)
|
||||||
|
button(text : "Share files", actionPerformed : shareFiles)
|
||||||
|
}
|
||||||
|
panel {
|
||||||
|
label("Shared:")
|
||||||
|
label(text : bind {model.loadedFiles.toString()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panel (border : etchedBorder()) {
|
||||||
borderLayout()
|
borderLayout()
|
||||||
panel (constraints : BorderLayout.NORTH){
|
panel (constraints : BorderLayout.NORTH){
|
||||||
label("Uploads")
|
label("Uploads")
|
||||||
@@ -200,7 +224,18 @@ class MainFrameView {
|
|||||||
row.getDownloader()
|
row.getDownloader()
|
||||||
})
|
})
|
||||||
closureColumn(header : "Remote Pieces", type : String, read : { row ->
|
closureColumn(header : "Remote Pieces", type : String, read : { row ->
|
||||||
"${row.getDonePieces()}/${row.getTotalPieces()}".toString()
|
int pieces = row.getTotalPieces()
|
||||||
|
int done = row.getDonePieces()
|
||||||
|
int percent = -1
|
||||||
|
if ( pieces != 0 ) {
|
||||||
|
percent = (done * 100) / pieces
|
||||||
|
}
|
||||||
|
long size = row.getTotalSize()
|
||||||
|
String totalSize = ""
|
||||||
|
if (size >= 0 ) {
|
||||||
|
totalSize = " of " + DataHelper.formatSize2Decimal(size, false) + "B"
|
||||||
|
}
|
||||||
|
String.format("%02d", percent) + "% ${totalSize} ($done/$pieces pcs)".toString()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,6 +282,14 @@ class MainFrameView {
|
|||||||
return it.replyTo.toBase32()
|
return it.replyTo.toBase32()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
closureColumn(header : "Count", type : String, read : {
|
||||||
|
it.count.toString()
|
||||||
|
})
|
||||||
|
closureColumn(header : "Timestamp", type : String, read : {
|
||||||
|
String.format("%02d", it.timestamp.get(Calendar.HOUR_OF_DAY)) + ":" +
|
||||||
|
String.format("%02d", it.timestamp.get(Calendar.MINUTE)) + ":" +
|
||||||
|
String.format("%02d", it.timestamp.get(Calendar.SECOND))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,11 +308,11 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel (constraints : BorderLayout.EAST) {
|
panel (constraints : BorderLayout.SOUTH) {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
button(text : "Mark Neutral", constraints : gbc(gridx: 0, gridy: 0), markNeutralFromTrustedAction)
|
button(text : "Subscribe", enabled : bind {model.subscribeButtonEnabled}, constraints : gbc(gridx: 0, gridy : 0), subscribeAction)
|
||||||
button(text : "Mark Distrusted", constraints : gbc(gridx: 0, gridy:1), markDistrustedAction)
|
button(text : "Mark Neutral", enabled : bind {model.markNeutralFromTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy: 0), markNeutralFromTrustedAction)
|
||||||
button(text : "Subscribe", constraints : gbc(gridx: 0, gridy : 2), subscribeAction)
|
button(text : "Mark Distrusted", enabled : bind {model.markDistrustedButtonEnabled}, constraints : gbc(gridx: 2, gridy:0), markDistrustedAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel (border : etchedBorder()){
|
panel (border : etchedBorder()){
|
||||||
@@ -281,10 +324,10 @@ class MainFrameView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panel(constraints : BorderLayout.WEST) {
|
panel(constraints : BorderLayout.SOUTH) {
|
||||||
gridBagLayout()
|
gridBagLayout()
|
||||||
button(text: "Mark Neutral", constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction)
|
button(text: "Mark Neutral", enabled : bind {model.markNeutralFromDistrustedButtonEnabled}, constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction)
|
||||||
button(text: "Mark Trusted", constraints : gbc(gridx: 0, gridy : 1), markTrustedAction)
|
button(text: "Mark Trusted", enabled : bind {model.markTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy : 0), markTrustedAction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,11 +339,11 @@ class MainFrameView {
|
|||||||
scrollPane(constraints : BorderLayout.CENTER) {
|
scrollPane(constraints : BorderLayout.CENTER) {
|
||||||
table(id : "subscription-table", autoCreateRowSorter : true) {
|
table(id : "subscription-table", autoCreateRowSorter : true) {
|
||||||
tableModel(list : model.subscriptions) {
|
tableModel(list : model.subscriptions) {
|
||||||
closureColumn(header : "Name", type: String, read : {it.persona.getHumanReadableName()})
|
closureColumn(header : "Name", preferredWidth: 200, type: String, read : {it.persona.getHumanReadableName()})
|
||||||
closureColumn(header : "Trusted", type: Integer, read : {it.good.size()})
|
closureColumn(header : "Trusted", preferredWidth : 20, type: Integer, read : {it.good.size()})
|
||||||
closureColumn(header : "Distrusted", type: Integer, read : {it.bad.size()})
|
closureColumn(header : "Distrusted", preferredWidth: 20, type: Integer, read : {it.bad.size()})
|
||||||
closureColumn(header : "Status", type: String, read : {it.status.toString()})
|
closureColumn(header : "Status", preferredWidth: 30, type: String, read : {it.status.toString()})
|
||||||
closureColumn(header : "Last Updated", type : String, read : {
|
closureColumn(header : "Last Updated", preferredWidth: 200, type : String, read : {
|
||||||
if (it.timestamp == 0)
|
if (it.timestamp == 0)
|
||||||
return "Never"
|
return "Never"
|
||||||
else
|
else
|
||||||
@@ -461,6 +504,7 @@ class MainFrameView {
|
|||||||
|
|
||||||
// subscription table
|
// subscription table
|
||||||
def subscriptionTable = builder.getVariable("subscription-table")
|
def subscriptionTable = builder.getVariable("subscription-table")
|
||||||
|
subscriptionTable.setDefaultRenderer(Integer.class, centerRenderer)
|
||||||
subscriptionTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["subscription-table"] = evt})
|
subscriptionTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["subscription-table"] = evt})
|
||||||
subscriptionTable.rowSorter.setSortsOnUpdates(true)
|
subscriptionTable.rowSorter.setSortsOnUpdates(true)
|
||||||
selectionModel = subscriptionTable.getSelectionModel()
|
selectionModel = subscriptionTable.getSelectionModel()
|
||||||
@@ -488,6 +532,11 @@ class MainFrameView {
|
|||||||
model.updateButtonEnabled = true
|
model.updateButtonEnabled = true
|
||||||
model.unsubscribeButtonEnabled = true
|
model.unsubscribeButtonEnabled = true
|
||||||
break
|
break
|
||||||
|
case RemoteTrustList.Status.UPDATE_FAILED:
|
||||||
|
model.reviewButtonEnabled = false
|
||||||
|
model.updateButtonEnabled = true
|
||||||
|
model.unsubscribeButtonEnabled = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -495,11 +544,37 @@ class MainFrameView {
|
|||||||
def trustedTable = builder.getVariable("trusted-table")
|
def trustedTable = builder.getVariable("trusted-table")
|
||||||
trustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["trusted-table"] = evt})
|
trustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["trusted-table"] = evt})
|
||||||
trustedTable.rowSorter.setSortsOnUpdates(true)
|
trustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
selectionModel = trustedTable.getSelectionModel()
|
||||||
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
selectionModel.addListSelectionListener({
|
||||||
|
int selectedRow = getSelectedTrustTablesRow("trusted-table")
|
||||||
|
if (selectedRow < 0) {
|
||||||
|
model.subscribeButtonEnabled = false
|
||||||
|
model.markDistrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = false
|
||||||
|
} else {
|
||||||
|
model.subscribeButtonEnabled = true
|
||||||
|
model.markDistrustedButtonEnabled = true
|
||||||
|
model.markNeutralFromTrustedButtonEnabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// distrusted table
|
// distrusted table
|
||||||
def distrustedTable = builder.getVariable("distrusted-table")
|
def distrustedTable = builder.getVariable("distrusted-table")
|
||||||
distrustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["distrusted-table"] = evt})
|
distrustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["distrusted-table"] = evt})
|
||||||
distrustedTable.rowSorter.setSortsOnUpdates(true)
|
distrustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
|
selectionModel = distrustedTable.getSelectionModel()
|
||||||
|
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
selectionModel.addListSelectionListener({
|
||||||
|
int selectedRow = getSelectedTrustTablesRow("distrusted-table")
|
||||||
|
if (selectedRow < 0) {
|
||||||
|
model.markTrustedButtonEnabled = false
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = false
|
||||||
|
} else {
|
||||||
|
model.markTrustedButtonEnabled = true
|
||||||
|
model.markNeutralFromDistrustedButtonEnabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void showPopupMenu(JPopupMenu menu, MouseEvent event) {
|
private static void showPopupMenu(JPopupMenu menu, MouseEvent event) {
|
||||||
|
|||||||
@@ -83,10 +83,12 @@ class TrustListView {
|
|||||||
|
|
||||||
def trustedTable = builder.getVariable("trusted-table")
|
def trustedTable = builder.getVariable("trusted-table")
|
||||||
trustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["trusted-table"] = evt})
|
trustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["trusted-table"] = evt})
|
||||||
|
trustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
trustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
trustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
|
||||||
def distrustedTable = builder.getVariable("distrusted-table")
|
def distrustedTable = builder.getVariable("distrusted-table")
|
||||||
distrustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["distrusted-table"] = evt})
|
distrustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["distrusted-table"] = evt})
|
||||||
|
distrustedTable.rowSorter.setSortsOnUpdates(true)
|
||||||
distrustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
distrustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||||
|
|
||||||
dialog.getContentPane().add(mainPanel)
|
dialog.getContentPane().add(mainPanel)
|
||||||
@@ -110,4 +112,9 @@ class TrustListView {
|
|||||||
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
|
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
|
||||||
selectedRow
|
selectedRow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fireUpdate(String tableName) {
|
||||||
|
def table = builder.getVariable(tableName)
|
||||||
|
table.model.fireTableDataChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ import net.i2p.client.I2PSession
|
|||||||
import net.i2p.client.I2PSessionMuxedListener
|
import net.i2p.client.I2PSessionMuxedListener
|
||||||
import net.i2p.client.datagram.I2PDatagramDissector
|
import net.i2p.client.datagram.I2PDatagramDissector
|
||||||
import net.i2p.client.datagram.I2PDatagramMaker
|
import net.i2p.client.datagram.I2PDatagramMaker
|
||||||
|
import net.i2p.crypto.SigType
|
||||||
import net.i2p.util.SystemVersion
|
import net.i2p.util.SystemVersion
|
||||||
import net.i2p.data.*
|
import net.i2p.data.*
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ public class HostCache {
|
|||||||
def session
|
def session
|
||||||
if (!keyfile.exists()) {
|
if (!keyfile.exists()) {
|
||||||
def os = new FileOutputStream(keyfile);
|
def os = new FileOutputStream(keyfile);
|
||||||
myDest = i2pClient.createDestination(os)
|
myDest = i2pClient.createDestination(os, SigType.EdDSA_SHA512_Ed25519)
|
||||||
os.close()
|
os.close()
|
||||||
println "No key.dat file was found, so creating a new destination."
|
println "No key.dat file was found, so creating a new destination."
|
||||||
println "This is the destination you want to give out for your new HostCache"
|
println "This is the destination you want to give out for your new HostCache"
|
||||||
@@ -63,6 +64,9 @@ public class HostCache {
|
|||||||
Timer timer = new Timer("timer", true)
|
Timer timer = new Timer("timer", true)
|
||||||
timer.schedule({hostPool.age()} as TimerTask, 1000,1000)
|
timer.schedule({hostPool.age()} as TimerTask, 1000,1000)
|
||||||
timer.schedule({crawler.startCrawl()} as TimerTask, 10000, 10000)
|
timer.schedule({crawler.startCrawl()} as TimerTask, 10000, 10000)
|
||||||
|
File verified = new File("verified.json")
|
||||||
|
File unverified = new File("unverified.json")
|
||||||
|
timer.schedule({hostPool.serialize(verified, unverified)} as TimerTask, 10000, 60 * 60 * 1000)
|
||||||
|
|
||||||
session.addMuxedSessionListener(new Listener(hostPool: hostPool, toReturn: 2, crawler: crawler),
|
session.addMuxedSessionListener(new Listener(hostPool: hostPool, toReturn: 2, crawler: crawler),
|
||||||
I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)
|
I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.muwire.hostcache
|
|||||||
|
|
||||||
import java.util.stream.Collectors
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
|
import groovy.json.JsonOutput
|
||||||
|
|
||||||
class HostPool {
|
class HostPool {
|
||||||
|
|
||||||
final def maxFailures
|
final def maxFailures
|
||||||
@@ -74,4 +76,25 @@ class HostPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized void serialize(File verifiedFile, File unverifiedFile) {
|
||||||
|
write(verifiedFile, verified.values())
|
||||||
|
write(unverifiedFile, unverified.values())
|
||||||
|
}
|
||||||
|
|
||||||
|
private void write(File target, Collection hosts) {
|
||||||
|
JsonOutput jsonOutput = new JsonOutput()
|
||||||
|
target.withPrintWriter { writer ->
|
||||||
|
hosts.each {
|
||||||
|
def json = [:]
|
||||||
|
json.destination = it.destination.toBase64()
|
||||||
|
json.verifyTime = it.verifyTime
|
||||||
|
json.leafSlots = it.leafSlots
|
||||||
|
json.peerSlots = it.peerSlots
|
||||||
|
json.verificationFailures = it.verificationFailures
|
||||||
|
def str = jsonOutput.toJson(json)
|
||||||
|
writer.println(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user