Compare commits

...

35 Commits

Author SHA1 Message Date
Zlatin Balevsky
7eea8be67d Release 0.0.11 for file loading bug 2019-06-06 09:22:16 +01:00
Zlatin Balevsky
f114302bdb hopefully fix the shared file loss 2019-06-06 09:19:00 +01:00
Zlatin Balevsky
05b9b37488 emit an event when all files are loaded 2019-06-06 09:10:09 +01:00
Zlatin Balevsky
52f317a5b7 prevent division by zero 2019-06-06 07:09:54 +01:00
Zlatin Balevsky
fb8227a1f3 prevent division by zero 2019-06-06 07:09:05 +01:00
Zlatin Balevsky
5677d9f46a release 0.0.10 2019-06-06 00:23:59 +01:00
Zlatin Balevsky
c5192e3845 update readme for fix 2019-06-06 00:21:41 +01:00
Zlatin Balevsky
43c2a55cb8 0 not null 2019-06-06 00:03:22 +01:00
Zlatin Balevsky
94f6de6bea do not create new objects because that clears the successes 2019-06-05 21:07:23 +01:00
Zlatin Balevsky
6782849a12 retry hosts received from hostcache even if marked as failed 2019-06-05 20:58:28 +01:00
Zlatin Balevsky
c07d351c5d switch to jul, reduce aging interval 2019-06-05 20:14:38 +01:00
Zlatin Balevsky
dc2f675dd3 delete pieces file when download finishes 2019-06-05 19:52:50 +01:00
Zlatin Balevsky
a8e795ec51 do not accept connections if already try to connect to them 2019-06-05 19:07:36 +01:00
Zlatin Balevsky
33c5b3b18e option to disable sharing of downloaded files 2019-06-05 17:46:55 +01:00
Zlatin Balevsky
581fce4643 share downloaded files 2019-06-05 17:33:34 +01:00
Zlatin Balevsky
7fe78a0719 more clear name 2019-06-05 16:47:10 +01:00
Zlatin Balevsky
cdb6e22522 ui option for allowing untrusted connections 2019-06-05 15:47:44 +01:00
Zlatin Balevsky
2edeb046be drop neutral queries if configured 2019-06-05 15:38:39 +01:00
Zlatin Balevsky
4021f3c244 fix jullog 2019-06-05 13:04:46 +01:00
Zlatin Balevsky
9008fac24d shutdown cleanly on exit 2019-06-05 12:38:56 +01:00
Zlatin Balevsky
e2f92c5c5e print reported version 2019-06-05 10:07:04 +01:00
Zlatin Balevsky
7b33a16fd8 update list of known issues 2019-06-05 09:22:56 +01:00
Zlatin Balevsky
9a2531b264 release 0.0.9 2019-06-05 09:04:52 +01:00
Zlatin Balevsky
9a8dadff57 center the sources column 2019-06-05 08:43:58 +01:00
Zlatin Balevsky
4a274010f9 fix close tab button not appearing on duplicate searches 2019-06-05 08:34:09 +01:00
Zlatin Balevsky
1eb930435b fix hashing errors in large files 2019-06-05 00:34:38 +01:00
Zlatin Balevsky
9df28552ad try to load persisted files before hashing new ones 2019-06-05 00:22:36 +01:00
Zlatin Balevsky
ac0204dffc hopefully more accurate bandwidth gauge 2019-06-04 23:50:36 +01:00
Zlatin Balevsky
e5c402a400 retry download workers on resume 2019-06-04 23:36:57 +01:00
Zlatin Balevsky
7704c73b68 pass logging.properties to cli 2019-06-04 22:19:19 +01:00
Zlatin Balevsky
a9aa8dd840 do not count finished downloaders towards bandwidth 2019-06-04 21:55:59 +01:00
Zlatin Balevsky
de682a802a options panel for i2p tunnel options 2019-06-04 21:14:23 +01:00
Zlatin Balevsky
5435518212 core-side i2cp options 2019-06-04 20:20:25 +01:00
Zlatin Balevsky
bd01f983c9 break html in search results 2019-06-04 19:27:22 +01:00
Zlatin Balevsky
8b63864b90 utility to share files in headless mode 2019-06-04 18:58:02 +01:00
36 changed files with 373 additions and 89 deletions

View File

@@ -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 ### Known bugs and limitations
* Any shared files get re-hashed on startup
* Sometimes the list of shared files gets lost * Sometimes the list of shared files gets lost
* Many UI features you would expect are not there yet * Many UI features you would expect are not there yet

View File

