remove trailing spaces

This commit is contained in:
Zlatin Balevsky
2019-07-05 16:24:19 +01:00
parent 94bb7022eb
commit 29cdbf018c
126 changed files with 1952 additions and 1541 deletions

View File

@ -16,24 +16,24 @@ import com.muwire.core.upload.UploadEvent
import com.muwire.core.upload.UploadFinishedEvent
class Cli {
public static void main(String[] args) {
def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home)
if (!home.exists())
home.mkdirs()
def propsFile = new File(home,"MuWire.properties")
if (!propsFile.exists()) {
println "create props file ${propsFile.getAbsoluteFile()} before launching MuWire"
System.exit(1)
}
def props = new Properties()
propsFile.withInputStream { props.load(it) }
props = new MuWireSettings(props)
Core core
Core core
try {
core = new Core(props, home, "0.4.7")
} catch (Exception bad) {
@ -42,40 +42,40 @@ class Cli {
System.exit(1)
}
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
} else
filesList = args[0]
Thread.sleep(1000)
println "loading shared files from $filesList"
// listener for shared files
def sharedListener = new SharedListener()
core.eventBus.register(FileHashedEvent.class, sharedListener)
core.eventBus.register(FileLoadedEvent.class, sharedListener)
// for connections
def connectionsListener = new ConnectionListener()
core.eventBus.register(ConnectionEvent.class, connectionsListener)
core.eventBus.register(DisconnectionEvent.class, connectionsListener)
// for uploads
def uploadsListener = new UploadsListener()
core.eventBus.register(UploadEvent.class, uploadsListener)
core.eventBus.register(UploadFinishedEvent.class, uploadsListener)
Timer timer = new Timer("status-printer", true)
timer.schedule({
println String.valueOf(new Date()) + " Connections $connectionsListener.connections Uploads $uploadsListener.uploads Shared $sharedListener.shared"
} as TimerTask, 60000, 60000)
def latch = new CountDownLatch(1)
def fileLoader = new Object() {
public void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
@ -85,14 +85,14 @@ class Cli {
core.eventBus.register(AllFilesLoadedEvent.class, fileLoader)
core.startServices()
core.eventBus.publish(new UILoadedEvent())
core.eventBus.publish(new UILoadedEvent())
println "waiting for files to load"
latch.await()
// now we begin
println "MuWire is ready"
filesList = new File(filesList)
filesList.withReader {
filesList.withReader {
def toShare = it.readLine()
core.eventBus.publish(new FileSharedEvent(file : new File(toShare)))
}
@ -103,7 +103,7 @@ class Cli {
})
Thread.sleep(Integer.MAX_VALUE)
}
static class ConnectionListener {
volatile int connections
public void onConnectionEvent(ConnectionEvent e) {
@ -114,7 +114,7 @@ class Cli {
connections--
}
}
static class UploadsListener {
volatile int uploads
public void onUploadEvent(UploadEvent e) {
@ -126,7 +126,7 @@ class Cli {
println String.valueOf(new Date()) + " Finished upload of ${e.uploader.file.getName()} to ${e.uploader.request.downloader.getHumanReadableName()}"
}
}
static class SharedListener {
volatile int shared
void onFileHashedEvent(FileHashedEvent e) {

View File

@ -17,31 +17,31 @@ import com.muwire.core.search.UIResultEvent
import net.i2p.data.Base64
class CliDownloader {
private static final List<Downloader> downloaders = Collections.synchronizedList(new ArrayList<>())
private static final Map<UUID,ResultsHolder> resultsListeners = new ConcurrentHashMap<>()
public static void main(String []args) {
def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home)
if (!home.exists())
home.mkdirs()
def propsFile = new File(home,"MuWire.properties")
if (!propsFile.exists()) {
println "create props file ${propsFile.getAbsoluteFile()} before launching MuWire"
System.exit(1)
}
def props = new Properties()
propsFile.withInputStream { props.load(it) }
props = new MuWireSettings(props)
def filesList
int connections
int resultWait
if (args.length != 3) {
println "Enter a file containing list of hashes of files to download, " +
println "Enter a file containing list of hashes of files to download, " +
"how many connections you want before searching" +
"and how long to wait for results to arrive"
System.exit(1)
@ -59,18 +59,18 @@ class CliDownloader {
println "Failed to initialize core, exiting"
System.exit(1)
}
def latch = new CountDownLatch(connections)
def connectionListener = new ConnectionWaiter(latch : latch)
core.eventBus.register(ConnectionEvent.class, connectionListener)
core.startServices()
println "starting to wait until there are $connections connections"
latch.await()
println "connected, searching for files"
def file = new File(filesList)
file.eachLine {
String[] split = it.split(",")
@ -79,22 +79,22 @@ class CliDownloader {
def hash = Base64.decode(split[0])
def searchEvent = new SearchEvent(searchHash : hash, uuid : uuid)
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop:true,
replyTo: core.me.destination, receivedOn : core.me.destination, originator: core.me))
replyTo: core.me.destination, receivedOn : core.me.destination, originator: core.me))
}
println "waiting for results to arrive"
Thread.sleep(resultWait * 1000)
core.eventBus.register(DownloadStartedEvent.class, new DownloadListener())
resultsListeners.each { uuid, resultsListener ->
println "starting download of $resultsListener.fileName from ${resultsListener.getResults().size()} hosts"
File target = new File(resultsListener.fileName)
core.eventBus.publish(new UIDownloadEvent(target : target, result : resultsListener.getResults()))
}
Thread.sleep(1000)
Timer timer = new Timer("stats-printer")
timer.schedule({
println "==== STATUS UPDATE ==="
@ -109,7 +109,7 @@ class CliDownloader {
}
println "==== END ==="
} as TimerTask, 60000, 60000)
println "waiting for downloads to finish"
while(true) {
boolean allFinished = true
@ -120,10 +120,10 @@ class CliDownloader {
break
Thread.sleep(1000)
}
println "all downloads finished"
}
static class ResultsHolder {
final List<UIResultEvent> results = Collections.synchronizedList(new ArrayList<>())
String fileName
@ -134,7 +134,7 @@ class CliDownloader {
results
}
}
static class ResultsListener {
UUID uuid
String fileName
@ -148,7 +148,7 @@ class CliDownloader {
listener.add(e)
}
}
static class ConnectionWaiter {
CountDownLatch latch
public void onConnectionEvent(ConnectionEvent e) {
@ -156,7 +156,7 @@ class CliDownloader {
latch.countDown()
}
}
static class DownloadListener {
public void onDownloadStartedEvent(DownloadStartedEvent e) {

View File

@ -11,10 +11,10 @@ class FileList {
println "pass files.json as argument"
System.exit(1)
}
def slurper = new JsonSlurper()
File filesJson = new File(args[0])
filesJson.eachLine {
filesJson.eachLine {
def json = slurper.parseText(it)
String name = DataUtil.readi18nString(Base64.decode(json.file))
println "$name,$json.length,$json.pieceSize,$json.infoHash"

View File

@ -4,10 +4,10 @@ import net.i2p.crypto.SigType
class Constants {
public static final byte PERSONA_VERSION = (byte)1
public static final SigType SIG_TYPE = SigType.EdDSA_SHA512_Ed25519
public static final SigType SIG_TYPE = SigType.EdDSA_SHA512_Ed25519
public static final int MAX_HEADER_SIZE = 0x1 << 14
public static final int MAX_HEADERS = 16
public static final String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}\\?]"
}

View File

@ -69,13 +69,13 @@ import net.i2p.router.RouterContext
@Log
public class Core {
final EventBus eventBus
final Persona me
final File home
final Properties i2pOptions
final MuWireSettings muOptions
private final TrustService trustService
private final TrustSubscriber trustSubscriber
private final PersisterService persisterService
@ -90,20 +90,20 @@ public class Core {
private final DirectoryWatcher directoryWatcher
final FileManager fileManager
final UploadManager uploadManager
private final Router router
final AtomicBoolean shutdown = new AtomicBoolean()
public Core(MuWireSettings props, File home, String myVersion) {
this.home = home
this.home = home
this.muOptions = props
i2pOptions = new Properties()
def i2pOptionsFile = new File(home,"i2p.properties")
if (i2pOptionsFile.exists()) {
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
if (!i2pOptions.containsKey("inbound.nickname"))
i2pOptions["inbound.nickname"] = "MuWire"
if (!i2pOptions.containsKey("outbound.nickname"))
@ -123,7 +123,7 @@ public class Core {
i2pOptions["i2np.udp.port"] = String.valueOf(port)
i2pOptionsFile.withOutputStream { i2pOptions.store(it, "") }
}
if (!props.embeddedRouter) {
log.info "Initializing I2P context"
I2PAppContext.getGlobalContext().logManager()
@ -146,18 +146,18 @@ public class Core {
while(!router.isRunning())
Thread.sleep(100)
}
log.info("initializing I2P socket manager")
def i2pClient = new I2PClientFactory().createClient()
File keyDat = new File(home, "key.dat")
if (!keyDat.exists()) {
log.info("Creating new key.dat")
keyDat.withOutputStream {
keyDat.withOutputStream {
i2pClient.createDestination(it, Constants.SIG_TYPE)
}
}
// options like tunnel length and quantity
I2PSession i2pSession
I2PSocketManager socketManager
@ -168,7 +168,7 @@ public class Core {
socketManager.getDefaultOptions().setConnectTimeout(30000)
socketManager.addDisconnectListener({eventBus.publish(new RouterDisconnectedEvent())} as DisconnectListener)
i2pSession = socketManager.getSession()
def destination = new Destination()
def spk = new SigningPrivateKey(Constants.SIG_TYPE)
keyDat.withInputStream {
@ -177,7 +177,7 @@ public class Core {
privateKey.readBytes(it)
spk.readBytes(it)
}
def baos = new ByteArrayOutputStream()
def daos = new DataOutputStream(baos)
daos.write(Constants.PERSONA_VERSION)
@ -195,14 +195,14 @@ public class Core {
log.info("Loaded myself as "+me.getHumanReadableName())
eventBus = new EventBus()
log.info("initializing trust service")
File goodTrust = new File(home, "trusted")
File badTrust = new File(home, "distrusted")
trustService = new TrustService(goodTrust, badTrust, 5000)
eventBus.register(TrustEvent.class, trustService)
log.info "initializing file manager"
fileManager = new FileManager(eventBus, props)
eventBus.register(FileHashedEvent.class, fileManager)
@ -211,49 +211,49 @@ public class Core {
eventBus.register(FileUnsharedEvent.class, fileManager)
eventBus.register(SearchEvent.class, fileManager)
eventBus.register(DirectoryUnsharedEvent.class, fileManager)
log.info("initializing mesh manager")
MeshManager meshManager = new MeshManager(fileManager, home, props)
eventBus.register(SourceDiscoveredEvent.class, meshManager)
log.info "initializing persistence service"
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 15000, fileManager)
eventBus.register(UILoadedEvent.class, persisterService)
log.info("initializing host cache")
File hostStorage = new File(home, "hosts.json")
hostCache = new HostCache(trustService,hostStorage, 30000, props, i2pSession.getMyDestination())
eventBus.register(HostDiscoveredEvent.class, hostCache)
eventBus.register(ConnectionEvent.class, hostCache)
log.info("initializing connection manager")
connectionManager = props.isLeaf() ?
new LeafConnectionManager(eventBus, me, 3, hostCache, props) :
connectionManager = props.isLeaf() ?
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)
eventBus.register(QueryEvent.class, connectionManager)
log.info("initializing cache client")
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
log.info("initializing update client")
updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props, fileManager, me)
eventBus.register(FileDownloadedEvent.class, updateClient)
eventBus.register(UIResultBatchEvent.class, updateClient)
log.info("initializing connector")
I2PConnector i2pConnector = new I2PConnector(socketManager)
log.info "initializing results sender"
ResultsSender resultsSender = new ResultsSender(eventBus, i2pConnector, me)
log.info "initializing search manager"
SearchManager searchManager = new SearchManager(eventBus, me, resultsSender)
eventBus.register(QueryEvent.class, searchManager)
eventBus.register(ResultsEvent.class, searchManager)
log.info("initializing download manager")
downloadManager = new DownloadManager(eventBus, trustService, meshManager, props, i2pConnector, home, me)
eventBus.register(UIDownloadEvent.class, downloadManager)
@ -263,34 +263,34 @@ public class Core {
eventBus.register(SourceDiscoveredEvent.class, downloadManager)
eventBus.register(UIDownloadPausedEvent.class, downloadManager)
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
log.info("initializing upload manager")
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
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,
connectionAcceptor = new ConnectionAcceptor(eventBus, connectionManager, props,
i2pAcceptor, hostCache, trustService, searchManager, uploadManager, connectionEstablisher)
log.info("initializing directory watcher")
directoryWatcher = new DirectoryWatcher(eventBus, fileManager)
eventBus.register(FileSharedEvent.class, directoryWatcher)
eventBus.register(AllFilesLoadedEvent.class, directoryWatcher)
eventBus.register(DirectoryUnsharedEvent.class, directoryWatcher)
log.info("initializing hasher service")
hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
eventBus.register(FileSharedEvent.class, hasherService)
log.info("initializing trust subscriber")
trustSubscriber = new TrustSubscriber(eventBus, i2pConnector, props)
eventBus.register(UILoadedEvent.class, trustSubscriber)
eventBus.register(TrustSubscriptionEvent.class, trustSubscriber)
}
public void startServices() {
hasherService.start()
trustService.start()
@ -303,7 +303,7 @@ public class Core {
hostCache.waitForLoad()
updateClient.start()
}
public void shutdown() {
if (!shutdown.compareAndSet(false, true)) {
log.info("already shutting down")
@ -336,7 +336,7 @@ public class Core {
log.info("creating home dir")
home.mkdir()
}
def props = new Properties()
def propsFile = new File(home, "MuWire.properties")
if (propsFile.exists()) {
@ -352,10 +352,10 @@ public class Core {
props.write(it)
}
}
Core core = new Core(props, home, "0.4.7")
core.startServices()
// ... at the end, sleep or execute script
if (args.length == 0) {
log.info("initialized everything, sleeping")

View File

@ -0,0 +1,380 @@
package com.muwire.core
import java.nio.charset.StandardCharsets
import java.util.concurrent.atomic.AtomicBoolean
import com.muwire.core.connection.ConnectionAcceptor
import com.muwire.core.connection.ConnectionEstablisher
import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.connection.ConnectionManager
import com.muwire.core.connection.DisconnectionEvent
import com.muwire.core.connection.I2PAcceptor
import com.muwire.core.connection.I2PConnector
import com.muwire.core.connection.LeafConnectionManager
import com.muwire.core.connection.UltrapeerConnectionManager
import com.muwire.core.download.DownloadManager
import com.muwire.core.download.SourceDiscoveredEvent
import com.muwire.core.download.UIDownloadCancelledEvent
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.download.UIDownloadPausedEvent
import com.muwire.core.download.UIDownloadResumedEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileHashingEvent
import com.muwire.core.files.FileHasher
import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileManager
import com.muwire.core.files.FileSharedEvent
import com.muwire.core.files.FileUnsharedEvent
import com.muwire.core.files.HasherService
import com.muwire.core.files.PersisterService
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.DirectoryUnsharedEvent
import com.muwire.core.files.DirectoryWatcher
import com.muwire.core.hostcache.CacheClient
import com.muwire.core.hostcache.HostCache
import com.muwire.core.hostcache.HostDiscoveredEvent
import com.muwire.core.mesh.MeshManager
import com.muwire.core.search.QueryEvent
import com.muwire.core.search.ResultsEvent
import com.muwire.core.search.ResultsSender
import com.muwire.core.search.SearchEvent
import com.muwire.core.search.SearchManager
import com.muwire.core.search.UIResultBatchEvent
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustService
import com.muwire.core.trust.TrustSubscriber
import com.muwire.core.trust.TrustSubscriptionEvent
import com.muwire.core.update.UpdateClient
import com.muwire.core.upload.UploadManager
import com.muwire.core.util.MuWireLogManager
import groovy.util.logging.Log
import net.i2p.I2PAppContext
import net.i2p.client.I2PClientFactory
import net.i2p.client.I2PSession
import net.i2p.client.streaming.I2PSocketManager
import net.i2p.client.streaming.I2PSocketManagerFactory
import net.i2p.client.streaming.I2PSocketOptions
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener
import net.i2p.crypto.DSAEngine
import net.i2p.crypto.SigType
import net.i2p.data.Destination
import net.i2p.data.PrivateKey
import net.i2p.data.Signature
import net.i2p.data.SigningPrivateKey
import net.i2p.router.Router
import net.i2p.router.RouterContext
@Log
public class Core {
final EventBus eventBus
final Persona me
final File home
final Properties i2pOptions
final MuWireSettings muOptions
private final TrustService trustService
private final TrustSubscriber trustSubscriber
private final PersisterService persisterService
private final HostCache hostCache
private final ConnectionManager connectionManager
private final CacheClient cacheClient
private final UpdateClient updateClient
private final ConnectionAcceptor connectionAcceptor
private final ConnectionEstablisher connectionEstablisher
private final HasherService hasherService
private final DownloadManager downloadManager
private final DirectoryWatcher directoryWatcher
final FileManager fileManager
final UploadManager uploadManager
private final Router router
final AtomicBoolean shutdown = new AtomicBoolean()
public Core(MuWireSettings props, File home, String myVersion) {
this.home = home
this.muOptions = props
i2pOptions = new Properties()
def i2pOptionsFile = new File(home,"i2p.properties")
if (i2pOptionsFile.exists()) {
i2pOptionsFile.withInputStream { i2pOptions.load(it) }
if (!i2pOptions.containsKey("inbound.nickname"))
i2pOptions["inbound.nickname"] = "MuWire"
if (!i2pOptions.containsKey("outbound.nickname"))
i2pOptions["outbound.nickname"] = "MuWire"
} else {
i2pOptions["inbound.nickname"] = "MuWire"
i2pOptions["outbound.nickname"] = "MuWire"
i2pOptions["inbound.length"] = "3"
i2pOptions["inbound.quantity"] = "4"
i2pOptions["outbound.length"] = "3"
i2pOptions["outbound.quantity"] = "4"
i2pOptions["i2cp.tcp.host"] = "127.0.0.1"
i2pOptions["i2cp.tcp.port"] = "7654"
Random r = new Random()
int port = r.nextInt(60000) + 4000
i2pOptions["i2np.ntcp.port"] = String.valueOf(port)
i2pOptions["i2np.udp.port"] = String.valueOf(port)
i2pOptionsFile.withOutputStream { i2pOptions.store(it, "") }
}
if (!props.embeddedRouter) {
log.info "Initializing I2P context"
I2PAppContext.getGlobalContext().logManager()
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
router = null
} else {
log.info("launching embedded router")
Properties routerProps = new Properties()
routerProps.setProperty("i2p.dir.config", home.getAbsolutePath())
routerProps.setProperty("router.excludePeerCaps", "KLM")
routerProps.setProperty("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
routerProps.setProperty("i2np.outboundKBytesPerSecond", String.valueOf(props.outBw))
routerProps.setProperty("i2cp.disableInterface", "true")
routerProps.setProperty("i2np.ntcp.port", i2pOptions["i2np.ntcp.port"])
routerProps.setProperty("i2np.udp.port", i2pOptions["i2np.udp.port"])
routerProps.setProperty("i2np.udp.internalPort", i2pOptions["i2np.udp.port"])
router = new Router(routerProps)
router.getContext().setLogManager(new MuWireLogManager())
router.runRouter()
while(!router.isRunning())
Thread.sleep(100)
}
log.info("initializing I2P socket manager")
def i2pClient = new I2PClientFactory().createClient()
File keyDat = new File(home, "key.dat")
if (!keyDat.exists()) {
log.info("Creating new key.dat")
keyDat.withOutputStream {
i2pClient.createDestination(it, Constants.SIG_TYPE)
}
}
// options like tunnel length and quantity
I2PSession i2pSession
I2PSocketManager socketManager
keyDat.withInputStream {
socketManager = new I2PSocketManagerFactory().createManager(it, i2pOptions["i2cp.tcp.host"], i2pOptions["i2cp.tcp.port"].toInteger(), i2pOptions)
}
socketManager.getDefaultOptions().setReadTimeout(60000)
socketManager.getDefaultOptions().setConnectTimeout(30000)
socketManager.addDisconnectListener({eventBus.publish(new RouterDisconnectedEvent())} as DisconnectListener)
i2pSession = socketManager.getSession()
def destination = new Destination()
def spk = new SigningPrivateKey(Constants.SIG_TYPE)
keyDat.withInputStream {
destination.readBytes(it)
def privateKey = new PrivateKey()
privateKey.readBytes(it)
spk.readBytes(it)
}
def baos = new ByteArrayOutputStream()
def daos = new DataOutputStream(baos)
daos.write(Constants.PERSONA_VERSION)
daos.writeShort((short)props.getNickname().length())
daos.write(props.getNickname().getBytes(StandardCharsets.UTF_8))
destination.writeBytes(daos)
daos.flush()
byte [] payload = baos.toByteArray()
Signature sig = DSAEngine.getInstance().sign(payload, spk)
baos = new ByteArrayOutputStream()
baos.write(payload)
sig.writeBytes(baos)
me = new Persona(new ByteArrayInputStream(baos.toByteArray()))
log.info("Loaded myself as "+me.getHumanReadableName())
eventBus = new EventBus()
log.info("initializing trust service")
File goodTrust = new File(home, "trusted")
File badTrust = new File(home, "distrusted")
trustService = new TrustService(goodTrust, badTrust, 5000)
eventBus.register(TrustEvent.class, trustService)
log.info "initializing file manager"
fileManager = new FileManager(eventBus, props)
eventBus.register(FileHashedEvent.class, fileManager)
eventBus.register(FileLoadedEvent.class, fileManager)
eventBus.register(FileDownloadedEvent.class, fileManager)
eventBus.register(FileUnsharedEvent.class, fileManager)
eventBus.register(SearchEvent.class, fileManager)
eventBus.register(DirectoryUnsharedEvent.class, fileManager)
log.info("initializing mesh manager")
MeshManager meshManager = new MeshManager(fileManager, home, props)
eventBus.register(SourceDiscoveredEvent.class, meshManager)
log.info "initializing persistence service"
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 15000, fileManager)
eventBus.register(UILoadedEvent.class, persisterService)
log.info("initializing host cache")
File hostStorage = new File(home, "hosts.json")
hostCache = new HostCache(trustService,hostStorage, 30000, props, i2pSession.getMyDestination())
eventBus.register(HostDiscoveredEvent.class, hostCache)
eventBus.register(ConnectionEvent.class, hostCache)
log.info("initializing connection manager")
connectionManager = props.isLeaf() ?
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)
eventBus.register(QueryEvent.class, connectionManager)
log.info("initializing cache client")
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
log.info("initializing update client")
updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props, fileManager, me)
eventBus.register(FileDownloadedEvent.class, updateClient)
eventBus.register(UIResultBatchEvent.class, updateClient)
log.info("initializing connector")
I2PConnector i2pConnector = new I2PConnector(socketManager)
log.info "initializing results sender"
ResultsSender resultsSender = new ResultsSender(eventBus, i2pConnector, me)
log.info "initializing search manager"
SearchManager searchManager = new SearchManager(eventBus, me, resultsSender)
eventBus.register(QueryEvent.class, searchManager)
eventBus.register(ResultsEvent.class, searchManager)
log.info("initializing download manager")
downloadManager = new DownloadManager(eventBus, trustService, meshManager, props, i2pConnector, home, me)
eventBus.register(UIDownloadEvent.class, downloadManager)
eventBus.register(UILoadedEvent.class, downloadManager)
eventBus.register(FileDownloadedEvent.class, downloadManager)
eventBus.register(UIDownloadCancelledEvent.class, downloadManager)
eventBus.register(SourceDiscoveredEvent.class, downloadManager)
eventBus.register(UIDownloadPausedEvent.class, downloadManager)
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
log.info("initializing upload manager")
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
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, connectionEstablisher)
log.info("initializing directory watcher")
directoryWatcher = new DirectoryWatcher(eventBus, fileManager)
eventBus.register(FileSharedEvent.class, directoryWatcher)
eventBus.register(AllFilesLoadedEvent.class, directoryWatcher)
eventBus.register(DirectoryUnsharedEvent.class, directoryWatcher)
log.info("initializing hasher service")
hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
eventBus.register(FileSharedEvent.class, hasherService)
log.info("initializing trust subscriber")
trustSubscriber = new TrustSubscriber(eventBus, i2pConnector, props)
eventBus.register(UILoadedEvent.class, trustSubscriber)
eventBus.register(TrustSubscriptionEvent.class, trustSubscriber)
}
public void startServices() {
hasherService.start()
trustService.start()
trustService.waitForLoad()
hostCache.start()
connectionManager.start()
cacheClient.start()
connectionAcceptor.start()
connectionEstablisher.start()
hostCache.waitForLoad()
updateClient.start()
}
public void shutdown() {
if (!shutdown.compareAndSet(false, true)) {
log.info("already shutting down")
return
}
log.info("shutting down trust subscriber")
trustSubscriber.stop()
log.info("shutting down download manageer")
downloadManager.shutdown()
log.info("shutting down connection acceeptor")
connectionAcceptor.stop()
log.info("shutting down connection establisher")
connectionEstablisher.stop()
log.info("shutting down directory watcher")
directoryWatcher.stop()
log.info("shutting down cache client")
cacheClient.stop()
log.info("shutting down connection manager")
connectionManager.shutdown()
if (router != null) {
log.info("shutting down embedded router")
router.shutdown(0)
}
}
static main(args) {
def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home)
if (!home.exists()) {
log.info("creating home dir")
home.mkdir()
}
def props = new Properties()
def propsFile = new File(home, "MuWire.properties")
if (propsFile.exists()) {
log.info("loading existing props file")
propsFile.withInputStream {
props.load(it)
}
props = new MuWireSettings(props)
} else {
log.info("creating default properties")
props = new MuWireSettings()
propsFile.withOutputStream {
props.write(it)
}
}
Core core = new Core(props, home, "0.4.7")
core.startServices()
// ... at the end, sleep or execute script
if (args.length == 0) {
log.info("initialized everything, sleeping")
Thread.sleep(Integer.MAX_VALUE)
} else {
log.info("executing script ${args[0]}")
File f = new File(args[0])
if (!f.exists()) {
log.warning("Script file doesn't exist")
System.exit(1)
}
def binding = new Binding()
def shell = new GroovyShell(binding)
binding.setProperty('eventBus', core.eventBus)
binding.setProperty('me', core.me)
// TOOD: other bindings?
def script = shell.parse(f)
script.run()
}
}
}

View File

@ -0,0 +1,31 @@
***************
*** 334,347 ****
RouterContextMetaClass() {
super(RouterContext.class)
}
-
Object invokeMethod(Object object, String name, Object[] args) {
if (name == "logManager")
return logManager
super.invokeMethod(object, name, args)
}
}
-
static main(args) {
def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home)
--- 334,347 ----
RouterContextMetaClass() {
super(RouterContext.class)
}
+
Object invokeMethod(Object object, String name, Object[] args) {
if (name == "logManager")
return logManager
super.invokeMethod(object, name, args)
}
}
+
static main(args) {
def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home)

View File

@ -7,12 +7,12 @@ class Event {
private static final AtomicLong SEQ_NO = new AtomicLong();
final long seqNo
final long timestamp
Event() {
seqNo = SEQ_NO.getAndIncrement()
timestamp = System.currentTimeMillis()
}
@Override
public String toString() {
"seqNo $seqNo timestamp $timestamp"

View File

@ -10,7 +10,7 @@ import com.muwire.core.files.FileSharedEvent
import groovy.util.logging.Log
@Log
class EventBus {
private Map handlers = new HashMap()
private final Executor executor = Executors.newSingleThreadExecutor {r ->
def rv = new Thread(r)
@ -22,7 +22,7 @@ class EventBus {
void publish(Event e) {
executor.execute({publishInternal(e)} as Runnable)
}
private void publishInternal(Event e) {
log.fine "publishing event $e of type ${e.getClass().getSimpleName()} event $e"
def currentHandlers
@ -38,7 +38,7 @@ class EventBus {
}
}
}
synchronized void register(Class<? extends Event> eventType, def handler) {
log.info "Registering $handler for type $eventType"
def currentHandlers = handlers.get(eventType)

View File

@ -13,5 +13,5 @@ class InvalidSignatureException extends Exception {
public InvalidSignatureException(Throwable cause) {
super(cause);
}
}

View File

@ -8,7 +8,7 @@ import com.muwire.core.util.DataUtil
import net.i2p.data.Base64
class MuWireSettings {
final boolean isLeaf
boolean allowUntrusted
boolean allowTrustLists
@ -28,11 +28,11 @@ class MuWireSettings {
int meshExpiration
boolean embeddedRouter
int inBw, outBw
MuWireSettings() {
this(new Properties())
}
MuWireSettings(Properties props) {
isLeaf = Boolean.valueOf(props.get("leaf","false"))
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
@ -40,7 +40,7 @@ class MuWireSettings {
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
nickname = props.getProperty("nickname","MuWireUser")
downloadLocation = new File((String)props.getProperty("downloadLocation",
downloadLocation = new File((String)props.getProperty("downloadLocation",
System.getProperty("user.home")))
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
@ -53,21 +53,21 @@ class MuWireSettings {
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
inBw = Integer.valueOf(props.getProperty("inBw","256"))
outBw = Integer.valueOf(props.getProperty("outBw","128"))
watchedDirectories = new HashSet<>()
if (props.containsKey("watchedDirectories")) {
String[] encoded = props.getProperty("watchedDirectories").split(",")
encoded.each { watchedDirectories << DataUtil.readi18nString(Base64.decode(it)) }
}
trustSubscriptions = new HashSet<>()
if (props.containsKey("trustSubscriptions")) {
props.getProperty("trustSubscriptions").split(",").each {
props.getProperty("trustSubscriptions").split(",").each {
trustSubscriptions.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
}
}
}
void write(OutputStream out) throws IOException {
Properties props = new Properties()
props.setProperty("leaf", isLeaf.toString())
@ -88,44 +88,44 @@ class MuWireSettings {
props.setProperty("embeddedRouter", String.valueOf(embeddedRouter))
props.setProperty("inBw", String.valueOf(inBw))
props.setProperty("outBw", String.valueOf(outBw))
if (!watchedDirectories.isEmpty()) {
String encoded = watchedDirectories.stream().
map({Base64.encode(DataUtil.encodei18nString(it))}).
collect(Collectors.joining(","))
props.setProperty("watchedDirectories", encoded)
}
if (!trustSubscriptions.isEmpty()) {
String encoded = trustSubscriptions.stream().
map({it.toBase64()}).
collect(Collectors.joining(","))
props.setProperty("trustSubscriptions", encoded)
}
props.store(out, "")
}
boolean isLeaf() {
isLeaf
}
boolean allowUntrusted() {
allowUntrusted
}
}
void setAllowUntrusted(boolean allowUntrusted) {
this.allowUntrusted = allowUntrusted
}
CrawlerResponse getCrawlerResponse() {
crawlerResponse
}
void setCrawlerResponse(CrawlerResponse crawlerResponse) {
this.crawlerResponse = crawlerResponse
}
String getNickname() {
nickname
}

View File

@ -7,11 +7,11 @@ import java.nio.charset.StandardCharsets
*/
public class Name {
final String name
Name(String name) {
this.name = name
}
Name(InputStream nameStream) throws IOException {
DataInputStream dis = new DataInputStream(nameStream)
int length = dis.readUnsignedShort()
@ -19,22 +19,22 @@ public class Name {
dis.readFully(nameBytes)
this.name = new String(nameBytes, StandardCharsets.UTF_8)
}
public void write(OutputStream out) throws IOException {
DataOutputStream dos = new DataOutputStream(out)
dos.writeShort(name.length())
dos.write(name.getBytes(StandardCharsets.UTF_8))
}
public getName() {
name
}
@Override
public int hashCode() {
name.hashCode()
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Name))

View File

@ -9,7 +9,7 @@ import net.i2p.data.SigningPublicKey
public class Persona {
private static final int SIG_LEN = Constants.SIG_TYPE.getSigLen()
private final byte version
private final Name name
private final Destination destination
@ -17,12 +17,12 @@ public class Persona {
private volatile String humanReadableName
private volatile String base64
private volatile byte[] payload
public Persona(InputStream personaStream) throws IOException, InvalidSignatureException {
version = (byte) (personaStream.read() & 0xFF)
if (version != Constants.PERSONA_VERSION)
throw new IOException("Unknown version "+version)
name = new Name(personaStream)
destination = Destination.create(personaStream)
sig = new byte[SIG_LEN]
@ -31,7 +31,7 @@ public class Persona {
if (!verify(version, name, destination, sig))
throw new InvalidSignatureException(getHumanReadableName() + " didn't verify")
}
private static boolean verify(byte version, Name name, Destination destination, byte [] sig) {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
baos.write(version)
@ -42,7 +42,7 @@ public class Persona {
Signature signature = new Signature(Constants.SIG_TYPE, sig)
DSAEngine.getInstance().verifySignature(signature, payload, spk)
}
public void write(OutputStream out) throws IOException {
if (payload == null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
@ -54,13 +54,13 @@ public class Persona {
}
out.write(payload)
}
public String getHumanReadableName() {
if (humanReadableName == null)
if (humanReadableName == null)
humanReadableName = name.getName() + "@" + destination.toBase32().substring(0,32)
humanReadableName
}
public String toBase64() {
if (base64 == null) {
def baos = new ByteArrayOutputStream()
@ -69,12 +69,12 @@ public class Persona {
}
base64
}
@Override
public int hashCode() {
name.hashCode() ^ destination.hashCode()
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Persona))
@ -82,7 +82,7 @@ public class Persona {
Persona other = (Persona)o
name.equals(other.name) && destination.equals(other.destination)
}
public static void main(String []args) {
if (args.length != 1) {
println "This utility decodes a bas64-encoded persona"

View File

@ -3,9 +3,9 @@ package com.muwire.core
abstract class Service {
volatile boolean loaded
abstract void load()
void waitForLoad() {
while (!loaded)
Thread.sleep(10)

View File

@ -21,7 +21,7 @@ import net.i2p.data.Destination
@Log
abstract class Connection implements Closeable {
private static final int SEARCHES = 10
private static final long INTERVAL = 1000
@ -31,17 +31,17 @@ abstract class Connection implements Closeable {
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
private final LinkedList<Long> searchTimestamps = new LinkedList<>()
protected final String name
long lastPingSentTime, lastPongReceivedTime
Connection(EventBus eventBus, Endpoint endpoint, boolean incoming,
Connection(EventBus eventBus, Endpoint endpoint, boolean incoming,
HostCache hostCache, TrustService trustService, MuWireSettings settings) {
this.eventBus = eventBus
this.incoming = incoming
@ -49,18 +49,18 @@ abstract class Connection implements Closeable {
this.hostCache = hostCache
this.trustService = trustService
this.settings = settings
this.name = endpoint.destination.toBase32().substring(0,8)
this.reader = new Thread({readLoop()} as Runnable)
this.reader.setName("reader-$name")
this.reader.setDaemon(true)
this.writer = new Thread({writeLoop()} as Runnable)
this.writer.setName("writer-$name")
this.writer.setDaemon(true)
}
/**
* starts the connection threads
*/
@ -72,7 +72,7 @@ abstract class Connection implements Closeable {
reader.start()
writer.start()
}
@Override
public void close() {
if (!running.compareAndSet(true, false)) {
@ -85,7 +85,7 @@ abstract class Connection implements Closeable {
endpoint.close()
eventBus.publish(new DisconnectionEvent(destination: endpoint.destination))
}
protected void readLoop() {
try {
while(running.get()) {
@ -98,9 +98,9 @@ abstract class Connection implements Closeable {
close()
}
}
protected abstract void read()
protected void writeLoop() {
try {
while(running.get()) {
@ -113,9 +113,9 @@ abstract class Connection implements Closeable {
close()
}
}
protected abstract void write(def message);
void sendPing() {
def ping = [:]
ping.type = "Ping"
@ -123,7 +123,7 @@ abstract class Connection implements Closeable {
messages.put(ping)
lastPingSentTime = System.currentTimeMillis()
}
void sendQuery(QueryEvent e) {
def query = [:]
query.type = "Search"
@ -139,7 +139,7 @@ abstract class Connection implements Closeable {
query.originator = e.originator.toBase64()
messages.put(query)
}
protected void handlePing() {
log.fine("$name received ping")
def pong = [:]
@ -148,18 +148,18 @@ abstract class Connection implements Closeable {
pong.pongs = hostCache.getGoodHosts(10).collect { d -> d.toBase64() }
messages.put(pong)
}
protected void handlePong(def pong) {
log.fine("$name received pong")
lastPongReceivedTime = System.currentTimeMillis()
if (pong.pongs == null)
throw new Exception("Pong doesn't have pongs")
pong.pongs.each {
pong.pongs.each {
def dest = new Destination(it)
eventBus.publish(new HostDiscoveredEvent(destination: dest))
}
}
private boolean throttleSearch() {
final long now = System.currentTimeMillis()
if (searchTimestamps.size() < SEARCHES) {
@ -173,19 +173,19 @@ abstract class Connection implements Closeable {
searchTimestamps.removeFirst()
false
}
protected void handleSearch(def search) {
if (throttleSearch()) {
log.info("dropping excessive search")
return
}
}
UUID uuid = UUID.fromString(search.uuid)
byte [] infohash = null
if (search.infohash != null) {
search.keywords = null
infohash = Base64.decode(search.infohash)
}
Destination replyTo = new Destination(search.replyTo)
TrustLevel trustLevel = trustService.getLevel(replyTo)
if (trustLevel == TrustLevel.DISTRUSTED) {
@ -196,7 +196,7 @@ abstract class Connection implements Closeable {
log.info("dropping search from neutral peer")
return
}
Persona originator = null
if (search.originator != null) {
originator = new Persona(new ByteArrayInputStream(Base64.decode(search.originator)))
@ -205,11 +205,11 @@ abstract class Connection implements Closeable {
return
}
}
boolean oob = false
if (search.oobInfohash != null)
oob = search.oobInfohash
SearchEvent searchEvent = new SearchEvent(searchTerms : search.keywords,
searchHash : infohash,
uuid : uuid,
@ -220,6 +220,6 @@ abstract class Connection implements Closeable {
receivedOn : endpoint.destination,
firstHop : search.firstHop )
eventBus.publish(event)
}
}

View File

@ -35,15 +35,15 @@ class ConnectionAcceptor {
final I2PAcceptor acceptor
final HostCache hostCache
final TrustService trustService
final SearchManager searchManager
final SearchManager searchManager
final UploadManager uploadManager
final ConnectionEstablisher establisher
final ExecutorService acceptorThread
final ExecutorService handshakerThreads
private volatile shutdown
ConnectionAcceptor(EventBus eventBus, UltrapeerConnectionManager manager,
MuWireSettings settings, I2PAcceptor acceptor, HostCache hostCache,
TrustService trustService, SearchManager searchManager, UploadManager uploadManager,
@ -57,14 +57,14 @@ class ConnectionAcceptor {
this.searchManager = searchManager
this.uploadManager = uploadManager
this.establisher = establisher
acceptorThread = Executors.newSingleThreadExecutor { r ->
acceptorThread = Executors.newSingleThreadExecutor { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("acceptor")
rv
}
handshakerThreads = Executors.newCachedThreadPool { r ->
def rv = new Thread(r)
rv.setDaemon(true)
@ -72,17 +72,17 @@ class ConnectionAcceptor {
rv
}
}
void start() {
acceptorThread.execute({acceptLoop()} as Runnable)
}
void stop() {
shutdown = true
acceptorThread.shutdownNow()
handshakerThreads.shutdownNow()
}
private void acceptLoop() {
try {
while(true) {
@ -106,7 +106,7 @@ class ConnectionAcceptor {
throw e
}
}
private void processIncoming(Endpoint e) {
InputStream is = e.inputStream
try {
@ -138,7 +138,7 @@ class ConnectionAcceptor {
eventBus.publish new ConnectionEvent(endpoint: e, incoming: true, leaf: null, status: ConnectionAttemptStatus.FAILED)
}
}
private void processMuWire(Endpoint e) {
byte[] uWire = "uWire ".bytes
for (int i = 0; i < uWire.length; i++) {
@ -147,21 +147,21 @@ class ConnectionAcceptor {
throw new IOException("unexpected value $read at position $i")
}
}
byte[] type = new byte[4]
DataInputStream dis = new DataInputStream(e.inputStream)
dis.readFully(type)
if (type == "leaf".bytes)
handleIncoming(e, true)
else if (type == "peer".bytes)
handleIncoming(e, false)
else
else
throw new IOException("unknown connection type $type")
}
private void handleIncoming(Endpoint e, boolean leaf) {
boolean accept = !manager.isConnected(e.destination) &&
boolean accept = !manager.isConnected(e.destination) &&
!establisher.isInProgress(e.destination) &&
(leaf ? manager.hasLeafSlots() : manager.hasPeerSlots())
if (accept) {
@ -187,9 +187,9 @@ class ConnectionAcceptor {
eventBus.publish(new ConnectionEvent(endpoint: e, incoming: true, leaf: leaf, status: ConnectionAttemptStatus.REJECTED))
}
}
private void processGET(Endpoint e) {
byte[] et = new byte[3]
final DataInputStream dis = new DataInputStream(e.getInputStream())
@ -198,7 +198,7 @@ class ConnectionAcceptor {
throw new IOException("Invalid GET connection")
uploadManager.processGET(e)
}
private void processHashList(Endpoint e) {
byte[] ashList = new byte[8]
final DataInputStream dis = new DataInputStream(e.getInputStream())
@ -207,7 +207,7 @@ class ConnectionAcceptor {
throw new IOException("Invalid HASHLIST connection")
uploadManager.processHashList(e)
}
private void processPOST(final Endpoint e) throws IOException {
byte [] ost = new byte[4]
final DataInputStream dis = new DataInputStream(e.getInputStream())
@ -246,7 +246,7 @@ class ConnectionAcceptor {
e.close()
}
}
private void processTRUST(Endpoint e) {
byte[] RUST = new byte[6]
DataInputStream dis = new DataInputStream(e.getInputStream())
@ -255,7 +255,7 @@ class ConnectionAcceptor {
throw new IOException("Invalid TRUST connection")
String header
while ((header = DataUtil.readTillRN(dis)) != ""); // ignore headers for now
OutputStream os = e.getOutputStream()
if (!settings.allowTrustLists) {
os.write("403 Not Allowed\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
@ -263,7 +263,7 @@ class ConnectionAcceptor {
e.close()
return
}
os.write("200 OK\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
List<Persona> good = new ArrayList<>(trustService.good.values())
int size = Math.min(Short.MAX_VALUE * 2, good.size())
@ -273,7 +273,7 @@ class ConnectionAcceptor {
good.each {
it.write(dos)
}
List<Persona> bad = new ArrayList<>(trustService.bad.values())
size = Math.min(Short.MAX_VALUE * 2, bad.size())
bad = bad.subList(0, size)
@ -281,9 +281,9 @@ class ConnectionAcceptor {
bad.each {
it.write(dos)
}
dos.flush()
e.close()
}
}

View File

@ -21,7 +21,7 @@ import net.i2p.util.ConcurrentHashSet
@Log
class ConnectionEstablisher {
private static final int CONCURRENT = 4
final EventBus eventBus
@ -29,14 +29,14 @@ class ConnectionEstablisher {
final MuWireSettings settings
final ConnectionManager connectionManager
final HostCache hostCache
final Timer timer
final ExecutorService executor
final Set inProgress = new ConcurrentHashSet()
ConnectionEstablisher(){}
ConnectionEstablisher(EventBus eventBus, I2PConnector i2pConnector, MuWireSettings settings,
ConnectionManager connectionManager, HostCache hostCache) {
this.eventBus = eventBus
@ -45,23 +45,23 @@ class ConnectionEstablisher {
this.connectionManager = connectionManager
this.hostCache = hostCache
timer = new Timer("connection-timer",true)
executor = Executors.newFixedThreadPool(CONCURRENT, { r ->
executor = Executors.newFixedThreadPool(CONCURRENT, { r ->
def rv = new Thread(r)
rv.setDaemon(true)
rv.setName("connector-${System.currentTimeMillis()}")
rv
rv
} as ThreadFactory)
}
void start() {
timer.schedule({connectIfNeeded()} as TimerTask, 100, 1000)
}
void stop() {
timer.cancel()
executor.shutdownNow()
}
private void connectIfNeeded() {
if (!connectionManager.needsConnections())
return
@ -84,19 +84,19 @@ class ConnectionEstablisher {
if (!connectionManager.isConnected(toTry) && inProgress.add(toTry))
executor.execute({connect(toTry)} as Runnable)
}
private void connect(Destination toTry) {
log.info("starting connect to ${toTry.toBase32()}")
try {
def endpoint = i2pConnector.connect(toTry)
log.info("successful transport connect to ${toTry.toBase32()}")
// outgoing handshake
endpoint.outputStream.write("MuWire ".bytes)
def type = settings.isLeaf() ? "leaf" : "peer"
endpoint.outputStream.write(type.bytes)
endpoint.outputStream.flush()
InputStream is = endpoint.inputStream
int read = is.read()
if (read == -1) {
@ -118,12 +118,12 @@ class ConnectionEstablisher {
inProgress.remove(toTry)
}
}
private void fail(Endpoint endpoint) {
endpoint.close()
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
}
private void readK(Endpoint e) {
int read = e.inputStream.read()
if (read != 'K') {
@ -131,14 +131,14 @@ class ConnectionEstablisher {
fail e
return
}
log.info("connection to ${e.destination.toBase32()} established")
// wrap into deflater / inflater streams and publish
def wrapped = new Endpoint(e.destination, new InflaterInputStream(e.inputStream), new DeflaterOutputStream(e.outputStream, true), e.toClose)
eventBus.publish(new ConnectionEvent(endpoint: wrapped, incoming: false, leaf: false, status: ConnectionAttemptStatus.SUCCESSFUL))
}
private void readEJECT(Endpoint e) {
byte[] eject = "EJECT".bytes
for (int i = 0; i < eject.length; i++) {
@ -150,8 +150,8 @@ class ConnectionEstablisher {
}
}
log.info("connection to ${e.destination.toBase32()} rejected")
eventBus.publish(new ConnectionEvent(endpoint: e, incoming: false, leaf: false, status: ConnectionAttemptStatus.REJECTED))
try {
DataInputStream dais = new DataInputStream(e.inputStream)
@ -178,7 +178,7 @@ class ConnectionEstablisher {
e.close()
}
}
public boolean isInProgress(Destination d) {
inProgress.contains(d)
}

View File

@ -10,7 +10,7 @@ class ConnectionEvent extends Event {
boolean incoming
Boolean leaf // can be null if uknown
ConnectionAttemptStatus status
@Override
public String toString() {
"ConnectionEvent ${super.toString()} endpoint: $endpoint incoming: $incoming leaf : $leaf status : $status"

View File

@ -11,19 +11,19 @@ import com.muwire.core.trust.TrustLevel
import net.i2p.data.Destination
abstract class ConnectionManager {
private static final int PING_TIME = 20000
final EventBus eventBus
private final Timer timer
protected final HostCache hostCache
protected final Persona me
protected final MuWireSettings settings
ConnectionManager() {}
ConnectionManager(EventBus eventBus, Persona me, HostCache hostCache, MuWireSettings settings) {
this.eventBus = eventBus
this.me = me
@ -31,42 +31,42 @@ abstract class ConnectionManager {
this.settings = settings
this.timer = new Timer("connections-pinger",true)
}
void start() {
timer.schedule({sendPings()} as TimerTask, 1000,1000)
}
void stop() {
timer.cancel()
getConnections().each { it.close() }
}
void onTrustEvent(TrustEvent e) {
if (e.level == TrustLevel.DISTRUSTED)
drop(e.persona.destination)
}
abstract void drop(Destination d)
abstract Collection<Connection> getConnections()
protected abstract int getDesiredConnections()
boolean needsConnections() {
return getConnections().size() < getDesiredConnections()
}
abstract boolean isConnected(Destination d)
abstract void onConnectionEvent(ConnectionEvent e)
abstract void onDisconnectionEvent(DisconnectionEvent e)
abstract void shutdown()
protected void sendPings() {
final long now = System.currentTimeMillis()
getConnections().each {
getConnections().each {
if (now - it.lastPingSentTime > PING_TIME)
it.sendPing()
}

View File

@ -5,7 +5,7 @@ import com.muwire.core.Event
import net.i2p.data.Destination
class DisconnectionEvent extends Event {
Destination destination
@Override

View File

@ -12,16 +12,16 @@ class Endpoint implements Closeable {
final InputStream inputStream
final OutputStream outputStream
final def toClose
private final AtomicBoolean closed = new AtomicBoolean()
Endpoint(Destination destination, InputStream inputStream, OutputStream outputStream, def toClose) {
this.destination = destination
this.inputStream = inputStream
this.outputStream = outputStream
this.toClose = toClose
}
@Override
public void close() {
if (!closed.compareAndSet(false, true)) {
@ -38,9 +38,9 @@ class Endpoint implements Closeable {
try {toClose.reset()} catch (Exception ignore) {}
}
}
@Override
public String toString() {
"destination: ${destination.toBase32()}"
}
}
}

View File

@ -7,14 +7,14 @@ class I2PAcceptor {
final I2PSocketManager socketManager
final I2PServerSocket serverSocket
I2PAcceptor() {}
I2PAcceptor(I2PSocketManager socketManager) {
this.socketManager = socketManager
this.serverSocket = socketManager.getServerSocket()
}
Endpoint accept() {
def socket = serverSocket.accept()
new Endpoint(socket.getPeerDestination(), socket.getInputStream(), socket.getOutputStream(), socket)

View File

@ -4,15 +4,15 @@ import net.i2p.client.streaming.I2PSocketManager
import net.i2p.data.Destination
class I2PConnector {
final I2PSocketManager socketManager
I2PConnector() {}
I2PConnector(I2PSocketManager socketManager) {
this.socketManager = socketManager
}
Endpoint connect(Destination dest) {
def socket = socketManager.connect(dest)
new Endpoint(dest, socket.getInputStream(), socket.getOutputStream(), socket)

View File

@ -11,13 +11,13 @@ import com.muwire.core.trust.TrustService
import net.i2p.data.Destination
/**
* Connection where the other side is a leaf.
* Connection where the other side is a leaf.
* Such connections can only be incoming.
* @author zab
*/
class LeafConnection extends Connection {
public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache,
public LeafConnection(EventBus eventBus, Endpoint endpoint, HostCache hostCache,
TrustService trustService, MuWireSettings settings) {
super(eventBus, endpoint, true, hostCache, trustService, settings);
}
@ -25,13 +25,13 @@ class LeafConnection extends Connection {
@Override
protected void read() {
// TODO Auto-generated method stub
}
@Override
protected void write(Object message) {
// TODO Auto-generated method stub
}
}

View File

@ -13,28 +13,28 @@ import net.i2p.data.Destination
@Log
class LeafConnectionManager extends ConnectionManager {
final int maxConnections
final Map<Destination, UltrapeerConnection> connections = new ConcurrentHashMap()
public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections,
public LeafConnectionManager(EventBus eventBus, Persona me, int maxConnections,
HostCache hostCache, MuWireSettings settings) {
super(eventBus, me, hostCache, settings)
this.maxConnections = maxConnections
}
@Override
public void drop(Destination d) {
// TODO Auto-generated method stub
}
void onQueryEvent(QueryEvent e) {
if (me.destination == e.receivedOn) {
connections.values().each { it.sendQuery(e) }
}
}
@Override
@ -60,21 +60,21 @@ class LeafConnectionManager extends ConnectionManager {
}
if (e.status != ConnectionAttemptStatus.SUCCESSFUL)
return
Connection c = new UltrapeerConnection(eventBus, e.endpoint)
connections.put(e.endpoint.destination, c)
c.start()
}
@Override
@Override
public void onDisconnectionEvent(DisconnectionEvent e) {
def removed = connections.remove(e.destination)
if (removed == null)
log.severe("removed destination not present in connection manager ${e.destination.toBase32()}")
}
@Override
void shutdown() {
}
}

View File

@ -20,13 +20,13 @@ import net.i2p.data.Destination
*/
@Log
class PeerConnection extends Connection {
private final DataInputStream dis
private final DataOutputStream dos
private final byte[] readHeader = new byte[3]
private final byte[] writeHeader = new byte[3]
private final JsonSlurper slurper = new JsonSlurper()
public PeerConnection(EventBus eventBus, Endpoint endpoint,
@ -42,10 +42,10 @@ class PeerConnection extends Connection {
dis.readFully(readHeader)
int length = DataUtil.readLength(readHeader)
log.fine("$name read length $length")
byte[] payload = new byte[length]
dis.readFully(payload)
if ((readHeader[0] & (byte)0x80) == 0x80) {
// TODO process binary
} else {
@ -73,7 +73,7 @@ class PeerConnection extends Connection {
} else {
// TODO: write binary
}
dos.write(writeHeader)
dos.write(payload)
dos.flush()

View File

@ -24,7 +24,7 @@ class UltrapeerConnection extends Connection {
@Override
protected void read() {
// TODO Auto-generated method stub
}
@Override
@ -37,10 +37,10 @@ class UltrapeerConnection extends Connection {
}
private void writeJsonMessage(def message) {
}
private void writeBinaryMessage(def message) {
}
}

View File

@ -15,16 +15,16 @@ import net.i2p.data.Destination
@Log
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,
public UltrapeerConnectionManager(EventBus eventBus, Persona me, int maxPeers, int maxLeafs,
HostCache hostCache, TrustService trustService, MuWireSettings settings) {
super(eventBus, me, hostCache, settings)
this.maxPeers = maxPeers
@ -36,7 +36,7 @@ class UltrapeerConnectionManager extends ConnectionManager {
peerConnections.get(d)?.close()
leafConnections.get(d)?.close()
}
void onQueryEvent(QueryEvent e) {
forwardQueryToLeafs(e)
if (!e.firstHop)
@ -57,15 +57,15 @@ class UltrapeerConnectionManager extends ConnectionManager {
rv.addAll(leafConnections.values())
rv
}
boolean hasLeafSlots() {
leafConnections.size() < maxLeafs
}
boolean hasPeerSlots() {
peerConnections.size() < maxPeers
}
@Override
protected int getDesiredConnections() {
return maxPeers / 2;
@ -81,18 +81,18 @@ class UltrapeerConnectionManager extends ConnectionManager {
log.severe("Inconsistent event $e")
return
}
if (e.status != ConnectionAttemptStatus.SUCCESSFUL)
return
Connection c = e.leaf ?
new LeafConnection(eventBus, e.endpoint, hostCache, trustService, settings) :
Connection c = e.leaf ?
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()
}
@Override
public void onDisconnectionEvent(DisconnectionEvent e) {
def removed = peerConnections.remove(e.destination)
@ -101,7 +101,7 @@ class UltrapeerConnectionManager extends ConnectionManager {
if (removed == null)
log.severe("Removed connection not present in either leaf or peer map ${e.destination.toBase32()}")
}
@Override
void shutdown() {
peerConnections.values().stream().parallel().forEach({v -> v.close()})
@ -109,8 +109,8 @@ class UltrapeerConnectionManager extends ConnectionManager {
peerConnections.clear()
leafConnections.clear()
}
void forwardQueryToLeafs(QueryEvent e) {
}
}

View File

@ -21,5 +21,5 @@ class BadHashException extends Exception {
public BadHashException(Throwable cause) {
super(cause);
}
}

View File

@ -27,7 +27,7 @@ import java.util.concurrent.Executor
import java.util.concurrent.Executors
public class DownloadManager {
private final EventBus eventBus
private final TrustService trustService
private final MeshManager meshManager
@ -36,9 +36,9 @@ public class DownloadManager {
private final Executor executor
private final File incompletes, home
private final Persona me
private final Map<InfoHash, Downloader> downloaders = new ConcurrentHashMap<>()
public DownloadManager(EventBus eventBus, TrustService trustService, MeshManager meshManager, MuWireSettings muSettings,
I2PConnector connector, File home, Persona me) {
this.eventBus = eventBus
@ -49,9 +49,9 @@ public class DownloadManager {
this.incompletes = new File(home,"incompletes")
this.home = home
this.me = me
incompletes.mkdir()
this.executor = Executors.newCachedThreadPool({ r ->
Thread rv = new Thread(r)
rv.setName("download-worker")
@ -59,23 +59,23 @@ public class DownloadManager {
rv
})
}
public void onUIDownloadEvent(UIDownloadEvent e) {
def size = e.result[0].size
def infohash = e.result[0].infohash
def pieceSize = e.result[0].pieceSize
Set<Destination> destinations = new HashSet<>()
e.result.each {
e.result.each {
destinations.add(it.sender.destination)
}
destinations.addAll(e.sources)
destinations.remove(me.destination)
Pieces pieces = getPieces(infohash, size, pieceSize)
def downloader = new Downloader(eventBus, this, me, e.target, size,
infohash, pieceSize, connector, destinations,
incompletes, pieces)
@ -84,24 +84,24 @@ public class DownloadManager {
executor.execute({downloader.download()} as Runnable)
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
}
public void onUIDownloadCancelledEvent(UIDownloadCancelledEvent e) {
downloaders.remove(e.downloader.infoHash)
persistDownloaders()
}
public void onUIDownloadPausedEvent(UIDownloadPausedEvent e) {
persistDownloaders()
}
public void onUIDownloadResumedEvent(UIDownloadResumedEvent e) {
persistDownloaders()
}
void resume(Downloader downloader) {
executor.execute({downloader.download() as Runnable})
}
void onUILoadedEvent(UILoadedEvent e) {
File downloadsFile = new File(home, "downloads.json")
if (!downloadsFile.exists())
@ -111,7 +111,7 @@ public class DownloadManager {
def json = slurper.parseText(it)
File file = new File(DataUtil.readi18nString(Base64.decode(json.file)))
def destinations = new HashSet<>()
json.destinations.each { destination ->
json.destinations.each { destination ->
destinations.add new Destination(destination)
}
InfoHash infoHash
@ -122,9 +122,9 @@ public class DownloadManager {
byte [] root = Base64.decode(json.hashRoot)
infoHash = new InfoHash(root)
}
Pieces pieces = getPieces(infoHash, (long)json.length, json.pieceSizePow2)
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
infoHash, json.pieceSizePow2, connector, destinations, incompletes, pieces)
if (json.paused != null)
@ -136,7 +136,7 @@ public class DownloadManager {
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
}
}
private Pieces getPieces(InfoHash infoHash, long length, int pieceSizePow2) {
int pieceSize = 0x1 << pieceSizePow2
int nPieces = (int)(length / pieceSize)
@ -145,7 +145,7 @@ public class DownloadManager {
Mesh mesh = meshManager.getOrCreate(infoHash, nPieces)
mesh.pieces
}
void onSourceDiscoveredEvent(SourceDiscoveredEvent e) {
Downloader downloader = downloaders.get(e.infoHash)
if (downloader == null)
@ -156,19 +156,19 @@ public class DownloadManager {
case TrustLevel.NEUTRAL: ok = muSettings.allowUntrusted; break
case TrustLevel.DISTRUSTED: ok = false; break
}
if (ok)
downloader.addSource(e.source.destination)
}
void onFileDownloadedEvent(FileDownloadedEvent e) {
downloaders.remove(e.downloader.infoHash)
persistDownloaders()
}
private void persistDownloaders() {
File downloadsFile = new File(home,"downloads.json")
downloadsFile.withPrintWriter { writer ->
downloadsFile.withPrintWriter { writer ->
downloaders.values().each { downloader ->
if (!downloader.cancelled) {
def json = [:]
@ -180,20 +180,20 @@ public class DownloadManager {
destinations << it.toBase64()
}
json.destinations = destinations
InfoHash infoHash = downloader.getInfoHash()
if (infoHash.hashList != null)
json.hashList = Base64.encode(infoHash.hashList)
else
json.hashRoot = Base64.encode(infoHash.getRoot())
json.paused = downloader.paused
writer.println(JsonOutput.toJson(json))
}
}
}
}
public void shutdown() {
downloaders.values().each { it.stop() }
Downloader.executorService.shutdownNow()

View File

@ -24,7 +24,7 @@ import java.util.logging.Level
@Log
class DownloadSession {
private final EventBus eventBus
private final String meB64
private final Pieces pieces
@ -37,11 +37,11 @@ class DownloadSession {
private final MessageDigest digest
private long lastSpeedRead = System.currentTimeMillis()
private long dataSinceLastRead
private long dataSinceLastRead
private ByteBuffer mapped
DownloadSession(EventBus eventBus, String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file,
DownloadSession(EventBus eventBus, String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file,
int pieceSize, long fileLength, Set<Integer> available) {
this.eventBus = eventBus
this.meB64 = meB64
@ -59,7 +59,7 @@ class DownloadSession {
System.exit(1)
}
}
/**
* @return if the request will proceed. The only time it may not
* is if all the pieces have been claimed by other sessions.
@ -68,7 +68,7 @@ class DownloadSession {
public boolean request() throws IOException {
OutputStream os = endpoint.getOutputStream()
InputStream is = endpoint.getInputStream()
int piece
if (available.isEmpty())
piece = pieces.claim()
@ -77,35 +77,35 @@ class DownloadSession {
if (piece == -1)
return false
boolean unclaim = true
log.info("will download piece $piece")
long start = piece * pieceSize
long end = Math.min(fileLength, start + pieceSize) - 1
long length = end - start + 1
String root = Base64.encode(infoHash.getRoot())
try {
os.write("GET $root\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("Range: $start-$end\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("X-Persona: $meB64\r\n".getBytes(StandardCharsets.US_ASCII))
String xHave = DataUtil.encodeXHave(pieces.getDownloaded(), pieces.nPieces)
os.write("X-Have: $xHave\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("X-Have: $xHave\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
os.flush()
String codeString = readTillRN(is)
int space = codeString.indexOf(' ')
if (space > 0)
codeString = codeString.substring(0, space)
int code = Integer.parseInt(codeString.trim())
if (code == 404) {
log.warning("file not found")
endpoint.close()
return false
}
if (!(code == 200 || code == 416)) {
log.warning("unknown code $code")
endpoint.close()
@ -120,10 +120,10 @@ class DownloadSession {
if (colon == -1 || colon == header.length() - 1)
throw new IOException("invalid header $header")
String key = header.substring(0, colon)
String value = header.substring(colon + 1)
String value = header.substring(colon + 1)
headers[key] = value.trim()
}
// prase X-Alt if present
if (headers.containsKey("X-Alt")) {
headers["X-Alt"].split(",").each {
@ -136,7 +136,7 @@ class DownloadSession {
}
// parse X-Have if present
if (headers.containsKey("X-Have")) {
if (headers.containsKey("X-Have")) {
DataUtil.decodeXHave(headers["X-Have"]).each {
available.add(it)
}
@ -147,16 +147,16 @@ class DownloadSession {
throw new IOException("Code $code but no X-Have")
available.clear()
}
if (code != 200)
return true
String range = headers["Content-Range"]
if (range == null)
if (range == null)
throw new IOException("Code 200 but no Content-Range")
def group = (range =~ /^(\d+)-(\d+)$/)
if (group.size() != 1)
if (group.size() != 1)
throw new IOException("invalid Content-Range header $range")
long receivedStart = Long.parseLong(group[0][1])
@ -167,7 +167,7 @@ class DownloadSession {
endpoint.close()
return false
}
// start the download
FileChannel channel
try {
@ -207,13 +207,13 @@ class DownloadSession {
}
return true
}
synchronized int positionInPiece() {
if (mapped == null)
return 0
mapped.position()
}
synchronized int speed() {
final long now = System.currentTimeMillis()
long interval = Math.max(1000, now - lastSpeedRead)

View File

@ -29,7 +29,7 @@ import net.i2p.util.ConcurrentHashSet
public class Downloader {
public enum DownloadState { CONNECTING, HASHLIST, DOWNLOADING, FAILED, CANCELLED, PAUSED, FINISHED }
private enum WorkerState { CONNECTING, HASHLIST, DOWNLOADING, FINISHED}
private static final ExecutorService executorService = Executors.newCachedThreadPool({r ->
Thread rv = new Thread(r)
rv.setName("download worker")
@ -38,8 +38,8 @@ public class Downloader {
})
private final EventBus eventBus
private final DownloadManager downloadManager
private final Persona me
private final DownloadManager downloadManager
private final Persona me
private final File file
private final Pieces pieces
private final long length
@ -53,8 +53,8 @@ public class Downloader {
final int pieceSizePow2
private final Map<Destination, DownloadWorker> activeWorkers = new ConcurrentHashMap<>()
private final Set<Destination> successfulDestinations = new ConcurrentHashSet<>()
private volatile boolean cancelled, paused
private final AtomicBoolean eventFired = new AtomicBoolean()
private boolean piecesFileClosed
@ -64,8 +64,8 @@ public class Downloader {
private int speedAvg = 0
private long timestamp = Instant.now().toEpochMilli()
public Downloader(EventBus eventBus, 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, Pieces pieces) {
this.eventBus = eventBus
@ -87,15 +87,15 @@ public class Downloader {
// it's easily adjustable by resizing the size of speedArr
this.speedArr = [ 0, 0, 0, 0, 0 ]
}
public synchronized InfoHash getInfoHash() {
infoHash
}
private synchronized void setInfoHash(InfoHash infoHash) {
this.infoHash = infoHash
}
void download() {
readPieces()
destinations.each {
@ -106,16 +106,16 @@ public class Downloader {
}
}
}
void readPieces() {
if (!piecesFile.exists())
return
piecesFile.eachLine {
piecesFile.eachLine {
int piece = Integer.parseInt(it)
pieces.markDownloaded(piece)
}
}
void writePieces() {
synchronized(piecesFile) {
if (piecesFileClosed)
@ -127,12 +127,12 @@ public class Downloader {
}
}
}
public long donePieces() {
pieces.donePieces()
}
public int speed() {
int currSpeed = 0
if (getCurrentState() == DownloadState.DOWNLOADING) {
@ -164,15 +164,15 @@ public class Downloader {
speedAvg
}
public DownloadState getCurrentState() {
if (cancelled)
return DownloadState.CANCELLED
if (paused)
return DownloadState.PAUSED
boolean allFinished = true
activeWorkers.values().each {
activeWorkers.values().each {
allFinished &= it.currentState == WorkerState.FINISHED
}
if (allFinished) {
@ -180,22 +180,22 @@ public class Downloader {
return DownloadState.FINISHED
return DownloadState.FAILED
}
// if at least one is downloading...
boolean oneDownloading = false
activeWorkers.values().each {
activeWorkers.values().each {
if (it.currentState == WorkerState.DOWNLOADING) {
oneDownloading = true
return
return
}
}
if (oneDownloading)
return DownloadState.DOWNLOADING
// at least one is requesting hashlist
boolean oneHashlist = false
activeWorkers.values().each {
activeWorkers.values().each {
if (it.currentState == WorkerState.HASHLIST) {
oneHashlist = true
return
@ -203,10 +203,10 @@ public class Downloader {
}
if (oneHashlist)
return DownloadState.HASHLIST
return DownloadState.CONNECTING
}
public void cancel() {
cancelled = true
stop()
@ -217,27 +217,27 @@ public class Downloader {
incompleteFile.delete()
pieces.clearAll()
}
public void pause() {
paused = true
stop()
}
void stop() {
activeWorkers.values().each {
activeWorkers.values().each {
it.cancel()
}
}
public int activeWorkers() {
int active = 0
activeWorkers.values().each {
activeWorkers.values().each {
if (it.currentState != WorkerState.FINISHED)
active++
}
active
}
public void resume() {
paused = false
readPieces()
@ -256,7 +256,7 @@ public class Downloader {
}
}
}
void addSource(Destination d) {
if (activeWorkers.containsKey(d))
return
@ -264,7 +264,7 @@ public class Downloader {
activeWorkers.put(d, newWorker)
executorService.submit(newWorker)
}
class DownloadWorker implements Runnable {
private final Destination destination
private volatile WorkerState currentState
@ -272,11 +272,11 @@ public class Downloader {
private Endpoint endpoint
private volatile DownloadSession currentSession
private final Set<Integer> available = new HashSet<>()
DownloadWorker(Destination destination) {
this.destination = destination
}
public void run() {
downloadThread = Thread.currentThread()
currentState = WorkerState.CONNECTING
@ -292,7 +292,7 @@ public class Downloader {
currentState = WorkerState.DOWNLOADING
boolean requestPerformed
while(!pieces.isComplete()) {
currentSession = new DownloadSession(eventBus, me.toBase64(), pieces, getInfoHash(),
currentSession = new DownloadSession(eventBus, me.toBase64(), pieces, getInfoHash(),
endpoint, incompleteFile, pieceSize, length, available)
requestPerformed = currentSession.request()
if (!requestPerformed)
@ -319,18 +319,18 @@ public class Downloader {
new FileDownloadedEvent(
downloadedFile : new DownloadedFile(file, getInfoHash(), pieceSizePow2, successfulDestinations),
downloader : Downloader.this))
}
}
endpoint?.close()
}
}
int speed() {
if (currentSession == null)
return 0
currentSession.speed()
}
void cancel() {
downloadThread?.interrupt()
}

View File

@ -20,32 +20,32 @@ class HashListSession {
private final String meB64
private final InfoHash infoHash
private final Endpoint endpoint
HashListSession(String meB64, InfoHash infoHash, Endpoint endpoint) {
this.meB64 = meB64
this.infoHash = infoHash
this.endpoint = endpoint
}
InfoHash request() throws IOException {
InputStream is = endpoint.getInputStream()
OutputStream os = endpoint.getOutputStream()
String root = Base64.encode(infoHash.getRoot())
os.write("HASHLIST $root\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("X-Persona: $meB64\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
os.flush()
String code = readTillRN(is)
if (!code.startsWith("200"))
throw new IOException("unknown code $code")
// parse all headers
Set<String> headers = new HashSet<>()
String header
while((header = readTillRN(is)) != "" && headers.size() < Constants.MAX_HEADERS)
headers.add(header)
long receivedStart = -1
long receivedEnd = -1
for (String receivedHeader : headers) {
@ -58,10 +58,10 @@ class HashListSession {
receivedStart = Long.parseLong(group[0][1])
receivedEnd = Long.parseLong(group[0][2])
}
if (receivedStart != 0)
throw new IOException("hashlist started at $receivedStart")
byte[] hashList = new byte[receivedEnd]
ByteBuffer hashListBuf = ByteBuffer.wrap(hashList)
byte[] tmp = new byte[0x1 << 13]
@ -73,7 +73,7 @@ class HashListSession {
throw new IOException()
hashListBuf.put(tmp, 0, read)
}
InfoHash received = InfoHash.fromHashList(hashList)
if (received.getRoot() != infoHash.getRoot())
throw new IOException("fetched list doesn't match root")

View File

@ -5,30 +5,30 @@ class Pieces {
private final int nPieces
private final float ratio
private final Random random = new Random()
Pieces(int nPieces) {
this(nPieces, 1.0f)
}
Pieces(int nPieces, float ratio) {
this.nPieces = nPieces
this.ratio = ratio
done = new BitSet(nPieces)
claimed = new BitSet(nPieces)
}
synchronized int claim() {
int claimedCardinality = claimed.cardinality()
if (claimedCardinality == nPieces)
return -1
// if fuller than ratio just do sequential
if ( (1.0f * claimedCardinality) / nPieces > ratio) {
int rv = claimed.nextClearBit(0)
claimed.set(rv)
return rv
}
while(true) {
int start = random.nextInt(nPieces)
if (claimed.get(start))
@ -37,7 +37,7 @@ class Pieces {
return start
}
}
synchronized int claim(Set<Integer> available) {
for (int i = claimed.nextSetBit(0); i >= 0; i = claimed.nextSetBit(i+1))
available.remove(i)
@ -49,7 +49,7 @@ class Pieces {
claimed.set(rv)
rv
}
synchronized def getDownloaded() {
def rv = []
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1)) {
@ -57,28 +57,28 @@ class Pieces {
}
rv
}
synchronized void markDownloaded(int piece) {
done.set(piece)
claimed.set(piece)
}
synchronized void unclaim(int piece) {
claimed.clear(piece)
}
synchronized boolean isComplete() {
done.cardinality() == nPieces
}
synchronized int donePieces() {
done.cardinality()
}
synchronized boolean isDownloaded(int piece) {
done.get(piece)
}
synchronized void clearAll() {
done.clear()
claimed.clear()

View File

@ -6,7 +6,7 @@ import com.muwire.core.search.UIResultEvent
import net.i2p.data.Destination
class UIDownloadEvent extends Event {
UIResultEvent[] result
Set<Destination> sources
File target

View File

@ -20,9 +20,9 @@ import net.i2p.util.SystemVersion
@Log
class DirectoryWatcher {
private static final long WAIT_TIME = 1000
private static final WatchEvent.Kind[] kinds
static {
if (SystemVersion.isMac())
@ -30,7 +30,7 @@ class DirectoryWatcher {
else
kinds = [ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE]
}
private final EventBus eventBus
private final FileManager fileManager
private final Thread watcherThread, publisherThread
@ -38,7 +38,7 @@ class DirectoryWatcher {
private final Map<File, WatchKey> watchedDirectories = new ConcurrentHashMap<>()
private WatchService watchService
private volatile boolean shutdown
DirectoryWatcher(EventBus eventBus, FileManager fileManager) {
this.eventBus = eventBus
this.fileManager = fileManager
@ -47,29 +47,29 @@ class DirectoryWatcher {
this.publisherThread = new Thread({publish()} as Runnable, "watched-files-publisher")
publisherThread.setDaemon(true)
}
void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
watchService = FileSystems.getDefault().newWatchService()
watcherThread.start()
publisherThread.start()
}
void stop() {
shutdown = true
watcherThread?.interrupt()
publisherThread?.interrupt()
watchService?.close()
}
void onFileSharedEvent(FileSharedEvent e) {
if (!e.file.isDirectory())
return
Path path = e.file.getCanonicalFile().toPath()
WatchKey wk = path.register(watchService, kinds)
watchedDirectories.put(e.file, wk)
}
void onDirectoryUnsharedEvent(DirectoryUnsharedEvent e) {
WatchKey wk = watchedDirectories.remove(e.directory)
wk?.cancel()
@ -93,7 +93,7 @@ class DirectoryWatcher {
throw e
}
}
private void processCreated(Path parent, Path path) {
File f= join(parent, path)
@ -103,13 +103,13 @@ class DirectoryWatcher {
else
waitingFiles.put(f, System.currentTimeMillis())
}
private void processModified(Path parent, Path path) {
File f = join(parent, path)
log.fine("modified entry $f")
waitingFiles.put(f, System.currentTimeMillis())
}
private void processDeleted(Path parent, Path path) {
File f = join(parent, path)
log.fine("deleted entry $f")
@ -117,12 +117,12 @@ class DirectoryWatcher {
if (sf != null)
eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
}
private static File join(Path parent, Path path) {
File parentFile = parent.toFile().getCanonicalFile()
new File(parentFile, path.toFile().getName()).getCanonicalFile()
}
private void publish() {
try {
while(!shutdown) {

View File

@ -7,10 +7,10 @@ class FileHashedEvent extends Event {
SharedFile sharedFile
String error
@Override
public String toString() {
super.toString() + " sharedFile " + sharedFile?.file.getAbsolutePath() + " error: $error"
}
}

View File

@ -15,7 +15,7 @@ class FileHasher {
/** max size of shared file is 128 GB */
public static final long MAX_SIZE = 0x1L << 37
/**
* @param size of the file to be shared
* @return the size of each piece in power of 2
@ -25,18 +25,18 @@ class FileHasher {
static int getPieceSize(long size) {
if (size <= 0x1 << 30)
return 17
for (int i = 31; i <= 37; i++) {
if (size <= 0x1L << i) {
return i-13
}
}
throw new IllegalArgumentException("File too large $size")
}
final MessageDigest digest
FileHasher() {
try {
digest = MessageDigest.getInstance("SHA-256")
@ -45,14 +45,14 @@ class FileHasher {
System.exit(1)
}
}
InfoHash hashFile(File file) {
final long length = file.length()
final int size = 0x1 << getPieceSize(length)
int numPieces = (int) (length / size)
if (numPieces * size < length)
numPieces++
def output = new ByteArrayOutputStream()
RandomAccessFile raf = new RandomAccessFile(file, "r")
try {
@ -70,18 +70,18 @@ class FileHasher {
} finally {
raf.close()
}
byte [] hashList = output.toByteArray()
InfoHash.fromHashList(hashList)
}
public static void main(String[] args) {
if (args.length != 1) {
println "This utility computes an infohash of a file"
println "Pass absolute path to a file as an argument"
System.exit(1)
}
def file = new File(args[0])
file = file.getAbsoluteFile()
def hasher = new FileHasher()

View File

@ -21,27 +21,27 @@ class FileManager {
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, MuWireSettings settings) {
this.settings = settings
this.eventBus = eventBus
}
void onFileHashedEvent(FileHashedEvent e) {
if (e.sharedFile != null)
addToIndex(e.sharedFile)
}
void onFileLoadedEvent(FileLoadedEvent e) {
addToIndex(e.loadedFile)
}
void onFileDownloadedEvent(FileDownloadedEvent e) {
if (settings.shareDownloadedFiles) {
addToIndex(e.downloadedFile)
}
}
private void addToIndex(SharedFile sf) {
log.info("Adding shared file " + sf.getFile())
InfoHash infoHash = sf.getInfoHash()
@ -53,7 +53,7 @@ class FileManager {
}
existing.add(sf)
fileToSharedFile.put(sf.file, sf)
String name = sf.getFile().getName()
Set<File> existingFiles = nameToFiles.get(name)
if (existingFiles == null) {
@ -61,10 +61,10 @@ class FileManager {
nameToFiles.put(name, existingFiles)
}
existingFiles.add(sf.getFile())
index.add(name)
}
void onFileUnsharedEvent(FileUnsharedEvent e) {
SharedFile sf = e.unsharedFile
InfoHash infoHash = sf.getInfoHash()
@ -75,9 +75,9 @@ class FileManager {
rootToFiles.remove(infoHash)
}
}
fileToSharedFile.remove(sf.file)
String name = sf.getFile().getName()
Set<File> existingFiles = nameToFiles.get(name)
if (existingFiles != null) {
@ -86,20 +86,20 @@ class FileManager {
nameToFiles.remove(name)
}
}
index.remove(name)
}
Map<File, SharedFile> getSharedFiles() {
synchronized(fileToSharedFile) {
return new HashMap<>(fileToSharedFile)
}
}
Set<SharedFile> getSharedFiles(byte []root) {
return rootToFiles.get(new InfoHash(root))
}
void onSearchEvent(SearchEvent e) {
// hash takes precedence
ResultsEvent re = null
@ -118,26 +118,26 @@ class FileManager {
files = filter(sharedFiles, e.oobInfohash)
if (!sharedFiles.isEmpty())
re = new ResultsEvent(results: sharedFiles.asList(), uuid: e.uuid, searchEvent: e)
}
if (re != null)
eventBus.publish(re)
}
private static Set<SharedFile> filter(Set<SharedFile> files, boolean oob) {
if (!oob)
return files
Set<SharedFile> rv = new HashSet<>()
files.each {
files.each {
if (it.getPieceSize() != 0)
rv.add(it)
}
rv
}
void onDirectoryUnsharedEvent(DirectoryUnsharedEvent e) {
e.directory.listFiles().each {
e.directory.listFiles().each {
if (it.isDirectory())
eventBus.publish(new DirectoryUnsharedEvent(directory : it))
else {

View File

@ -5,7 +5,7 @@ import com.muwire.core.Event
class FileSharedEvent extends Event {
File file
@Override
public String toString() {
return super.toString() + " file: "+file.getAbsolutePath()

View File

@ -12,23 +12,23 @@ class HasherService {
final EventBus eventBus
final FileManager fileManager
Executor executor
HasherService(FileHasher hasher, EventBus eventBus, FileManager fileManager) {
this.hasher = hasher
this.eventBus = eventBus
this.fileManager = fileManager
}
void start() {
executor = Executors.newSingleThreadExecutor()
}
void onFileSharedEvent(FileSharedEvent evt) {
if (fileManager.fileToSharedFile.containsKey(evt.file.getCanonicalFile()))
return
executor.execute( { -> process(evt.file) } as Runnable)
}
private void process(File f) {
f = f.getCanonicalFile()
if (f.isDirectory()) {

View File

@ -28,7 +28,7 @@ class PersisterService extends Service {
final int interval
final Timer timer
final FileManager fileManager
PersisterService(File location, EventBus listener, int interval, FileManager fileManager) {
this.location = location
this.listener = listener
@ -36,7 +36,7 @@ class PersisterService extends Service {
this.fileManager = fileManager
timer = new Timer("file persister", true)
}
void stop() {
timer.cancel()
}
@ -44,7 +44,7 @@ class PersisterService extends Service {
void onUILoadedEvent(UILoadedEvent e) {
timer.schedule({load()} as TimerTask, 1)
}
void load() {
if (location.exists() && location.isFile()) {
def slurper = new JsonSlurper()
@ -69,13 +69,13 @@ class PersisterService extends Service {
timer.schedule({persistFiles()} as TimerTask, 0, interval)
loaded = true
}
private static FileLoadedEvent fromJson(def json) {
if (json.file == null || json.length == null || json.infoHash == null || json.hashList == null)
throw new IllegalArgumentException()
if (!(json.hashList instanceof List))
throw new IllegalArgumentException()
def file = new File(DataUtil.readi18nString(Base64.decode(json.file)))
file = file.getCanonicalFile()
if (!file.exists() || file.isDirectory())
@ -83,7 +83,7 @@ class PersisterService extends Service {
long length = Long.valueOf(json.length)
if (length != file.length())
return null
List hashList = (List) json.hashList
ByteArrayOutputStream baos = new ByteArrayOutputStream()
hashList.each {
@ -93,34 +93,34 @@ class PersisterService extends Service {
baos.write hash
}
byte[] hashListBytes = baos.toByteArray()
InfoHash ih = InfoHash.fromHashList(hashListBytes)
byte [] root = Base64.decode(json.infoHash.toString())
if (root == null)
throw new IllegalArgumentException()
if (!Arrays.equals(root, ih.getRoot()))
return null
int pieceSize = 0
if (json.pieceSize != null)
pieceSize = json.pieceSize
if (json.sources != null) {
List sources = (List)json.sources
Set<Destination> sourceSet = sources.stream().map({d -> new Destination(d.toString())}).collect Collectors.toSet()
DownloadedFile df = new DownloadedFile(file, ih, pieceSize, sourceSet)
return new FileLoadedEvent(loadedFile : df)
}
SharedFile sf = new SharedFile(file, ih, pieceSize)
return new FileLoadedEvent(loadedFile: sf)
}
private void persistFiles() {
def sharedFiles = fileManager.getSharedFiles()
File tmp = File.createTempFile("muwire-files", "tmp")
tmp.deleteOnExit()
tmp.withPrintWriter { writer ->
@ -133,7 +133,7 @@ class PersisterService extends Service {
Files.copy(tmp.toPath(), location.toPath(), StandardCopyOption.REPLACE_EXISTING)
tmp.delete()
}
private def toJson(File f, SharedFile sf) {
def json = [:]
json.file = Base64.encode DataUtil.encodei18nString(f.getCanonicalFile().toString())
@ -147,11 +147,11 @@ class PersisterService extends Service {
System.arraycopy(ih.getHashList(), i * 32, tmp, 0, 32)
json.hashList.add Base64.encode(tmp)
}
if (sf instanceof DownloadedFile) {
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
}
json
}
}

View File

@ -17,9 +17,9 @@ import net.i2p.data.Destination
@Log
class CacheClient {
private static final int CRAWLER_RETURN = 10
final EventBus eventBus
final HostCache cache
final ConnectionManager manager
@ -28,7 +28,7 @@ class CacheClient {
final MuWireSettings settings
final Timer timer
public CacheClient(EventBus eventBus, HostCache cache,
public CacheClient(EventBus eventBus, HostCache cache,
ConnectionManager manager, I2PSession session,
MuWireSettings settings, long interval) {
this.eventBus = eventBus
@ -39,24 +39,24 @@ class CacheClient {
this.interval = interval
this.timer = new Timer("hostcache-client",true)
}
void start() {
session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, 0)
timer.schedule({queryIfNeeded()} as TimerTask, 1, interval)
}
void stop() {
timer.cancel()
}
private void queryIfNeeded() {
if (!manager.getConnections().isEmpty())
return
if (!cache.getHosts(1).isEmpty())
return
log.info "Will query hostcaches"
log.info "Will query hostcaches"
def ping = [type: "Ping", version: 1, leaf: settings.isLeaf()]
ping = JsonOutput.toJson(ping)
def maker = new I2PDatagramMaker(session)
@ -68,9 +68,9 @@ class CacheClient {
session.sendMessage(it, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 1, 0, options)
}
}
class Listener implements I2PSessionMuxedListener {
private final JsonSlurper slurper = new JsonSlurper()
@Override
@ -79,27 +79,27 @@ class CacheClient {
@Override
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
if (proto != I2PSession.PROTO_DATAGRAM) {
log.warning "Received unexpected protocol $proto"
return
}
def payload = session.receiveMessage(msgId)
def dissector = new I2PDatagramDissector()
try {
dissector.loadI2PDatagram(payload)
def sender = dissector.getSender()
log.info("Received something from ${sender.toBase32()}")
payload = dissector.getPayload()
payload = slurper.parse(payload)
if (payload.type == null) {
log.warning("type missing")
return
}
switch(payload.type) {
case "Pong" : handlePong(sender, payload); break
case "CrawlerPing": handleCrawlerPing(session, sender, payload); break
@ -123,34 +123,34 @@ class CacheClient {
public void errorOccurred(I2PSession session, String message, Throwable error) {
log.severe "I2P error occured $message $error"
}
}
private void handlePong(Destination from, def pong) {
if (!CacheServers.isRegistered(from)) {
log.warning("received pong from non-registered destination")
return
}
if (pong.pongs == null) {
log.warning("malformed pong - no pongs")
return
}
pong.pongs.asList().each {
pong.pongs.asList().each {
Destination dest = new Destination(it)
if (!session.getMyDestination().equals(dest))
eventBus.publish(new HostDiscoveredEvent(destination: dest, fromHostcache : true))
eventBus.publish(new HostDiscoveredEvent(destination: dest, fromHostcache : true))
}
}
private void handleCrawlerPing(I2PSession session, Destination from, def ping) {
if (settings.isLeaf()) {
log.warning("Received crawler ping but I'm a leaf")
return
}
switch(settings.getCrawlerResponse()) {
case CrawlerResponse.NONE:
log.info("Responding to crawlers is disabled by user")
@ -166,15 +166,15 @@ class CacheClient {
break
}
}
private void respondToCrawler(I2PSession session, Destination from, def ping) {
log.info "responding to crawler ping"
def neighbors = manager.getConnections().collect { c -> c.endpoint.destination.toBase64() }
Collections.shuffle(neighbors)
if (neighbors.size() > CRAWLER_RETURN)
neighbors = neighbors[0..CRAWLER_RETURN - 1]
def upManager = (UltrapeerConnectionManager) manager;
def pong = [:]
pong.peers = neighbors
@ -184,7 +184,7 @@ class CacheClient {
pong.leafSlots = upManager.hasLeafSlots()
pong.peerSlots = upManager.hasPeerSlots()
pong = JsonOutput.toJson(pong)
def maker = new I2PDatagramMaker(session)
pong = maker.makeI2PDatagram(pong.bytes)
session.sendMessage(from, pong, I2PSession.PROTO_DATAGRAM, 0, 0)

View File

@ -17,7 +17,7 @@ class CacheServers {
return allCaches
allCaches[0..TO_GIVE-1]
}
static boolean isRegistered(Destination d) {
return CACHES.contains(d)
}

View File

@ -5,12 +5,12 @@ import net.i2p.data.Destination
class Host {
private static final int MAX_FAILURES = 3
final Destination destination
private final int clearInterval
int failures,successes
long lastAttempt
public Host(Destination destination, int clearInterval) {
this.destination = destination
this.clearInterval = clearInterval
@ -21,25 +21,25 @@ class Host {
successes++
lastAttempt = System.currentTimeMillis()
}
synchronized void onFailure() {
failures++
successes = 0
lastAttempt = System.currentTimeMillis()
}
synchronized boolean isFailed() {
failures >= MAX_FAILURES
}
synchronized boolean hasSucceeded() {
successes > 0
}
synchronized void clearFailures() {
failures = 0
}
synchronized void canTryAgain() {
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
}

View File

@ -22,10 +22,10 @@ class HostCache extends Service {
final MuWireSettings settings
final Destination myself
final Map<Destination, Host> hosts = new ConcurrentHashMap<>()
HostCache(){}
public HostCache(TrustService trustService, File storage, int interval,
public HostCache(TrustService trustService, File storage, int interval,
MuWireSettings settings, Destination myself) {
this.trustService = trustService
this.storage = storage
@ -38,11 +38,11 @@ class HostCache extends Service {
void start() {
timer.schedule({load()} as TimerTask, 1)
}
void stop() {
timer.cancel()
}
void onHostDiscoveredEvent(HostDiscoveredEvent e) {
if (myself == e.destination)
return
@ -57,7 +57,7 @@ class HostCache extends Service {
hosts.put(e.destination, host)
}
}
void onConnectionEvent(ConnectionEvent e) {
if (e.leaf)
return
@ -78,7 +78,7 @@ class HostCache extends Service {
break
}
}
List<Destination> getHosts(int n) {
List<Destination> rv = new ArrayList<>(hosts.keySet())
rv.retainAll {allowHost(hosts[it])}
@ -87,7 +87,7 @@ class HostCache extends Service {
Collections.shuffle(rv)
rv[0..n-1]
}
List<Destination> getGoodHosts(int n) {
List<Destination> rv = new ArrayList<>(hosts.keySet())
rv.retainAll {
@ -99,7 +99,7 @@ class HostCache extends Service {
Collections.shuffle(rv)
rv[0..n-1]
}
void load() {
if (storage.exists()) {
JsonSlurper slurper = new JsonSlurper()
@ -118,7 +118,7 @@ class HostCache extends Service {
timer.schedule({save()} as TimerTask, interval, interval)
loaded = true
}
private boolean allowHost(Host host) {
if (host.isFailed() && !host.canTryAgain())
return false
@ -135,7 +135,7 @@ class HostCache extends Service {
}
false
}
private void save() {
storage.delete()
storage.withPrintWriter { writer ->

View File

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

View File

@ -11,12 +11,12 @@ class Mesh {
private final InfoHash infoHash
private final Set<Persona> sources = new ConcurrentHashSet<>()
private final Pieces pieces
Mesh(InfoHash infoHash, Pieces pieces) {
this.infoHash = infoHash
this.pieces = pieces
}
Set<Persona> getRandom(int n, Persona exclude) {
List<Persona> tmp = new ArrayList<>(sources)
tmp.remove(exclude)

View File

@ -16,23 +16,23 @@ import groovy.json.JsonSlurper
import net.i2p.data.Base64
class MeshManager {
private final Map<InfoHash, Mesh> meshes = Collections.synchronizedMap(new HashMap<>())
private final FileManager fileManager
private final File home
private final MuWireSettings settings
MeshManager(FileManager fileManager, File home, MuWireSettings settings) {
this.fileManager = fileManager
this.home = home
this.settings = settings
load()
}
Mesh get(InfoHash infoHash) {
meshes.get(infoHash)
}
Mesh getOrCreate(InfoHash infoHash, int nPieces) {
synchronized(meshes) {
if (meshes.containsKey(infoHash))
@ -47,7 +47,7 @@ class MeshManager {
return rv
}
}
void onSourceDiscoveredEvent(SourceDiscoveredEvent e) {
Mesh mesh = meshes.get(e.infoHash)
if (mesh == null)
@ -55,7 +55,7 @@ class MeshManager {
mesh.sources.add(e.source)
save()
}
private void save() {
File meshFile = new File(home, "mesh.json")
synchronized(meshes) {
@ -72,29 +72,29 @@ class MeshManager {
}
}
}
private void load() {
File meshFile = new File(home, "mesh.json")
if (!meshFile.exists())
return
long now = System.currentTimeMillis()
JsonSlurper slurper = new JsonSlurper()
meshFile.eachLine {
meshFile.eachLine {
def json = slurper.parseText(it)
if (now - json.timestamp > settings.meshExpiration * 60 * 1000)
return
InfoHash infoHash = new InfoHash(Base64.decode(json.infoHash))
Pieces pieces = new Pieces(json.nPieces, settings.downloadSequentialRatio)
Mesh mesh = new Mesh(infoHash, pieces)
json.sources.each { source ->
json.sources.each { source ->
Persona persona = new Persona(new ByteArrayInputStream(Base64.decode(source)))
mesh.sources.add(persona)
}
if (json.xHave != null)
if (json.xHave != null)
DataUtil.decodeXHave(json.xHave).each { pieces.markDownloaded(it) }
if (!mesh.sources.isEmpty())
meshes.put(infoHash, mesh)
}

View File

@ -8,7 +8,7 @@ import net.i2p.data.Destination
class DeleteEvent extends Event {
byte [] infoHash
Destination leaf
@Override
public String toString() {
"DeleteEvent ${super.toString()} infoHash:${Base32.encode(infoHash)} leaf:${leaf.toBase32()}"

View File

@ -21,5 +21,5 @@ class InvalidSearchResultException extends Exception {
super(cause);
// TODO Auto-generated constructor stub
}
}

View File

@ -6,33 +6,33 @@ import com.muwire.core.connection.UltrapeerConnectionManager
import net.i2p.data.Destination
class LeafSearcher {
final UltrapeerConnectionManager connectionManager
final SearchIndex searchIndex = new SearchIndex()
final Map<String, Set<byte[]>> fileNameToHashes = new HashMap<>()
final Map<byte[], Set<Destination>> hashToLeafs = new HashMap<>()
final Map<Destination, Map<byte[], Set<String>>> leafToFiles = new HashMap<>()
LeafSearcher(UltrapeerConnectionManager connectionManager) {
this.connectionManager = connectionManager
}
void onUpsertEvent(UpsertEvent e) {
// TODO: implement
}
void onDeleteEvent(DeleteEvent e) {
// TODO: implement
}
void onDisconnectionEvent(DisconnectionEvent e) {
// TODO: implement
}
void onQueryEvent(QueryEvent e) {
// TODO: implement
}
}

View File

@ -6,7 +6,7 @@ import com.muwire.core.Persona
import net.i2p.data.Destination
class QueryEvent extends Event {
SearchEvent searchEvent
boolean firstHop
Destination replyTo

View File

@ -26,7 +26,7 @@ class ResultsParser {
}
}
private static parseV1(Persona p, UUID uuid, def json) {
if (json.name == null)
throw new InvalidSearchResultException("name missing")
@ -55,7 +55,7 @@ class ResultsParser {
InfoHash parsedIH = InfoHash.fromHashList(hashList)
if (parsedIH.getRoot() != infoHash)
throw new InvalidSearchControlsException("infohash root doesn't match")
return new UIResultEvent( sender : p,
name : name,
size : size,
@ -67,7 +67,7 @@ class ResultsParser {
throw new InvalidSearchResultException("parsing search result failed",e)
}
}
private static UIResultEvent parseV2(Persona p, UUID uuid, def json) {
if (json.name == null)
throw new InvalidSearchResultException("name missing")
@ -86,11 +86,11 @@ class ResultsParser {
if (infoHash.length != InfoHash.SIZE)
throw new InvalidSearchResultException("invalid infohash size $infoHash.length")
int pieceSize = json.pieceSize
Set<Destination> sources = Collections.emptySet()
if (json.sources != null)
if (json.sources != null)
sources = json.sources.stream().map({new Destination(it)}).collect(Collectors.toSet())
return new UIResultEvent( sender : p,
name : name,
size : size,

View File

@ -25,9 +25,9 @@ import net.i2p.data.Destination
@Log
class ResultsSender {
private static final AtomicInteger THREAD_NO = new AtomicInteger()
private final Executor executor = Executors.newCachedThreadPool(
new ThreadFactory() {
@Override
@ -38,27 +38,27 @@ class ResultsSender {
rv
}
})
private final I2PConnector connector
private final Persona me
private final EventBus eventBus
ResultsSender(EventBus eventBus, I2PConnector connector, Persona me) {
this.connector = connector;
this.eventBus = eventBus
this.me = me
}
void sendResults(UUID uuid, SharedFile[] results, Destination target, boolean oobInfohash) {
log.info("Sending $results.length results for uuid $uuid to ${target.toBase32()} oobInfohash : $oobInfohash")
if (target.equals(me.destination)) {
results.each {
results.each {
long length = it.getFile().length()
int pieceSize = it.getPieceSize()
if (pieceSize == 0)
pieceSize = FileHasher.getPieceSize(length)
Set<Destination> suggested = Collections.emptySet()
if (it instanceof DownloadedFile)
if (it instanceof DownloadedFile)
suggested = it.sources
def uiResultEvent = new UIResultEvent( sender : me,
name : it.getFile().getName(),
@ -71,17 +71,17 @@ class ResultsSender {
eventBus.publish(uiResultEvent)
}
} else {
executor.execute(new ResultSendJob(uuid : uuid, results : results,
executor.execute(new ResultSendJob(uuid : uuid, results : results,
target: target, oobInfohash : oobInfohash))
}
}
private class ResultSendJob implements Runnable {
UUID uuid
SharedFile [] results
Destination target
boolean oobInfohash
@Override
public void run() {
try {

View File

@ -9,7 +9,7 @@ class SearchEvent extends Event {
byte [] searchHash
UUID uuid
boolean oobInfohash
String toString() {
def infoHash = null
if (searchHash != null)

View File

@ -5,7 +5,7 @@ import com.muwire.core.Constants
class SearchIndex {
final Map<String, Set<String>> keywords = new HashMap<>()
void add(String string) {
String [] split = split(string)
split.each {
@ -17,7 +17,7 @@ class SearchIndex {
existing.add(string)
}
}
void remove(String string) {
String [] split = split(string)
split.each {
@ -30,7 +30,7 @@ class SearchIndex {
}
}
}
private static String[] split(String source) {
source = source.replaceAll(Constants.SPLIT_PATTERN, " ").toLowerCase()
String [] split = source.split(" ")
@ -38,10 +38,10 @@ class SearchIndex {
split.each { if (it.length() > 0) rv << it }
rv.toArray(new String[0])
}
String[] search(List<String> terms) {
Set<String> rv = null;
terms.each {
Set<String> forWord = keywords.getOrDefault(it,[])
if (rv == null) {
@ -49,9 +49,9 @@ class SearchIndex {
} else {
rv.retainAll(forWord)
}
}
if (rv != null)
return rv.asList()
[]

View File

@ -8,17 +8,17 @@ import net.i2p.data.Destination
@Log
public class SearchManager {
private static final int EXPIRE_TIME = 60 * 1000 * 1000
private static final int CHECK_INTERVAL = 60 * 1000
private final EventBus eventBus
private final Persona me
private final ResultsSender resultsSender
private final Map<UUID, QueryEvent> responderAddress = Collections.synchronizedMap(new HashMap<>())
SearchManager(){}
SearchManager(EventBus eventBus, Persona me, ResultsSender resultsSender) {
this.eventBus = eventBus
this.me = me
@ -26,7 +26,7 @@ public class SearchManager {
Timer timer = new Timer("query-expirer", true)
timer.schedule({cleanup()} as TimerTask, CHECK_INTERVAL, CHECK_INTERVAL)
}
void onQueryEvent(QueryEvent event) {
if (responderAddress.containsKey(event.searchEvent.uuid)) {
log.info("Dropping duplicate search uuid $event.searchEvent.uuid")
@ -35,7 +35,7 @@ public class SearchManager {
responderAddress.put(event.searchEvent.uuid, event)
eventBus.publish(event.searchEvent)
}
void onResultsEvent(ResultsEvent event) {
Destination target = responderAddress.get(event.uuid)?.replyTo
if (target == null)
@ -46,11 +46,11 @@ public class SearchManager {
}
resultsSender.sendResults(event.uuid, event.results, target, event.searchEvent.oobInfohash)
}
boolean hasLocalSearch(UUID uuid) {
me.destination.equals(responderAddress.get(uuid)?.replyTo)
}
private void cleanup() {
final long now = System.currentTimeMillis()
synchronized(responderAddress) {

View File

@ -14,7 +14,7 @@ class UIResultEvent extends Event {
long size
InfoHash infohash
int pieceSize
@Override
public String toString() {
super.toString() + "name:$name size:$size sender:${sender.getHumanReadableName()} pieceSize $pieceSize"

View File

@ -18,5 +18,5 @@ class UnexpectedResultsException extends Exception {
public UnexpectedResultsException(String message) {
super(message);
}
}

View File

@ -10,7 +10,7 @@ class UpsertEvent extends Event {
Set<String> names
byte [] infoHash
Destination leaf
@Override
public String toString() {
"UpsertEvent ${super.toString()} names:$names infoHash:${Base32.encode(infoHash)} leaf:${leaf.toBase32()}"

View File

@ -8,19 +8,19 @@ import net.i2p.util.ConcurrentHashSet
class RemoteTrustList {
public enum Status { NEW, UPDATING, UPDATED, UPDATE_FAILED }
private final Persona persona
private final Set<Persona> good, bad
volatile long timestamp
volatile boolean forceUpdate
Status status = Status.NEW
RemoteTrustList(Persona persona) {
this.persona = persona
good = new ConcurrentHashSet<>()
bad = new ConcurrentHashSet<>()
}
@Override
public boolean equals(Object o) {
if (!(o instanceof RemoteTrustList))

View File

@ -13,29 +13,29 @@ class TrustService extends Service {
final File persistGood, persistBad
final long persistInterval
final Map<Destination, Persona> good = new ConcurrentHashMap<>()
final Map<Destination, Persona> bad = new ConcurrentHashMap<>()
final Timer timer
TrustService() {}
TrustService(File persistGood, File persistBad, long persistInterval) {
this.persistBad = persistBad
this.persistGood = persistGood
this.persistInterval = persistInterval
this.timer = new Timer("trust-persister",true)
}
void start() {
timer.schedule({load()} as TimerTask, 1)
}
void stop() {
timer.cancel()
}
void load() {
if (persistGood.exists()) {
persistGood.eachLine {
@ -54,7 +54,7 @@ class TrustService extends Service {
timer.schedule({persist()} as TimerTask, persistInterval, persistInterval)
loaded = true
}
private void persist() {
persistGood.delete()
persistGood.withPrintWriter { writer ->
@ -69,7 +69,7 @@ class TrustService extends Service {
}
}
}
TrustLevel getLevel(Destination dest) {
if (good.containsKey(dest))
return TrustLevel.TRUSTED
@ -77,7 +77,7 @@ class TrustService extends Service {
return TrustLevel.DISTRUSTED
TrustLevel.NEUTRAL
}
void onTrustEvent(TrustEvent e) {
switch(e.level) {
case TrustLevel.TRUSTED:

View File

@ -22,32 +22,32 @@ class TrustSubscriber {
private final EventBus eventBus
private final I2PConnector i2pConnector
private final MuWireSettings settings
private final Map<Destination, RemoteTrustList> remoteTrustLists = new ConcurrentHashMap<>()
private final Object waitLock = new Object()
private volatile boolean shutdown
private volatile Thread thread
private volatile Thread thread
private final ExecutorService updateThreads = Executors.newCachedThreadPool()
TrustSubscriber(EventBus eventBus, I2PConnector i2pConnector, MuWireSettings settings) {
this.eventBus = eventBus
this.i2pConnector = i2pConnector
this.settings = settings
}
void onUILoadedEvent(UILoadedEvent e) {
thread = new Thread({checkLoop()} as Runnable, "trust-subscriber")
thread.setDaemon(true)
thread.start()
}
void stop() {
shutdown = true
thread?.interrupt()
updateThreads.shutdownNow()
}
void onTrustSubscriptionEvent(TrustSubscriptionEvent e) {
if (!e.subscribe) {
remoteTrustLists.remove(e.persona.destination)
@ -59,7 +59,7 @@ class TrustSubscriber {
}
}
}
private void checkLoop() {
try {
while(!shutdown) {
@ -82,15 +82,15 @@ class TrustSubscriber {
throw e
}
}
private class UpdateJob implements Runnable {
private final RemoteTrustList trustList
UpdateJob(RemoteTrustList trustList) {
this.trustList = trustList
}
public void run() {
trustList.status = RemoteTrustList.Status.UPDATING
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
@ -111,44 +111,44 @@ class TrustSubscriber {
InputStream is = endpoint.getInputStream()
os.write("TRUST\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
os.flush()
String codeString = DataUtil.readTillRN(is)
int space = codeString.indexOf(' ')
if (space > 0)
codeString = codeString.substring(0,space)
int code = Integer.parseInt(codeString.trim())
if (code != 200) {
log.info("couldn't fetch trust list, code $code")
return false
}
// swallow any headers
String header
while (( header = DataUtil.readTillRN(is)) != "");
DataInputStream dis = new DataInputStream(is)
Set<Persona> good = new HashSet<>()
int nGood = dis.readUnsignedShort()
for (int i = 0; i < nGood; i++) {
Persona p = new Persona(dis)
good.add(p)
}
Set<Persona> bad = new HashSet<>()
int nBad = dis.readUnsignedShort()
for (int i = 0; i < nBad; i++) {
Persona p = new Persona(dis)
bad.add(p)
}
trustList.timestamp = now
trustList.good.clear()
trustList.good.addAll(good)
trustList.bad.clear()
trustList.bad.addAll(bad)
return true
} catch (Exception e) {
log.log(Level.WARNING,"exception fetching trust list from ${trustList.persona.getHumanReadableName()}",e)
@ -156,6 +156,6 @@ class TrustSubscriber {
} finally {
endpoint?.close()
}
}
}

View File

@ -32,15 +32,15 @@ class UpdateClient {
final MuWireSettings settings
final FileManager fileManager
final Persona me
private final Timer timer
private long lastUpdateCheckTime
private volatile InfoHash updateInfoHash
private volatile String version, signer
private volatile boolean updateDownloading
UpdateClient(EventBus eventBus, I2PSession session, String myVersion, MuWireSettings settings, FileManager fileManager, Persona me) {
this.eventBus = eventBus
this.session = session
@ -50,16 +50,16 @@ class UpdateClient {
this.me = me
timer = new Timer("update-client",true)
}
void start() {
session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, 2)
timer.schedule({checkUpdate()} as TimerTask, 60000, 60 * 60 * 1000)
}
void stop() {
timer.cancel()
}
void onUIResultBatchEvent(UIResultBatchEvent results) {
if (results.results[0].infohash != updateInfoHash)
return
@ -70,14 +70,14 @@ class UpdateClient {
def downloadEvent = new UIDownloadEvent(result: results.results[0], sources : results.results[0].sources, target : file)
eventBus.publish(downloadEvent)
}
void onFileDownloadedEvent(FileDownloadedEvent e) {
if (e.downloadedFile.infoHash != updateInfoHash)
return
updateDownloading = false
eventBus.publish(new UpdateDownloadedEvent(version : version, signer : signer))
}
private void checkUpdate() {
final long now = System.currentTimeMillis()
if (lastUpdateCheckTime > 0) {
@ -85,20 +85,20 @@ class UpdateClient {
return
}
lastUpdateCheckTime = now
log.info("checking for update")
def ping = [version : 1, myVersion : myVersion]
ping = JsonOutput.toJson(ping)
def maker = new I2PDatagramMaker(session)
ping = maker.makeI2PDatagram(ping.bytes)
def options = new SendMessageOptions()
def options = new SendMessageOptions()
options.setSendLeaseSet(true)
session.sendMessage(UpdateServers.UPDATE_SERVER, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 2, 0, options)
session.sendMessage(UpdateServers.UPDATE_SERVER, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 2, 0, options)
}
class Listener implements I2PSessionMuxedListener {
final JsonSlurper slurper = new JsonSlurper()
@Override
@ -111,7 +111,7 @@ class UpdateClient {
log.warning "Received unexpected protocol $proto"
return
}
def payload = session.receiveMessage(msgId)
def dissector = new I2PDatagramDissector()
try {
@ -121,33 +121,33 @@ class UpdateClient {
log.warning("received something not from update server " + sender.toBase32())
return
}
log.info("Received something from update server")
payload = dissector.getPayload()
payload = slurper.parse(payload)
if (payload.version == null) {
log.warning("version missing")
return
}
if (payload.signer == null) {
log.warning("signer missing")
}
if (VersionComparator.comp(myVersion, payload.version) >= 0) {
log.info("no new version available")
return
}
String infoHash
if (settings.updateType == "jar") {
infoHash = payload.infoHash
} else
infoHash = payload[settings.updateType]
if (!settings.autoDownloadUpdate) {
log.info("new version $payload.version available, publishing event")
eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : infoHash))
@ -167,7 +167,7 @@ class UpdateClient {
eventBus.publish(queryEvent)
}
}
} catch (Exception e) {
log.log(Level.WARNING,"Invalid datagram",e)
}
@ -186,6 +186,6 @@ class UpdateClient {
public void errorOccurred(I2PSession session, String message, Throwable error) {
log.log(Level.SEVERE, message, error)
}
}
}

View File

@ -15,12 +15,12 @@ import com.muwire.core.util.DataUtil
import net.i2p.data.Destination
class ContentUploader extends Uploader {
private final File file
private final ContentRequest request
private final Mesh mesh
private final int pieceSize
ContentUploader(File file, ContentRequest request, Endpoint endpoint, Mesh mesh, int pieceSize) {
super(endpoint)
this.file = file
@ -28,7 +28,7 @@ class ContentUploader extends Uploader {
this.mesh = mesh
this.pieceSize = pieceSize
}
@Override
void respond() {
OutputStream os = endpoint.getOutputStream()
@ -55,7 +55,7 @@ class ContentUploader extends Uploader {
os.write("Content-Range: $range.start-$range.end\r\n".getBytes(StandardCharsets.US_ASCII))
writeMesh(request.downloader)
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
FileChannel channel = null
try {
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ))
@ -78,11 +78,11 @@ class ContentUploader extends Uploader {
}
}
}
private void writeMesh(Persona toExclude) {
String xHave = DataUtil.encodeXHave(mesh.pieces.getDownloaded(), mesh.pieces.nPieces)
endpoint.getOutputStream().write("X-Have: $xHave\r\n".getBytes(StandardCharsets.US_ASCII))
Set<Persona> sources = mesh.getRandom(3, toExclude)
if (!sources.isEmpty()) {
String xAlts = sources.stream().map({ it.toBase64() }).collect(Collectors.joining(","))

View File

@ -11,19 +11,19 @@ import net.i2p.data.Base64
class HashListUploader extends Uploader {
private final InfoHash infoHash
private final HashListRequest request
HashListUploader(Endpoint endpoint, InfoHash infoHash, HashListRequest request) {
super(endpoint)
this.infoHash = infoHash
mapped = ByteBuffer.wrap(infoHash.getHashList())
this.request = request
}
void respond() {
OutputStream os = endpoint.getOutputStream()
os.write("200 OK\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("Content-Range: 0-${mapped.remaining()}\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
byte[]tmp = new byte[0x1 << 13]
while(mapped.hasRemaining()) {
int start = mapped.position()
@ -60,7 +60,7 @@ class HashListUploader extends Uploader {
public int getTotalPieces() {
return 1;
}
@Override
public long getTotalSize() {
return -1;

View File

@ -2,7 +2,7 @@ package com.muwire.core.upload
class Range {
final long start, end
Range(long start, long end) {
this.start = start
this.end = end

View File

@ -12,21 +12,21 @@ import net.i2p.data.Base64
@Log
class Request {
private static final byte R = "\r".getBytes(StandardCharsets.US_ASCII)[0]
private static final byte N = "\n".getBytes(StandardCharsets.US_ASCII)[0]
InfoHash infoHash
Persona downloader
Map<String, String> headers
static Request parseContentRequest(InfoHash infoHash, InputStream is) throws IOException {
Map<String, String> headers = parseHeaders(is)
if (!headers.containsKey("Range"))
throw new IOException("Range header not found")
String range = headers.get("Range").trim()
String[] split = range.split("-")
if (split.length != 2)
@ -39,26 +39,26 @@ class Request {
} catch (NumberFormatException nfe) {
throw new IOException(nfe)
}
if (start < 0 || end < start)
throw new IOException("Invalid range $start - $end")
Persona downloader = null
if (headers.containsKey("X-Persona")) {
def encoded = headers["X-Persona"].trim()
def decoded = Base64.decode(encoded)
downloader = new Persona(new ByteArrayInputStream(decoded))
}
int have = 0
if (headers.containsKey("X-Have")) {
def encoded = headers["X-Have"].trim()
have = DataUtil.decodeXHave(encoded).size()
}
new ContentRequest( infoHash : infoHash, range : new Range(start, end),
new ContentRequest( infoHash : infoHash, range : new Range(start, end),
headers : headers, downloader : downloader, have : have)
}
static Request parseHashListRequest(InfoHash infoHash, InputStream is) throws IOException {
Map<String,String> headers = parseHeaders(is)
Persona downloader = null
@ -69,7 +69,7 @@ class Request {
}
new HashListRequest(infoHash : infoHash, headers : headers, downloader : downloader)
}
private static Map<String, String> parseHeaders(InputStream is) {
Map<String,String> headers = new HashMap<>()
byte [] tmp = new byte[Constants.MAX_HEADER_SIZE]
@ -81,7 +81,7 @@ class Request {
byte read = is.read()
if (read == -1)
throw new IOException("Stream closed")
if (!r && read == N)
throw new IOException("Received N before R")
if (read == R) {
@ -90,7 +90,7 @@ class Request {
r = true
continue
}
if (r && !n) {
if (read != N)
throw new IOException("R not followed by N")
@ -101,13 +101,13 @@ class Request {
throw new IOException("Header too long")
tmp[idx++] = read
}
if (idx == 0)
break
String header = new String(tmp, 0, idx, StandardCharsets.US_ASCII)
log.fine("Read header $header")
int keyIdx = header.indexOf(":")
if (keyIdx < 1)
throw new IOException("Header key not found")
@ -119,5 +119,5 @@ class Request {
}
headers
}
}

View File

@ -22,17 +22,17 @@ public class UploadManager {
private final FileManager fileManager
private final MeshManager meshManager
private final DownloadManager downloadManager
public UploadManager() {}
public UploadManager(EventBus eventBus, FileManager fileManager,
public UploadManager(EventBus eventBus, FileManager fileManager,
MeshManager meshManager, DownloadManager downloadManager) {
this.eventBus = eventBus
this.fileManager = fileManager
this.meshManager = meshManager
this.downloadManager = downloadManager
}
public void processGET(Endpoint e) throws IOException {
byte [] infoHashStringBytes = new byte[44]
DataInputStream dis = new DataInputStream(e.getInputStream())
@ -79,10 +79,10 @@ public class UploadManager {
e.close()
return
}
if (request.have > 0)
if (request.have > 0)
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
Mesh mesh
File file
int pieceSize
@ -96,7 +96,7 @@ public class UploadManager {
file = sharedFile.file
pieceSize = sharedFile.pieceSize
}
Uploader uploader = new ContentUploader(file, request, e, mesh, pieceSize)
eventBus.publish(new UploadEvent(uploader : uploader))
try {
@ -106,14 +106,14 @@ public class UploadManager {
}
}
}
public void processHashList(Endpoint e) {
byte [] infoHashStringBytes = new byte[44]
DataInputStream dis = new DataInputStream(e.getInputStream())
dis.readFully(infoHashStringBytes)
String infoHashString = new String(infoHashStringBytes, StandardCharsets.US_ASCII)
log.info("Responding to hashlist request for root $infoHashString")
byte [] infoHashRoot = Base64.decode(infoHashString)
InfoHash infoHash = new InfoHash(infoHashRoot)
Downloader downloader = downloadManager.downloaders.get(infoHash)
@ -125,7 +125,7 @@ public class UploadManager {
e.close()
return
}
byte [] rn = new byte[2]
dis.readFully(rn)
if (rn != "\r\n".getBytes(StandardCharsets.US_ASCII)) {
@ -133,14 +133,14 @@ public class UploadManager {
e.close()
return
}
Request request = Request.parseHashListRequest(infoHash, e.getInputStream())
if (request.downloader != null && request.downloader.destination != e.destination) {
log.info("Downloader persona doesn't match their destination")
e.close()
return
}
InfoHash fullInfoHash
if (downloader == null) {
fullInfoHash = sharedFiles.iterator().next().infoHash
@ -156,7 +156,7 @@ public class UploadManager {
return
}
}
Uploader uploader = new HashListUploader(e, fullInfoHash, request)
eventBus.publish(new UploadEvent(uploader : uploader))
try {
@ -164,7 +164,7 @@ public class UploadManager {
} finally {
eventBus.publish(new UploadFinishedEvent(uploader : uploader))
}
// proceed with content
while(true) {
byte[] get = new byte[4]
@ -204,10 +204,10 @@ public class UploadManager {
e.close()
return
}
if (request.have > 0)
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
Mesh mesh
File file
int pieceSize

View File

@ -11,31 +11,31 @@ import com.muwire.core.connection.Endpoint
abstract class Uploader {
protected final Endpoint endpoint
protected ByteBuffer mapped
Uploader(Endpoint endpoint) {
this.endpoint = endpoint
}
abstract void respond()
public synchronized int getPosition() {
if (mapped == null)
return -1
mapped.position()
}
abstract String getName();
/**
* @return an integer between 0 and 100
*/
abstract int getProgress();
abstract String getDownloader();
abstract int getDonePieces();
abstract int getTotalPieces();
abstract long getTotalSize();
}

View File

@ -10,42 +10,42 @@ import com.muwire.core.Constants
import net.i2p.data.Base64
class DataUtil {
private final static int MAX_SHORT = (0x1 << 16) - 1
static void writeUnsignedShort(int value, OutputStream os) {
if (value > MAX_SHORT || value < 0)
throw new IllegalArgumentException("$value invalid")
byte lsb = (byte) (value & 0xFF)
byte msb = (byte) (value >> 8)
os.write(msb)
os.write(lsb)
os.write(lsb)
}
private final static int MAX_HEADER = 0x7FFFFF
static void packHeader(int length, byte [] header) {
if (header.length != 3)
throw new IllegalArgumentException("header length $header.length")
if (length < 0 || length > MAX_HEADER)
throw new IllegalArgumentException("length $length")
header[2] = (byte) (length & 0xFF)
header[1] = (byte) ((length >> 8) & 0xFF)
header[0] = (byte) ((length >> 16) & 0x7F)
}
static int readLength(byte [] header) {
if (header.length != 3)
throw new IllegalArgumentException("header length $header.length")
return (((int)(header[0] & 0x7F)) << 16) |
(((int)(header[1] & 0xFF) << 8)) |
((int)header[2] & 0xFF)
}
static String readi18nString(byte [] encoded) {
if (encoded.length < 2)
throw new IllegalArgumentException("encoding too short $encoded.length")
@ -54,9 +54,9 @@ class DataUtil {
throw new IllegalArgumentException("encoding doesn't match length, expected $length found $encoded.length")
byte [] string = new byte[length]
System.arraycopy(encoded, 2, string, 0, length)
new String(string, StandardCharsets.UTF_8)
new String(string, StandardCharsets.UTF_8)
}
static byte[] encodei18nString(String string) {
byte [] utf8 = string.getBytes(StandardCharsets.UTF_8)
if (utf8.length > Short.MAX_VALUE)
@ -64,11 +64,11 @@ class DataUtil {
def baos = new ByteArrayOutputStream()
def daos = new DataOutputStream(baos)
daos.writeShort((short) utf8.length)
daos.write(utf8)
daos.write(utf8)
daos.close()
baos.toByteArray()
}
public static String readTillRN(InputStream is) {
def baos = new ByteArrayOutputStream()
while(baos.size() < (Constants.MAX_HEADER_SIZE)) {
@ -84,7 +84,7 @@ class DataUtil {
}
new String(baos.toByteArray(), StandardCharsets.US_ASCII)
}
public static String encodeXHave(List<Integer> pieces, int totalPieces) {
int bytes = totalPieces / 8
if (totalPieces % 8 != 0)
@ -98,7 +98,7 @@ class DataUtil {
}
Base64.encode(raw)
}
public static List<Integer> decodeXHave(String xHave) {
byte [] availablePieces = Base64.decode(xHave)
List<Integer> available = new ArrayList<>()
@ -112,19 +112,19 @@ class DataUtil {
}
available
}
public static Exception findRoot(Exception e) {
while(e.getCause() != null)
e = e.getCause()
e
}
public static void tryUnmap(ByteBuffer cb) {
if (cb==null || !cb.isDirect()) return;
// we could use this type cast and call functions without reflection code,
// but static import from sun.* package is risky for non-SUN virtual machine.
//try { ((sun.nio.ch.DirectBuffer)cb).cleaner().clean(); } catch (Exception ex) { }
// JavaSpecVer: 1.6, 1.7, 1.8, 9, 10
boolean isOldJDK = System.getProperty("java.specification.version","99").startsWith("1.");
try {

View File

@ -16,10 +16,10 @@ class JULLog extends Log {
I2P_TO_JUL.put(Log.ERROR, Level.SEVERE)
I2P_TO_JUL.put(Log.CRIT, Level.SEVERE)
}
private final Logger delegate
private final Level level
public JULLog(Class<?> cls) {
super(cls)
delegate = Logger.getLogger(cls.getName())
@ -31,7 +31,7 @@ class JULLog extends Log {
delegate = Logger.getLogger(name)
level = findLevel(delegate)
}
private static Level findLevel(Logger log) {
while (log.getLevel() == null)
log = log.getParent()

View File

@ -5,14 +5,14 @@ import net.i2p.util.Log
import net.i2p.util.LogManager
class MuWireLogManager extends LogManager {
private static final Map<Class<?>, Log> classLogs = new HashMap<>()
private static final Map<String, Log> stringLogs = new HashMap<>()
MuWireLogManager() {
super(I2PAppContext.getGlobalContext())
}
@Override
public synchronized Log getLog(Class<?> cls, String name) {
@ -24,7 +24,7 @@ class MuWireLogManager extends LogManager {
}
return rv
}
Log rv = stringLogs.get(name)
if (rv == null) {
rv = new JULLog(name)
@ -32,5 +32,5 @@ class MuWireLogManager extends LogManager {
}
rv
}
}

View File

@ -7,15 +7,15 @@ import java.util.Set;
import net.i2p.data.Destination;
public class DownloadedFile extends SharedFile {
private final Set<Destination> sources;
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources)
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources)
throws IOException {
super(file, infoHash, pieceSize);
this.sources = sources;
}
public Set<Destination> getSources() {
return sources;
}

View File

@ -12,16 +12,16 @@ import net.i2p.data.Base64;
public class InfoHash {
public static final int SIZE = 0x1 << 5;
private final byte[] root;
private final byte[] hashList;
private final int hashCode;
public InfoHash(byte[] root, byte[] hashList) {
if (root.length != SIZE)
throw new IllegalArgumentException("invalid root size "+root.length);
if (hashList != null && hashList.length % SIZE != 0)
if (hashList != null && hashList.length % SIZE != 0)
throw new IllegalArgumentException("invalid hashList size " + hashList.length);
this.root = root;
this.hashList = hashList;
@ -30,15 +30,15 @@ public class InfoHash {
root[2] << 8 |
root[3];
}
public InfoHash(byte[] root) {
this(root, null);
}
public InfoHash(String base32) {
this(Base32.decode(base32));
}
public static InfoHash fromHashList(byte []hashList) {
try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
@ -50,20 +50,20 @@ public class InfoHash {
}
return null;
}
public byte[] getRoot() {
return root;
}
public byte[] getHashList() {
return hashList;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -75,11 +75,11 @@ public class InfoHash {
InfoHash other = (InfoHash) o;
return Arrays.equals(root, other.root);
}
public String toString() {
String rv = "InfoHash[root:"+Base64.encode(root) + " hashList:";
List<String> b64HashList = new ArrayList<>();
if (hashList != null) {
if (hashList != null) {
byte [] tmp = new byte[SIZE];
for (int i = 0; i < hashList.length / SIZE; i++) {
System.arraycopy(hashList, SIZE * i, tmp, 0, SIZE);

View File

@ -8,10 +8,10 @@ public class SharedFile {
private final File file;
private final InfoHash infoHash;
private final int pieceSize;
private final String cachedPath;
private final long cachedLength;
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
this.file = file;
this.infoHash = infoHash;
@ -27,11 +27,11 @@ public class SharedFile {
public InfoHash getInfoHash() {
return infoHash;
}
public int getPieceSize() {
return pieceSize;
}
public int getNPieces() {
long length = file.length();
int rawPieceSize = 0x1 << pieceSize;
@ -40,20 +40,20 @@ public class SharedFile {
rv++;
return rv;
}
public String getCachedPath() {
return cachedPath;
}
public long getCachedLength() {
return cachedLength;
}
@Override
public int hashCode() {
return file.hashCode() ^ infoHash.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof SharedFile))

View File

@ -5,18 +5,18 @@ import org.junit.Test
class EventBusTest {
class FakeEvent extends Event {}
class FakeEventHandler {
def onFakeEvent(FakeEvent e) {
assert e == fakeEvent
}
}
FakeEvent fakeEvent = new FakeEvent()
EventBus bus = new EventBus()
def handler = new FakeEventHandler()
@Test
void testDynamicEvent() {
bus.register(FakeEvent.class, handler)

View File

@ -5,7 +5,7 @@ import net.i2p.data.Base64
class Personas {
private final String encoded1 = "AQADemFiO~pgSoEo8wQfwncYMvBQWkvPY9I7DYUllHp289UE~zBaLdbl~wbliktAUsW-S70f3UeYgHq34~c7zVuUQjgHZ506iG9hX8B9S3a9gQ3CSG0GuDpeNyiXmZkpHp5m8vT9PZ1zMWzxvzZY~fP9yKFKgO4yrso5I9~DGOPeyJZJ4BFsTJDERv41aZqjFLYUBDmeHGgg9RjYy~93h-nQMVYj9JSO3AgowW-ix49rtiKYIXHMa2PxWHUXkUHWJZtIZntNIDEFeMnPdzLxjAl8so2G6pDcTMZPLLwyb73Ee5ZVfxUynPqyp~fIGVP8Rl4rlaGFli2~ATGBz3XY54aObC~0p7us2JnWaTC~oQT5DVDM7gaOO885o-m8BB8b0duzMBelbdnMZFQJ5jIHVKxkC6Niw4fxTOoXTyOqQmVhtK-9xcwxMuN5DF9IewkR5bhpq5rgnfBP5zvyBaAHMq-d3TCOjTsZ-d3liB98xX5p8G5zmS7gfKArQtM5~CcK~AlX-lGLBQAEAAcAAN5MW1Tq983szfZgY1l8tQFqy8I9tdMf7vc1Ktj~TCIvXYw6AYMbMGy3S67FSPLZVmfHEMQKj2KLAdaRKQkHPAY"
private final String encoded2 = "AQAHemxhdGluYiN~3G-hPoBfJ04mhcC52lC6TYSwWxH-WNWno9Y35JS-WrXlnPsodZtwy96ttEaiKTg-hkRqMsaYKpWar1FwayR6qlo0pZCo5pQOLfR7GIM3~wde0JIBEp8BUpgzF1-QXLhuRG1t7tBbenW2tSgp5jQH61RI-c9flyUlOvf6nrhQMZ3aoviZ4aZW23Fx-ajYQBDk7PIxuyn8qYNwWy3kWOhGan05c54NnumS3XCzQWFDDPlADmco1WROeY9qrwwtmLM8lzDCEtJQXJlk~K5yLbyB63hmAeTK7J4iS6f9nnWv7TbB5r-Z3kC6D9TLYrQbu3h4AAxrqso45P8yHQtKUA4QJicS-6NJoBOnlCCU887wx2k9YSxxwNydlIxb1mZsX65Ke4uY0HDFokZHTzUcxvfLB6G~5JkSPDCyZz~2fREgW2-VXu7gokEdEugkuZRrsiQzyfAOOkv53ti5MzTbMOXinBskSb1vZyN2-XcZNaDJvEqUNj~qpfhe-ov2F7FuwQUABAAHAAAfqq-MneIqWBQY92-sy9Z0s~iQsq6lUFa~sYMdY-5o-94fF8a140dm-emF3rO8vuidUIPNaS-37Rl05mAKUCcB"
Persona persona1 = new Persona(new ByteArrayInputStream(Base64.decode(encoded1)))
Persona persona2 = new Persona(new ByteArrayInputStream(Base64.decode(encoded2)))
}

View File

@ -25,33 +25,33 @@ class ConnectionAcceptorTest {
EventBus eventBus
final Destinations destinations = new Destinations()
def settings
def connectionManagerMock
UltrapeerConnectionManager connectionManager
def i2pAcceptorMock
I2PAcceptor i2pAcceptor
def hostCacheMock
HostCache hostCache
def trustServiceMock
TrustService trustService
def searchManagerMock
SearchManager searchManager
def uploadManagerMock
UploadManager uploadManager
def connectionEstablisherMock
ConnectionEstablisher connectionEstablisher
ConnectionAcceptor acceptor
List<ConnectionEvent> connectionEvents
InputStream inputStream
OutputStream outputStream
@Before
void before() {
connectionManagerMock = new MockFor(UltrapeerConnectionManager.class)
@ -62,7 +62,7 @@ class ConnectionAcceptorTest {
uploadManagerMock = new MockFor(UploadManager.class)
connectionEstablisherMock = new MockFor(ConnectionEstablisher.class)
}
@After
void after() {
acceptor?.stop()
@ -75,7 +75,7 @@ class ConnectionAcceptorTest {
connectionEstablisherMock.verify connectionEstablisher
Thread.sleep(100)
}
private void initMocks() {
connectionEvents = new CopyOnWriteArrayList()
eventBus = new EventBus()
@ -85,7 +85,7 @@ class ConnectionAcceptorTest {
}
}
eventBus.register(ConnectionEvent.class, listener)
connectionManager = connectionManagerMock.proxyInstance()
i2pAcceptor = i2pAcceptorMock.proxyInstance()
hostCache = hostCacheMock.proxyInstance()
@ -93,13 +93,13 @@ class ConnectionAcceptorTest {
searchManager = searchManagerMock.proxyInstance()
uploadManager = uploadManagerMock.proxyInstance()
connectionEstablisher = connectionEstablisherMock.proxyInstance()
acceptor = new ConnectionAcceptor(eventBus, connectionManager, settings, i2pAcceptor,
acceptor = new ConnectionAcceptor(eventBus, connectionManager, settings, i2pAcceptor,
hostCache, trustService, searchManager, uploadManager, connectionEstablisher)
acceptor.start()
Thread.sleep(100)
}
@Test
void testSuccessfulLeaf() {
settings = new MuWireSettings() {
@ -125,15 +125,15 @@ class ConnectionAcceptorTest {
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
outputStream.write("MuWire leaf".bytes)
byte [] OK = new byte[2]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "OK".bytes
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
@ -142,7 +142,7 @@ class ConnectionAcceptorTest {
assert event.incoming == true
assert event.leaf == true
}
@Test
void testSuccessfulPeer() {
settings = new MuWireSettings() {
@ -168,15 +168,15 @@ class ConnectionAcceptorTest {
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[2]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "OK".bytes
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
@ -185,7 +185,7 @@ class ConnectionAcceptorTest {
assert event.incoming == true
assert event.leaf == false
}
@Test
void testLeafRejectsLeaf() {
settings = new MuWireSettings() {
@ -205,14 +205,14 @@ class ConnectionAcceptorTest {
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
outputStream.write("MuWire leaf".bytes)
outputStream.flush()
Thread.sleep(50)
assert inputStream.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
@ -221,7 +221,7 @@ class ConnectionAcceptorTest {
assert event.incoming == true
assert event.leaf == null
}
@Test
void testLeafRejectsPeer() {
settings = new MuWireSettings() {
@ -241,14 +241,14 @@ class ConnectionAcceptorTest {
assert dest == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
outputStream.write("MuWire peer".bytes)
outputStream.flush()
Thread.sleep(50)
assert inputStream.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
@ -257,7 +257,7 @@ class ConnectionAcceptorTest {
assert event.incoming == true
assert event.leaf == null
}
@Test
void testPeerRejectsPeerSlots() {
settings = new MuWireSettings() {
@ -284,18 +284,18 @@ class ConnectionAcceptorTest {
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [] }
initMocks()
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
Thread.sleep(50)
assert dis.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
@ -304,7 +304,7 @@ class ConnectionAcceptorTest {
assert event.incoming == true
assert event.leaf == false
}
@Test
void testPeerRejectsLeafSlots() {
settings = new MuWireSettings() {
@ -331,18 +331,18 @@ class ConnectionAcceptorTest {
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [] }
initMocks()
outputStream.write("MuWire leaf".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
Thread.sleep(50)
assert dis.read() == -1
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
@ -351,7 +351,7 @@ class ConnectionAcceptorTest {
assert event.incoming == true
assert event.leaf == true
}
@Test
void testPeerRejectsPeerSuggests() {
settings = new MuWireSettings() {
@ -378,26 +378,26 @@ class ConnectionAcceptorTest {
TrustLevel.TRUSTED
}
hostCacheMock.ignore.getGoodHosts { n -> [destinations.dest2] }
initMocks()
outputStream.write("MuWire peer".bytes)
byte [] OK = new byte[6]
def dis = new DataInputStream(inputStream)
dis.readFully(OK)
assert OK == "REJECT".bytes
short payloadSize = dis.readUnsignedShort()
byte[] payload = new byte[payloadSize]
dis.readFully(payload)
assert dis.read() == -1
def json = new JsonSlurper()
json = json.parse(payload)
assert json.tryHosts != null
assert json.tryHosts.size() == 1
assert json.tryHosts.contains(destinations.dest2.toBase64())
Thread.sleep(50)
assert connectionEvents.size() == 1
def event = connectionEvents[0]

View File

@ -23,21 +23,21 @@ class ConnectionEstablisherTest {
List<HostDiscoveredEvent> discoveredEvents
DataInputStream inputStream
DataOutputStream outputStream
def i2pConnectorMock
I2PConnector i2pConnector
MuWireSettings settings
def connectionManagerMock
ConnectionManager connectionManager
def hostCacheMock
HostCache hostCache
ConnectionEstablisher establisher
@Before
void before() {
connectionEvents = new CopyOnWriteArrayList()
@ -57,7 +57,7 @@ class ConnectionEstablisherTest {
connectionManagerMock = new MockFor(ConnectionManager.class)
hostCacheMock = new MockFor(HostCache.class)
}
@After
void after() {
establisher?.stop()
@ -66,7 +66,7 @@ class ConnectionEstablisherTest {
hostCacheMock.verify hostCache
Thread.sleep(100)
}
private void initMocks() {
i2pConnector = i2pConnectorMock.proxyInstance()
connectionManager = connectionManagerMock.proxyInstance()
@ -75,13 +75,13 @@ class ConnectionEstablisherTest {
establisher.start()
Thread.sleep(250)
}
@Test
void testConnectFails() {
settings = new MuWireSettings()
connectionManagerMock.ignore.needsConnections {
true
true
}
hostCacheMock.ignore.getHosts { num ->
assert num == 1
@ -95,16 +95,16 @@ class ConnectionEstablisherTest {
assert dest == destinations.dest1
throw new IOException()
}
initMocks()
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.FAILED
}
@Test
void testConnectionSucceedsPeer() {
settings = new MuWireSettings() {
@ -128,26 +128,26 @@ class ConnectionEstablisherTest {
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
outputStream.write("OK".bytes)
outputStream.flush()
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
}
@Test
void testConnectionSucceedsLeaf() {
settings = new MuWireSettings() {
@ -171,25 +171,25 @@ class ConnectionEstablisherTest {
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire leaf".bytes
outputStream.write("OK".bytes)
outputStream.flush()
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.SUCCESSFUL
}
@Test
void testConnectionRejected() {
settings = new MuWireSettings() {
@ -213,18 +213,18 @@ class ConnectionEstablisherTest {
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
outputStream.write("REJECT".bytes)
outputStream.flush()
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
@ -232,7 +232,7 @@ class ConnectionEstablisherTest {
assert event.status == ConnectionAttemptStatus.REJECTED
assert discoveredEvents.isEmpty()
}
@Test
void testConnectionRejectedSuggestions() {
settings = new MuWireSettings() {
@ -256,16 +256,16 @@ class ConnectionEstablisherTest {
outputStream = new DataOutputStream(new PipedOutputStream(is))
new Endpoint(dest, is, os, null)
}
initMocks()
byte [] header = new byte[11]
inputStream.readFully(header)
assert header == "MuWire peer".bytes
outputStream.write("REJECT".bytes)
outputStream.flush()
def json = [:]
json.tryHosts = [destinations.dest2.toBase64()]
json = JsonOutput.toJson(json)
@ -273,13 +273,13 @@ class ConnectionEstablisherTest {
outputStream.write(json.bytes)
outputStream.flush()
Thread.sleep(100)
assert connectionEvents.size() == 1
def event = connectionEvents[0]
assert event.endpoint.destination == destinations.dest1
assert event.incoming == false
assert event.status == ConnectionAttemptStatus.REJECTED
assert discoveredEvents.size() == 1
event = discoveredEvents[0]
assert event.destination == destinations.dest2

View File

@ -19,48 +19,48 @@ import net.i2p.data.Base64
import net.i2p.util.ConcurrentHashSet
class DownloadSessionTest {
private EventBus eventBus
private File source, target
private InfoHash infoHash
private Endpoint endpoint
private Pieces pieces
private String rootBase64
private DownloadSession session
private Thread downloadThread
private InputStream fromDownloader, fromUploader
private OutputStream toDownloader, toUploader
private volatile boolean performed
private Set<Integer> available = new ConcurrentHashSet<>()
private volatile IOException thrown
@Before
public void setUp() {
eventBus = new EventBus()
}
private void initSession(int size, def claimedPieces = []) {
Random r = new Random()
byte [] content = new byte[size]
r.nextBytes(content)
source = File.createTempFile("source", "tmp")
source.deleteOnExit()
def fos = new FileOutputStream(source)
fos.write(content)
fos.close()
def hasher = new FileHasher()
infoHash = hasher.hashFile(source)
rootBase64 = Base64.encode(infoHash.getRoot())
target = File.createTempFile("target", "tmp")
int pieceSize = 1 << FileHasher.getPieceSize(size)
int nPieces
if (size % pieceSize == 0)
nPieces = size / pieceSize
@ -68,27 +68,27 @@ class DownloadSessionTest {
nPieces = size / pieceSize + 1
pieces = new Pieces(nPieces)
claimedPieces.each {pieces.claimed.set(it)}
fromDownloader = new PipedInputStream()
fromUploader = new PipedInputStream()
toDownloader = new PipedOutputStream(fromUploader)
toUploader = new PipedOutputStream(fromDownloader)
endpoint = new Endpoint(null, fromUploader, toUploader, null)
session = new DownloadSession(eventBus, "",pieces, infoHash, endpoint, target, pieceSize, size, available)
downloadThread = new Thread( { perform() } as Runnable)
downloadThread.setDaemon(true)
downloadThread.start()
}
private void perform() {
try {
try {
performed = session.request()
} catch (IOException e) {
thrown = e
}
}
@After
public void teardown() {
source?.delete()
@ -96,7 +96,7 @@ class DownloadSessionTest {
downloadThread?.interrupt()
Thread.sleep(50)
}
@Test
public void testSmallFile() {
initSession(20)
@ -105,38 +105,38 @@ class DownloadSessionTest {
readTillRN(fromDownloader)
readTillRN(fromDownloader)
assert "" == readTillRN(fromDownloader)
toDownloader.write("200 OK\r\n".bytes)
toDownloader.write("Content-Range: 0-19\r\n\r\n".bytes)
toDownloader.write(source.bytes)
toDownloader.flush()
Thread.sleep(150)
assert pieces.isComplete()
assert target.bytes == source.bytes
assert performed
assert available.isEmpty()
assert thrown == null
}
@Test
public void testPieceSizeFile() {
int size = FileHasher.getPieceSize(1)
size = 1 << size
initSession(size)
assert "GET $rootBase64" == readTillRN(fromDownloader)
readTillRN(fromDownloader)
readTillRN(fromDownloader)
readTillRN(fromDownloader)
assert "" == readTillRN(fromDownloader)
toDownloader.write("200 OK\r\n".bytes)
toDownloader.write(("Content-Range: 0-"+(size - 1)+"\r\n\r\n").bytes)
toDownloader.write(source.bytes)
toDownloader.flush()
Thread.sleep(150)
assert pieces.isComplete()
assert target.bytes == source.bytes
@ -144,33 +144,33 @@ class DownloadSessionTest {
assert available.isEmpty()
assert thrown == null
}
@Test
public void testPieceSizePlusOne() {
int pieceSize = FileHasher.getPieceSize(1)
int size = (1 << pieceSize) + 1
initSession(size)
assert "GET $rootBase64" == readTillRN(fromDownloader)
String range = readTillRN(fromDownloader)
def matcher = (range =~ /^Range: (\d+)-(\d+)$/)
int start = Integer.parseInt(matcher[0][1])
int end = Integer.parseInt(matcher[0][2])
assert (start == 0 && end == ((1 << pieceSize) - 1)) ||
assert (start == 0 && end == ((1 << pieceSize) - 1)) ||
(start == (1 << pieceSize) && end == (1 << pieceSize))
readTillRN(fromDownloader)
readTillRN(fromDownloader)
assert "" == readTillRN(fromDownloader)
toDownloader.write("200 OK\r\n".bytes)
toDownloader.write(("Content-Range: $start-$end\r\n\r\n").bytes)
byte [] piece = new byte[end - start + 1]
System.arraycopy(source.bytes, start, piece, 0, piece.length)
toDownloader.write(piece)
toDownloader.flush()
Thread.sleep(150)
assert !pieces.isComplete()
assert 1 == pieces.donePieces()
@ -178,7 +178,7 @@ class DownloadSessionTest {
assert available.isEmpty()
assert thrown == null
}
@Test
public void testSmallFileClaimed() {
initSession(20, [0])
@ -189,29 +189,29 @@ class DownloadSessionTest {
assert available.isEmpty()
assert thrown == null
}
@Test
public void testClaimedPiecesAvoided() {
int pieceSize = FileHasher.getPieceSize(1)
int size = (1 << pieceSize) * 10
initSession(size, [1,2,3,4,5,6,7,8,9])
assert !pieces.claimed.get(0)
assert "GET $rootBase64" == readTillRN(fromDownloader)
String range = readTillRN(fromDownloader)
def matcher = (range =~ /^Range: (\d+)-(\d+)$/)
int start = Integer.parseInt(matcher[0][1])
int end = Integer.parseInt(matcher[0][2])
assert pieces.claimed.get(0)
assert start == 0 && end == (1 << pieceSize) - 1
}
@Test
public void test416NoHave() {
initSession(20)
readAllHeaders(fromDownloader)
toDownloader.write("416 don't have it\r\n\r\n".bytes)
toDownloader.flush()
Thread.sleep(150)
@ -219,45 +219,45 @@ class DownloadSessionTest {
assert available.isEmpty()
assert thrown != null
}
@Test
public void test416Have() {
initSession(20)
readAllHeaders(fromDownloader)
toDownloader.write("416 don't have it\r\n".bytes)
toDownloader.write("X-Have: ${encodeXHave([0], 1)}\r\n\r\n".bytes)
toDownloader.flush()
Thread.sleep(150)
assert performed
assert available.contains(0)
assert thrown == null
}
@Test
public void test416Have2Pieces() {
int pieceSize = FileHasher.getPieceSize(1)
int size = (1 << pieceSize) + 1
initSession(size)
readAllHeaders(fromDownloader)
toDownloader.write("416 don't have it\r\n".bytes)
toDownloader.write("X-Have: ${encodeXHave([1], 2)}\r\n\r\n".bytes)
toDownloader.flush()
Thread.sleep(150)
assert performed
assert available.contains(1)
assert thrown == null
}
@Test
public void test200TwoPieces1Available() {
int pieceSize = FileHasher.getPieceSize(1)
int size = (1 << pieceSize) * 9 + 1
initSession(size)
Set<String> headers = readAllHeaders(fromDownloader)
def matcher = null
headers.each {
@ -267,27 +267,27 @@ class DownloadSessionTest {
assert matcher.groupCount() > 0
int start = Integer.parseInt(matcher[0][1])
int end = Integer.parseInt(matcher[0][2])
if (start == 0)
if (start == 0)
fail("inconlcusive")
toDownloader.write("416 don't have it \r\n".bytes)
toDownloader.write("X-Have: ${encodeXHave([0],2)}\r\n\r\n".bytes)
toDownloader.flush()
downloadThread.join()
assert performed
performed = false
assert available.contains(0)
assert thrown == null
// request same session
downloadThread = new Thread( { perform() } as Runnable)
downloadThread.setDaemon(true)
downloadThread.start()
Thread.sleep(150)
headers = readAllHeaders(fromDownloader)
matcher = null
headers.each {
@ -299,7 +299,7 @@ class DownloadSessionTest {
end = Integer.parseInt(matcher[0][2])
assert start == 0
}
@Test
public void testXAlt() throws Exception {
Personas personas = new Personas()
@ -310,19 +310,19 @@ class DownloadSessionTest {
}
}
eventBus.register(SourceDiscoveredEvent.class, listener)
initSession(20)
readAllHeaders(fromDownloader)
toDownloader.write("416 don't have it\r\n".bytes)
toDownloader.write("X-Alt: ${personas.persona1.toBase64()},${personas.persona2.toBase64()}\r\n\r\n".bytes)
toDownloader.flush()
Thread.sleep(150)
assert sources.contains(personas.persona1)
assert sources.contains(personas.persona2)
assert 2 == sources.size()
}
private static Set<String> readAllHeaders(InputStream is) {
Set<String> rv = new HashSet<>()
String header

View File

@ -3,15 +3,15 @@ package com.muwire.core.download
import org.junit.Test
class PiecesTest {
Pieces pieces
@Test
public void testEmpty() {
pieces = new Pieces(20)
assert !pieces.isComplete()
}
@Test
public void testSinglePiece() {
pieces = new Pieces(1)
@ -20,7 +20,7 @@ class PiecesTest {
pieces.markDownloaded(0)
assert pieces.isComplete()
}
@Test
public void testTwoPieces() {
pieces = new Pieces(2)
@ -34,7 +34,7 @@ class PiecesTest {
pieces.markDownloaded(piece2)
assert pieces.isComplete()
}
@Test
public void testClaimAvailable() {
pieces = new Pieces(2)
@ -42,7 +42,7 @@ class PiecesTest {
assert claimed == 0
assert -1 == pieces.claim([0].toSet())
}
@Test
public void testClaimNoneAvailable() {
pieces = new Pieces(20)

View File

@ -10,18 +10,18 @@ class FileHasherTest extends GroovyTestCase {
def hasher = new FileHasher()
File tmp
@Before
void setUp() {
tmp = File.createTempFile("testFile", "test")
tmp.deleteOnExit()
}
@After
void tearDown() {
tmp?.delete()
}
@Test
void testPieceSize() {
assert 17 == FileHasher.getPieceSize(1000000)
@ -31,7 +31,7 @@ class FileHasherTest extends GroovyTestCase {
FileHasher.getPieceSize(Long.MAX_VALUE)
}
}
@Test
void testHash1Byte() {
def fos = new FileOutputStream(tmp)
@ -40,7 +40,7 @@ class FileHasherTest extends GroovyTestCase {
def ih = hasher.hashFile(tmp)
assert ih.getHashList().length == 32
}
@Test
void testHash1PieceExact() {
def fos = new FileOutputStream(tmp)
@ -50,7 +50,7 @@ class FileHasherTest extends GroovyTestCase {
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 64
}
@Test
void testHash1Piece1Byte() {
def fos = new FileOutputStream(tmp)
@ -60,7 +60,7 @@ class FileHasherTest extends GroovyTestCase {
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 96
}
@Test
void testHash2Pieces() {
def fos = new FileOutputStream(tmp)
@ -70,7 +70,7 @@ class FileHasherTest extends GroovyTestCase {
def ih = hasher.hashFile tmp
assert ih.getHashList().length == 128
}
@Test
void testHash2Pieces2Bytes() {
def fos = new FileOutputStream(tmp)

View File

@ -13,16 +13,16 @@ import com.muwire.core.search.SearchEvent
class FileManagerTest {
EventBus eventBus
FileManager manager
volatile ResultsEvent results
def listener = new Object() {
void onResultsEvent(ResultsEvent e) {
results = e
}
}
@Before
void before() {
eventBus = new EventBus()
@ -30,7 +30,7 @@ class FileManagerTest {
manager = new FileManager(eventBus, new MuWireSettings())
results = null
}
@Test
void testHash1Result() {
File f = new File("a b.c")
@ -38,19 +38,19 @@ class FileManagerTest {
SharedFile sf = new SharedFile(f,ih, 0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
UUID uuid = UUID.randomUUID()
SearchEvent se = new SearchEvent(searchHash: ih.getRoot(), uuid: uuid)
manager.onSearchEvent(se)
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf)
assert results.uuid == uuid
}
@Test
void testHash2Results() {
InfoHash ih = InfoHash.fromHashList(new byte[32])
@ -61,17 +61,17 @@ class FileManagerTest {
UUID uuid = UUID.randomUUID()
SearchEvent se = new SearchEvent(searchHash: ih.getRoot(), uuid: uuid)
manager.onSearchEvent(se)
Thread.sleep(20)
assert results != null
assert results.results.size() == 2
assert results.results.contains(sf1)
assert results.results.contains(sf2)
assert results.uuid == uuid
}
@Test
void testHash0Results() {
File f = new File("a b.c")
@ -79,13 +79,13 @@ class FileManagerTest {
SharedFile sf = new SharedFile(f,ih, 0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
manager.onSearchEvent new SearchEvent(searchHash: new byte[32], uuid: UUID.randomUUID())
Thread.sleep(20)
assert results == null
}
@Test
void testKeyword1Result() {
File f = new File("a b.c")
@ -93,40 +93,40 @@ class FileManagerTest {
SharedFile sf = new SharedFile(f,ih,0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
UUID uuid = UUID.randomUUID()
manager.onSearchEvent new SearchEvent(searchTerms: ["a"], uuid:uuid)
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf)
assert results.uuid == uuid
assert results.uuid == uuid
}
@Test
void testKeyword2Results() {
File f1 = new File("a b.c")
InfoHash ih1 = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(f1, ih1, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf1)
File f2 = new File("c d.e")
InfoHash ih2 = InfoHash.fromHashList(new byte[64])
SharedFile sf2 = new SharedFile(f2, ih2, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf2)
UUID uuid = UUID.randomUUID()
manager.onSearchEvent new SearchEvent(searchTerms: ["c"], uuid:uuid)
Thread.sleep(20)
assert results != null
assert results.results.size() == 2
assert results.results.contains(sf1)
assert results.results.contains(sf2)
assert results.uuid == uuid
}
@Test
void testKeyword0Results() {
File f = new File("a b.c")
@ -134,13 +134,13 @@ class FileManagerTest {
SharedFile sf = new SharedFile(f,ih,0)
FileHashedEvent fhe = new FileHashedEvent(sharedFile: sf)
manager.onFileHashedEvent(fhe)
manager.onSearchEvent new SearchEvent(searchTerms: ["d"], uuid: UUID.randomUUID())
Thread.sleep(20)
assert results == null
}
@Test
void testRemoveFileExistingHash() {
InfoHash ih = InfoHash.fromHashList(new byte[32])
@ -148,41 +148,41 @@ class FileManagerTest {
SharedFile sf2 = new SharedFile(new File("d e.f"), ih, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf1)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile : sf2)
manager.onFileUnsharedEvent new FileUnsharedEvent(unsharedFile: sf2)
manager.onSearchEvent new SearchEvent(searchHash : ih.getRoot())
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf1)
}
@Test
void testRemoveFile() {
File f1 = new File("a b.c")
InfoHash ih1 = InfoHash.fromHashList(new byte[32])
SharedFile sf1 = new SharedFile(f1, ih1, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf1)
File f2 = new File("c d.e")
InfoHash ih2 = InfoHash.fromHashList(new byte[64])
SharedFile sf2 = new SharedFile(f2, ih2, 0)
manager.onFileLoadedEvent new FileLoadedEvent(loadedFile: sf2)
manager.onFileUnsharedEvent new FileUnsharedEvent(unsharedFile: sf2)
// 1 match left
manager.onSearchEvent new SearchEvent(searchTerms: ["c"])
Thread.sleep(20)
assert results != null
assert results.results.size() == 1
assert results.results.contains(sf1)
// no match
results = null
manager.onSearchEvent new SearchEvent(searchTerms: ["d"])
assert results == null
}
}

View File

@ -20,7 +20,7 @@ class HasherServiceTest {
offer evt
}
}
@Before
void before() {
eventBus = new EventBus()
@ -30,12 +30,12 @@ class HasherServiceTest {
eventBus.register(FileSharedEvent.class, service)
service.start()
}
@After
void after() {
listener.clear()
}
@Test
void testSingleFile() {
File f = new File("build.gradle")
@ -47,7 +47,7 @@ class HasherServiceTest {
assert hashed.sharedFile.infoHash != null
assert listener.isEmpty()
}
@Test
void testDirectory() {
File f = new File(".")

View File

@ -23,35 +23,35 @@ class PersisterServiceLoadingTest {
publishedFiles.add(e.loadedFile)
}
}
EventBus eventBus
Listener listener
File sharedDir
File sharedFile1, sharedFile2
@Before
void setup() {
eventBus = new EventBus()
listener = new Listener()
eventBus.register(FileLoadedEvent.class, listener)
sharedDir = new File("sharedDir")
sharedDir.mkdir()
sharedDir.deleteOnExit()
sharedFile1 = new File(sharedDir,"file1")
sharedFile1.deleteOnExit()
sharedFile2 = new File(sharedDir,"file2")
sharedFile2.deleteOnExit()
}
private void writeToSharedFile(File file, int size) {
FileOutputStream fos = new FileOutputStream(file);
fos.write new byte[size]
fos.close()
}
private File initPersisted() {
File persisted = new File("persisted")
if (persisted.exists())
@ -59,28 +59,28 @@ class PersisterServiceLoadingTest {
persisted.deleteOnExit()
persisted
}
@Test
void test1SharedFile1Piece() {
writeToSharedFile(sharedFile1, 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
def json = [:]
json.file = getSharedFileJsonName(sharedFile1)
json.length = 1
json.infoHash = Base64.encode(ih1.getRoot())
json.hashList = [Base64.encode(ih1.getHashList())]
json = JsonOutput.toJson(json)
File persisted = initPersisted()
persisted.write json
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 1
def loadedFile = listener.publishedFiles[0]
assert loadedFile != null
@ -92,20 +92,20 @@ class PersisterServiceLoadingTest {
def encoded = DataUtil.encodei18nString(sharedFile.getCanonicalFile().toString())
Base64.encode(encoded)
}
@Test
public void test1SharedFile2Pieces() {
writeToSharedFile(sharedFile1, (0x1 << 18) + 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
assert ih1.getHashList().length == 96
def json = [:]
json.file = getSharedFileJsonName(sharedFile1)
json.length = sharedFile1.length()
json.infoHash = Base64.encode ih1.getRoot()
byte [] tmp = new byte[32]
System.arraycopy(ih1.getHashList(), 0, tmp, 0, 32)
String hash1 = Base64.encode(tmp)
@ -114,23 +114,23 @@ class PersisterServiceLoadingTest {
System.arraycopy(ih1.getHashList(), 64, tmp, 0, 32)
String hash3 = Base64.encode(tmp)
json.hashList = [hash1, hash2, hash3]
json = JsonOutput.toJson(json)
File persisted = initPersisted()
persisted.write json
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 1
def loadedFile = listener.publishedFiles[0]
assert loadedFile != null
assert loadedFile.file == sharedFile1.getCanonicalFile()
assert loadedFile.infoHash == ih1
}
@Test
void test2SharedFiles() {
writeToSharedFile(sharedFile1, 1)
@ -138,34 +138,34 @@ class PersisterServiceLoadingTest {
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
InfoHash ih2 = fh.hashFile(sharedFile2)
assert ih1 != ih2
File persisted = initPersisted()
def json1 = [:]
json1.file = getSharedFileJsonName(sharedFile1)
json1.length = 1
json1.infoHash = Base64.encode(ih1.getRoot())
json1.hashList = [Base64.encode(ih1.getHashList())]
json1 = JsonOutput.toJson(json1)
def json2 = [:]
json2.file = getSharedFileJsonName(sharedFile2)
json2.length = 2
json2.infoHash = Base64.encode(ih2.getRoot())
json2.hashList = [Base64.encode(ih2.getHashList())]
json2 = JsonOutput.toJson(json2)
persisted.append "$json1\n"
persisted.append "$json2\n"
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 2
def loadedFile1 = listener.publishedFiles[0]
assert loadedFile1.file == sharedFile1.getCanonicalFile()
@ -174,15 +174,15 @@ class PersisterServiceLoadingTest {
assert loadedFile2.file == sharedFile2.getCanonicalFile()
assert loadedFile2.infoHash == ih2
}
@Test
void testDownloadedFile() {
writeToSharedFile(sharedFile1, 1)
FileHasher fh = new FileHasher()
InfoHash ih1 = fh.hashFile(sharedFile1)
File persisted = initPersisted()
Destinations dests = new Destinations()
def json1 = [:]
json1.file = getSharedFileJsonName(sharedFile1)
@ -190,20 +190,20 @@ class PersisterServiceLoadingTest {
json1.infoHash = Base64.encode(ih1.getRoot())
json1.hashList = [Base64.encode(ih1.getHashList())]
json1.sources = [ dests.dest1.toBase64(), dests.dest2.toBase64()]
json1 = JsonOutput.toJson(json1)
persisted.write json1
PersisterService ps = new PersisterService(persisted, eventBus, 100, null)
ps.onUILoadedEvent(null)
Thread.sleep(2000)
assert listener.publishedFiles.size() == 1
def loadedFile1 = listener.publishedFiles[0]
assert loadedFile1 instanceof DownloadedFile
assert loadedFile1.sources.size() == 2
assert loadedFile1.sources.contains(dests.dest1)
assert loadedFile1.sources.contains(dests.dest2)
}
}

View File

@ -26,7 +26,7 @@ class PersisterServiceSavingTest {
EventBus eventBus = new EventBus()
File persisted
PersisterService ps
@Before
void before() {
f = new File("build.gradle")
@ -43,26 +43,26 @@ class PersisterServiceSavingTest {
persisted.delete()
persisted.deleteOnExit()
}
@After
void after() {
ps?.stop()
}
private static String fromB64(String text) {
DataUtil.readi18nString(Base64.decode(text))
}
@Test
void testSavingSharedFile() {
sf = new SharedFile(f, ih, 0)
ps = new PersisterService(persisted, eventBus, 100, fileSource)
ps.onUILoadedEvent(null)
Thread.sleep(1500)
JsonSlurper jsonSlurper = new JsonSlurper()
persisted.eachLine {
JsonSlurper jsonSlurper = new JsonSlurper()
persisted.eachLine {
def json = jsonSlurper.parseText(it)
assert fromB64(json.file) == f.toString()
assert json.length == f.length()
@ -70,16 +70,16 @@ class PersisterServiceSavingTest {
assert json.hashList == [Base64.encode(ih.getHashList())]
}
}
@Test
void testSavingDownloadedFile() {
Destinations dests = new Destinations()
sf = new DownloadedFile(f, ih, 0, new HashSet([dests.dest1, dests.dest2]))
ps = new PersisterService(persisted, eventBus, 100, fileSource)
ps.onUILoadedEvent(null)
Thread.sleep(1500)
JsonSlurper jsonSlurper = new JsonSlurper()
persisted.eachLine {
def json = jsonSlurper.parseText(it)

View File

@ -23,25 +23,25 @@ class HostCacheTest {
File persist
HostCache cache
def trustMock
TrustService trust
def settingsMock
MuWireSettings settings
Destinations destinations = new Destinations()
@Before
void before() {
persist = new File("hostPersist")
persist.delete()
persist.deleteOnExit()
trustMock = new MockFor(TrustService.class)
settingsMock = new MockFor(MuWireSettings.class)
}
@After
void after() {
cache?.stop()
@ -49,7 +49,7 @@ class HostCacheTest {
settingsMock.verify settings
Thread.sleep(150)
}
private void initMocks() {
trust = trustMock.proxyInstance()
settings = settingsMock.proxyInstance()
@ -57,7 +57,7 @@ class HostCacheTest {
cache.start()
Thread.sleep(150)
}
@Test
void testEmpty() {
initMocks()
@ -65,38 +65,38 @@ class HostCacheTest {
assert cache.getGoodHosts(5).size() == 0
}
@Test
@Test
void testOnDiscoveredEvent() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.NEUTRAL
}
settingsMock.ignore.allowUntrusted { true }
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert cache.getGoodHosts(5).size() == 0
}
@Test
void testOnDiscoveredUntrustedHost() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.DISTRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
assert cache.getHosts(5).size() == 0
}
@Test
void testOnDiscoverNeutralHostsProhibited() {
trustMock.ignore.getLevel { d ->
@ -104,13 +104,13 @@ class HostCacheTest {
TrustLevel.NEUTRAL
}
settingsMock.ignore.allowUntrusted { false }
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
assert cache.getHosts(5).size() == 0
}
@Test
void test2DiscoveredGoodHosts() {
trustMock.demand.getLevel { d ->
@ -123,86 +123,86 @@ class HostCacheTest {
}
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
trustMock.demand.getLevel{ d -> TrustLevel.TRUSTED }
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest2))
def rv = cache.getHosts(1)
assert rv.size() == 1
assert rv.contains(destinations.dest1) || rv.contains(destinations.dest2)
}
@Test
void testHostFailed() {
trustMock.demand.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
assert cache.getHosts(5).size() == 0
}
@Test
void testFailedHostSuceeds() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.SUCCESSFUL))
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
rv = cache.getGoodHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
}
@Test
void testFailedOnceNoLongerGood() {
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def endpoint = new Endpoint(destinations.dest1, null, null, null)
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.SUCCESSFUL))
def rv = cache.getHosts(5)
def rv2 = cache.getGoodHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert rv == rv2
cache.onConnectionEvent( new ConnectionEvent(endpoint: endpoint, status: ConnectionAttemptStatus.FAILED))
rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert cache.getGoodHosts(5).size() == 0
}
@Test
void testDuplicateHostDiscovered() {
trustMock.demand.getLevel { d ->
@ -213,16 +213,16 @@ class HostCacheTest {
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
}
@Test
void testSaving() {
trustMock.ignore.getLevel { d ->
@ -232,7 +232,7 @@ class HostCacheTest {
initMocks()
cache.onHostDiscoveredEvent(new HostDiscoveredEvent(destination: destinations.dest1))
Thread.sleep(150)
assert persist.exists()
int lines = 0
persist.eachLine {
@ -245,7 +245,7 @@ class HostCacheTest {
}
assert lines == 1
}
@Test
void testLoading() {
def json = [:]
@ -254,17 +254,17 @@ class HostCacheTest {
json.successes = 1
json = JsonOutput.toJson(json)
persist.append("${json}\n")
trustMock.ignore.getLevel { d ->
assert d == destinations.dest1
TrustLevel.TRUSTED
}
initMocks()
def rv = cache.getHosts(5)
assert rv.size() == 1
assert rv.contains(destinations.dest1)
assert cache.getGoodHosts(5) == rv
}
}

View File

@ -5,34 +5,34 @@ import org.junit.Test
class SearchIndexTest {
SearchIndex index
private void initIndex(List<String> entries) {
index = new SearchIndex()
entries.each {
index.add(it)
}
}
@Test
void testSingleTerm() {
initIndex(["a b.c", "d e.f"])
def found = index.search(["a"])
assert found.size() == 1
assert found.contains("a b.c")
}
@Test
void testSingleTermOverlap() {
initIndex(["a b.c", "c d.e"])
def found = index.search(["c"])
assert found.size() == 2
assert found.contains("a b.c")
assert found.contains("c d.e")
}
@Test
public void testDrillDownDoesNotModifyIndex() {
initIndex(["a b.c", "c d.e"])
@ -42,31 +42,31 @@ class SearchIndexTest {
assert found.contains("a b.c")
assert found.contains("c d.e")
}
@Test
void testDrillDown() {
initIndex(["a b.c", "c d.e"])
def found = index.search(["c", "e"])
assert found.size() == 1
assert found.contains("c d.e")
}
@Test
void testNotFound() {
initIndex(["a b.c"])
def found = index.search(["d"])
assert found.size() == 0
}
@Test
void testSomeNotFound() {
initIndex(["a b.c"])
def found = index.search(["a","d"])
assert found.size() == 0
}
@Test
void testRemove() {
initIndex(["a b.c"])
@ -74,7 +74,7 @@ class SearchIndexTest {
def found = index.search(["a"])
assert found.size() == 0
}
@Test
void testRemoveOverlap() {
initIndex(["a b.c", "b c.d"])
@ -83,7 +83,7 @@ class SearchIndexTest {
assert found.size() == 1
assert found.contains("b c.d")
}
@Test
void testDuplicateTerm() {
initIndex(["MuWire-0.3.3.jar"])

View File

@ -16,7 +16,7 @@ class TrustServiceTest {
TrustService service
File persistGood, persistBad
Personas personas = new Personas()
@Before
void before() {
persistGood = new File("good.trust")
@ -28,32 +28,32 @@ class TrustServiceTest {
service = new TrustService(persistGood, persistBad, 100)
service.start()
}
@After
void after() {
service.stop()
}
@Test
void testEmpty() {
assert TrustLevel.NEUTRAL == service.getLevel(personas.persona1.destination)
assert TrustLevel.NEUTRAL == service.getLevel(personas.persona2.destination)
}
@Test
void testOnEvent() {
service.onTrustEvent new TrustEvent(level: TrustLevel.TRUSTED, persona: personas.persona1)
service.onTrustEvent new TrustEvent(level: TrustLevel.DISTRUSTED, persona: personas.persona2)
assert TrustLevel.TRUSTED == service.getLevel(personas.persona1.destination)
assert TrustLevel.DISTRUSTED == service.getLevel(personas.persona2.destination)
}
@Test
@Test
void testPersist() {
service.onTrustEvent new TrustEvent(level: TrustLevel.TRUSTED, persona: personas.persona1)
service.onTrustEvent new TrustEvent(level: TrustLevel.DISTRUSTED, persona: personas.persona2)
Thread.sleep(250)
def trusted = new HashSet<>()
persistGood.eachLine {
@ -63,13 +63,13 @@ class TrustServiceTest {
persistBad.eachLine {
distrusted.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
}
assert trusted.size() == 1
assert trusted.contains(personas.persona1)
assert distrusted.size() == 1
assert distrusted.contains(personas.persona2)
}
@Test
void testLoad() {
service.stop()
@ -78,7 +78,7 @@ class TrustServiceTest {
service = new TrustService(persistGood, persistBad, 100)
service.start()
Thread.sleep(50)
assert TrustLevel.TRUSTED == service.getLevel(personas.persona1.destination)
assert TrustLevel.DISTRUSTED == service.getLevel(personas.persona2.destination)
}

View File

@ -8,15 +8,15 @@ import org.junit.Test
import com.muwire.core.InfoHash
class RequestParsingTest {
ContentRequest request
private void fromString(String requestString) {
def is = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.US_ASCII))
request = Request.parseContentRequest(new InfoHash(new byte[InfoHash.SIZE]), is)
}
private static void failed(String requestString) {
try {
def is = new ByteArrayInputStream(requestString.getBytes(StandardCharsets.US_ASCII))
@ -24,12 +24,12 @@ class RequestParsingTest {
assert false
} catch (IOException expected) {}
}
@Before
public void setup() {
request = null
}
@Test
public void testSuccessful() {
fromString("Range: 1-2\r\n\r\n")
@ -37,37 +37,37 @@ class RequestParsingTest {
assert request.getRange().start == 1
assert request.getRange().end == 2
}
@Test
public void testRNMissing() {
failed("Range: 1-2")
}
@Test
public void testRNMissing2() {
failed("Range: 1-2\r\n")
}
@Test
public void testRR() {
failed("Range: 1-2\r\r")
}
@Test
public void testNR() {
failed("Range: 1-2\n\r")
}
@Test
public void testR() {
failed("Range: 1-2\r")
}
@Test
public void testRX() {
failed("Range: 1-2\rx")
}
@Test
public void testTwoHeaders() {
fromString("Range: 1-2\r\nA:B\r\n\r\n")
@ -78,17 +78,17 @@ class RequestParsingTest {
assert request.getHeaders().get("Range").trim() == "1-2"
assert request.getHeaders().get("A").trim() == "B"
}
@Test
public void testRangeMissing() {
failed("A:B\r\n")
}
@Test
public void testNoHeaders() {
failed("\r\n")
}
@Test
public void testInvalidRange() {
failed("Range 1-2\r\n\r\n")
@ -98,7 +98,7 @@ class RequestParsingTest {
failed("Range: 1-x\r\n\r\n")
failed("Range: x")
}
@Test
public void testHeaderTooLong() {
StringBuilder sb = new StringBuilder()
@ -106,4 +106,4 @@ class RequestParsingTest {
sb.append("x")
failed(sb.toString())
}
}
}

View File

@ -11,19 +11,19 @@ import com.muwire.core.InfoHash
import com.muwire.core.connection.Endpoint
class UploaderTest {
Endpoint endpoint
File file
Thread uploadThread
InputStream is
OutputStream os
ContentRequest request
Uploader uploader
byte[] inFile
@Before
public void setup() {
file?.delete()
@ -33,14 +33,14 @@ class UploaderTest {
os = new PipedOutputStream(is)
endpoint = new Endpoint(null, is, os, null)
}
@After
public void teardown() {
file?.delete()
uploadThread?.interrupt()
Thread.sleep(50)
}
private void fillFile(int length) {
byte [] data = new byte[length]
def random = new Random()
@ -50,14 +50,14 @@ class UploaderTest {
fos.close()
inFile = data
}
private void startUpload() {
uploader = new ContentUploader(file, request, endpoint)
uploadThread = new Thread(uploader.respond() as Runnable)
uploadThread.setDaemon(true)
uploadThread.start()
}
private String readUntilRN() {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
while(true) {
@ -73,7 +73,7 @@ class UploaderTest {
}
new String(baos.toByteArray(), StandardCharsets.US_ASCII)
}
@Test
public void testSmallFile() {
fillFile(20)
@ -82,13 +82,13 @@ class UploaderTest {
assert "200 OK" == readUntilRN()
assert "Content-Range: 0-19" == readUntilRN()
assert "" == readUntilRN()
byte [] data = new byte[20]
DataInputStream dis = new DataInputStream(is)
dis.readFully(data)
assert inFile == data
}
@Test
public void testRequestMiddle() {
fillFile(20)
@ -97,14 +97,14 @@ class UploaderTest {
assert "200 OK" == readUntilRN()
assert "Content-Range: 5-15" == readUntilRN()
assert "" == readUntilRN()
byte [] data = new byte[11]
DataInputStream dis = new DataInputStream(is)
dis.readFully(data)
for (int i = 0; i < data.length; i++)
assert inFile[i+5] == data[i]
}
@Test
public void testOutOfRange() {
fillFile(20)
@ -123,10 +123,10 @@ class UploaderTest {
readUntilRN()
readUntilRN()
readUntilRN()
byte [] data = new byte[length]
DataInputStream dis = new DataInputStream(is)
dis.readFully(data)
assert data == inFile
}
}
}

View File

@ -6,7 +6,7 @@ import org.junit.Test
class DataUtilTest {
private static void usVal(int value) {
ByteArrayOutputStream baos = new ByteArrayOutputStream()
DataUtil.writeUnsignedShort(value, baos)
@ -20,19 +20,19 @@ class DataUtilTest {
usVal(Short.MAX_VALUE)
usVal(Short.MAX_VALUE + 1)
usVal(0xFFFF)
try {
usVal(0xFFFF + 1)
fail()
} catch (IllegalArgumentException expected) {}
}
private static header(int value) {
byte [] header = new byte[3]
DataUtil.packHeader(value, header)
assert value == DataUtil.readLength(header)
}
@Test
void testHeader() {
header(0)

View File

@ -30,9 +30,9 @@ class I2PStatusController {
model.receiveBps = router._context.bandwidthLimiter().getReceiveBps15s()
model.sendBps = router._context.bandwidthLimiter().getSendBps15s()
model.participatingBW = router._context.bandwidthLimiter().getCurrentParticipatingBandwidth()
}
@ControllerAction
void close() {
view.dialog.setVisible(false)

View File

@ -36,19 +36,19 @@ class MainFrameController {
@Inject @Nonnull GriffonApplication application
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
MainFrameModel model
@MVCMember @Nonnull
MainFrameView view
private volatile Core core
@ControllerAction
void search() {
def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, "search window")
def search = builder.getVariable("search-field").text
search = search.trim()
if (search.length() == 0)
@ -72,7 +72,7 @@ class MainFrameController {
// not a hash search
}
}
def searchEvent
if (hashSearch) {
searchEvent = new SearchEvent(searchHash : root, uuid : uuid, oobInfohash: true)
@ -84,11 +84,11 @@ class MainFrameController {
terms.each { if (it.length() > 0) nonEmpty << it }
searchEvent = new SearchEvent(searchTerms : nonEmpty, uuid : uuid, oobInfohash: true)
}
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
replyTo: core.me.destination, receivedOn: core.me.destination,
originator : core.me))
}
void search(String infoHash, String tabTitle) {
def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, "search window")
@ -98,14 +98,14 @@ class MainFrameController {
params["uuid"] = uuid.toString()
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
model.results[uuid.toString()] = group
def searchEvent = new SearchEvent(searchHash : Base64.decode(infoHash), uuid:uuid,
oobInfohash: true)
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
replyTo: core.me.destination, receivedOn: core.me.destination,
originator : core.me))
}
private def selectedResult() {
def selected = builder.getVariable("result-tabs").getSelectedComponent()
def group = selected.getClientProperty("mvc-group")
@ -116,10 +116,10 @@ class MainFrameController {
def sortEvt = group.view.lastSortEvent
if (sortEvt != null) {
row = group.view.resultsTable.rowSorter.convertRowIndexToModel(row)
}
group.model.results[row]
}
group.model.results[row]
}
private int selectedDownload() {
def downloadsTable = builder.getVariable("downloads-table")
def selected = downloadsTable.getSelectedRow()
@ -128,27 +128,27 @@ class MainFrameController {
selected = downloadsTable.rowSorter.convertRowIndexToModel(selected)
selected
}
@ControllerAction
void download() {
def result = selectedResult()
if (result == null)
return
if (!model.canDownload(result.infohash))
return
return
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
def selected = builder.getVariable("result-tabs").getSelectedComponent()
def group = selected.getClientProperty("mvc-group")
def resultsBucket = group.model.hashBucket[result.infohash]
def sources = group.model.sourcesBucket[result.infohash]
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources, target : file))
}
@ControllerAction
void trust() {
def result = selectedResult()
@ -156,7 +156,7 @@ class MainFrameController {
return // TODO disable button
core.eventBus.publish( new TrustEvent(persona : result.sender, level : TrustLevel.TRUSTED))
}
@ControllerAction
void distrust() {
def result = selectedResult()
@ -164,22 +164,22 @@ class MainFrameController {
return // TODO disable button
core.eventBus.publish( new TrustEvent(persona : result.sender, level : TrustLevel.DISTRUSTED))
}
@ControllerAction
@ControllerAction
void cancel() {
def downloader = model.downloads[selectedDownload()].downloader
downloader.cancel()
model.downloadInfoHashes.remove(downloader.getInfoHash())
core.eventBus.publish(new UIDownloadCancelledEvent(downloader : downloader))
}
@ControllerAction
void resume() {
def downloader = model.downloads[selectedDownload()].downloader
downloader.resume()
core.eventBus.publish(new UIDownloadResumedEvent())
}
@ControllerAction
void pause() {
def downloader = model.downloads[selectedDownload()].downloader
@ -194,21 +194,21 @@ class MainFrameController {
builder.getVariable(tableName).model.fireTableDataChanged()
core.eventBus.publish(new TrustEvent(persona : list[row], level : level))
}
@ControllerAction
void markTrusted() {
markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted)
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
}
@ControllerAction
void markNeutralFromDistrusted() {
markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted)
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
}
@ControllerAction
void markDistrusted() {
markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted)
@ -216,7 +216,7 @@ class MainFrameController {
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
}
@ControllerAction
void markNeutralFromTrusted() {
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
@ -224,7 +224,7 @@ class MainFrameController {
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
}
@ControllerAction
void subscribe() {
int row = view.getSelectedTrustTablesRow("trusted-table")
@ -238,7 +238,7 @@ class MainFrameController {
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
}
@ControllerAction
void review() {
RemoteTrustList list = getSelectedTrustList()
@ -249,9 +249,9 @@ class MainFrameController {
env["trustService"] = core.trustService
env["eventBus"] = core.eventBus
mvcGroup.createMVCGroup("trust-list", env)
}
@ControllerAction
void update() {
RemoteTrustList list = getSelectedTrustList()
@ -259,7 +259,7 @@ class MainFrameController {
return
core.eventBus.publish(new TrustSubscriptionEvent(persona : list.persona, subscribe : true))
}
@ControllerAction
void unsubscribe() {
RemoteTrustList list = getSelectedTrustList()
@ -272,42 +272,42 @@ class MainFrameController {
table.model.fireTableDataChanged()
core.eventBus.publish(new TrustSubscriptionEvent(persona : list.persona, subscribe : false))
}
private RemoteTrustList getSelectedTrustList() {
int row = view.getSelectedTrustTablesRow("subscription-table")
if (row < 0)
return null
model.subscriptions[row]
}
void unshareSelectedFile() {
SharedFile sf = view.selectedSharedFile()
if (sf == null)
return
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
}
void stopWatchingDirectory() {
String directory = mvcGroup.view.getSelectedWatchedDirectory()
if (directory == null)
return
core.muOptions.watchedDirectories.remove(directory)
saveMuWireSettings()
saveMuWireSettings()
core.eventBus.publish(new DirectoryUnsharedEvent(directory : new File(directory)))
model.watched.remove(directory)
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
}
void saveMuWireSettings() {
File f = new File(core.home, "MuWire.properties")
f.withOutputStream {
f.withOutputStream {
core.muOptions.write(it)
}
}
void mvcGroupInit(Map<String, String> args) {
application.addPropertyChangeListener("core", {e->
application.addPropertyChangeListener("core", {e->
core = e.getNewValue()
})
}

View File

@ -18,7 +18,7 @@ class MuWireStatusController {
@ControllerAction
void refresh() {
Core core = application.context.get("core")
int incoming = 0
int outgoing = 0
core.connectionManager.getConnections().each {
@ -29,14 +29,14 @@ class MuWireStatusController {
}
model.incomingConnections = incoming
model.outgoingConnections = outgoing
model.knownHosts = core.hostCache.hosts.size()
model.sharedFiles = core.fileManager.fileToSharedFile.size()
model.downloads = core.downloadManager.downloaders.size()
}
@ControllerAction
void close() {
view.dialog.setVisible(false)

View File

@ -26,41 +26,41 @@ class OptionsController {
String text
Core core = application.context.get("core")
MuWireSettings settings = application.context.get("muwire-settings")
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
if (settings.embeddedRouter) {
text = view.i2pNTCPPortField.text
model.i2pNTCPPort = text
i2pProps["i2np.ntcp.port"] = text
text = view.i2pUDPPortField.text
model.i2pUDPPort = text
i2pProps["i2np.udp.port"] = text
}
File i2pSettingsFile = new File(core.home, "i2p.properties")
i2pSettingsFile.withOutputStream {
i2pSettingsFile.withOutputStream {
i2pProps.store(it,"")
}
text = view.retryField.text
model.downloadRetryInterval = text
@ -73,12 +73,12 @@ class OptionsController {
boolean autoDownloadUpdate = view.autoDownloadUpdateCheckbox.model.isSelected()
model.autoDownloadUpdate = autoDownloadUpdate
settings.autoDownloadUpdate = autoDownloadUpdate
boolean shareDownloaded = view.shareDownloadedCheckbox.model.isSelected()
model.shareDownloadedFiles = shareDownloaded
settings.shareDownloadedFiles = shareDownloaded
String downloadLocation = model.downloadLocation
settings.downloadLocation = new File(downloadLocation)
@ -90,70 +90,70 @@ class OptionsController {
model.outBw = text
settings.outBw = Integer.valueOf(text)
}
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
model.onlyTrusted = onlyTrusted
settings.setAllowUntrusted(!onlyTrusted)
boolean trustLists = view.allowTrustListsCheckbox.model.isSelected()
model.trustLists = trustLists
settings.allowTrustLists = trustLists
String trustListInterval = view.trustListIntervalField.text
model.trustListInterval = trustListInterval
settings.trustListInterval = Integer.parseInt(trustListInterval)
File settingsFile = new File(core.home, "MuWire.properties")
settingsFile.withOutputStream {
settingsFile.withOutputStream {
settings.write(it)
}
// UI Setttings
UISettings uiSettings = application.context.get("ui-settings")
text = view.lnfField.text
model.lnf = text
uiSettings.lnf = text
text = view.fontField.text
model.font = text
uiSettings.font = text
// boolean showMonitor = view.monitorCheckbox.model.isSelected()
// model.showMonitor = showMonitor
// uiSettings.showMonitor = showMonitor
boolean clearCancelledDownloads = view.clearCancelledDownloadsCheckbox.model.isSelected()
model.clearCancelledDownloads = clearCancelledDownloads
uiSettings.clearCancelledDownloads = clearCancelledDownloads
boolean clearFinishedDownloads = view.clearFinishedDownloadsCheckbox.model.isSelected()
model.clearFinishedDownloads = clearFinishedDownloads
uiSettings.clearFinishedDownloads = clearFinishedDownloads
boolean excludeLocalResult = view.excludeLocalResultCheckbox.model.isSelected()
model.excludeLocalResult = excludeLocalResult
uiSettings.excludeLocalResult = excludeLocalResult
// boolean showSearchHashes = view.showSearchHashesCheckbox.model.isSelected()
// model.showSearchHashes = showSearchHashes
// uiSettings.showSearchHashes = showSearchHashes
File uiSettingsFile = new File(core.home, "gui.properties")
uiSettingsFile.withOutputStream {
uiSettingsFile.withOutputStream {
uiSettings.write(it)
}
cancel()
}
@ControllerAction
void cancel() {
view.d.setVisible(false)
mvcGroup.destroy()
}
@ControllerAction
void downloadLocation() {
def chooser = new JFileChooser()

View File

@ -17,9 +17,9 @@ class TrustListController {
TrustListModel model
@MVCMember @Nonnull
TrustListView view
EventBus eventBus
@ControllerAction
void trustFromTrusted() {
int selectedRow = view.getSelectedRow("trusted-table")
@ -29,7 +29,7 @@ class TrustListController {
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
view.fireUpdate("trusted-table")
}
@ControllerAction
void trustFromDistrusted() {
int selectedRow = view.getSelectedRow("distrusted-table")
@ -39,7 +39,7 @@ class TrustListController {
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
view.fireUpdate("distrusted-table")
}
@ControllerAction
void distrustFromTrusted() {
int selectedRow = view.getSelectedRow("trusted-table")
@ -49,7 +49,7 @@ class TrustListController {
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
view.fireUpdate("trusted-table")
}
@ControllerAction
void distrustFromDistrusted() {
int selectedRow = view.getSelectedRow("distrusted-table")

View File

@ -34,24 +34,24 @@ class Initialize extends AbstractLifecycleHandler {
def home = portableHome == null ?
selectHome() :
portableHome
home = new File(home)
if (!home.exists()) {
log.info("creating home dir $home")
home.mkdirs()
}
application.context.put("muwire-home", home.getAbsolutePath())
System.getProperties().setProperty("awt.useSystemAAFontSettings", "true")
def guiPropsFile = new File(home, "gui.properties")
UISettings uiSettings
if (guiPropsFile.exists()) {
Properties props = new Properties()
guiPropsFile.withInputStream { props.load(it) }
uiSettings = new UISettings(props)
log.info("settting user-specified lnf $uiSettings.lnf")
try {
lookAndFeel(uiSettings.lnf)
@ -59,7 +59,7 @@ class Initialize extends AbstractLifecycleHandler {
log.log(Level.WARNING,"couldn't set desired look and feeel, switching to defaults", bad)
uiSettings.lnf = lookAndFeel("system","gtk","metal").getID()
}
if (uiSettings.font != null) {
log.info("setting user-specified font $uiSettings.font")
Font font = new Font(uiSettings.font, Font.PLAIN, 12)
@ -90,10 +90,10 @@ class Initialize extends AbstractLifecycleHandler {
log.info("ended up applying $chosen.name")
}
}
application.context.put("ui-settings", uiSettings)
}
private static String selectHome() {
def home = new File(System.properties["user.home"])
def defaultHome = new File(home, ".MuWire")

Some files were not shown because too many files have changed in this diff Show More