Compare commits
35 Commits
muwire-0.0
...
muwire-0.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7eea8be67d | ||
![]() |
f114302bdb | ||
![]() |
05b9b37488 | ||
![]() |
52f317a5b7 | ||
![]() |
fb8227a1f3 | ||
![]() |
5677d9f46a | ||
![]() |
c5192e3845 | ||
![]() |
43c2a55cb8 | ||
![]() |
94f6de6bea | ||
![]() |
6782849a12 | ||
![]() |
c07d351c5d | ||
![]() |
dc2f675dd3 | ||
![]() |
a8e795ec51 | ||
![]() |
33c5b3b18e | ||
![]() |
581fce4643 | ||
![]() |
7fe78a0719 | ||
![]() |
cdb6e22522 | ||
![]() |
2edeb046be | ||
![]() |
4021f3c244 | ||
![]() |
9008fac24d | ||
![]() |
e2f92c5c5e | ||
![]() |
7b33a16fd8 | ||
![]() |
9a2531b264 | ||
![]() |
9a8dadff57 | ||
![]() |
4a274010f9 | ||
![]() |
1eb930435b | ||
![]() |
9df28552ad | ||
![]() |
ac0204dffc | ||
![]() |
e5c402a400 | ||
![]() |
7704c73b68 | ||
![]() |
a9aa8dd840 | ||
![]() |
de682a802a | ||
![]() |
5435518212 | ||
![]() |
bd01f983c9 | ||
![]() |
8b63864b90 |
@@ -32,7 +32,6 @@ At the moment there are very few nodes on the network, so you will see very few
|
||||
|
||||
### Known bugs and limitations
|
||||
|
||||
* Any shared files get re-hashed on startup
|
||||
* Sometimes the list of shared files gets lost
|
||||
* Many UI features you would expect are not there yet
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
apply plugin : 'application'
|
||||
|
||||
mainClassName = 'com.muwire.cli.Cli'
|
||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||
|
||||
dependencies {
|
||||
compile project(":core")
|
||||
|
@@ -1,7 +1,12 @@
|
||||
package com.muwire.cli
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.files.AllFilesLoadedEvent
|
||||
import com.muwire.core.files.FileHashedEvent
|
||||
import com.muwire.core.files.FileSharedEvent
|
||||
|
||||
class Cli {
|
||||
|
||||
@@ -23,20 +28,59 @@ class Cli {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.0.8")
|
||||
core = new Core(props, home, "0.0.11")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
|
||||
def latch = new CountDownLatch(1)
|
||||
def fileLoader = new Object() {
|
||||
public void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
|
||||
latch.countDown()
|
||||
}
|
||||
}
|
||||
core.eventBus.register(AllFilesLoadedEvent.class, fileLoader)
|
||||
core.startServices()
|
||||
|
||||
println "waiting for files to load"
|
||||
latch.await()
|
||||
|
||||
|
||||
// now we begin
|
||||
println "MuWire is ready"
|
||||
println "Enter a file containing list of files to share"
|
||||
def reader = new BufferedReader(new InputStreamReader(System.in))
|
||||
def filesList = reader.readLine()
|
||||
|
||||
def filesList
|
||||
if (args.length == 0) {
|
||||
println "Enter a file containing list of files to share"
|
||||
def reader = new BufferedReader(new InputStreamReader(System.in))
|
||||
filesList = reader.readLine()
|
||||
} else
|
||||
filesList = args[0]
|
||||
|
||||
Thread.sleep(1000)
|
||||
println "loading shared files from $filesList"
|
||||
|
||||
core.eventBus.register(FileHashedEvent.class, new Object() {
|
||||
void onFileHashedEvent(FileHashedEvent e) {
|
||||
if (e.error != null)
|
||||
println "ERROR $e.error"
|
||||
else
|
||||
println "Shared file : $e.sharedFile.file"
|
||||
}
|
||||
})
|
||||
|
||||
filesList = new File(filesList)
|
||||
filesList.withReader {
|
||||
def toShare = it.readLine()
|
||||
core.eventBus.publish(new FileSharedEvent(file : new File(toShare)))
|
||||
}
|
||||
Runtime.getRuntime().addShutdownHook({
|
||||
println "shutting down.."
|
||||
core.shutdown()
|
||||
println "shutdown."
|
||||
})
|
||||
Thread.sleep(Integer.MAX_VALUE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -56,6 +56,8 @@ public class Core {
|
||||
final EventBus eventBus
|
||||
final Persona me
|
||||
final File home
|
||||
final Properties i2pOptions
|
||||
final MuWireSettings muOptions
|
||||
|
||||
private final TrustService trustService
|
||||
private final PersisterService persisterService
|
||||
@@ -69,6 +71,7 @@ public class Core {
|
||||
|
||||
public Core(MuWireSettings props, File home, String myVersion) {
|
||||
this.home = home
|
||||
this.muOptions = props
|
||||
log.info "Initializing I2P context"
|
||||
I2PAppContext.getGlobalContext().logManager()
|
||||
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
|
||||
@@ -83,12 +86,23 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
def sysProps = System.getProperties().clone()
|
||||
sysProps["inbound.nickname"] = "MuWire"
|
||||
i2pOptions = new Properties()
|
||||
def i2pOptionsFile = new File(home,"i2p.properties")
|
||||
if (i2pOptionsFile.exists()) {
|
||||
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
|
||||
} else {
|
||||
i2pOptions["inbound.nickname"] = "MuWire"
|
||||
i2pOptions["inbound.length"] = "3"
|
||||
i2pOptions["inbound.quantity"] = "2"
|
||||
i2pOptions["outbound.length"] = "3"
|
||||
i2pOptions["outbound.quantity"] = "2"
|
||||
}
|
||||
|
||||
// options like tunnel length and quantity
|
||||
I2PSession i2pSession
|
||||
I2PSocketManager socketManager
|
||||
keyDat.withInputStream {
|
||||
socketManager = new I2PSocketManagerFactory().createManager(it, sysProps)
|
||||
socketManager = new I2PSocketManagerFactory().createManager(it, i2pOptions)
|
||||
}
|
||||
socketManager.getDefaultOptions().setReadTimeout(60000)
|
||||
socketManager.getDefaultOptions().setConnectTimeout(30000)
|
||||
@@ -129,7 +143,7 @@ public class Core {
|
||||
|
||||
|
||||
log.info "initializing file manager"
|
||||
FileManager fileManager = new FileManager(eventBus)
|
||||
FileManager fileManager = new FileManager(eventBus, props)
|
||||
eventBus.register(FileHashedEvent.class, fileManager)
|
||||
eventBus.register(FileLoadedEvent.class, fileManager)
|
||||
eventBus.register(FileDownloadedEvent.class, fileManager)
|
||||
@@ -147,7 +161,8 @@ public class Core {
|
||||
|
||||
log.info("initializing connection manager")
|
||||
connectionManager = props.isLeaf() ?
|
||||
new LeafConnectionManager(eventBus, me, 3, hostCache) : new UltrapeerConnectionManager(eventBus, me, 512, 512, hostCache, trustService)
|
||||
new LeafConnectionManager(eventBus, me, 3, hostCache, props) :
|
||||
new UltrapeerConnectionManager(eventBus, me, 512, 512, hostCache, trustService, props)
|
||||
eventBus.register(TrustEvent.class, connectionManager)
|
||||
eventBus.register(ConnectionEvent.class, connectionManager)
|
||||
eventBus.register(DisconnectionEvent.class, connectionManager)
|
||||
@@ -177,17 +192,17 @@ public class Core {
|
||||
log.info("initializing upload manager")
|
||||
UploadManager uploadManager = new UploadManager(eventBus, fileManager)
|
||||
|
||||
log.info("initializing connection establisher")
|
||||
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
|
||||
|
||||
log.info("initializing acceptor")
|
||||
I2PAcceptor i2pAcceptor = new I2PAcceptor(socketManager)
|
||||
connectionAcceptor = new ConnectionAcceptor(eventBus, connectionManager, props,
|
||||
i2pAcceptor, hostCache, trustService, searchManager, uploadManager)
|
||||
i2pAcceptor, hostCache, trustService, searchManager, uploadManager, connectionEstablisher)
|
||||
|
||||
|
||||
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
|
||||
|
||||
log.info("initializing hasher service")
|
||||
hasherService = new HasherService(new FileHasher(), eventBus)
|
||||
hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
|
||||
eventBus.register(FileSharedEvent.class, hasherService)
|
||||
}
|
||||
|
||||
@@ -233,7 +248,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.0.8")
|
||||
Core core = new Core(props, home, "0.0.11")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -12,6 +12,7 @@ class MuWireSettings {
|
||||
File downloadLocation
|
||||
String sharedFiles
|
||||
CrawlerResponse crawlerResponse
|
||||
boolean shareDownloadedFiles
|
||||
|
||||
MuWireSettings() {
|
||||
this(new Properties())
|
||||
@@ -27,6 +28,7 @@ class MuWireSettings {
|
||||
sharedFiles = props.getProperty("sharedFiles")
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15"))
|
||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
|
||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||
}
|
||||
|
||||
void write(OutputStream out) throws IOException {
|
||||
@@ -38,6 +40,7 @@ class MuWireSettings {
|
||||
props.setProperty("downloadLocation", downloadLocation.getAbsolutePath())
|
||||
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
||||
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||
if (sharedFiles != null)
|
||||
props.setProperty("sharedFiles", sharedFiles)
|
||||
props.store(out, "")
|
||||
|
@@ -6,6 +6,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.logging.Level
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.hostcache.HostCache
|
||||
import com.muwire.core.hostcache.HostDiscoveredEvent
|
||||
@@ -26,7 +27,8 @@ abstract class Connection implements Closeable {
|
||||
final boolean incoming
|
||||
final HostCache hostCache
|
||||
final TrustService trustService
|
||||
|
||||
final MuWireSettings settings
|
||||
|
||||
private final AtomicBoolean running = new AtomicBoolean()
|
||||
private final BlockingQueue messages = new LinkedBlockingQueue()
|
||||
private final Thread reader, writer
|
||||
@@ -35,12 +37,14 @@ abstract class Connection implements Closeable {
|
||||
|
||||
long lastPingSentTime, lastPongReceivedTime
|
||||
|
||||
Connection(EventBus eventBus, Endpoint endpoint, boolean incoming, HostCache hostCache, TrustService trustService) {
|
||||
Connection(EventBus eventBus, Endpoint endpoint, boolean incoming,
|
||||
HostCache hostCache, TrustService trustService, MuWireSettings settings) {
|
||||
this.eventBus = eventBus
|
||||
this.incoming = incoming
|
||||
this.endpoint = endpoint
|
||||
this.hostCache = hostCache
|
||||
this.trustService = trustService
|
||||
this.settings = settings
|
||||
|
||||
this.name = endpoint.destination.toBase32().substring(0,8)
|
||||
|
||||
@@ -155,11 +159,15 @@ abstract class Connection implements Closeable {
|
||||
search.keywords = null
|
||||
|
||||
Destination replyTo = new Destination(search.replyTo)
|
||||
if (trustService.getLevel(replyTo) == TrustLevel.DISTRUSTED) {
|
||||
TrustLevel trustLevel = trustService.getLevel(replyTo)
|
||||
if (trustLevel == TrustLevel.DISTRUSTED) {
|
||||
log.info "dropping search from distrusted peer"
|
||||
return
|
||||
}
|
||||
// TODO: add option to respond only to trusted peers
|
||||
if (trustLevel == TrustLevel.NEUTRAL && !settings.allowUntrusted()) {
|
||||
log.info("dropping search from neutral peer")
|
||||
return
|
||||
}
|
||||
|
||||
Persona originator = null
|
||||
if (search.originator != null) {
|
||||
|
@@ -34,13 +34,15 @@ class ConnectionAcceptor {
|
||||
final TrustService trustService
|
||||
final SearchManager searchManager
|
||||
final UploadManager uploadManager
|
||||
final ConnectionEstablisher establisher
|
||||
|
||||
final ExecutorService acceptorThread
|
||||
final ExecutorService handshakerThreads
|
||||
|
||||
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
|
||||
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
|
||||
TrustService trustService, SearchManager searchManager, UploadManager uploadManager) {
|
||||
TrustService trustService, SearchManager searchManager, UploadManager uploadManager,
|
||||
ConnectionEstablisher establisher) {
|
||||
this.eventBus = eventBus
|
||||
this.manager = manager
|
||||
this.settings = settings
|
||||
@@ -49,7 +51,8 @@ class ConnectionAcceptor {
|
||||
this.trustService = trustService
|
||||
this.searchManager = searchManager
|
||||
this.uploadManager = uploadManager
|
||||
|
||||
this.establisher = establisher
|
||||
|
||||
acceptorThread = Executors.newSingleThreadExecutor { r ->
|
||||
def rv = new Thread(r)
|
||||
rv.setDaemon(true)
|
||||
@@ -140,7 +143,9 @@ class ConnectionAcceptor {
|
||||
}
|
||||
|
||||
private void handleIncoming(Endpoint e, boolean leaf) {
|
||||
boolean accept = !manager.isConnected(e.destination) && (leaf ? manager.hasLeafSlots() : manager.hasPeerSlots())
|
||||
boolean accept = !manager.isConnected(e.destination) &&
|
||||
!establisher.inProgress.contains(e.destination) &&
|
||||
(leaf ? manager.hasLeafSlots() : manager.hasPeerSlots())
|
||||
if (accept) {
|
||||
log.info("accepting connection, leaf:$leaf")
|
||||
e.outputStream.write("OK".bytes)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.muwire.core.connection
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.hostcache.HostCache
|
||||
import com.muwire.core.search.QueryEvent
|
||||
@@ -19,13 +20,15 @@ abstract class ConnectionManager {
|
||||
|
||||
protected final HostCache hostCache
|
||||
protected final Persona me
|
||||
protected final MuWireSettings settings
|
||||
|
||||
ConnectionManager() {}
|
||||
|
||||
ConnectionManager(EventBus eventBus, Persona me, HostCache hostCache) {
|
||||
ConnectionManager(EventBus eventBus, Persona me, HostCache hostCache, MuWireSettings settings) {
|
||||
this.eventBus = eventBus
|
||||
this.me = me
|
||||
this.hostCache = hostCache
|
||||
this.settings = settings
|
||||
this.timer = new Timer("connections-pinger",true)
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.hostcache.HostCache
|
||||
import com.muwire.core.trust.TrustService
|
||||
|
||||
@@ -16,8 +17,9 @@ import net.i2p.data.Destination
|
||||
*/
|
||||
class LeafConnection extends Connection {
|
||||
|
||||
public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache, TrustService trustService) {
|
||||
super(eventBus, endpoint, true, hostCache, trustService);
|
||||
public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache,
|
||||
TrustService trustService, MuWireSettings settings) {
|
||||
super(eventBus, endpoint, true, hostCache, trustService, settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -3,6 +3,7 @@ package com.muwire.core.connection
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.hostcache.HostCache
|
||||
import com.muwire.core.search.QueryEvent
|
||||
@@ -17,8 +18,9 @@ class LeafConnectionManager extends ConnectionManager {
|
||||
|
||||
final Map<Destination, UltrapeerConnection> connections = new ConcurrentHashMap()
|
||||
|
||||
public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections, HostCache hostCache) {
|
||||
super(eventBus, me, hostCache)
|
||||
public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections,
|
||||
HostCache hostCache, MuWireSettings settings) {
|
||||
super(eventBus, me, hostCache, settings)
|
||||
this.maxConnections = maxConnections
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.hostcache.HostCache
|
||||
import com.muwire.core.trust.TrustService
|
||||
import com.muwire.core.util.DataUtil
|
||||
@@ -29,8 +30,9 @@ class PeerConnection extends Connection {
|
||||
private final JsonSlurper slurper = new JsonSlurper()
|
||||
|
||||
public PeerConnection(EventBus eventBus, Endpoint endpoint,
|
||||
boolean incoming, HostCache hostCache, TrustService trustService) {
|
||||
super(eventBus, endpoint, incoming, hostCache, trustService)
|
||||
boolean incoming, HostCache hostCache, TrustService trustService,
|
||||
MuWireSettings settings) {
|
||||
super(eventBus, endpoint, incoming, hostCache, trustService, settings)
|
||||
this.dis = new DataInputStream(endpoint.inputStream)
|
||||
this.dos = new DataOutputStream(endpoint.outputStream)
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import java.util.Collection
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.hostcache.HostCache
|
||||
import com.muwire.core.search.QueryEvent
|
||||
@@ -17,15 +18,15 @@ class UltrapeerConnectionManager extends ConnectionManager {
|
||||
|
||||
final int maxPeers, maxLeafs
|
||||
final TrustService trustService
|
||||
|
||||
|
||||
final Map<Destination, PeerConnection> peerConnections = new ConcurrentHashMap()
|
||||
final Map<Destination, LeafConnection> leafConnections = new ConcurrentHashMap()
|
||||
|
||||
UltrapeerConnectionManager() {}
|
||||
|
||||
public UltrapeerConnectionManager(EventBus eventBus, Persona me, int maxPeers, int maxLeafs,
|
||||
HostCache hostCache, TrustService trustService) {
|
||||
super(eventBus, me, hostCache)
|
||||
HostCache hostCache, TrustService trustService, MuWireSettings settings) {
|
||||
super(eventBus, me, hostCache, settings)
|
||||
this.maxPeers = maxPeers
|
||||
this.maxLeafs = maxLeafs
|
||||
this.trustService = trustService
|
||||
@@ -85,8 +86,8 @@ class UltrapeerConnectionManager extends ConnectionManager {
|
||||
return
|
||||
|
||||
Connection c = e.leaf ?
|
||||
new LeafConnection(eventBus, e.endpoint, hostCache, trustService) :
|
||||
new PeerConnection(eventBus, e.endpoint, e.incoming, hostCache, trustService)
|
||||
new LeafConnection(eventBus, e.endpoint, hostCache, trustService, settings) :
|
||||
new PeerConnection(eventBus, e.endpoint, e.incoming, hostCache, trustService, settings)
|
||||
def map = e.leaf ? leafConnections : peerConnections
|
||||
map.put(e.endpoint.destination, c)
|
||||
c.start()
|
||||
|
@@ -47,7 +47,7 @@ public class DownloadManager {
|
||||
destinations.add(it.sender.destination)
|
||||
}
|
||||
|
||||
def downloader = new Downloader(this, me, e.target, size,
|
||||
def downloader = new Downloader(eventBus, this, me, e.target, size,
|
||||
infohash, pieceSize, connector, destinations,
|
||||
incompletes)
|
||||
executor.execute({downloader.download()} as Runnable)
|
||||
|
@@ -31,8 +31,8 @@ class DownloadSession {
|
||||
private final long fileLength
|
||||
private final MessageDigest digest
|
||||
|
||||
private final ArrayDeque<Long> timestamps = new ArrayDeque<>(SAMPLES)
|
||||
private final ArrayDeque<Integer> reads = new ArrayDeque<>(SAMPLES)
|
||||
private final LinkedList<Long> timestamps = new LinkedList<>()
|
||||
private final LinkedList<Integer> reads = new LinkedList<>()
|
||||
|
||||
private ByteBuffer mapped
|
||||
|
||||
@@ -183,9 +183,22 @@ class DownloadSession {
|
||||
synchronized int speed() {
|
||||
if (timestamps.size() < SAMPLES)
|
||||
return 0
|
||||
long interval = timestamps.last - timestamps.first
|
||||
int totalRead = 0
|
||||
reads.each { totalRead += it }
|
||||
int idx = 0
|
||||
final long now = System.currentTimeMillis()
|
||||
|
||||
while(idx < SAMPLES && timestamps.get(idx) < now - 1000)
|
||||
idx++
|
||||
if (idx == SAMPLES)
|
||||
return 0
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,10 @@ import java.util.concurrent.Executors
|
||||
import java.util.logging.Level
|
||||
|
||||
import com.muwire.core.Constants
|
||||
import com.muwire.core.DownloadedFile
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.connection.I2PConnector
|
||||
import com.muwire.core.files.FileDownloadedEvent
|
||||
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.data.Destination
|
||||
@@ -27,6 +30,7 @@ public class Downloader {
|
||||
rv
|
||||
})
|
||||
|
||||
private final EventBus eventBus
|
||||
private final DownloadManager downloadManager
|
||||
private final Persona me
|
||||
private final File file
|
||||
@@ -42,10 +46,13 @@ public class Downloader {
|
||||
|
||||
|
||||
private volatile boolean cancelled
|
||||
private volatile boolean eventFired
|
||||
|
||||
public Downloader(DownloadManager downloadManager, Persona me, File file, long length, InfoHash infoHash,
|
||||
public Downloader(EventBus eventBus, DownloadManager downloadManager,
|
||||
Persona me, File file, long length, InfoHash infoHash,
|
||||
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
|
||||
File incompletes) {
|
||||
this.eventBus = eventBus
|
||||
this.me = me
|
||||
this.downloadManager = downloadManager
|
||||
this.file = file
|
||||
@@ -104,7 +111,8 @@ public class Downloader {
|
||||
int total = 0
|
||||
if (getCurrentState() == DownloadState.DOWNLOADING) {
|
||||
activeWorkers.values().each {
|
||||
total += it.speed()
|
||||
if (it.currentState == WorkerState.DOWNLOADING)
|
||||
total += it.speed()
|
||||
}
|
||||
}
|
||||
total
|
||||
@@ -155,7 +163,13 @@ public class Downloader {
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
downloadManager.resume(this)
|
||||
activeWorkers.each { destination, worker ->
|
||||
if (worker.currentState == WorkerState.FINISHED) {
|
||||
def newWorker = new DownloadWorker(destination)
|
||||
activeWorkers.put(destination, newWorker)
|
||||
executorService.submit(newWorker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadWorker implements Runnable {
|
||||
@@ -188,6 +202,11 @@ public class Downloader {
|
||||
log.log(Level.WARNING,"Exception while downloading",bad)
|
||||
} finally {
|
||||
currentState = WorkerState.FINISHED
|
||||
if (downloaded.isComplete() && !eventFired) {
|
||||
piecesFile.delete()
|
||||
eventFired = true
|
||||
eventBus.publish(new FileDownloadedEvent(downloadedFile : new DownloadedFile(file, infoHash, Collections.emptySet())))
|
||||
}
|
||||
endpoint?.close()
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,6 @@
|
||||
package com.muwire.core.files
|
||||
|
||||
import com.muwire.core.Event
|
||||
|
||||
class AllFilesLoadedEvent extends Event {
|
||||
}
|
@@ -52,11 +52,11 @@ class FileHasher {
|
||||
try {
|
||||
MappedByteBuffer buf
|
||||
for (int i = 0; i < numPieces - 1; i++) {
|
||||
buf = raf.getChannel().map(MapMode.READ_ONLY, size * i, size)
|
||||
buf = raf.getChannel().map(MapMode.READ_ONLY, ((long)size) * i, size)
|
||||
digest.update buf
|
||||
output.write(digest.digest(), 0, 32)
|
||||
}
|
||||
def lastPieceLength = length - (numPieces - 1) * size
|
||||
def lastPieceLength = length - (numPieces - 1) * ((long)size)
|
||||
buf = raf.getChannel().map(MapMode.READ_ONLY, length - lastPieceLength, lastPieceLength)
|
||||
digest.update buf
|
||||
output.write(digest.digest(), 0, 32)
|
||||
|
@@ -2,6 +2,7 @@ package com.muwire.core.files
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.InfoHash
|
||||
import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.SharedFile
|
||||
import com.muwire.core.search.ResultsEvent
|
||||
import com.muwire.core.search.SearchEvent
|
||||
@@ -14,18 +15,22 @@ class FileManager {
|
||||
|
||||
|
||||
final EventBus eventBus
|
||||
final MuWireSettings settings
|
||||
final Map<InfoHash, Set<SharedFile>> rootToFiles = Collections.synchronizedMap(new HashMap<>())
|
||||
final Map<File, SharedFile> fileToSharedFile = Collections.synchronizedMap(new HashMap<>())
|
||||
final Map<String, Set<File>> nameToFiles = new HashMap<>()
|
||||
final SearchIndex index = new SearchIndex()
|
||||
|
||||
FileManager(EventBus eventBus) {
|
||||
FileManager(EventBus eventBus, MuWireSettings settings) {
|
||||
this.settings = settings
|
||||
this.eventBus = eventBus
|
||||
}
|
||||
|
||||
void onFileHashedEvent(FileHashedEvent e) {
|
||||
if (e.sharedFile != null)
|
||||
addToIndex(e.sharedFile)
|
||||
if (settings.shareDownloadedFiles) {
|
||||
if (e.sharedFile != null)
|
||||
addToIndex(e.sharedFile)
|
||||
}
|
||||
}
|
||||
|
||||
void onFileLoadedEvent(FileLoadedEvent e) {
|
||||
|
@@ -10,11 +10,13 @@ class HasherService {
|
||||
|
||||
final FileHasher hasher
|
||||
final EventBus eventBus
|
||||
final FileManager fileManager
|
||||
Executor executor
|
||||
|
||||
HasherService(FileHasher hasher, EventBus eventBus) {
|
||||
HasherService(FileHasher hasher, EventBus eventBus, FileManager fileManager) {
|
||||
this.hasher = hasher
|
||||
this.eventBus = eventBus
|
||||
this.fileManager = fileManager
|
||||
}
|
||||
|
||||
void start() {
|
||||
@@ -22,6 +24,8 @@ class HasherService {
|
||||
}
|
||||
|
||||
void onFileSharedEvent(FileSharedEvent evt) {
|
||||
if (fileManager.fileToSharedFile.containsKey(evt.file))
|
||||
return
|
||||
executor.execute( { -> process(evt.file) } as Runnable)
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,8 @@
|
||||
package com.muwire.core.files
|
||||
|
||||
import java.nio.file.CopyOption
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.util.logging.Level
|
||||
import java.util.stream.Collectors
|
||||
|
||||
@@ -34,7 +37,7 @@ class PersisterService extends Service {
|
||||
}
|
||||
|
||||
void start() {
|
||||
timer.schedule({load()} as TimerTask, 1000)
|
||||
timer.schedule({load()} as TimerTask, 1)
|
||||
}
|
||||
|
||||
void stop() {
|
||||
@@ -55,6 +58,7 @@ class PersisterService extends Service {
|
||||
}
|
||||
}
|
||||
}
|
||||
listener.publish(new AllFilesLoadedEvent())
|
||||
} catch (IllegalArgumentException|NumberFormatException e) {
|
||||
log.log(Level.WARNING, "couldn't load files",e)
|
||||
}
|
||||
@@ -107,15 +111,19 @@ class PersisterService extends Service {
|
||||
}
|
||||
|
||||
private void persistFiles() {
|
||||
location.delete()
|
||||
def sharedFiles = fileManager.getSharedFiles()
|
||||
location.withPrintWriter { writer ->
|
||||
|
||||
File tmp = File.createTempFile("muwire-files", "tmp")
|
||||
tmp.deleteOnExit()
|
||||
tmp.withPrintWriter { writer ->
|
||||
sharedFiles.each { k, v ->
|
||||
def json = toJson(k,v)
|
||||
json = JsonOutput.toJson(json)
|
||||
writer.println json
|
||||
}
|
||||
}
|
||||
Files.copy(tmp.toPath(), location.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
tmp.delete()
|
||||
}
|
||||
|
||||
private def toJson(File f, SharedFile sf) {
|
||||
|
@@ -140,7 +140,7 @@ class CacheClient {
|
||||
pong.pongs.asList().each {
|
||||
Destination dest = new Destination(it)
|
||||
if (!session.getMyDestination().equals(dest))
|
||||
eventBus.publish(new HostDiscoveredEvent(destination: dest))
|
||||
eventBus.publish(new HostDiscoveredEvent(destination: dest, fromHostcache : true))
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -30,4 +30,8 @@ class Host {
|
||||
synchronized boolean hasSucceeded() {
|
||||
successes > 0
|
||||
}
|
||||
|
||||
synchronized void clearFailures() {
|
||||
failures = 0
|
||||
}
|
||||
}
|
||||
|
@@ -46,8 +46,12 @@ class HostCache extends Service {
|
||||
void onHostDiscoveredEvent(HostDiscoveredEvent e) {
|
||||
if (myself == e.destination)
|
||||
return
|
||||
if (hosts.containsKey(e.destination))
|
||||
return
|
||||
if (hosts.containsKey(e.destination)) {
|
||||
if (!e.fromHostcache)
|
||||
return
|
||||
hosts.get(e.destination).clearFailures()
|
||||
return
|
||||
}
|
||||
Host host = new Host(e.destination)
|
||||
if (allowHost(host)) {
|
||||
hosts.put(e.destination, host)
|
||||
|
@@ -7,9 +7,10 @@ import net.i2p.data.Destination
|
||||
class HostDiscoveredEvent extends Event {
|
||||
|
||||
Destination destination
|
||||
|
||||
boolean fromHostcache
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
"HostDiscoveredEvent ${super.toString()} destination:${destination.toBase32()}"
|
||||
"HostDiscoveredEvent ${super.toString()} destination:${destination.toBase32()} from hostcache $fromHostcache"
|
||||
}
|
||||
}
|
||||
|
@@ -55,21 +55,21 @@ class JULLog extends Log {
|
||||
|
||||
@Override
|
||||
public boolean shouldDebug() {
|
||||
level.intValue().intValue() >= Level.FINE.intValue()
|
||||
level.intValue().intValue() <= Level.FINE.intValue()
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldInfo() {
|
||||
level.intValue().intValue() >= Level.INFO.intValue()
|
||||
level.intValue().intValue() <= Level.INFO.intValue()
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldWarn() {
|
||||
level.intValue().intValue() >= Level.WARNING.intValue()
|
||||
level.intValue().intValue() <= Level.WARNING.intValue()
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldError() {
|
||||
level.intValue().intValue() >= Level.SEVERE.intValue()
|
||||
level.intValue().intValue() <= Level.SEVERE.intValue()
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.0.8
|
||||
version = 0.0.11
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
|
@@ -6,6 +6,8 @@ import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import javax.annotation.Nonnull
|
||||
|
||||
import com.muwire.core.Core
|
||||
|
||||
@ArtifactProviderFor(GriffonController)
|
||||
class OptionsController {
|
||||
@MVCMember @Nonnull
|
||||
@@ -15,17 +17,51 @@ class OptionsController {
|
||||
|
||||
@ControllerAction
|
||||
void save() {
|
||||
String text = view.retryField.text
|
||||
model.downloadRetryInterval = text
|
||||
String text
|
||||
Core core = application.context.get("core")
|
||||
|
||||
def i2pProps = core.i2pOptions
|
||||
|
||||
text = view.inboundLengthField.text
|
||||
model.inboundLength = text
|
||||
i2pProps["inbound.length"] = text
|
||||
|
||||
text = view.inboundQuantityField.text
|
||||
model.inboundQuantity = text
|
||||
i2pProps["inbound.quantity"] = text
|
||||
|
||||
text = view.outboundQuantityField.text
|
||||
model.outboundQuantity = text
|
||||
i2pProps["outbound.quantity"] = text
|
||||
|
||||
text = view.outboundLengthField.text
|
||||
model.outboundLength = text
|
||||
i2pProps["outbound.length"] = text
|
||||
|
||||
File i2pSettingsFile = new File(core.home, "i2p.properties")
|
||||
i2pSettingsFile.withOutputStream {
|
||||
i2pProps.store(it,"")
|
||||
}
|
||||
|
||||
text = view.retryField.text
|
||||
model.downloadRetryInterval = text
|
||||
|
||||
def settings = application.context.get("muwire-settings")
|
||||
settings.downloadRetryInterval = Integer.valueOf(text)
|
||||
|
||||
text = view.updateField.text
|
||||
model.updateCheckInterval = text
|
||||
settings.updateCheckInterval = Integer.valueOf(text)
|
||||
|
||||
File settingsFile = new File(application.context.get("core").home, "MuWire.properties")
|
||||
|
||||
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
|
||||
model.onlyTrusted = onlyTrusted
|
||||
settings.setAllowUntrusted(!onlyTrusted)
|
||||
|
||||
boolean shareDownloaded = view.shareDownloadedCheckbox.model.isSelected()
|
||||
model.shareDownloadedFiles = shareDownloaded
|
||||
settings.shareDownloadedFiles = shareDownloaded
|
||||
|
||||
File settingsFile = new File(core.home, "MuWire.properties")
|
||||
settingsFile.withOutputStream {
|
||||
settings.write(it)
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ import com.muwire.core.connection.ConnectionEvent
|
||||
import com.muwire.core.connection.DisconnectionEvent
|
||||
import com.muwire.core.download.DownloadStartedEvent
|
||||
import com.muwire.core.download.Downloader
|
||||
import com.muwire.core.files.FileDownloadedEvent
|
||||
import com.muwire.core.files.FileHashedEvent
|
||||
import com.muwire.core.files.FileLoadedEvent
|
||||
import com.muwire.core.files.FileSharedEvent
|
||||
@@ -100,6 +101,7 @@ class MainFrameModel {
|
||||
core.eventBus.register(TrustEvent.class, this)
|
||||
core.eventBus.register(QueryEvent.class, this)
|
||||
core.eventBus.register(UpdateAvailableEvent.class, this)
|
||||
core.eventBus.register(FileDownloadedEvent.class, this)
|
||||
|
||||
timer.schedule({
|
||||
int retryInterval = application.context.get("muwire-settings").downloadRetryInterval
|
||||
@@ -110,7 +112,9 @@ class MainFrameModel {
|
||||
lastRetryTime = now
|
||||
runInsideUIAsync {
|
||||
downloads.each {
|
||||
if (it.downloader.currentState == Downloader.DownloadState.FAILED)
|
||||
def state = it.downloader.currentState
|
||||
if (state == Downloader.DownloadState.FAILED ||
|
||||
state == Downloader.DownloadState.DOWNLOADING)
|
||||
it.downloader.resume()
|
||||
updateTablePreservingSelection("downloads-table")
|
||||
}
|
||||
@@ -259,4 +263,15 @@ class MainFrameModel {
|
||||
JOptionPane.showMessageDialog(null, "A new version of MuWire is available from $e.signer. Please update to $e.version")
|
||||
}
|
||||
}
|
||||
|
||||
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
||||
if (!core.muOptions.shareDownloadedFiles)
|
||||
return
|
||||
infoHashes.add(e.downloadedFile.infoHash)
|
||||
runInsideUIAsync {
|
||||
shared << e.downloadedFile
|
||||
JTable table = builder.getVariable("shared-files-table")
|
||||
table.model.fireTableDataChanged()
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,8 @@
|
||||
package com.muwire.gui
|
||||
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.MuWireSettings
|
||||
|
||||
import griffon.core.artifact.GriffonModel
|
||||
import griffon.transform.Observable
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
@@ -8,9 +11,26 @@ import griffon.metadata.ArtifactProviderFor
|
||||
class OptionsModel {
|
||||
@Observable String downloadRetryInterval
|
||||
@Observable String updateCheckInterval
|
||||
@Observable boolean onlyTrusted
|
||||
@Observable boolean shareDownloadedFiles
|
||||
|
||||
// i2p options
|
||||
@Observable String inboundLength
|
||||
@Observable String inboundQuantity
|
||||
@Observable String outboundLength
|
||||
@Observable String outboundQuantity
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
downloadRetryInterval = application.context.get("muwire-settings").downloadRetryInterval
|
||||
updateCheckInterval = application.context.get("muwire-settings").updateCheckInterval
|
||||
MuWireSettings settings = application.context.get("muwire-settings")
|
||||
downloadRetryInterval = settings.downloadRetryInterval
|
||||
updateCheckInterval = settings.updateCheckInterval
|
||||
onlyTrusted = !settings.allowUntrusted()
|
||||
shareDownloadedFiles = settings.shareDownloadedFiles
|
||||
|
||||
Core core = application.context.get("core")
|
||||
inboundLength = core.i2pOptions["inbound.length"]
|
||||
inboundQuantity = core.i2pOptions["inbound.quantity"]
|
||||
outboundLength = core.i2pOptions["outbound.length"]
|
||||
outboundQuantity = core.i2pOptions["outbound.quantity"]
|
||||
}
|
||||
}
|
@@ -9,10 +9,12 @@ import javax.swing.BorderFactory
|
||||
import javax.swing.Box
|
||||
import javax.swing.BoxLayout
|
||||
import javax.swing.JFileChooser
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JSplitPane
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.SwingConstants
|
||||
import javax.swing.border.Border
|
||||
import javax.swing.table.DefaultTableCellRenderer
|
||||
|
||||
import com.muwire.core.Constants
|
||||
import com.muwire.core.download.Downloader
|
||||
@@ -269,6 +271,10 @@ class MainFrameView {
|
||||
model.retryButtonEnabled = false
|
||||
}
|
||||
})
|
||||
|
||||
def centerRenderer = new DefaultTableCellRenderer()
|
||||
centerRenderer.setHorizontalAlignment(JLabel.CENTER)
|
||||
builder.getVariable("downloads-table").setDefaultRenderer(Integer.class, centerRenderer)
|
||||
}
|
||||
|
||||
def showSearchWindow = {
|
||||
|
@@ -5,8 +5,11 @@ import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.JTabbedPane
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
|
||||
@@ -21,8 +24,19 @@ class OptionsView {
|
||||
|
||||
def d
|
||||
def p
|
||||
def i
|
||||
def retryField
|
||||
def updateField
|
||||
def allowUntrustedCheckbox
|
||||
def shareDownloadedCheckbox
|
||||
|
||||
def inboundLengthField
|
||||
def inboundQuantityField
|
||||
def outboundLengthField
|
||||
def outboundQuantityField
|
||||
|
||||
def buttonsPanel
|
||||
|
||||
def mainFrame
|
||||
|
||||
void initUI() {
|
||||
@@ -38,14 +52,44 @@ class OptionsView {
|
||||
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))
|
||||
label(text : "hours", constraints : gbc(gridx: 2, gridy : 1))
|
||||
|
||||
label(text : "Only allow trusted connections", constraints : gbc(gridx: 0, gridy : 2))
|
||||
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 2))
|
||||
|
||||
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
|
||||
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3))
|
||||
|
||||
}
|
||||
i = builder.panel {
|
||||
gridBagLayout()
|
||||
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
|
||||
label(text : "Inbound Length", constraints : gbc(gridx:0, gridy:1))
|
||||
inboundLengthField = textField(text : bind {model.inboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:1))
|
||||
label(text : "Inbound Quantity", constraints : gbc(gridx:0, gridy:2))
|
||||
inboundQuantityField = textField(text : bind {model.inboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:2))
|
||||
label(text : "Outbound Length", constraints : gbc(gridx:0, gridy:3))
|
||||
outboundLengthField = textField(text : bind {model.outboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:3))
|
||||
label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4))
|
||||
outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:4))
|
||||
}
|
||||
buttonsPanel = builder.panel {
|
||||
gridBagLayout()
|
||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
||||
button(text : "Cancel", constraints : gbc(gridx : 2, gridy: 2), cancelAction)
|
||||
}
|
||||
}
|
||||
|
||||
void mvcGroupInit(Map<String,String> args) {
|
||||
d.getContentPane().add(p)
|
||||
def tabbedPane = new JTabbedPane()
|
||||
tabbedPane.addTab("MuWire Options", p)
|
||||
tabbedPane.addTab("I2P Options", i)
|
||||
|
||||
JPanel panel = new JPanel()
|
||||
panel.setLayout(new BorderLayout())
|
||||
panel.add(tabbedPane, BorderLayout.CENTER)
|
||||
panel.add(buttonsPanel, BorderLayout.SOUTH)
|
||||
|
||||
d.getContentPane().add(panel)
|
||||
d.pack()
|
||||
d.setLocationRelativeTo(mainFrame)
|
||||
d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
||||
|
@@ -33,7 +33,7 @@ class SearchTabView {
|
||||
def pane = scrollPane {
|
||||
resultsTable = table(id : "results-table") {
|
||||
tableModel(list: model.results) {
|
||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name})
|
||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')})
|
||||
closureColumn(header: "Size", preferredWidth: 50, type: String, read : {row -> DataHelper.formatSize2Decimal(row.size, false)+"B"})
|
||||
closureColumn(header: "Sources", preferredWidth: 10, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
||||
closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
|
||||
@@ -62,7 +62,7 @@ class SearchTabView {
|
||||
searchTerms = args["search-terms"]
|
||||
parent = mvcGroup.parentGroup.view.builder.getVariable("result-tabs")
|
||||
parent.addTab(searchTerms, pane)
|
||||
int index = parent.indexOfTab(searchTerms)
|
||||
int index = parent.indexOfComponent(pane)
|
||||
parent.setSelectedIndex(index)
|
||||
|
||||
def tabPanel
|
||||
|
@@ -1,2 +1,3 @@
|
||||
apply plugin : 'application'
|
||||
mainClassName = 'com.muwire.hostcache.HostCache'
|
||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||
|
@@ -1,9 +1,12 @@
|
||||
package com.muwire.hostcache
|
||||
|
||||
import java.util.logging.Level
|
||||
import java.util.stream.Collectors
|
||||
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.data.Destination
|
||||
|
||||
@Log
|
||||
class Crawler {
|
||||
|
||||
final def pinger
|
||||
@@ -22,12 +25,14 @@ class Crawler {
|
||||
|
||||
synchronized def handleCrawlerPong(pong, Destination source) {
|
||||
if (!inFlight.containsKey(source)) {
|
||||
log.info("response from host that hasn't been crawled")
|
||||
return
|
||||
}
|
||||
Host host = inFlight.remove(source)
|
||||
|
||||
if (pong.uuid == null || pong.leafSlots == null || pong.peerSlots == null || pong.peers == null) {
|
||||
hostPool.fail(host)
|
||||
log.info("invalid crawler pong")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -40,6 +45,7 @@ class Crawler {
|
||||
}
|
||||
|
||||
if (!uuid.equals(currentUUID)) {
|
||||
log.info("uuid mismatch")
|
||||
hostPool.fail(host)
|
||||
return
|
||||
}
|
||||
@@ -50,7 +56,9 @@ class Crawler {
|
||||
def peers
|
||||
try {
|
||||
peers = pong.peers.stream().map({b64 -> new Destination(b64)}).collect(Collectors.toSet())
|
||||
log.info("received ${peers.size()} peers")
|
||||
} catch (Exception e) {
|
||||
log.log(Level.WARNING,"couldn't parse peers", e)
|
||||
hostPool.fail(host)
|
||||
return
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
package com.muwire.hostcache
|
||||
|
||||
import java.util.logging.Level
|
||||
import java.util.stream.Collectors
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.client.I2PClientFactory
|
||||
import net.i2p.client.I2PSession
|
||||
import net.i2p.client.I2PSessionMuxedListener
|
||||
@@ -12,6 +14,7 @@ import net.i2p.client.datagram.I2PDatagramMaker
|
||||
import net.i2p.util.SystemVersion
|
||||
import net.i2p.data.*
|
||||
|
||||
@Log
|
||||
public class HostCache {
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -53,7 +56,7 @@ public class HostCache {
|
||||
myDest = session.getMyDestination()
|
||||
|
||||
// initialize hostpool and crawler
|
||||
HostPool hostPool = new HostPool(3, 60 * 1000 * 1000)
|
||||
HostPool hostPool = new HostPool(3, 60 * 60 * 1000)
|
||||
Pinger pinger = new Pinger(session)
|
||||
Crawler crawler = new Crawler(pinger, hostPool, 5)
|
||||
|
||||
@@ -64,7 +67,7 @@ public class HostCache {
|
||||
session.addMuxedSessionListener(new Listener(hostPool: hostPool, toReturn: 2, crawler: crawler),
|
||||
I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)
|
||||
session.connect()
|
||||
println "INFO: connected, going to sleep"
|
||||
log.info("connected, going to sleep")
|
||||
Thread.sleep(Integer.MAX_VALUE)
|
||||
|
||||
}
|
||||
@@ -77,16 +80,16 @@ public class HostCache {
|
||||
|
||||
void reportAbuse(I2PSession sesison, int severity) {}
|
||||
void disconnected(I2PSession session) {
|
||||
println "ERROR: session disconnected, exiting"
|
||||
log.severe("session disconnected, exiting")
|
||||
System.exit(1)
|
||||
}
|
||||
void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
println "ERROR: ${message} ${error}"
|
||||
log.warning("${message} ${error}")
|
||||
}
|
||||
void messageAvailable(I2PSession session, int msgId, long size, int proto,
|
||||
int fromport, int toport) {
|
||||
if (proto != I2PSession.PROTO_DATAGRAM) {
|
||||
println "WARN: received unexpected protocol ${proto}"
|
||||
log.warning("received unexpected protocol ${proto}")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -95,19 +98,19 @@ public class HostCache {
|
||||
try {
|
||||
dissector.loadI2PDatagram(payload)
|
||||
def sender = dissector.getSender()
|
||||
println "INFO: Received something from ${sender.toBase32()}"
|
||||
def b32 = sender.toBase32()
|
||||
|
||||
payload = dissector.getPayload()
|
||||
payload = json.parse(payload)
|
||||
if (payload.type == null) {
|
||||
println "WARN: type field missing"
|
||||
log.warning("type field missing from $b32")
|
||||
return
|
||||
}
|
||||
switch(payload.type) {
|
||||
case "Ping" :
|
||||
println "Ping"
|
||||
case "Ping" :
|
||||
log.info("ping from $b32")
|
||||
if (payload.leaf == null) {
|
||||
println "WARN: ping didn't specify if leaf"
|
||||
log.warning("ping didn't specify if leaf from $b32")
|
||||
return
|
||||
}
|
||||
payload.leaf = Boolean.parseBoolean(payload.leaf.toString())
|
||||
@@ -116,14 +119,14 @@ public class HostCache {
|
||||
respond(session, sender, payload)
|
||||
break
|
||||
case "CrawlerPong":
|
||||
println "CrawlerPong"
|
||||
log.info("CrawlerPong from $b32")
|
||||
crawler.handleCrawlerPong(payload, sender)
|
||||
break
|
||||
default:
|
||||
println "WARN: Unexpected message type ${payload.type}, dropping"
|
||||
log.warning("Unexpected message type ${payload.type}, dropping from $b32")
|
||||
}
|
||||
} catch (Exception dfe) {
|
||||
println "WARN: invalid datagram ${dfe}"
|
||||
log.log(Level.WARNING,"invalid datagram", dfe)
|
||||
}
|
||||
}
|
||||
void messageAvailable(I2PSession session, int msgId, long size) {
|
||||
|
@@ -2,6 +2,7 @@ package com.muwire.update
|
||||
|
||||
import java.util.logging.Level
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.client.I2PClientFactory
|
||||
import net.i2p.client.I2PSession
|
||||
@@ -55,7 +56,7 @@ class UpdateServer {
|
||||
static class Listener implements I2PSessionMuxedListener {
|
||||
|
||||
private final File json
|
||||
|
||||
private final def slurper = new JsonSlurper()
|
||||
Listener(File json) {
|
||||
this.json = json
|
||||
}
|
||||
@@ -76,8 +77,9 @@ class UpdateServer {
|
||||
try {
|
||||
dissector.loadI2PDatagram(payload)
|
||||
def sender = dissector.getSender()
|
||||
log.info("Got an update ping from "+sender.toBase32())
|
||||
// I don't think we care about the payload at this point
|
||||
payload = slurper.parse(dissector.getPayload())
|
||||
log.info("Got an update ping from "+sender.toBase32() + " reported version "+payload?.myVersion)
|
||||
|
||||
def maker = new I2PDatagramMaker(session)
|
||||
def response = maker.makeI2PDatagram(json.bytes)
|
||||
session.sendMessage(sender, response, I2PSession.PROTO_DATAGRAM, 0, 2)
|
||||
|
Reference in New Issue
Block a user