@@ -1,6 +1,7 @@
apply plugin : 'application' apply plugin : 'application'
mainClassName = 'com.muwire.cli.Cli' mainClassName = 'com.muwire.cli.Cli'
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
dependencies { dependencies {
compile project(":core") compile project(":core")

View File

@@ -1,7 +1,12 @@
package com.muwire.cli package com.muwire.cli
import java.util.concurrent.CountDownLatch
import com.muwire.core.Core import com.muwire.core.Core
import com.muwire.core.MuWireSettings 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 { class Cli {
@@ -23,20 +28,59 @@ class Cli {
Core core Core core
try { try {
core = new Core(props, home, "0.0.8") core = new Core(props, home, "0.0.11")
} 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"
System.exit(1) 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() core.startServices()
println "waiting for files to load"
latch.await()
// now we begin // now we begin
println "MuWire is ready" println "MuWire is ready"
println "Enter a file containing list of files to share"
def reader = new BufferedReader(new InputStreamReader(System.in)) def filesList
def filesList = reader.readLine() 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) Thread.sleep(Integer.MAX_VALUE)
} }
} }

View File

@@ -56,6 +56,8 @@ public class Core {
final EventBus eventBus final EventBus eventBus
final Persona me final Persona me
final File home final File home
final Properties i2pOptions
final MuWireSettings muOptions
private final TrustService trustService private final TrustService trustService
private final PersisterService persisterService private final PersisterService persisterService
@@ -69,6 +71,7 @@ public class Core {
public Core(MuWireSettings props, File home, String myVersion) { public Core(MuWireSettings props, File home, String myVersion) {
this.home = home this.home = home
this.muOptions = props
log.info "Initializing I2P context" log.info "Initializing I2P context"
I2PAppContext.getGlobalContext().logManager() I2PAppContext.getGlobalContext().logManager()
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager() I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
@@ -83,12 +86,23 @@ public class Core {
} }
} }
def sysProps = System.getProperties().clone() i2pOptions = new Properties()
sysProps["inbound.nickname"] = "MuWire" 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 I2PSession i2pSession
I2PSocketManager socketManager I2PSocketManager socketManager
keyDat.withInputStream { keyDat.withInputStream {
socketManager = new I2PSocketManagerFactory().createManager(it, sysProps) socketManager = new I2PSocketManagerFactory().createManager(it, i2pOptions)
} }
socketManager.getDefaultOptions().setReadTimeout(60000) socketManager.getDefaultOptions().setReadTimeout(60000)
socketManager.getDefaultOptions().setConnectTimeout(30000) socketManager.getDefaultOptions().setConnectTimeout(30000)
@@ -129,7 +143,7 @@ public class Core {
log.info "initializing file manager" log.info "initializing file manager"
FileManager fileManager = new FileManager(eventBus) FileManager fileManager = new FileManager(eventBus, props)
eventBus.register(FileHashedEvent.class, fileManager) eventBus.register(FileHashedEvent.class, fileManager)
eventBus.register(FileLoadedEvent.class, fileManager) eventBus.register(FileLoadedEvent.class, fileManager)
eventBus.register(FileDownloadedEvent.class, fileManager) eventBus.register(FileDownloadedEvent.class, fileManager)
@@ -147,7 +161,8 @@ public class Core {
log.info("initializing connection manager") log.info("initializing connection manager")
connectionManager = props.isLeaf() ? 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(TrustEvent.class, connectionManager)
eventBus.register(ConnectionEvent.class, connectionManager) eventBus.register(ConnectionEvent.class, connectionManager)
eventBus.register(DisconnectionEvent.class, connectionManager) eventBus.register(DisconnectionEvent.class, connectionManager)
@@ -177,17 +192,17 @@ public class Core {
log.info("initializing upload manager") log.info("initializing upload manager")
UploadManager uploadManager = new UploadManager(eventBus, fileManager) UploadManager uploadManager = new UploadManager(eventBus, fileManager)
log.info("initializing connection establisher")
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
log.info("initializing acceptor") log.info("initializing acceptor")
I2PAcceptor i2pAcceptor = new I2PAcceptor(socketManager) I2PAcceptor i2pAcceptor = new I2PAcceptor(socketManager)
connectionAcceptor = new ConnectionAcceptor(eventBus, connectionManager, props, 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") log.info("initializing hasher service")
hasherService = new HasherService(new FileHasher(), eventBus) hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
eventBus.register(FileSharedEvent.class, hasherService) 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() core.startServices()
// ... at the end, sleep or execute script // ... at the end, sleep or execute script

View File

@@ -12,6 +12,7 @@ class MuWireSettings {
File downloadLocation File downloadLocation
String sharedFiles String sharedFiles
CrawlerResponse crawlerResponse CrawlerResponse crawlerResponse
boolean shareDownloadedFiles
MuWireSettings() { MuWireSettings() {
this(new Properties()) this(new Properties())
@@ -27,6 +28,7 @@ class MuWireSettings {
sharedFiles = props.getProperty("sharedFiles") sharedFiles = props.getProperty("sharedFiles")
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15")) downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15"))
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36")) updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
} }
void write(OutputStream out) throws IOException { void write(OutputStream out) throws IOException {
@@ -38,6 +40,7 @@ class MuWireSettings {
props.setProperty("downloadLocation", downloadLocation.getAbsolutePath()) props.setProperty("downloadLocation", downloadLocation.getAbsolutePath())
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval)) props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval)) props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
if (sharedFiles != null) if (sharedFiles != null)
props.setProperty("sharedFiles", sharedFiles) props.setProperty("sharedFiles", sharedFiles)
props.store(out, "") props.store(out, "")

View File

@@ -6,6 +6,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import java.util.logging.Level import java.util.logging.Level
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona import com.muwire.core.Persona
import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostCache
import com.muwire.core.hostcache.HostDiscoveredEvent import com.muwire.core.hostcache.HostDiscoveredEvent
@@ -26,7 +27,8 @@ abstract class Connection implements Closeable {
final boolean incoming final boolean incoming
final HostCache hostCache final HostCache hostCache
final TrustService trustService final TrustService trustService
final MuWireSettings settings
private final AtomicBoolean running = new AtomicBoolean() private final AtomicBoolean running = new AtomicBoolean()
private final BlockingQueue messages = new LinkedBlockingQueue() private final BlockingQueue messages = new LinkedBlockingQueue()
private final Thread reader, writer private final Thread reader, writer
@@ -35,12 +37,14 @@ abstract class Connection implements Closeable {
long lastPingSentTime, lastPongReceivedTime 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.eventBus = eventBus
this.incoming = incoming this.incoming = incoming
this.endpoint = endpoint this.endpoint = endpoint
this.hostCache = hostCache this.hostCache = hostCache
this.trustService = trustService this.trustService = trustService
this.settings = settings
this.name = endpoint.destination.toBase32().substring(0,8) this.name = endpoint.destination.toBase32().substring(0,8)
@@ -155,11 +159,15 @@ abstract class Connection implements Closeable {
search.keywords = null search.keywords = null
Destination replyTo = new Destination(search.replyTo) 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" log.info "dropping search from distrusted peer"
return 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 Persona originator = null
if (search.originator != null) { if (search.originator != null) {

View File

@@ -34,13 +34,15 @@ class ConnectionAcceptor {
final TrustService trustService final TrustService trustService
final SearchManager searchManager final SearchManager searchManager
final UploadManager uploadManager final UploadManager uploadManager
final ConnectionEstablisher establisher
final ExecutorService acceptorThread final ExecutorService acceptorThread
final ExecutorService handshakerThreads final ExecutorService handshakerThreads
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager, ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache, MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
TrustService trustService, SearchManager searchManager, UploadManager uploadManager) { TrustService trustService, SearchManager searchManager, UploadManager uploadManager,
ConnectionEstablisher establisher) {
this.eventBus = eventBus this.eventBus = eventBus
this.manager = manager this.manager = manager
this.settings = settings this.settings = settings
@@ -49,7 +51,8 @@ class ConnectionAcceptor {
this.trustService = trustService this.trustService = trustService
this.searchManager = searchManager this.searchManager = searchManager
this.uploadManager = uploadManager this.uploadManager = uploadManager
this.establisher = establisher
acceptorThread = Executors.newSingleThreadExecutor { r -> acceptorThread = Executors.newSingleThreadExecutor { r ->
def rv = new Thread(r) def rv = new Thread(r)
rv.setDaemon(true) rv.setDaemon(true)
@@ -140,7 +143,9 @@ class ConnectionAcceptor {
} }
private void handleIncoming(Endpoint e, boolean leaf) { 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) { if (accept) {
log.info("accepting connection, leaf:$leaf") log.info("accepting connection, leaf:$leaf")
e.outputStream.write("OK".bytes) e.outputStream.write("OK".bytes)

View File

@@ -1,6 +1,7 @@
package com.muwire.core.connection package com.muwire.core.connection
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona import com.muwire.core.Persona
import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostCache
import com.muwire.core.search.QueryEvent import com.muwire.core.search.QueryEvent
@@ -19,13 +20,15 @@ abstract class ConnectionManager {
protected final HostCache hostCache protected final HostCache hostCache
protected final Persona me protected final Persona me
protected final MuWireSettings settings
ConnectionManager() {} ConnectionManager() {}
ConnectionManager(EventBus eventBus, Persona me, HostCache hostCache) { ConnectionManager(EventBus eventBus, Persona me, HostCache hostCache, MuWireSettings settings) {
this.eventBus = eventBus this.eventBus = eventBus
this.me = me this.me = me
this.hostCache = hostCache this.hostCache = hostCache
this.settings = settings
this.timer = new Timer("connections-pinger",true) this.timer = new Timer("connections-pinger",true)
} }

View File

@@ -4,6 +4,7 @@ import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.MuWireSettings
import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostCache
import com.muwire.core.trust.TrustService import com.muwire.core.trust.TrustService
@@ -16,8 +17,9 @@ import net.i2p.data.Destination
*/ */
class LeafConnection extends Connection { class LeafConnection extends Connection {
public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache, TrustService trustService) { public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache,
super(eventBus, endpoint, true, hostCache, trustService); TrustService trustService, MuWireSettings settings) {
super(eventBus, endpoint, true, hostCache, trustService, settings);
} }
@Override @Override

View File

@@ -3,6 +3,7 @@ package com.muwire.core.connection
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona import com.muwire.core.Persona
import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostCache
import com.muwire.core.search.QueryEvent import com.muwire.core.search.QueryEvent
@@ -17,8 +18,9 @@ class LeafConnectionManager extends ConnectionManager {
final Map<Destination, UltrapeerConnection> connections = new ConcurrentHashMap() final Map<Destination, UltrapeerConnection> connections = new ConcurrentHashMap()
public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections, HostCache hostCache) { public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections,
super(eventBus, me, hostCache) HostCache hostCache, MuWireSettings settings) {
super(eventBus, me, hostCache, settings)
this.maxConnections = maxConnections this.maxConnections = maxConnections
} }

View File

@@ -4,6 +4,7 @@ import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.MuWireSettings
import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostCache
import com.muwire.core.trust.TrustService import com.muwire.core.trust.TrustService
import com.muwire.core.util.DataUtil import com.muwire.core.util.DataUtil
@@ -29,8 +30,9 @@ class PeerConnection extends Connection {
private final JsonSlurper slurper = new JsonSlurper() private final JsonSlurper slurper = new JsonSlurper()
public PeerConnection(EventBus eventBus, Endpoint endpoint, public PeerConnection(EventBus eventBus, Endpoint endpoint,
boolean incoming, HostCache hostCache, TrustService trustService) { boolean incoming, HostCache hostCache, TrustService trustService,
super(eventBus, endpoint, incoming, hostCache, trustService) MuWireSettings settings) {
super(eventBus, endpoint, incoming, hostCache, trustService, settings)
this.dis = new DataInputStream(endpoint.inputStream) this.dis = new DataInputStream(endpoint.inputStream)
this.dos = new DataOutputStream(endpoint.outputStream) this.dos = new DataOutputStream(endpoint.outputStream)
} }

View File

@@ -4,6 +4,7 @@ import java.util.Collection
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona import com.muwire.core.Persona
import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostCache
import com.muwire.core.search.QueryEvent import com.muwire.core.search.QueryEvent
@@ -17,15 +18,15 @@ class UltrapeerConnectionManager extends ConnectionManager {
final int maxPeers, maxLeafs final int maxPeers, maxLeafs
final TrustService trustService final TrustService trustService
final Map<Destination, PeerConnection> peerConnections = new ConcurrentHashMap() final Map<Destination, PeerConnection> peerConnections = new ConcurrentHashMap()
final Map<Destination, LeafConnection> leafConnections = new ConcurrentHashMap() final Map<Destination, LeafConnection> leafConnections = new ConcurrentHashMap()
UltrapeerConnectionManager() {} UltrapeerConnectionManager() {}
public UltrapeerConnectionManager(EventBus eventBus, Persona me, int maxPeers, int maxLeafs, public UltrapeerConnectionManager(EventBus eventBus, Persona me, int maxPeers, int maxLeafs,
HostCache hostCache, TrustService trustService) { HostCache hostCache, TrustService trustService, MuWireSettings settings) {
super(eventBus, me, hostCache) super(eventBus, me, hostCache, settings)
this.maxPeers = maxPeers this.maxPeers = maxPeers
this.maxLeafs = maxLeafs this.maxLeafs = maxLeafs
this.trustService = trustService this.trustService = trustService
@@ -85,8 +86,8 @@ class UltrapeerConnectionManager extends ConnectionManager {
return return
Connection c = e.leaf ? Connection c = e.leaf ?
new LeafConnection(eventBus, e.endpoint, hostCache, trustService) : new LeafConnection(eventBus, e.endpoint, hostCache, trustService, settings) :
new PeerConnection(eventBus, e.endpoint, e.incoming, hostCache, trustService) new PeerConnection(eventBus, e.endpoint, e.incoming, hostCache, trustService, settings)
def map = e.leaf ? leafConnections : peerConnections def map = e.leaf ? leafConnections : peerConnections
map.put(e.endpoint.destination, c) map.put(e.endpoint.destination, c)
c.start() c.start()

View File

@@ -47,7 +47,7 @@ public class DownloadManager {
destinations.add(it.sender.destination) 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, infohash, pieceSize, connector, destinations,
incompletes) incompletes)
executor.execute({downloader.download()} as Runnable) executor.execute({downloader.download()} as Runnable)

View File

@@ -31,8 +31,8 @@ class DownloadSession {
private final long fileLength private final long fileLength
private final MessageDigest digest private final MessageDigest digest
private final ArrayDeque<Long> timestamps = new ArrayDeque<>(SAMPLES) private final LinkedList<Long> timestamps = new LinkedList<>()
private final ArrayDeque<Integer> reads = new ArrayDeque<>(SAMPLES) private final LinkedList<Integer> reads = new LinkedList<>()
private ByteBuffer mapped private ByteBuffer mapped
@@ -183,9 +183,22 @@ class DownloadSession {
synchronized int speed() { synchronized int speed() {
if (timestamps.size() < SAMPLES) if (timestamps.size() < SAMPLES)
return 0 return 0
long interval = timestamps.last - timestamps.first
int totalRead = 0 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) (int)(totalRead * 1000.0 / interval)
} }
} }

View File

@@ -10,7 +10,10 @@ import java.util.concurrent.Executors
import java.util.logging.Level import java.util.logging.Level
import com.muwire.core.Constants 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.connection.I2PConnector
import com.muwire.core.files.FileDownloadedEvent
import groovy.util.logging.Log import groovy.util.logging.Log
import net.i2p.data.Destination import net.i2p.data.Destination
@@ -27,6 +30,7 @@ public class Downloader {
rv rv
}) })
private final EventBus eventBus
private final DownloadManager downloadManager private final DownloadManager downloadManager
private final Persona me private final Persona me
private final File file private final File file
@@ -42,10 +46,13 @@ public class Downloader {
private volatile boolean cancelled 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, int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
File incompletes) { File incompletes) {
this.eventBus = eventBus
this.me = me this.me = me
this.downloadManager = downloadManager this.downloadManager = downloadManager
this.file = file this.file = file
@@ -104,7 +111,8 @@ public class Downloader {
int total = 0 int total = 0
if (getCurrentState() == DownloadState.DOWNLOADING) { if (getCurrentState() == DownloadState.DOWNLOADING) {
activeWorkers.values().each { activeWorkers.values().each {
total += it.speed() if (it.currentState == WorkerState.DOWNLOADING)
total += it.speed()
} }
} }
total total
@@ -155,7 +163,13 @@ public class Downloader {
} }
public void resume() { 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 { class DownloadWorker implements Runnable {
@@ -188,6 +202,11 @@ public class Downloader {
log.log(Level.WARNING,"Exception while downloading",bad) log.log(Level.WARNING,"Exception while downloading",bad)
} finally { } finally {
currentState = WorkerState.FINISHED currentState = WorkerState.FINISHED
if (downloaded.isComplete() && !eventFired) {
piecesFile.delete()
eventFired = true
eventBus.publish(new FileDownloadedEvent(downloadedFile : new DownloadedFile(file, infoHash, Collections.emptySet())))
}
endpoint?.close() endpoint?.close()
} }
} }

View File

@@ -0,0 +1,6 @@
package com.muwire.core.files
import com.muwire.core.Event
class AllFilesLoadedEvent extends Event {
}

View File

@@ -52,11 +52,11 @@ class FileHasher {
try { try {
MappedByteBuffer buf MappedByteBuffer buf
for (int i = 0; i < numPieces - 1; i++) { 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 digest.update buf
output.write(digest.digest(), 0, 32) 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) buf = raf.getChannel().map(MapMode.READ_ONLY, length - lastPieceLength, lastPieceLength)
digest.update buf digest.update buf
output.write(digest.digest(), 0, 32) output.write(digest.digest(), 0, 32)

View File

@@ -2,6 +2,7 @@ package com.muwire.core.files
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.InfoHash import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings
import com.muwire.core.SharedFile import com.muwire.core.SharedFile
import com.muwire.core.search.ResultsEvent import com.muwire.core.search.ResultsEvent
import com.muwire.core.search.SearchEvent import com.muwire.core.search.SearchEvent
@@ -14,18 +15,22 @@ class FileManager {
final EventBus eventBus final EventBus eventBus
final MuWireSettings settings
final Map<InfoHash, Set<SharedFile>> rootToFiles = Collections.synchronizedMap(new HashMap<>()) final Map<InfoHash, Set<SharedFile>> rootToFiles = Collections.synchronizedMap(new HashMap<>())
final Map<File, SharedFile> fileToSharedFile = Collections.synchronizedMap(new HashMap<>()) final Map<File, SharedFile> fileToSharedFile = Collections.synchronizedMap(new HashMap<>())
final Map<String, Set<File>> nameToFiles = new HashMap<>() final Map<String, Set<File>> nameToFiles = new HashMap<>()
final SearchIndex index = new SearchIndex() final SearchIndex index = new SearchIndex()
FileManager(EventBus eventBus) { FileManager(EventBus eventBus, MuWireSettings settings) {
this.settings = settings
this.eventBus = eventBus this.eventBus = eventBus
} }
void onFileHashedEvent(FileHashedEvent e) { void onFileHashedEvent(FileHashedEvent e) {
if (e.sharedFile != null) if (settings.shareDownloadedFiles) {
addToIndex(e.sharedFile) if (e.sharedFile != null)
addToIndex(e.sharedFile)
}
} }
void onFileLoadedEvent(FileLoadedEvent e) { void onFileLoadedEvent(FileLoadedEvent e) {

View File

@@ -10,11 +10,13 @@ class HasherService {
final FileHasher hasher final FileHasher hasher
final EventBus eventBus final EventBus eventBus
final FileManager fileManager
Executor executor Executor executor
HasherService(FileHasher hasher, EventBus eventBus) { HasherService(FileHasher hasher, EventBus eventBus, FileManager fileManager) {
this.hasher = hasher this.hasher = hasher
this.eventBus = eventBus this.eventBus = eventBus
this.fileManager = fileManager
} }
void start() { void start() {
@@ -22,6 +24,8 @@ class HasherService {
} }
void onFileSharedEvent(FileSharedEvent evt) { void onFileSharedEvent(FileSharedEvent evt) {
if (fileManager.fileToSharedFile.containsKey(evt.file))
return
executor.execute( { -> process(evt.file) } as Runnable) executor.execute( { -> process(evt.file) } as Runnable)
} }

View File

@@ -1,5 +1,8 @@
package com.muwire.core.files 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.logging.Level
import java.util.stream.Collectors import java.util.stream.Collectors
@@ -34,7 +37,7 @@ class PersisterService extends Service {
} }
void start() { void start() {
timer.schedule({load()} as TimerTask, 1000) timer.schedule({load()} as TimerTask, 1)
} }
void stop() { void stop() {
@@ -55,6 +58,7 @@ class PersisterService extends Service {
} }
} }
} }
listener.publish(new AllFilesLoadedEvent())
} catch (IllegalArgumentException|NumberFormatException e) { } catch (IllegalArgumentException|NumberFormatException e) {
log.log(Level.WARNING, "couldn't load files",e) log.log(Level.WARNING, "couldn't load files",e)
} }
@@ -107,15 +111,19 @@ class PersisterService extends Service {
} }
private void persistFiles() { private void persistFiles() {
location.delete()
def sharedFiles = fileManager.getSharedFiles() def sharedFiles = fileManager.getSharedFiles()
location.withPrintWriter { writer ->
File tmp = File.createTempFile("muwire-files", "tmp")
tmp.deleteOnExit()
tmp.withPrintWriter { writer ->
sharedFiles.each { k, v -> sharedFiles.each { k, v ->
def json = toJson(k,v) def json = toJson(k,v)
json = JsonOutput.toJson(json) json = JsonOutput.toJson(json)
writer.println json writer.println json
} }
} }
Files.copy(tmp.toPath(), location.toPath(), StandardCopyOption.REPLACE_EXISTING)
tmp.delete()
} }
private def toJson(File f, SharedFile sf) { private def toJson(File f, SharedFile sf) {

View File

@@ -140,7 +140,7 @@ class CacheClient {
pong.pongs.asList().each { pong.pongs.asList().each {
Destination dest = new Destination(it) Destination dest = new Destination(it)
if (!session.getMyDestination().equals(dest)) if (!session.getMyDestination().equals(dest))
eventBus.publish(new HostDiscoveredEvent(destination: dest)) eventBus.publish(new HostDiscoveredEvent(destination: dest, fromHostcache : true))
} }
} }

View File

@@ -30,4 +30,8 @@ class Host {
synchronized boolean hasSucceeded() { synchronized boolean hasSucceeded() {
successes > 0 successes > 0
} }
synchronized void clearFailures() {
failures = 0
}
} }

View File

@@ -46,8 +46,12 @@ class HostCache extends Service {
void onHostDiscoveredEvent(HostDiscoveredEvent e) { void onHostDiscoveredEvent(HostDiscoveredEvent e) {
if (myself == e.destination) if (myself == e.destination)
return return
if (hosts.containsKey(e.destination)) if (hosts.containsKey(e.destination)) {
return if (!e.fromHostcache)
return
hosts.get(e.destination).clearFailures()
return
}
Host host = new Host(e.destination) Host host = new Host(e.destination)
if (allowHost(host)) { if (allowHost(host)) {
hosts.put(e.destination, host) hosts.put(e.destination, host)

View File

@@ -7,9 +7,10 @@ import net.i2p.data.Destination
class HostDiscoveredEvent extends Event { class HostDiscoveredEvent extends Event {
Destination destination Destination destination
boolean fromHostcache
@Override @Override
public String toString() { public String toString() {
"HostDiscoveredEvent ${super.toString()} destination:${destination.toBase32()}" "HostDiscoveredEvent ${super.toString()} destination:${destination.toBase32()} from hostcache $fromHostcache"
} }
} }

View File

@@ -55,21 +55,21 @@ class JULLog extends Log {
@Override @Override
public boolean shouldDebug() { public boolean shouldDebug() {
level.intValue().intValue() >= Level.FINE.intValue() level.intValue().intValue() <= Level.FINE.intValue()
} }
@Override @Override
public boolean shouldInfo() { public boolean shouldInfo() {
level.intValue().intValue() >= Level.INFO.intValue() level.intValue().intValue() <= Level.INFO.intValue()
} }
@Override @Override
public boolean shouldWarn() { public boolean shouldWarn() {
level.intValue().intValue() >= Level.WARNING.intValue() level.intValue().intValue() <= Level.WARNING.intValue()
} }
@Override @Override
public boolean shouldError() { public boolean shouldError() {
level.intValue().intValue() >= Level.SEVERE.intValue() level.intValue().intValue() <= Level.SEVERE.intValue()
} }
} }

View File

@@ -1,5 +1,5 @@
group = com.muwire group = com.muwire
version = 0.0.8 version = 0.0.11
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

View File

@@ -6,6 +6,8 @@ import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull import javax.annotation.Nonnull
import com.muwire.core.Core
@ArtifactProviderFor(GriffonController) @ArtifactProviderFor(GriffonController)
class OptionsController { class OptionsController {
@MVCMember @Nonnull @MVCMember @Nonnull
@@ -15,17 +17,51 @@ class OptionsController {
@ControllerAction @ControllerAction
void save() { void save() {
String text = view.retryField.text String text
model.downloadRetryInterval = 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") def settings = application.context.get("muwire-settings")
settings.downloadRetryInterval = Integer.valueOf(text) settings.downloadRetryInterval = Integer.valueOf(text)
text = view.updateField.text text = view.updateField.text
model.updateCheckInterval = text model.updateCheckInterval = text
settings.updateCheckInterval = Integer.valueOf(text) settings.updateCheckInterval = Integer.valueOf(text)
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 { settingsFile.withOutputStream {
settings.write(it) settings.write(it)
} }

View File

@@ -15,6 +15,7 @@ import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.connection.DisconnectionEvent import com.muwire.core.connection.DisconnectionEvent
import com.muwire.core.download.DownloadStartedEvent import com.muwire.core.download.DownloadStartedEvent
import com.muwire.core.download.Downloader import com.muwire.core.download.Downloader
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileLoadedEvent import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileSharedEvent import com.muwire.core.files.FileSharedEvent
@@ -100,6 +101,7 @@ class MainFrameModel {
core.eventBus.register(TrustEvent.class, this) core.eventBus.register(TrustEvent.class, this)
core.eventBus.register(QueryEvent.class, this) core.eventBus.register(QueryEvent.class, this)
core.eventBus.register(UpdateAvailableEvent.class, this) core.eventBus.register(UpdateAvailableEvent.class, this)
core.eventBus.register(FileDownloadedEvent.class, this)
timer.schedule({ timer.schedule({
int retryInterval = application.context.get("muwire-settings").downloadRetryInterval int retryInterval = application.context.get("muwire-settings").downloadRetryInterval
@@ -110,7 +112,9 @@ class MainFrameModel {
lastRetryTime = now lastRetryTime = now
runInsideUIAsync { runInsideUIAsync {
downloads.each { 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() it.downloader.resume()
updateTablePreservingSelection("downloads-table") 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") 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()
}
}
} }

View File

@@ -1,5 +1,8 @@
package com.muwire.gui package com.muwire.gui
import com.muwire.core.Core
import com.muwire.core.MuWireSettings
import griffon.core.artifact.GriffonModel import griffon.core.artifact.GriffonModel
import griffon.transform.Observable import griffon.transform.Observable
import griffon.metadata.ArtifactProviderFor import griffon.metadata.ArtifactProviderFor
@@ -8,9 +11,26 @@ import griffon.metadata.ArtifactProviderFor
class OptionsModel { class OptionsModel {
@Observable String downloadRetryInterval @Observable String downloadRetryInterval
@Observable String updateCheckInterval @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) { void mvcGroupInit(Map<String, String> args) {
downloadRetryInterval = application.context.get("muwire-settings").downloadRetryInterval MuWireSettings settings = application.context.get("muwire-settings")
updateCheckInterval = application.context.get("muwire-settings").updateCheckInterval 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"]
} }
} }

View File

@@ -9,10 +9,12 @@ import javax.swing.BorderFactory
import javax.swing.Box import javax.swing.Box
import javax.swing.BoxLayout import javax.swing.BoxLayout
import javax.swing.JFileChooser import javax.swing.JFileChooser
import javax.swing.JLabel
import javax.swing.JSplitPane import javax.swing.JSplitPane
import javax.swing.ListSelectionModel import javax.swing.ListSelectionModel
import javax.swing.SwingConstants import javax.swing.SwingConstants
import javax.swing.border.Border import javax.swing.border.Border
import javax.swing.table.DefaultTableCellRenderer
import com.muwire.core.Constants import com.muwire.core.Constants
import com.muwire.core.download.Downloader import com.muwire.core.download.Downloader
@@ -269,6 +271,10 @@ class MainFrameView {
model.retryButtonEnabled = false model.retryButtonEnabled = false
} }
}) })
def centerRenderer = new DefaultTableCellRenderer()
centerRenderer.setHorizontalAlignment(JLabel.CENTER)
builder.getVariable("downloads-table").setDefaultRenderer(Integer.class, centerRenderer)
} }
def showSearchWindow = { def showSearchWindow = {

View File

@@ -5,8 +5,11 @@ import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor import griffon.metadata.ArtifactProviderFor
import javax.swing.JDialog import javax.swing.JDialog
import javax.swing.JPanel
import javax.swing.JTabbedPane
import javax.swing.SwingConstants import javax.swing.SwingConstants
import java.awt.BorderLayout
import java.awt.event.WindowAdapter import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent import java.awt.event.WindowEvent
@@ -21,8 +24,19 @@ class OptionsView {
def d def d
def p def p
def i
def retryField def retryField
def updateField def updateField
def allowUntrustedCheckbox
def shareDownloadedCheckbox
def inboundLengthField
def inboundQuantityField
def outboundLengthField
def outboundQuantityField
def buttonsPanel
def mainFrame def mainFrame
void initUI() { void initUI() {
@@ -38,14 +52,44 @@ class OptionsView {
label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1)) label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1))
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1)) updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
label(text : "hours", constraints : gbc(gridx: 2, gridy : 1)) label(text : "hours", constraints : gbc(gridx: 2, gridy : 1))
label(text : "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 : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
button(text : "Cancel", constraints : gbc(gridx : 2, gridy: 2), cancelAction) button(text : "Cancel", constraints : gbc(gridx : 2, gridy: 2), cancelAction)
} }
} }
void mvcGroupInit(Map<String,String> args) { 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.pack()
d.setLocationRelativeTo(mainFrame) d.setLocationRelativeTo(mainFrame)
d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE) d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)

View File

@@ -33,7 +33,7 @@ class SearchTabView {
def pane = scrollPane { def pane = scrollPane {
resultsTable = table(id : "results-table") { resultsTable = table(id : "results-table") {
tableModel(list: model.results) { 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: "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: "Sources", preferredWidth: 10, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()}) closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
@@ -62,7 +62,7 @@ class SearchTabView {
searchTerms = args["search-terms"] searchTerms = args["search-terms"]
parent = mvcGroup.parentGroup.view.builder.getVariable("result-tabs") parent = mvcGroup.parentGroup.view.builder.getVariable("result-tabs")
parent.addTab(searchTerms, pane) parent.addTab(searchTerms, pane)
int index = parent.indexOfTab(searchTerms) int index = parent.indexOfComponent(pane)
parent.setSelectedIndex(index) parent.setSelectedIndex(index)
def tabPanel def tabPanel

View File

@@ -1,2 +1,3 @@
apply plugin : 'application' apply plugin : 'application'
mainClassName = 'com.muwire.hostcache.HostCache' mainClassName = 'com.muwire.hostcache.HostCache'
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']

View File

@@ -1,9 +1,12 @@
package com.muwire.hostcache package com.muwire.hostcache
import java.util.logging.Level
import java.util.stream.Collectors import java.util.stream.Collectors
import groovy.util.logging.Log
import net.i2p.data.Destination import net.i2p.data.Destination
@Log
class Crawler { class Crawler {
final def pinger final def pinger
@@ -22,12 +25,14 @@ class Crawler {
synchronized def handleCrawlerPong(pong, Destination source) { synchronized def handleCrawlerPong(pong, Destination source) {
if (!inFlight.containsKey(source)) { if (!inFlight.containsKey(source)) {
log.info("response from host that hasn't been crawled")
return return
} }
Host host = inFlight.remove(source) Host host = inFlight.remove(source)
if (pong.uuid == null || pong.leafSlots == null || pong.peerSlots == null || pong.peers == null) { if (pong.uuid == null || pong.leafSlots == null || pong.peerSlots == null || pong.peers == null) {
hostPool.fail(host) hostPool.fail(host)
log.info("invalid crawler pong")
return return
} }
@@ -40,6 +45,7 @@ class Crawler {
} }
if (!uuid.equals(currentUUID)) { if (!uuid.equals(currentUUID)) {
log.info("uuid mismatch")
hostPool.fail(host) hostPool.fail(host)
return return
} }
@@ -50,7 +56,9 @@ class Crawler {
def peers def peers
try { try {
peers = pong.peers.stream().map({b64 -> new Destination(b64)}).collect(Collectors.toSet()) peers = pong.peers.stream().map({b64 -> new Destination(b64)}).collect(Collectors.toSet())
log.info("received ${peers.size()} peers")
} catch (Exception e) { } catch (Exception e) {
log.log(Level.WARNING,"couldn't parse peers", e)
hostPool.fail(host) hostPool.fail(host)
return return
} }

View File

@@ -1,9 +1,11 @@
package com.muwire.hostcache package com.muwire.hostcache
import java.util.logging.Level
import java.util.stream.Collectors import java.util.stream.Collectors
import groovy.json.JsonOutput import groovy.json.JsonOutput
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import groovy.util.logging.Log
import net.i2p.client.I2PClientFactory import net.i2p.client.I2PClientFactory
import net.i2p.client.I2PSession import net.i2p.client.I2PSession
import net.i2p.client.I2PSessionMuxedListener import net.i2p.client.I2PSessionMuxedListener
@@ -12,6 +14,7 @@ import net.i2p.client.datagram.I2PDatagramMaker
import net.i2p.util.SystemVersion import net.i2p.util.SystemVersion
import net.i2p.data.* import net.i2p.data.*
@Log
public class HostCache { public class HostCache {
public static void main(String[] args) { public static void main(String[] args) {
@@ -53,7 +56,7 @@ public class HostCache {
myDest = session.getMyDestination() myDest = session.getMyDestination()
// initialize hostpool and crawler // 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) Pinger pinger = new Pinger(session)
Crawler crawler = new Crawler(pinger, hostPool, 5) Crawler crawler = new Crawler(pinger, hostPool, 5)
@@ -64,7 +67,7 @@ public class HostCache {
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)
session.connect() session.connect()
println "INFO: connected, going to sleep" log.info("connected, going to sleep")
Thread.sleep(Integer.MAX_VALUE) Thread.sleep(Integer.MAX_VALUE)
} }
@@ -77,16 +80,16 @@ public class HostCache {
void reportAbuse(I2PSession sesison, int severity) {} void reportAbuse(I2PSession sesison, int severity) {}
void disconnected(I2PSession session) { void disconnected(I2PSession session) {
println "ERROR: session disconnected, exiting" log.severe("session disconnected, exiting")
System.exit(1) System.exit(1)
} }
void errorOccurred(I2PSession session, String message, Throwable error) { 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, void messageAvailable(I2PSession session, int msgId, long size, int proto,
int fromport, int toport) { int fromport, int toport) {
if (proto != I2PSession.PROTO_DATAGRAM) { if (proto != I2PSession.PROTO_DATAGRAM) {
println "WARN: received unexpected protocol ${proto}" log.warning("received unexpected protocol ${proto}")
return return
} }
@@ -95,19 +98,19 @@ public class HostCache {
try { try {
dissector.loadI2PDatagram(payload) dissector.loadI2PDatagram(payload)
def sender = dissector.getSender() def sender = dissector.getSender()
println "INFO: Received something from ${sender.toBase32()}" def b32 = sender.toBase32()
payload = dissector.getPayload() payload = dissector.getPayload()
payload = json.parse(payload) payload = json.parse(payload)
if (payload.type == null) { if (payload.type == null) {
println "WARN: type field missing" log.warning("type field missing from $b32")
return return
} }
switch(payload.type) { switch(payload.type) {
case "Ping" : case "Ping" :
println "Ping" log.info("ping from $b32")
if (payload.leaf == null) { if (payload.leaf == null) {
println "WARN: ping didn't specify if leaf" log.warning("ping didn't specify if leaf from $b32")
return return
} }
payload.leaf = Boolean.parseBoolean(payload.leaf.toString()) payload.leaf = Boolean.parseBoolean(payload.leaf.toString())
@@ -116,14 +119,14 @@ public class HostCache {
respond(session, sender, payload) respond(session, sender, payload)
break break
case "CrawlerPong": case "CrawlerPong":
println "CrawlerPong" log.info("CrawlerPong from $b32")
crawler.handleCrawlerPong(payload, sender) crawler.handleCrawlerPong(payload, sender)
break break
default: default:
println "WARN: Unexpected message type ${payload.type}, dropping" log.warning("Unexpected message type ${payload.type}, dropping from $b32")
} }
} catch (Exception dfe) { } catch (Exception dfe) {
println "WARN: invalid datagram ${dfe}" log.log(Level.WARNING,"invalid datagram", dfe)
} }
} }
void messageAvailable(I2PSession session, int msgId, long size) { void messageAvailable(I2PSession session, int msgId, long size) {

View File

@@ -2,6 +2,7 @@ package com.muwire.update
import java.util.logging.Level import java.util.logging.Level
import groovy.json.JsonSlurper
import groovy.util.logging.Log import groovy.util.logging.Log
import net.i2p.client.I2PClientFactory import net.i2p.client.I2PClientFactory
import net.i2p.client.I2PSession import net.i2p.client.I2PSession
@@ -55,7 +56,7 @@ class UpdateServer {
static class Listener implements I2PSessionMuxedListener { static class Listener implements I2PSessionMuxedListener {
private final File json private final File json
private final def slurper = new JsonSlurper()
Listener(File json) { Listener(File json) {
this.json = json this.json = json
} }
@@ -76,8 +77,9 @@ class UpdateServer {
try { try {
dissector.loadI2PDatagram(payload) dissector.loadI2PDatagram(payload)
def sender = dissector.getSender() def sender = dissector.getSender()
log.info("Got an update ping from "+sender.toBase32()) payload = slurper.parse(dissector.getPayload())
// I don't think we care about the payload at this point log.info("Got an update ping from "+sender.toBase32() + " reported version "+payload?.myVersion)
def maker = new I2PDatagramMaker(session) def maker = new I2PDatagramMaker(session)
def response = maker.makeI2PDatagram(json.bytes) def response = maker.makeI2PDatagram(json.bytes)
session.sendMessage(sender, response, I2PSession.PROTO_DATAGRAM, 0, 2) session.sendMessage(sender, response, I2PSession.PROTO_DATAGRAM, 0, 2)