From 29cdbf018c3e6a0e7c1e0379bdc2fb0d8fd958fc Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Fri, 5 Jul 2019 16:24:19 +0100 Subject: [PATCH] remove trailing spaces --- cli/src/main/groovy/com/muwire/cli/Cli.groovy | 42 +- .../com/muwire/cli/CliDownloader.groovy | 46 +-- .../groovy/com/muwire/cli/FileList.groovy | 4 +- .../groovy/com/muwire/core/Constants.groovy | 6 +- .../main/groovy/com/muwire/core/Core.groovy | 84 ++-- .../groovy/com/muwire/core/Core.groovy.orig | 380 ++++++++++++++++++ .../groovy/com/muwire/core/Core.groovy.rej | 31 ++ .../main/groovy/com/muwire/core/Event.groovy | 4 +- .../groovy/com/muwire/core/EventBus.groovy | 6 +- .../core/InvalidSignatureException.groovy | 2 +- .../com/muwire/core/MuWireSettings.groovy | 34 +- .../main/groovy/com/muwire/core/Name.groovy | 12 +- .../groovy/com/muwire/core/Persona.groovy | 22 +- .../groovy/com/muwire/core/Service.groovy | 4 +- .../muwire/core/connection/Connection.groovy | 56 +-- .../core/connection/ConnectionAcceptor.groovy | 54 +-- .../connection/ConnectionEstablisher.groovy | 42 +- .../core/connection/ConnectionEvent.groovy | 2 +- .../core/connection/ConnectionManager.groovy | 36 +- .../core/connection/DisconnectionEvent.groovy | 2 +- .../muwire/core/connection/Endpoint.groovy | 10 +- .../muwire/core/connection/I2PAcceptor.groovy | 6 +- .../core/connection/I2PConnector.groovy | 8 +- .../core/connection/LeafConnection.groovy | 8 +- .../connection/LeafConnectionManager.groovy | 26 +- .../core/connection/PeerConnection.groovy | 12 +- .../connection/UltrapeerConnection.groovy | 8 +- .../UltrapeerConnectionManager.groovy | 32 +- .../core/download/BadHashException.groovy | 2 +- .../core/download/DownloadManager.groovy | 58 +-- .../core/download/DownloadSession.groovy | 52 +-- .../muwire/core/download/Downloader.groovy | 84 ++-- .../core/download/HashListSession.groovy | 18 +- .../com/muwire/core/download/Pieces.groovy | 26 +- .../core/download/UIDownloadEvent.groovy | 2 +- .../muwire/core/files/DirectoryWatcher.groovy | 28 +- .../muwire/core/files/FileHashedEvent.groovy | 4 +- .../com/muwire/core/files/FileHasher.groovy | 20 +- .../com/muwire/core/files/FileManager.groovy | 40 +- .../muwire/core/files/FileSharedEvent.groovy | 2 +- .../muwire/core/files/HasherService.groovy | 8 +- .../muwire/core/files/PersisterService.groovy | 34 +- .../muwire/core/hostcache/CacheClient.groovy | 58 +-- .../muwire/core/hostcache/CacheServers.groovy | 2 +- .../com/muwire/core/hostcache/Host.groovy | 14 +- .../muwire/core/hostcache/HostCache.groovy | 22 +- .../core/hostcache/HostDiscoveredEvent.groovy | 2 +- .../groovy/com/muwire/core/mesh/Mesh.groovy | 4 +- .../com/muwire/core/mesh/MeshManager.groovy | 26 +- .../com/muwire/core/search/DeleteEvent.groovy | 2 +- .../InvalidSearchResultException.groovy | 2 +- .../muwire/core/search/LeafSearcher.groovy | 18 +- .../com/muwire/core/search/QueryEvent.groovy | 2 +- .../muwire/core/search/ResultsParser.groovy | 12 +- .../muwire/core/search/ResultsSender.groovy | 20 +- .../com/muwire/core/search/SearchEvent.groovy | 2 +- .../com/muwire/core/search/SearchIndex.groovy | 14 +- .../muwire/core/search/SearchManager.groovy | 16 +- .../muwire/core/search/UIResultEvent.groovy | 2 +- .../search/UnexpectedResultsException.groovy | 2 +- .../com/muwire/core/search/UpsertEvent.groovy | 2 +- .../muwire/core/trust/RemoteTrustList.groovy | 6 +- .../com/muwire/core/trust/TrustService.groovy | 20 +- .../muwire/core/trust/TrustSubscriber.groovy | 40 +- .../muwire/core/update/UpdateClient.groovy | 52 +-- .../muwire/core/upload/ContentUploader.groovy | 12 +- .../core/upload/HashListUploader.groovy | 8 +- .../com/muwire/core/upload/Range.groovy | 2 +- .../com/muwire/core/upload/Request.groovy | 36 +- .../muwire/core/upload/UploadManager.groovy | 34 +- .../com/muwire/core/upload/Uploader.groovy | 18 +- .../com/muwire/core/util/DataUtil.groovy | 38 +- .../groovy/com/muwire/core/util/JULLog.groovy | 6 +- .../muwire/core/util/MuWireLogManager.groovy | 10 +- .../java/com/muwire/core/DownloadedFile.java | 6 +- .../main/java/com/muwire/core/InfoHash.java | 26 +- .../main/java/com/muwire/core/SharedFile.java | 16 +- .../com/muwire/core/EventBusTest.groovy | 8 +- .../groovy/com/muwire/core/Personas.groovy | 2 +- .../connection/ConnectionAcceptorTest.groovy | 92 ++--- .../ConnectionEstablisherTest.groovy | 80 ++-- .../core/download/DownloadSessionTest.groovy | 112 +++--- .../muwire/core/download/PiecesTest.groovy | 12 +- .../muwire/core/files/FileHasherTest.groovy | 16 +- .../muwire/core/files/FileManagerTest.groovy | 66 +-- .../core/files/HasherServiceTest.groovy | 8 +- .../files/PersisterServiceLoadingTest.groovy | 76 ++-- .../files/PersisterServiceSavingTest.groovy | 22 +- .../core/hostcache/HostCacheTest.groovy | 92 ++--- .../muwire/core/search/SearchIndexTest.groovy | 30 +- .../muwire/core/trust/TrustServiceTest.groovy | 22 +- .../core/upload/RequestParsingTest.groovy | 34 +- .../muwire/core/upload/UploaderTest.groovy | 32 +- .../com/muwire/core/util/DataUtilTest.groovy | 8 +- .../com/muwire/gui/I2PStatusController.groovy | 4 +- .../com/muwire/gui/MainFrameController.groovy | 84 ++-- .../muwire/gui/MuWireStatusController.groovy | 10 +- .../com/muwire/gui/OptionsController.groovy | 64 +-- .../com/muwire/gui/TrustListController.groovy | 10 +- gui/griffon-app/lifecycle/Initialize.groovy | 16 +- gui/griffon-app/lifecycle/Ready.groovy | 26 +- gui/griffon-app/lifecycle/Shutdown.groovy | 2 +- .../com/muwire/gui/EventListModel.groovy | 6 +- .../com/muwire/gui/I2PStatusModel.groovy | 4 +- .../com/muwire/gui/MainFrameModel.groovy | 112 +++--- .../com/muwire/gui/MuWireStatusModel.groovy | 6 +- .../models/com/muwire/gui/OptionsModel.groovy | 22 +- .../com/muwire/gui/SearchTabModel.groovy | 24 +- .../com/muwire/gui/TrustListModel.groovy | 4 +- .../views/com/muwire/gui/I2PStatusView.groovy | 12 +- .../views/com/muwire/gui/MainFrameView.groovy | 78 ++-- .../com/muwire/gui/MuWireStatusView.groovy | 10 +- .../views/com/muwire/gui/OptionsView.groovy | 38 +- .../views/com/muwire/gui/SearchTabView.groovy | 34 +- .../views/com/muwire/gui/TrustListView.groovy | 16 +- .../groovy/com/muwire/gui/Launcher.groovy | 2 +- .../groovy/com/muwire/gui/UISettings.groovy | 10 +- .../com/muwire/hostcache/Crawler.groovy | 24 +- .../groovy/com/muwire/hostcache/Host.groovy | 4 +- .../com/muwire/hostcache/HostCache.groovy | 38 +- .../com/muwire/hostcache/HostPool.groovy | 28 +- .../groovy/com/muwire/hostcache/Pinger.groovy | 4 +- .../com/muwire/hostcache/CrawlerTest.groovy | 60 +-- .../com/muwire/hostcache/HostPoolTest.groovy | 76 ++-- .../groovy/com/muwire/pinger/Pinger.groovy | 20 +- .../com/muwire/update/UpdateServer.groovy | 24 +- 126 files changed, 1952 insertions(+), 1541 deletions(-) create mode 100644 core/src/main/groovy/com/muwire/core/Core.groovy.orig create mode 100644 core/src/main/groovy/com/muwire/core/Core.groovy.rej diff --git a/cli/src/main/groovy/com/muwire/cli/Cli.groovy b/cli/src/main/groovy/com/muwire/cli/Cli.groovy index ba6af134..54cc6ce8 100644 --- a/cli/src/main/groovy/com/muwire/cli/Cli.groovy +++ b/cli/src/main/groovy/com/muwire/cli/Cli.groovy @@ -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) { diff --git a/cli/src/main/groovy/com/muwire/cli/CliDownloader.groovy b/cli/src/main/groovy/com/muwire/cli/CliDownloader.groovy index be19314a..2b1b82e3 100644 --- a/cli/src/main/groovy/com/muwire/cli/CliDownloader.groovy +++ b/cli/src/main/groovy/com/muwire/cli/CliDownloader.groovy @@ -17,31 +17,31 @@ import com.muwire.core.search.UIResultEvent import net.i2p.data.Base64 class CliDownloader { - + private static final List downloaders = Collections.synchronizedList(new ArrayList<>()) private static final Map 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 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) { diff --git a/cli/src/main/groovy/com/muwire/cli/FileList.groovy b/cli/src/main/groovy/com/muwire/cli/FileList.groovy index 9eb68e6b..8a01b9e5 100644 --- a/cli/src/main/groovy/com/muwire/cli/FileList.groovy +++ b/cli/src/main/groovy/com/muwire/cli/FileList.groovy @@ -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" diff --git a/core/src/main/groovy/com/muwire/core/Constants.groovy b/core/src/main/groovy/com/muwire/core/Constants.groovy index 075a9b4a..bdb8629d 100644 --- a/core/src/main/groovy/com/muwire/core/Constants.groovy +++ b/core/src/main/groovy/com/muwire/core/Constants.groovy @@ -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 = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}\\?]" } diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy b/core/src/main/groovy/com/muwire/core/Core.groovy index 1ce5f44f..d09cf29d 100644 --- a/core/src/main/groovy/com/muwire/core/Core.groovy +++ b/core/src/main/groovy/com/muwire/core/Core.groovy @@ -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") diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy.orig b/core/src/main/groovy/com/muwire/core/Core.groovy.orig new file mode 100644 index 00000000..1ce5f44f --- /dev/null +++ b/core/src/main/groovy/com/muwire/core/Core.groovy.orig @@ -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() + } + } +} diff --git a/core/src/main/groovy/com/muwire/core/Core.groovy.rej b/core/src/main/groovy/com/muwire/core/Core.groovy.rej new file mode 100644 index 00000000..b961dd90 --- /dev/null +++ b/core/src/main/groovy/com/muwire/core/Core.groovy.rej @@ -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) diff --git a/core/src/main/groovy/com/muwire/core/Event.groovy b/core/src/main/groovy/com/muwire/core/Event.groovy index c6c2fdec..a956e3a4 100644 --- a/core/src/main/groovy/com/muwire/core/Event.groovy +++ b/core/src/main/groovy/com/muwire/core/Event.groovy @@ -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" diff --git a/core/src/main/groovy/com/muwire/core/EventBus.groovy b/core/src/main/groovy/com/muwire/core/EventBus.groovy index 3b8c0b9a..1a8fcb58 100644 --- a/core/src/main/groovy/com/muwire/core/EventBus.groovy +++ b/core/src/main/groovy/com/muwire/core/EventBus.groovy @@ -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 eventType, def handler) { log.info "Registering $handler for type $eventType" def currentHandlers = handlers.get(eventType) diff --git a/core/src/main/groovy/com/muwire/core/InvalidSignatureException.groovy b/core/src/main/groovy/com/muwire/core/InvalidSignatureException.groovy index ecbe9d45..e0c235c9 100644 --- a/core/src/main/groovy/com/muwire/core/InvalidSignatureException.groovy +++ b/core/src/main/groovy/com/muwire/core/InvalidSignatureException.groovy @@ -13,5 +13,5 @@ class InvalidSignatureException extends Exception { public InvalidSignatureException(Throwable cause) { super(cause); } - + } diff --git a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy index 4dfa987c..ca8dbb17 100644 --- a/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy +++ b/core/src/main/groovy/com/muwire/core/MuWireSettings.groovy @@ -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 } diff --git a/core/src/main/groovy/com/muwire/core/Name.groovy b/core/src/main/groovy/com/muwire/core/Name.groovy index 4c9ac294..fb8e0e94 100644 --- a/core/src/main/groovy/com/muwire/core/Name.groovy +++ b/core/src/main/groovy/com/muwire/core/Name.groovy @@ -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)) diff --git a/core/src/main/groovy/com/muwire/core/Persona.groovy b/core/src/main/groovy/com/muwire/core/Persona.groovy index 17bc10e1..b703ab9d 100644 --- a/core/src/main/groovy/com/muwire/core/Persona.groovy +++ b/core/src/main/groovy/com/muwire/core/Persona.groovy @@ -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" diff --git a/core/src/main/groovy/com/muwire/core/Service.groovy b/core/src/main/groovy/com/muwire/core/Service.groovy index 53288afc..f380e7a3 100644 --- a/core/src/main/groovy/com/muwire/core/Service.groovy +++ b/core/src/main/groovy/com/muwire/core/Service.groovy @@ -3,9 +3,9 @@ package com.muwire.core abstract class Service { volatile boolean loaded - + abstract void load() - + void waitForLoad() { while (!loaded) Thread.sleep(10) diff --git a/core/src/main/groovy/com/muwire/core/connection/Connection.groovy b/core/src/main/groovy/com/muwire/core/connection/Connection.groovy index 022cc524..eaff57b8 100644 --- a/core/src/main/groovy/com/muwire/core/connection/Connection.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/Connection.groovy @@ -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 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) - + } } diff --git a/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy b/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy index 51b82955..041f8efb 100644 --- a/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy @@ -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 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 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() } - + } diff --git a/core/src/main/groovy/com/muwire/core/connection/ConnectionEstablisher.groovy b/core/src/main/groovy/com/muwire/core/connection/ConnectionEstablisher.groovy index 136e383a..fee58294 100644 --- a/core/src/main/groovy/com/muwire/core/connection/ConnectionEstablisher.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/ConnectionEstablisher.groovy @@ -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) } diff --git a/core/src/main/groovy/com/muwire/core/connection/ConnectionEvent.groovy b/core/src/main/groovy/com/muwire/core/connection/ConnectionEvent.groovy index 21573aae..f20c808e 100644 --- a/core/src/main/groovy/com/muwire/core/connection/ConnectionEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/ConnectionEvent.groovy @@ -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" diff --git a/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy b/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy index 4b3e83ca..bac34eac 100644 --- a/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/ConnectionManager.groovy @@ -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 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() } diff --git a/core/src/main/groovy/com/muwire/core/connection/DisconnectionEvent.groovy b/core/src/main/groovy/com/muwire/core/connection/DisconnectionEvent.groovy index bb6e65d8..c2d5f171 100644 --- a/core/src/main/groovy/com/muwire/core/connection/DisconnectionEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/DisconnectionEvent.groovy @@ -5,7 +5,7 @@ import com.muwire.core.Event import net.i2p.data.Destination class DisconnectionEvent extends Event { - + Destination destination @Override diff --git a/core/src/main/groovy/com/muwire/core/connection/Endpoint.groovy b/core/src/main/groovy/com/muwire/core/connection/Endpoint.groovy index 30fd0501..dff4b5e9 100644 --- a/core/src/main/groovy/com/muwire/core/connection/Endpoint.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/Endpoint.groovy @@ -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()}" } -} +} diff --git a/core/src/main/groovy/com/muwire/core/connection/I2PAcceptor.groovy b/core/src/main/groovy/com/muwire/core/connection/I2PAcceptor.groovy index 9d73f03b..3787105e 100644 --- a/core/src/main/groovy/com/muwire/core/connection/I2PAcceptor.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/I2PAcceptor.groovy @@ -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) diff --git a/core/src/main/groovy/com/muwire/core/connection/I2PConnector.groovy b/core/src/main/groovy/com/muwire/core/connection/I2PConnector.groovy index 6674432b..9335d9b7 100644 --- a/core/src/main/groovy/com/muwire/core/connection/I2PConnector.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/I2PConnector.groovy @@ -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) diff --git a/core/src/main/groovy/com/muwire/core/connection/LeafConnection.groovy b/core/src/main/groovy/com/muwire/core/connection/LeafConnection.groovy index 28bcccb1..928695c8 100644 --- a/core/src/main/groovy/com/muwire/core/connection/LeafConnection.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/LeafConnection.groovy @@ -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 - + } } diff --git a/core/src/main/groovy/com/muwire/core/connection/LeafConnectionManager.groovy b/core/src/main/groovy/com/muwire/core/connection/LeafConnectionManager.groovy index 2f8d9968..e7e6f723 100644 --- a/core/src/main/groovy/com/muwire/core/connection/LeafConnectionManager.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/LeafConnectionManager.groovy @@ -13,28 +13,28 @@ import net.i2p.data.Destination @Log class LeafConnectionManager extends ConnectionManager { - + final int maxConnections - + final Map 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() { - + } } diff --git a/core/src/main/groovy/com/muwire/core/connection/PeerConnection.groovy b/core/src/main/groovy/com/muwire/core/connection/PeerConnection.groovy index a731b08d..be8e55ec 100644 --- a/core/src/main/groovy/com/muwire/core/connection/PeerConnection.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/PeerConnection.groovy @@ -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() diff --git a/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnection.groovy b/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnection.groovy index 487d23df..971c831b 100644 --- a/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnection.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnection.groovy @@ -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) { - + } } diff --git a/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy b/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy index 8c61efdd..719588d1 100644 --- a/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/UltrapeerConnectionManager.groovy @@ -15,16 +15,16 @@ import net.i2p.data.Destination @Log class UltrapeerConnectionManager extends ConnectionManager { - + final int maxPeers, maxLeafs final TrustService trustService - + final Map peerConnections = new ConcurrentHashMap() final Map 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) { - + } } diff --git a/core/src/main/groovy/com/muwire/core/download/BadHashException.groovy b/core/src/main/groovy/com/muwire/core/download/BadHashException.groovy index c10d21ea..44d6cdfa 100644 --- a/core/src/main/groovy/com/muwire/core/download/BadHashException.groovy +++ b/core/src/main/groovy/com/muwire/core/download/BadHashException.groovy @@ -21,5 +21,5 @@ class BadHashException extends Exception { public BadHashException(Throwable cause) { super(cause); } - + } diff --git a/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy b/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy index f15efbcd..3438d867 100644 --- a/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy +++ b/core/src/main/groovy/com/muwire/core/download/DownloadManager.groovy @@ -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 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 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() diff --git a/core/src/main/groovy/com/muwire/core/download/DownloadSession.groovy b/core/src/main/groovy/com/muwire/core/download/DownloadSession.groovy index ae2740be..5f54ce0e 100644 --- a/core/src/main/groovy/com/muwire/core/download/DownloadSession.groovy +++ b/core/src/main/groovy/com/muwire/core/download/DownloadSession.groovy @@ -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 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) diff --git a/core/src/main/groovy/com/muwire/core/download/Downloader.groovy b/core/src/main/groovy/com/muwire/core/download/Downloader.groovy index 4db0f1b4..554647aa 100644 --- a/core/src/main/groovy/com/muwire/core/download/Downloader.groovy +++ b/core/src/main/groovy/com/muwire/core/download/Downloader.groovy @@ -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 activeWorkers = new ConcurrentHashMap<>() private final Set 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 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 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() } diff --git a/core/src/main/groovy/com/muwire/core/download/HashListSession.groovy b/core/src/main/groovy/com/muwire/core/download/HashListSession.groovy index 2eb9f56d..df5e14d1 100644 --- a/core/src/main/groovy/com/muwire/core/download/HashListSession.groovy +++ b/core/src/main/groovy/com/muwire/core/download/HashListSession.groovy @@ -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 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") diff --git a/core/src/main/groovy/com/muwire/core/download/Pieces.groovy b/core/src/main/groovy/com/muwire/core/download/Pieces.groovy index 1d947d76..f3eecedb 100644 --- a/core/src/main/groovy/com/muwire/core/download/Pieces.groovy +++ b/core/src/main/groovy/com/muwire/core/download/Pieces.groovy @@ -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 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() diff --git a/core/src/main/groovy/com/muwire/core/download/UIDownloadEvent.groovy b/core/src/main/groovy/com/muwire/core/download/UIDownloadEvent.groovy index 6e70dd88..8e03864f 100644 --- a/core/src/main/groovy/com/muwire/core/download/UIDownloadEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/download/UIDownloadEvent.groovy @@ -6,7 +6,7 @@ import com.muwire.core.search.UIResultEvent import net.i2p.data.Destination class UIDownloadEvent extends Event { - + UIResultEvent[] result Set sources File target diff --git a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy index 3ae3a64f..ccaf09bb 100644 --- a/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/DirectoryWatcher.groovy @@ -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 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) { diff --git a/core/src/main/groovy/com/muwire/core/files/FileHashedEvent.groovy b/core/src/main/groovy/com/muwire/core/files/FileHashedEvent.groovy index c943bbbc..07948faf 100644 --- a/core/src/main/groovy/com/muwire/core/files/FileHashedEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/files/FileHashedEvent.groovy @@ -7,10 +7,10 @@ class FileHashedEvent extends Event { SharedFile sharedFile String error - + @Override public String toString() { super.toString() + " sharedFile " + sharedFile?.file.getAbsolutePath() + " error: $error" } - + } diff --git a/core/src/main/groovy/com/muwire/core/files/FileHasher.groovy b/core/src/main/groovy/com/muwire/core/files/FileHasher.groovy index 4e5f1328..6eaaa243 100644 --- a/core/src/main/groovy/com/muwire/core/files/FileHasher.groovy +++ b/core/src/main/groovy/com/muwire/core/files/FileHasher.groovy @@ -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() diff --git a/core/src/main/groovy/com/muwire/core/files/FileManager.groovy b/core/src/main/groovy/com/muwire/core/files/FileManager.groovy index 9b5ea599..24b80602 100644 --- a/core/src/main/groovy/com/muwire/core/files/FileManager.groovy +++ b/core/src/main/groovy/com/muwire/core/files/FileManager.groovy @@ -21,27 +21,27 @@ class FileManager { final Map fileToSharedFile = Collections.synchronizedMap(new HashMap<>()) final Map> 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 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 existingFiles = nameToFiles.get(name) if (existingFiles != null) { @@ -86,20 +86,20 @@ class FileManager { nameToFiles.remove(name) } } - + index.remove(name) } - + Map getSharedFiles() { synchronized(fileToSharedFile) { return new HashMap<>(fileToSharedFile) } } - + Set 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 filter(Set files, boolean oob) { if (!oob) return files Set 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 { diff --git a/core/src/main/groovy/com/muwire/core/files/FileSharedEvent.groovy b/core/src/main/groovy/com/muwire/core/files/FileSharedEvent.groovy index 964df330..4d67bbcb 100644 --- a/core/src/main/groovy/com/muwire/core/files/FileSharedEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/files/FileSharedEvent.groovy @@ -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() diff --git a/core/src/main/groovy/com/muwire/core/files/HasherService.groovy b/core/src/main/groovy/com/muwire/core/files/HasherService.groovy index 0066403f..f1cc5758 100644 --- a/core/src/main/groovy/com/muwire/core/files/HasherService.groovy +++ b/core/src/main/groovy/com/muwire/core/files/HasherService.groovy @@ -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()) { diff --git a/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy b/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy index 5cc9d929..98df6e4a 100644 --- a/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy +++ b/core/src/main/groovy/com/muwire/core/files/PersisterService.groovy @@ -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 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 } } diff --git a/core/src/main/groovy/com/muwire/core/hostcache/CacheClient.groovy b/core/src/main/groovy/com/muwire/core/hostcache/CacheClient.groovy index 932c8f9c..0f605aa2 100644 --- a/core/src/main/groovy/com/muwire/core/hostcache/CacheClient.groovy +++ b/core/src/main/groovy/com/muwire/core/hostcache/CacheClient.groovy @@ -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) diff --git a/core/src/main/groovy/com/muwire/core/hostcache/CacheServers.groovy b/core/src/main/groovy/com/muwire/core/hostcache/CacheServers.groovy index f0ffec5a..fa38e224 100644 --- a/core/src/main/groovy/com/muwire/core/hostcache/CacheServers.groovy +++ b/core/src/main/groovy/com/muwire/core/hostcache/CacheServers.groovy @@ -17,7 +17,7 @@ class CacheServers { return allCaches allCaches[0..TO_GIVE-1] } - + static boolean isRegistered(Destination d) { return CACHES.contains(d) } diff --git a/core/src/main/groovy/com/muwire/core/hostcache/Host.groovy b/core/src/main/groovy/com/muwire/core/hostcache/Host.groovy index c42cd8a4..524c4a05 100644 --- a/core/src/main/groovy/com/muwire/core/hostcache/Host.groovy +++ b/core/src/main/groovy/com/muwire/core/hostcache/Host.groovy @@ -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) } diff --git a/core/src/main/groovy/com/muwire/core/hostcache/HostCache.groovy b/core/src/main/groovy/com/muwire/core/hostcache/HostCache.groovy index 35e7ff8f..32feeb6c 100644 --- a/core/src/main/groovy/com/muwire/core/hostcache/HostCache.groovy +++ b/core/src/main/groovy/com/muwire/core/hostcache/HostCache.groovy @@ -22,10 +22,10 @@ class HostCache extends Service { final MuWireSettings settings final Destination myself final Map 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 getHosts(int n) { List 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 getGoodHosts(int n) { List 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 -> diff --git a/core/src/main/groovy/com/muwire/core/hostcache/HostDiscoveredEvent.groovy b/core/src/main/groovy/com/muwire/core/hostcache/HostDiscoveredEvent.groovy index 2dfa45b1..5f3fefc1 100644 --- a/core/src/main/groovy/com/muwire/core/hostcache/HostDiscoveredEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/hostcache/HostDiscoveredEvent.groovy @@ -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" diff --git a/core/src/main/groovy/com/muwire/core/mesh/Mesh.groovy b/core/src/main/groovy/com/muwire/core/mesh/Mesh.groovy index e6e0faf7..95f0de87 100644 --- a/core/src/main/groovy/com/muwire/core/mesh/Mesh.groovy +++ b/core/src/main/groovy/com/muwire/core/mesh/Mesh.groovy @@ -11,12 +11,12 @@ class Mesh { private final InfoHash infoHash private final Set sources = new ConcurrentHashSet<>() private final Pieces pieces - + Mesh(InfoHash infoHash, Pieces pieces) { this.infoHash = infoHash this.pieces = pieces } - + Set getRandom(int n, Persona exclude) { List tmp = new ArrayList<>(sources) tmp.remove(exclude) diff --git a/core/src/main/groovy/com/muwire/core/mesh/MeshManager.groovy b/core/src/main/groovy/com/muwire/core/mesh/MeshManager.groovy index 474b0357..ef12f0b9 100644 --- a/core/src/main/groovy/com/muwire/core/mesh/MeshManager.groovy +++ b/core/src/main/groovy/com/muwire/core/mesh/MeshManager.groovy @@ -16,23 +16,23 @@ import groovy.json.JsonSlurper import net.i2p.data.Base64 class MeshManager { - + private final Map 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) } diff --git a/core/src/main/groovy/com/muwire/core/search/DeleteEvent.groovy b/core/src/main/groovy/com/muwire/core/search/DeleteEvent.groovy index 94a13115..a53c36ca 100644 --- a/core/src/main/groovy/com/muwire/core/search/DeleteEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/search/DeleteEvent.groovy @@ -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()}" diff --git a/core/src/main/groovy/com/muwire/core/search/InvalidSearchResultException.groovy b/core/src/main/groovy/com/muwire/core/search/InvalidSearchResultException.groovy index f203f43d..d1225acc 100644 --- a/core/src/main/groovy/com/muwire/core/search/InvalidSearchResultException.groovy +++ b/core/src/main/groovy/com/muwire/core/search/InvalidSearchResultException.groovy @@ -21,5 +21,5 @@ class InvalidSearchResultException extends Exception { super(cause); // TODO Auto-generated constructor stub } - + } diff --git a/core/src/main/groovy/com/muwire/core/search/LeafSearcher.groovy b/core/src/main/groovy/com/muwire/core/search/LeafSearcher.groovy index 2ae6669a..dde897b3 100644 --- a/core/src/main/groovy/com/muwire/core/search/LeafSearcher.groovy +++ b/core/src/main/groovy/com/muwire/core/search/LeafSearcher.groovy @@ -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> fileNameToHashes = new HashMap<>() final Map> hashToLeafs = new HashMap<>() - + final Map>> 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 } - + } diff --git a/core/src/main/groovy/com/muwire/core/search/QueryEvent.groovy b/core/src/main/groovy/com/muwire/core/search/QueryEvent.groovy index dda694e8..7cd3f631 100644 --- a/core/src/main/groovy/com/muwire/core/search/QueryEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/search/QueryEvent.groovy @@ -6,7 +6,7 @@ import com.muwire.core.Persona import net.i2p.data.Destination class QueryEvent extends Event { - + SearchEvent searchEvent boolean firstHop Destination replyTo diff --git a/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy b/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy index 4b541971..5be464e2 100644 --- a/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy +++ b/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy @@ -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 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, diff --git a/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy b/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy index 02980382..70b9d188 100644 --- a/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy +++ b/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy @@ -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 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 { diff --git a/core/src/main/groovy/com/muwire/core/search/SearchEvent.groovy b/core/src/main/groovy/com/muwire/core/search/SearchEvent.groovy index f7e19119..b8b65cf9 100644 --- a/core/src/main/groovy/com/muwire/core/search/SearchEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/search/SearchEvent.groovy @@ -9,7 +9,7 @@ class SearchEvent extends Event { byte [] searchHash UUID uuid boolean oobInfohash - + String toString() { def infoHash = null if (searchHash != null) diff --git a/core/src/main/groovy/com/muwire/core/search/SearchIndex.groovy b/core/src/main/groovy/com/muwire/core/search/SearchIndex.groovy index 3f4615ec..7c57f55d 100644 --- a/core/src/main/groovy/com/muwire/core/search/SearchIndex.groovy +++ b/core/src/main/groovy/com/muwire/core/search/SearchIndex.groovy @@ -5,7 +5,7 @@ import com.muwire.core.Constants class SearchIndex { final Map> 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 terms) { Set rv = null; - + terms.each { Set forWord = keywords.getOrDefault(it,[]) if (rv == null) { @@ -49,9 +49,9 @@ class SearchIndex { } else { rv.retainAll(forWord) } - + } - + if (rv != null) return rv.asList() [] diff --git a/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy b/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy index 3ab7eef6..ef31b8c9 100644 --- a/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy +++ b/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy @@ -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 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) { diff --git a/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy b/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy index c8d5be69..ef9a792e 100644 --- a/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/search/UIResultEvent.groovy @@ -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" diff --git a/core/src/main/groovy/com/muwire/core/search/UnexpectedResultsException.groovy b/core/src/main/groovy/com/muwire/core/search/UnexpectedResultsException.groovy index cd8a7f53..a78e09bb 100644 --- a/core/src/main/groovy/com/muwire/core/search/UnexpectedResultsException.groovy +++ b/core/src/main/groovy/com/muwire/core/search/UnexpectedResultsException.groovy @@ -18,5 +18,5 @@ class UnexpectedResultsException extends Exception { public UnexpectedResultsException(String message) { super(message); } - + } diff --git a/core/src/main/groovy/com/muwire/core/search/UpsertEvent.groovy b/core/src/main/groovy/com/muwire/core/search/UpsertEvent.groovy index 6ce32e21..e182a5d0 100644 --- a/core/src/main/groovy/com/muwire/core/search/UpsertEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/search/UpsertEvent.groovy @@ -10,7 +10,7 @@ class UpsertEvent extends Event { Set names byte [] infoHash Destination leaf - + @Override public String toString() { "UpsertEvent ${super.toString()} names:$names infoHash:${Base32.encode(infoHash)} leaf:${leaf.toBase32()}" diff --git a/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy b/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy index 2747f3cf..a09de744 100644 --- a/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy +++ b/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy @@ -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 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)) diff --git a/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy b/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy index f235c56e..9d19b231 100644 --- a/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy +++ b/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy @@ -13,29 +13,29 @@ class TrustService extends Service { final File persistGood, persistBad final long persistInterval - + final Map good = new ConcurrentHashMap<>() final Map 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: diff --git a/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy b/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy index 806cf8db..966ceb2c 100644 --- a/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy +++ b/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy @@ -22,32 +22,32 @@ class TrustSubscriber { private final EventBus eventBus private final I2PConnector i2pConnector private final MuWireSettings settings - + private final Map 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 good = new HashSet<>() int nGood = dis.readUnsignedShort() for (int i = 0; i < nGood; i++) { Persona p = new Persona(dis) good.add(p) } - + Set 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() } - + } } diff --git a/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy b/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy index 29998495..8a6727f8 100644 --- a/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy +++ b/core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy @@ -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) } - + } } diff --git a/core/src/main/groovy/com/muwire/core/upload/ContentUploader.groovy b/core/src/main/groovy/com/muwire/core/upload/ContentUploader.groovy index 27c921e6..9c47aa41 100644 --- a/core/src/main/groovy/com/muwire/core/upload/ContentUploader.groovy +++ b/core/src/main/groovy/com/muwire/core/upload/ContentUploader.groovy @@ -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 sources = mesh.getRandom(3, toExclude) if (!sources.isEmpty()) { String xAlts = sources.stream().map({ it.toBase64() }).collect(Collectors.joining(",")) diff --git a/core/src/main/groovy/com/muwire/core/upload/HashListUploader.groovy b/core/src/main/groovy/com/muwire/core/upload/HashListUploader.groovy index bd3aceec..e3adae79 100644 --- a/core/src/main/groovy/com/muwire/core/upload/HashListUploader.groovy +++ b/core/src/main/groovy/com/muwire/core/upload/HashListUploader.groovy @@ -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; diff --git a/core/src/main/groovy/com/muwire/core/upload/Range.groovy b/core/src/main/groovy/com/muwire/core/upload/Range.groovy index 34871153..9353663d 100644 --- a/core/src/main/groovy/com/muwire/core/upload/Range.groovy +++ b/core/src/main/groovy/com/muwire/core/upload/Range.groovy @@ -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 diff --git a/core/src/main/groovy/com/muwire/core/upload/Request.groovy b/core/src/main/groovy/com/muwire/core/upload/Request.groovy index 864ecf47..b57a5693 100644 --- a/core/src/main/groovy/com/muwire/core/upload/Request.groovy +++ b/core/src/main/groovy/com/muwire/core/upload/Request.groovy @@ -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 headers - + static Request parseContentRequest(InfoHash infoHash, InputStream is) throws IOException { - + Map 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 headers = parseHeaders(is) Persona downloader = null @@ -69,7 +69,7 @@ class Request { } new HashListRequest(infoHash : infoHash, headers : headers, downloader : downloader) } - + private static Map parseHeaders(InputStream is) { Map 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 } - + } diff --git a/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy b/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy index c268f490..63536258 100644 --- a/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy +++ b/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy @@ -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 diff --git a/core/src/main/groovy/com/muwire/core/upload/Uploader.groovy b/core/src/main/groovy/com/muwire/core/upload/Uploader.groovy index 76bbd37a..c065383a 100644 --- a/core/src/main/groovy/com/muwire/core/upload/Uploader.groovy +++ b/core/src/main/groovy/com/muwire/core/upload/Uploader.groovy @@ -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(); } diff --git a/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy b/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy index 4a80edb1..b646e834 100644 --- a/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy +++ b/core/src/main/groovy/com/muwire/core/util/DataUtil.groovy @@ -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 pieces, int totalPieces) { int bytes = totalPieces / 8 if (totalPieces % 8 != 0) @@ -98,7 +98,7 @@ class DataUtil { } Base64.encode(raw) } - + public static List decodeXHave(String xHave) { byte [] availablePieces = Base64.decode(xHave) List 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 { diff --git a/core/src/main/groovy/com/muwire/core/util/JULLog.groovy b/core/src/main/groovy/com/muwire/core/util/JULLog.groovy index 81764603..904b7c1b 100644 --- a/core/src/main/groovy/com/muwire/core/util/JULLog.groovy +++ b/core/src/main/groovy/com/muwire/core/util/JULLog.groovy @@ -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() diff --git a/core/src/main/groovy/com/muwire/core/util/MuWireLogManager.groovy b/core/src/main/groovy/com/muwire/core/util/MuWireLogManager.groovy index c00ca6fc..f97b249d 100644 --- a/core/src/main/groovy/com/muwire/core/util/MuWireLogManager.groovy +++ b/core/src/main/groovy/com/muwire/core/util/MuWireLogManager.groovy @@ -5,14 +5,14 @@ import net.i2p.util.Log import net.i2p.util.LogManager class MuWireLogManager extends LogManager { - + private static final Map, Log> classLogs = new HashMap<>() private static final Map 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 } - + } diff --git a/core/src/main/java/com/muwire/core/DownloadedFile.java b/core/src/main/java/com/muwire/core/DownloadedFile.java index dc1c486c..54feadf2 100644 --- a/core/src/main/java/com/muwire/core/DownloadedFile.java +++ b/core/src/main/java/com/muwire/core/DownloadedFile.java @@ -7,15 +7,15 @@ import java.util.Set; import net.i2p.data.Destination; public class DownloadedFile extends SharedFile { - + private final Set sources; - public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set sources) + public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set sources) throws IOException { super(file, infoHash, pieceSize); this.sources = sources; } - + public Set getSources() { return sources; } diff --git a/core/src/main/java/com/muwire/core/InfoHash.java b/core/src/main/java/com/muwire/core/InfoHash.java index c4b9311b..80e0f47e 100644 --- a/core/src/main/java/com/muwire/core/InfoHash.java +++ b/core/src/main/java/com/muwire/core/InfoHash.java @@ -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 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); diff --git a/core/src/main/java/com/muwire/core/SharedFile.java b/core/src/main/java/com/muwire/core/SharedFile.java index c090c409..2a91f93c 100644 --- a/core/src/main/java/com/muwire/core/SharedFile.java +++ b/core/src/main/java/com/muwire/core/SharedFile.java @@ -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)) diff --git a/core/src/test/groovy/com/muwire/core/EventBusTest.groovy b/core/src/test/groovy/com/muwire/core/EventBusTest.groovy index 7723db56..24191ae3 100644 --- a/core/src/test/groovy/com/muwire/core/EventBusTest.groovy +++ b/core/src/test/groovy/com/muwire/core/EventBusTest.groovy @@ -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) diff --git a/core/src/test/groovy/com/muwire/core/Personas.groovy b/core/src/test/groovy/com/muwire/core/Personas.groovy index 88039b1f..a2d8b2b3 100644 --- a/core/src/test/groovy/com/muwire/core/Personas.groovy +++ b/core/src/test/groovy/com/muwire/core/Personas.groovy @@ -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))) } diff --git a/core/src/test/groovy/com/muwire/core/connection/ConnectionAcceptorTest.groovy b/core/src/test/groovy/com/muwire/core/connection/ConnectionAcceptorTest.groovy index 855e04dd..b9410bbb 100644 --- a/core/src/test/groovy/com/muwire/core/connection/ConnectionAcceptorTest.groovy +++ b/core/src/test/groovy/com/muwire/core/connection/ConnectionAcceptorTest.groovy @@ -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 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] diff --git a/core/src/test/groovy/com/muwire/core/connection/ConnectionEstablisherTest.groovy b/core/src/test/groovy/com/muwire/core/connection/ConnectionEstablisherTest.groovy index c9ba29e7..442d5929 100644 --- a/core/src/test/groovy/com/muwire/core/connection/ConnectionEstablisherTest.groovy +++ b/core/src/test/groovy/com/muwire/core/connection/ConnectionEstablisherTest.groovy @@ -23,21 +23,21 @@ class ConnectionEstablisherTest { List 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 diff --git a/core/src/test/groovy/com/muwire/core/download/DownloadSessionTest.groovy b/core/src/test/groovy/com/muwire/core/download/DownloadSessionTest.groovy index aad57ebf..ca868f35 100644 --- a/core/src/test/groovy/com/muwire/core/download/DownloadSessionTest.groovy +++ b/core/src/test/groovy/com/muwire/core/download/DownloadSessionTest.groovy @@ -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 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 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 readAllHeaders(InputStream is) { Set rv = new HashSet<>() String header diff --git a/core/src/test/groovy/com/muwire/core/download/PiecesTest.groovy b/core/src/test/groovy/com/muwire/core/download/PiecesTest.groovy index 8fa7bfd4..1e3ef1cb 100644 --- a/core/src/test/groovy/com/muwire/core/download/PiecesTest.groovy +++ b/core/src/test/groovy/com/muwire/core/download/PiecesTest.groovy @@ -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) diff --git a/core/src/test/groovy/com/muwire/core/files/FileHasherTest.groovy b/core/src/test/groovy/com/muwire/core/files/FileHasherTest.groovy index d916bbb8..33cddac7 100644 --- a/core/src/test/groovy/com/muwire/core/files/FileHasherTest.groovy +++ b/core/src/test/groovy/com/muwire/core/files/FileHasherTest.groovy @@ -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) diff --git a/core/src/test/groovy/com/muwire/core/files/FileManagerTest.groovy b/core/src/test/groovy/com/muwire/core/files/FileManagerTest.groovy index 7260c3bd..d6cf9938 100644 --- a/core/src/test/groovy/com/muwire/core/files/FileManagerTest.groovy +++ b/core/src/test/groovy/com/muwire/core/files/FileManagerTest.groovy @@ -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 - + } } diff --git a/core/src/test/groovy/com/muwire/core/files/HasherServiceTest.groovy b/core/src/test/groovy/com/muwire/core/files/HasherServiceTest.groovy index 9e8d401a..da7d3671 100644 --- a/core/src/test/groovy/com/muwire/core/files/HasherServiceTest.groovy +++ b/core/src/test/groovy/com/muwire/core/files/HasherServiceTest.groovy @@ -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(".") diff --git a/core/src/test/groovy/com/muwire/core/files/PersisterServiceLoadingTest.groovy b/core/src/test/groovy/com/muwire/core/files/PersisterServiceLoadingTest.groovy index cae23d13..92562738 100644 --- a/core/src/test/groovy/com/muwire/core/files/PersisterServiceLoadingTest.groovy +++ b/core/src/test/groovy/com/muwire/core/files/PersisterServiceLoadingTest.groovy @@ -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) - + } } diff --git a/core/src/test/groovy/com/muwire/core/files/PersisterServiceSavingTest.groovy b/core/src/test/groovy/com/muwire/core/files/PersisterServiceSavingTest.groovy index 11c7f0fd..8652ca1b 100644 --- a/core/src/test/groovy/com/muwire/core/files/PersisterServiceSavingTest.groovy +++ b/core/src/test/groovy/com/muwire/core/files/PersisterServiceSavingTest.groovy @@ -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) diff --git a/core/src/test/groovy/com/muwire/core/hostcache/HostCacheTest.groovy b/core/src/test/groovy/com/muwire/core/hostcache/HostCacheTest.groovy index 789b5a7f..67cc356a 100644 --- a/core/src/test/groovy/com/muwire/core/hostcache/HostCacheTest.groovy +++ b/core/src/test/groovy/com/muwire/core/hostcache/HostCacheTest.groovy @@ -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 } } diff --git a/core/src/test/groovy/com/muwire/core/search/SearchIndexTest.groovy b/core/src/test/groovy/com/muwire/core/search/SearchIndexTest.groovy index 9813419d..03264808 100644 --- a/core/src/test/groovy/com/muwire/core/search/SearchIndexTest.groovy +++ b/core/src/test/groovy/com/muwire/core/search/SearchIndexTest.groovy @@ -5,34 +5,34 @@ import org.junit.Test class SearchIndexTest { SearchIndex index - + private void initIndex(List 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"]) diff --git a/core/src/test/groovy/com/muwire/core/trust/TrustServiceTest.groovy b/core/src/test/groovy/com/muwire/core/trust/TrustServiceTest.groovy index 01a2e9f4..49130c96 100644 --- a/core/src/test/groovy/com/muwire/core/trust/TrustServiceTest.groovy +++ b/core/src/test/groovy/com/muwire/core/trust/TrustServiceTest.groovy @@ -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) } diff --git a/core/src/test/groovy/com/muwire/core/upload/RequestParsingTest.groovy b/core/src/test/groovy/com/muwire/core/upload/RequestParsingTest.groovy index 7bd87ca1..8b3ab274 100644 --- a/core/src/test/groovy/com/muwire/core/upload/RequestParsingTest.groovy +++ b/core/src/test/groovy/com/muwire/core/upload/RequestParsingTest.groovy @@ -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()) } -} +} diff --git a/core/src/test/groovy/com/muwire/core/upload/UploaderTest.groovy b/core/src/test/groovy/com/muwire/core/upload/UploaderTest.groovy index 2e0bf31b..4f53729e 100644 --- a/core/src/test/groovy/com/muwire/core/upload/UploaderTest.groovy +++ b/core/src/test/groovy/com/muwire/core/upload/UploaderTest.groovy @@ -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 - } + } } diff --git a/core/src/test/groovy/com/muwire/core/util/DataUtilTest.groovy b/core/src/test/groovy/com/muwire/core/util/DataUtilTest.groovy index 7e760b09..598d7f56 100644 --- a/core/src/test/groovy/com/muwire/core/util/DataUtilTest.groovy +++ b/core/src/test/groovy/com/muwire/core/util/DataUtilTest.groovy @@ -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) diff --git a/gui/griffon-app/controllers/com/muwire/gui/I2PStatusController.groovy b/gui/griffon-app/controllers/com/muwire/gui/I2PStatusController.groovy index 896d714f..44b6db0f 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/I2PStatusController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/I2PStatusController.groovy @@ -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) diff --git a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy index 5031f700..be9dd602 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy @@ -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 args) { - application.addPropertyChangeListener("core", {e-> + application.addPropertyChangeListener("core", {e-> core = e.getNewValue() }) } diff --git a/gui/griffon-app/controllers/com/muwire/gui/MuWireStatusController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MuWireStatusController.groovy index 9aa4457a..9c7dca0c 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MuWireStatusController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MuWireStatusController.groovy @@ -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) diff --git a/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy b/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy index f78d9bb9..c42c99a9 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/OptionsController.groovy @@ -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() diff --git a/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy b/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy index 80bf04e5..f5d6fcbd 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy @@ -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") diff --git a/gui/griffon-app/lifecycle/Initialize.groovy b/gui/griffon-app/lifecycle/Initialize.groovy index 82046e89..f2d966fd 100644 --- a/gui/griffon-app/lifecycle/Initialize.groovy +++ b/gui/griffon-app/lifecycle/Initialize.groovy @@ -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") diff --git a/gui/griffon-app/lifecycle/Ready.groovy b/gui/griffon-app/lifecycle/Ready.groovy index 54f0647d..8a36f75d 100644 --- a/gui/griffon-app/lifecycle/Ready.groovy +++ b/gui/griffon-app/lifecycle/Ready.groovy @@ -24,9 +24,9 @@ import java.util.logging.Level @Log class Ready extends AbstractLifecycleHandler { - + @Inject Metadata metadata - + @Inject Ready(@Nonnull GriffonApplication application) { super(application) @@ -35,7 +35,7 @@ class Ready extends AbstractLifecycleHandler { @Override void execute() { log.info "starting core services" - + def home = new File(application.getContext().getAsString("muwire-home")) def props = new Properties() def propsFile = new File(home, "MuWire.properties") @@ -56,12 +56,12 @@ class Ready extends AbstractLifecycleHandler { "Your nickname is displayed when you send search results so other MuWire users can choose to trust you", "Please choose a nickname", JOptionPane.PLAIN_MESSAGE) if (nickname == null || nickname.trim().length() == 0) { - JOptionPane.showMessageDialog(null, "Nickname cannot be empty", "Select another nickname", + JOptionPane.showMessageDialog(null, "Nickname cannot be empty", "Select another nickname", JOptionPane.WARNING_MESSAGE) continue } if (nickname.contains("@")) { - JOptionPane.showMessageDialog(null, "Nickname cannot contain @, choose another", + JOptionPane.showMessageDialog(null, "Nickname cannot contain @, choose another", "Select another nickname", JOptionPane.WARNING_MESSAGE) continue } @@ -69,8 +69,8 @@ class Ready extends AbstractLifecycleHandler { break } props.setNickname(nickname) - - + + def portableDownloads = System.getProperty("portable.downloads") if (portableDownloads != null) { props.downloadLocation = new File(portableDownloads) @@ -86,12 +86,12 @@ class Ready extends AbstractLifecycleHandler { } props.downloadLocation = chooser.getSelectedFile() } - + propsFile.withOutputStream { props.write(it) } } - + Core core try { core = new Core(props, home, metadata["application.version"]) @@ -102,15 +102,15 @@ class Ready extends AbstractLifecycleHandler { System.exit(0) } Runtime.getRuntime().addShutdownHook({ - core.shutdown() + core.shutdown() }) core.startServices() application.context.put("muwire-settings", props) application.context.put("core",core) - application.getPropertyChangeListeners("core").each { - it.propertyChange(new PropertyChangeEvent(this, "core", null, core)) + application.getPropertyChangeListeners("core").each { + it.propertyChange(new PropertyChangeEvent(this, "core", null, core)) } - + core.eventBus.publish(new UILoadedEvent()) } } diff --git a/gui/griffon-app/lifecycle/Shutdown.groovy b/gui/griffon-app/lifecycle/Shutdown.groovy index 963f3cf0..1c82aff5 100644 --- a/gui/griffon-app/lifecycle/Shutdown.groovy +++ b/gui/griffon-app/lifecycle/Shutdown.groovy @@ -15,7 +15,7 @@ class Shutdown extends AbstractLifecycleHandler { Shutdown(@Nonnull GriffonApplication application) { super(application) } - + @Override void execute() { log.info("shutting down") diff --git a/gui/griffon-app/models/com/muwire/gui/EventListModel.groovy b/gui/griffon-app/models/com/muwire/gui/EventListModel.groovy index 3d271894..35ca48ee 100644 --- a/gui/griffon-app/models/com/muwire/gui/EventListModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/EventListModel.groovy @@ -17,10 +17,10 @@ import griffon.metadata.ArtifactProviderFor class EventListModel { @Inject @Nonnull GriffonApplication application @Observable boolean coreInitialized = false - + void mvcGroupInit(Map args) { - application.addPropertyChangeListener("core", {e -> - coreInitialized = (e.getNewValue() != null) + application.addPropertyChangeListener("core", {e -> + coreInitialized = (e.getNewValue() != null) }) } } \ No newline at end of file diff --git a/gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy b/gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy index cb61a34b..3124337e 100644 --- a/gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/I2PStatusModel.groovy @@ -11,7 +11,7 @@ import griffon.metadata.ArtifactProviderFor class I2PStatusModel { @MVCMember @Nonnull I2PStatusController controller - + @Observable int ntcpConnections @Observable int ssuConnections @Observable String networkStatus @@ -21,7 +21,7 @@ class I2PStatusModel { @Observable int receiveBps @Observable int sendBps @Observable int participatingBW - + void mvcGroupInit(Map args) { controller.refresh() } diff --git a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy index d341b3ae..e1d0b22b 100644 --- a/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MainFrameModel.groovy @@ -59,7 +59,7 @@ class MainFrameModel { @Inject @Nonnull GriffonApplication application @Observable boolean coreInitialized = false @Observable boolean routerPresent - + def results = new ConcurrentHashMap<>() def downloads = [] def uploads = [] @@ -70,7 +70,7 @@ class MainFrameModel { def trusted = [] def distrusted = [] def subscriptions = [] - + @Observable int connections @Observable String me @Observable int loadedFiles @@ -91,34 +91,34 @@ class MainFrameModel { @Observable boolean unsubscribeButtonEnabled private final Set infoHashes = new HashSet<>() - + private final Set downloadInfoHashes = new HashSet<>() - @Observable volatile Core core + @Observable volatile Core core private long lastRetryTime = System.currentTimeMillis() - + UISettings uiSettings - + void updateTablePreservingSelection(String tableName) { def downloadTable = builder.getVariable(tableName) int selectedRow = downloadTable.getSelectedRow() downloadTable.model.fireTableDataChanged() downloadTable.selectionModel.setSelectionInterval(selectedRow,selectedRow) } - + void mvcGroupInit(Map args) { - + uiSettings = application.context.get("ui-settings") - + Timer timer = new Timer("download-pumper", true) timer.schedule({ runInsideUIAsync { if (!mvcGroup.alive) return - + // remove cancelled or finished downloads - def toRemove = [] + def toRemove = [] downloads.each { if (uiSettings.clearCancelledDownloads && it.downloader.getCurrentState() == Downloader.DownloadState.CANCELLED) @@ -129,10 +129,10 @@ class MainFrameModel { } toRemove.each { downloads.remove(it) - } - + } + builder.getVariable("uploads-table")?.model.fireTableDataChanged() - + updateTablePreservingSelection("downloads-table") updateTablePreservingSelection("trusted-table") updateTablePreservingSelection("distrusted-table") @@ -163,7 +163,7 @@ class MainFrameModel { core.eventBus.register(AllFilesLoadedEvent.class, this) core.eventBus.register(UpdateDownloadedEvent.class, this) core.eventBus.register(TrustSubscriptionUpdatedEvent.class, this) - + timer.schedule({ if (core.shutdown.get()) return @@ -176,7 +176,7 @@ class MainFrameModel { runInsideUIAsync { downloads.each { def state = it.downloader.currentState - if (state == Downloader.DownloadState.FAILED || + if (state == Downloader.DownloadState.FAILED || state == Downloader.DownloadState.DOWNLOADING) it.downloader.resume() updateTablePreservingSelection("downloads-table") @@ -186,87 +186,87 @@ class MainFrameModel { } } }, 60000, 60000) - + runInsideUIAsync { trusted.addAll(core.trustService.good.values()) distrusted.addAll(core.trustService.bad.values()) - + resumeButtonText = "Retry" } }) - + } - + void onAllFilesLoadedEvent(AllFilesLoadedEvent e) { runInsideUIAsync { watched.addAll(core.muOptions.watchedDirectories) builder.getVariable("watched-directories-table").model.fireTableDataChanged() watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) } - + core.muOptions.trustSubscriptions.each { core.eventBus.publish(new TrustSubscriptionEvent(persona : it, subscribe : true)) } } } - + void onUpdateDownloadedEvent(UpdateDownloadedEvent e) { runInsideUIAsync { - JOptionPane.showMessageDialog(null, "MuWire $e.version has been downloaded. You can update now", + JOptionPane.showMessageDialog(null, "MuWire $e.version has been downloaded. You can update now", "Update Downloaded", JOptionPane.INFORMATION_MESSAGE) } } - + void onUIResultEvent(UIResultEvent e) { MVCGroup resultsGroup = results.get(e.uuid) resultsGroup?.model.handleResult(e) } - + void onUIResultBatchEvent(UIResultBatchEvent e) { MVCGroup resultsGroup = results.get(e.uuid) resultsGroup?.model?.handleResultBatch(e.results) } - + void onDownloadStartedEvent(DownloadStartedEvent e) { runInsideUIAsync { downloads << e downloadInfoHashes.add(e.downloader.infoHash) } } - + void onConnectionEvent(ConnectionEvent e) { if (e.getStatus() != ConnectionAttemptStatus.SUCCESSFUL) return runInsideUIAsync { connections = core.connectionManager.getConnections().size() - + if (connections > 0) { def topPanel = builder.getVariable("top-panel") topPanel.getLayout().show(topPanel, "top-search-panel") } - + UIConnection con = new UIConnection(destination : e.endpoint.destination, incoming : e.incoming) connectionList.add(con) JTable table = builder.getVariable("connections-table") table.model.fireTableDataChanged() } } - + void onDisconnectionEvent(DisconnectionEvent e) { runInsideUIAsync { connections = core.connectionManager.getConnections().size() - + if (connections == 0) { def topPanel = builder.getVariable("top-panel") topPanel.getLayout().show(topPanel, "top-connect-panel") } - + UIConnection con = new UIConnection(destination : e.destination) connectionList.remove(con) JTable table = builder.getVariable("connections-table") table.model.fireTableDataChanged() } } - + void onFileHashingEvent(FileHashingEvent e) { runInsideUIAsync { loadedFiles = shared.size() @@ -281,7 +281,7 @@ class MainFrameModel { if (e.error != null) return // TODO do something if (infoHashes.contains(e.sharedFile.infoHash)) - return + return infoHashes.add(e.sharedFile.infoHash) runInsideUIAsync { shared << e.sharedFile @@ -290,7 +290,7 @@ class MainFrameModel { table.model.fireTableDataChanged() } } - + void onFileLoadedEvent(FileLoadedEvent e) { if (infoHashes.contains(e.loadedFile.infoHash)) return @@ -302,7 +302,7 @@ class MainFrameModel { table.model.fireTableDataChanged() } } - + void onFileUnsharedEvent(FileUnsharedEvent e) { InfoHash infohash = e.unsharedFile.infoHash if (!infoHashes.remove(infohash)) @@ -314,7 +314,7 @@ class MainFrameModel { table.model.fireTableDataChanged() } } - + void onUploadEvent(UploadEvent e) { runInsideUIAsync { uploads << e.uploader @@ -322,7 +322,7 @@ class MainFrameModel { table.model.fireTableDataChanged() } } - + void onUploadFinishedEvent(UploadFinishedEvent e) { runInsideUIAsync { uploads.remove(e.uploader) @@ -330,26 +330,26 @@ class MainFrameModel { table.model.fireTableDataChanged() } } - + void onTrustEvent(TrustEvent e) { runInsideUIAsync { - + trusted.clear() trusted.addAll(core.trustService.good.values()) distrusted.clear() distrusted.addAll(core.trustService.bad.values()) - + updateTablePreservingSelection("trusted-table") updateTablePreservingSelection("distrusted-table") - - results.values().each { MVCGroup group -> + + results.values().each { MVCGroup group -> if (group.alive) { group.view.pane.getClientProperty("results-table")?.model.fireTableDataChanged() } } } } - + void onTrustSubscriptionUpdatedEvent(TrustSubscriptionUpdatedEvent e) { runInsideUIAsync { if (!subscriptions.contains(e.trustList)) @@ -357,11 +357,11 @@ class MainFrameModel { updateTablePreservingSelection("subscription-table") } } - + void onQueryEvent(QueryEvent e) { if (e.replyTo == core.me.destination) return - + def search if (e.searchEvent.searchHash != null) { if (!uiSettings.showSearchHashes) { @@ -408,7 +408,7 @@ class MainFrameModel { table.model.fireTableDataChanged() } } - + class IncomingSearch { String search Destination replyTo @@ -426,11 +426,11 @@ class MainFrameModel { this.timestamp = Calendar.getInstance() } } - + void onUpdateAvailableEvent(UpdateAvailableEvent e) { runInsideUIAsync { - - int option = JOptionPane.showConfirmDialog(null, + + int option = JOptionPane.showConfirmDialog(null, "MuWire $e.version is available from $e.signer. You have "+ metadata["application.version"]+" Update?", "New MuWire version availble", JOptionPane.OK_CANCEL_OPTION) if (option == JOptionPane.CANCEL_OPTION) @@ -438,7 +438,7 @@ class MainFrameModel { controller.search(e.infoHash,"MuWire update") } } - + void onRouterDisconnectedEvent(RouterDisconnectedEvent e) { runInsideUIAsync { JOptionPane.showMessageDialog(null, "MuWire lost connection to the I2P router and will now exit.", @@ -446,7 +446,7 @@ class MainFrameModel { System.exit(0) } } - + void onFileDownloadedEvent(FileDownloadedEvent e) { if (!core.muOptions.shareDownloadedFiles) return @@ -457,16 +457,16 @@ class MainFrameModel { table.model.fireTableDataChanged() } } - + private static class UIConnection { Destination destination boolean incoming - + @Override public int hashCode() { destination.hashCode() } - + @Override public boolean equals(Object o) { if (!(o instanceof UIConnection)) @@ -475,7 +475,7 @@ class MainFrameModel { return destination == other.destination } } - + boolean canDownload(InfoHash hash) { !downloadInfoHashes.contains(hash) } diff --git a/gui/griffon-app/models/com/muwire/gui/MuWireStatusModel.groovy b/gui/griffon-app/models/com/muwire/gui/MuWireStatusModel.groovy index 0e77eb8b..6d9516ae 100644 --- a/gui/griffon-app/models/com/muwire/gui/MuWireStatusModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/MuWireStatusModel.groovy @@ -9,16 +9,16 @@ import griffon.metadata.ArtifactProviderFor @ArtifactProviderFor(GriffonModel) class MuWireStatusModel { - + @MVCMember @Nonnull MuWireStatusController controller - + @Observable int incomingConnections @Observable int outgoingConnections @Observable int knownHosts @Observable int sharedFiles @Observable int downloads - + void mvcGroupInit(Map args) { controller.refresh() } diff --git a/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy b/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy index 2803c830..72888edc 100644 --- a/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/OptionsModel.groovy @@ -9,12 +9,12 @@ import griffon.metadata.ArtifactProviderFor @ArtifactProviderFor(GriffonModel) class OptionsModel { - @Observable String downloadRetryInterval + @Observable String downloadRetryInterval @Observable String updateCheckInterval @Observable boolean autoDownloadUpdate @Observable boolean shareDownloadedFiles @Observable String downloadLocation - + // i2p options @Observable String inboundLength @Observable String inboundQuantity @@ -22,7 +22,7 @@ class OptionsModel { @Observable String outboundQuantity @Observable String i2pUDPPort @Observable String i2pNTCPPort - + // gui options @Observable boolean showMonitor @Observable String lnf @@ -31,17 +31,17 @@ class OptionsModel { @Observable boolean clearFinishedDownloads @Observable boolean excludeLocalResult @Observable boolean showSearchHashes - + // bw options @Observable String inBw @Observable String outBw - + // trust options @Observable boolean onlyTrusted @Observable boolean trustLists @Observable String trustListInterval - - + + void mvcGroupInit(Map args) { MuWireSettings settings = application.context.get("muwire-settings") downloadRetryInterval = settings.downloadRetryInterval @@ -49,7 +49,7 @@ class OptionsModel { autoDownloadUpdate = settings.autoDownloadUpdate shareDownloadedFiles = settings.shareDownloadedFiles downloadLocation = settings.downloadLocation.getAbsolutePath() - + Core core = application.context.get("core") inboundLength = core.i2pOptions["inbound.length"] inboundQuantity = core.i2pOptions["inbound.quantity"] @@ -57,7 +57,7 @@ class OptionsModel { outboundQuantity = core.i2pOptions["outbound.quantity"] i2pUDPPort = core.i2pOptions["i2np.udp.port"] i2pNTCPPort = core.i2pOptions["i2np.ntcp.port"] - + UISettings uiSettings = application.context.get("ui-settings") showMonitor = uiSettings.showMonitor lnf = uiSettings.lnf @@ -66,12 +66,12 @@ class OptionsModel { clearFinishedDownloads = uiSettings.clearFinishedDownloads excludeLocalResult = uiSettings.excludeLocalResult showSearchHashes = uiSettings.showSearchHashes - + if (core.router != null) { inBw = String.valueOf(settings.inBw) outBw = String.valueOf(settings.outBw) } - + onlyTrusted = !settings.allowUntrusted() trustLists = settings.allowTrustLists trustListInterval = String.valueOf(settings.trustListInterval) diff --git a/gui/griffon-app/models/com/muwire/gui/SearchTabModel.groovy b/gui/griffon-app/models/com/muwire/gui/SearchTabModel.groovy index 54f0bbb4..dc58135f 100644 --- a/gui/griffon-app/models/com/muwire/gui/SearchTabModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/SearchTabModel.groovy @@ -17,25 +17,25 @@ import griffon.metadata.ArtifactProviderFor class SearchTabModel { @MVCMember @Nonnull FactoryBuilderSupport builder - + Core core UISettings uiSettings String uuid def results = [] def hashBucket = [:] def sourcesBucket = [:] - - + + void mvcGroupInit(Map args) { core = mvcGroup.parentGroup.model.core uiSettings = application.context.get("ui-settings") mvcGroup.parentGroup.model.results[UUID.fromString(uuid)] = mvcGroup } - + void mvcGroupDestroy() { mvcGroup.parentGroup.model.results.remove(uuid) } - + void handleResult(UIResultEvent e) { if (uiSettings.excludeLocalResult && core.fileManager.rootToFiles.containsKey(e.infohash)) @@ -47,24 +47,24 @@ class SearchTabModel { hashBucket[e.infohash] = bucket } bucket << e - + Set sourceBucket = sourcesBucket.get(e.infohash) if (sourceBucket == null) { sourceBucket = new HashSet() sourcesBucket.put(e.infohash, sourceBucket) } sourceBucket.addAll(e.sources) - + results << e JTable table = builder.getVariable("results-table") table.model.fireTableDataChanged() } } - + void handleResultBatch(UIResultEvent[] batch) { runInsideUIAsync { - batch.each { - if (uiSettings.excludeLocalResult && + batch.each { + if (uiSettings.excludeLocalResult && core.fileManager.rootToFiles.containsKey(it.infohash)) return def bucket = hashBucket.get(it.infohash) @@ -72,14 +72,14 @@ class SearchTabModel { bucket = [] hashBucket[it.infohash] = bucket } - + Set sourceBucket = sourcesBucket.get(it.infohash) if (sourceBucket == null) { sourceBucket = new HashSet() sourcesBucket.put(it.infohash, sourceBucket) } sourceBucket.addAll(it.sources) - + bucket << it results << it } diff --git a/gui/griffon-app/models/com/muwire/gui/TrustListModel.groovy b/gui/griffon-app/models/com/muwire/gui/TrustListModel.groovy index bbd6bb40..7027a2e8 100644 --- a/gui/griffon-app/models/com/muwire/gui/TrustListModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/TrustListModel.groovy @@ -11,10 +11,10 @@ import griffon.metadata.ArtifactProviderFor class TrustListModel { RemoteTrustList trustList TrustService trustService - + def trusted def distrusted - + void mvcGroupInit(Map args) { trusted = new ArrayList<>(trustList.good) distrusted = new ArrayList<>(trustList.bad) diff --git a/gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy b/gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy index f4e2b8ab..a9958a6f 100644 --- a/gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/I2PStatusView.groovy @@ -25,12 +25,12 @@ class I2PStatusView { def dialog def panel def buttonsPanel - + void initUI() { mainFrame = application.windowManager.findWindow("main-frame") - + dialog = new JDialog(mainFrame, "I2P Status", true) - + panel = builder.panel { gridBagLayout() label(text : "Network status", constraints : gbc(gridx:0, gridy:0)) @@ -52,20 +52,20 @@ class I2PStatusView { label(text : "Send Bps (15 seconds)", constraints : gbc(gridx:0, gridy:8)) label(text : bind {model.sendBps}, constraints : gbc(gridx: 1, gridy:8)) } - + buttonsPanel = builder.panel { gridBagLayout() button(text : "Refresh", constraints: gbc(gridx: 0, gridy: 0), refreshAction) button(text : "Close", constraints : gbc(gridx : 1, gridy :0), closeAction) } } - + void mvcGroupInit(Map args) { JPanel statusPanel = new JPanel() statusPanel.setLayout(new BorderLayout()) statusPanel.add(panel, BorderLayout.CENTER) statusPanel.add(buttonsPanel, BorderLayout.SOUTH) - + dialog.getContentPane().add(statusPanel) dialog.pack() dialog.setLocationRelativeTo(mainFrame) diff --git a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy index 4eb85eb7..fc88353d 100644 --- a/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MainFrameView.groovy @@ -48,21 +48,21 @@ class MainFrameView { FactoryBuilderSupport builder @MVCMember @Nonnull MainFrameModel model - + @Inject Metadata metadata - + def downloadsTable def lastDownloadSortEvent def lastSharedSortEvent def lastWatchedSortEvent def trustTablesSortEvents = [:] - + void initUI() { UISettings settings = application.context.get("ui-settings") builder.with { application(size : [1024,768], id: 'main-frame', locationRelativeTo : null, - title: application.configuration['application.title'] + " " + + title: application.configuration['application.title'] + " " + metadata["application.version"] + " revision " + metadata["build.revision"], iconImage: imageIcon('/MuWire-48x48.png').image, iconImages: [imageIcon('/MuWire-48x48.png').image, @@ -144,7 +144,7 @@ class MainFrameView { String.format("%02d", percent) + "% of ${totalSize} ($done/$pieces pcs)".toString() }) closureColumn(header: "Sources", preferredWidth : 10, type: Integer, read : {row -> row.downloader.activeWorkers()}) - closureColumn(header: "Speed", preferredWidth: 50, type:String, read :{row -> + closureColumn(header: "Speed", preferredWidth: 50, type:String, read :{row -> DataHelper.formatSize2Decimal(row.downloader.speed(), false) + "B/sec" }) } @@ -172,7 +172,7 @@ class MainFrameView { }) } panel (border : etchedBorder(), constraints : BorderLayout.CENTER) { - gridLayout(cols : 2, rows : 1) + gridLayout(cols : 2, rows : 1) panel (constraints : BorderLayout.WEST) { borderLayout() scrollPane (constraints : BorderLayout.CENTER) { @@ -220,7 +220,7 @@ class MainFrameView { int percent = row.getProgress() "$percent% of piece".toString() }) - closureColumn(header : "Downloader", type : String, read : { row -> + closureColumn(header : "Downloader", type : String, read : { row -> row.getDownloader() }) closureColumn(header : "Remote Pieces", type : String, read : { row -> @@ -256,7 +256,7 @@ class MainFrameView { closureColumn(header : "Direction", preferredWidth: 20, type: String, read : { row -> if (row.incoming) return "In" - else + else return "Out" }) } @@ -271,9 +271,9 @@ class MainFrameView { scrollPane(constraints : BorderLayout.CENTER) { table(id : "searches-table") { tableModel(list : model.searches) { - closureColumn(header : "Keywords", type : String, read : { + closureColumn(header : "Keywords", type : String, read : { sanitized = it.search.replace('<', ' ') - sanitized + sanitized }) closureColumn(header : "From", type : String, read : { if (it.originator != null) { @@ -372,7 +372,7 @@ class MainFrameView { } } } - + void mvcGroupInit(Map args) { def downloadsTable = builder.getVariable("downloads-table") def selectionModel = downloadsTable.getSelectionModel() @@ -414,14 +414,14 @@ class MainFrameView { model.pauseButtonEnabled = false } }) - + def centerRenderer = new DefaultTableCellRenderer() centerRenderer.setHorizontalAlignment(JLabel.CENTER) downloadsTable.setDefaultRenderer(Integer.class, centerRenderer) - + downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt}) downloadsTable.rowSorter.setSortsOnUpdates(true) - + downloadsTable.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { @@ -434,14 +434,14 @@ class MainFrameView { showDownloadsMenu(e) } }) - + // shared files table def sharedFilesTable = builder.getVariable("shared-files-table") sharedFilesTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer()) - + sharedFilesTable.rowSorter.addRowSorterListener({evt -> lastSharedSortEvent = evt}) sharedFilesTable.rowSorter.setSortsOnUpdates(true) - + JPopupMenu sharedFilesMenu = new JPopupMenu() JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard") copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()}) @@ -461,7 +461,7 @@ class MainFrameView { showPopupMenu(sharedFilesMenu, e) } }) - + // searches table def searchesTable = builder.getVariable("searches-table") JPopupMenu searchTableMenu = new JPopupMenu() @@ -480,7 +480,7 @@ class MainFrameView { showPopupMenu(searchTableMenu, e) } }) - + // watched directories table def watchedTable = builder.getVariable("watched-directories-table") watchedTable.rowSorter.addRowSorterListener({evt -> lastWatchedSortEvent = evt}) @@ -501,7 +501,7 @@ class MainFrameView { showPopupMenu(watchedMenu, e) } }) - + // subscription table def subscriptionTable = builder.getVariable("subscription-table") subscriptionTable.setDefaultRenderer(Integer.class, centerRenderer) @@ -539,7 +539,7 @@ class MainFrameView { break } }) - + // trusted table def trustedTable = builder.getVariable("trusted-table") trustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["trusted-table"] = evt}) @@ -558,7 +558,7 @@ class MainFrameView { model.markNeutralFromTrustedButtonEnabled = true } }) - + // distrusted table def distrustedTable = builder.getVariable("distrusted-table") distrustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["distrusted-table"] = evt}) @@ -576,11 +576,11 @@ class MainFrameView { } }) } - + private static void showPopupMenu(JPopupMenu menu, MouseEvent event) { menu.show(event.getComponent(), event.getX(), event.getY()) } - + def selectedSharedFile() { def sharedFilesTable = builder.getVariable("shared-files-table") int selected = sharedFilesTable.getSelectedRow() @@ -590,7 +590,7 @@ class MainFrameView { selected = sharedFilesTable.rowSorter.convertRowIndexToModel(selected) model.shared[selected] } - + def copyHashToClipboard() { def selected = selectedSharedFile() if (selected == null) @@ -600,7 +600,7 @@ class MainFrameView { def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard() clipboard.setContents(selection, null) } - + def copySearchToClipboard(JTable searchesTable) { int selected = searchesTable.getSelectedRow() if (selected < 0) @@ -617,7 +617,7 @@ class MainFrameView { selected = lastDownloadSortEvent.convertPreviousRowIndexToModel(selected) selected } - + def showDownloadsMenu(MouseEvent e) { int selected = selectedDownloaderRow() if (selected < 0) @@ -651,7 +651,7 @@ class MainFrameView { cancelEnabled = false retryEnabled = false } - + JPopupMenu menu = new JPopupMenu() JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard") copyHashToClipboard.addActionListener({ @@ -661,28 +661,28 @@ class MainFrameView { clipboard.setContents(selection, null) }) menu.add(copyHashToClipboard) - + if (pauseEnabled) { JMenuItem pause = new JMenuItem("Pause") pause.addActionListener({mvcGroup.controller.pause()}) menu.add(pause) } - + if (cancelEnabled) { JMenuItem cancel = new JMenuItem("Cancel") cancel.addActionListener({mvcGroup.controller.cancel()}) menu.add(cancel) } - + if (retryEnabled) { JMenuItem retry = new JMenuItem(resumeText) retry.addActionListener({mvcGroup.controller.resume()}) menu.add(retry) } - + showPopupMenu(menu, e) } - + def showSearchWindow = { def cardsPanel = builder.getVariable("cards-panel") cardsPanel.getLayout().show(cardsPanel, "search window") @@ -692,17 +692,17 @@ class MainFrameView { def cardsPanel = builder.getVariable("cards-panel") cardsPanel.getLayout().show(cardsPanel, "uploads window") } - + def showMonitorWindow = { def cardsPanel = builder.getVariable("cards-panel") cardsPanel.getLayout().show(cardsPanel,"monitor window") } - + def showTrustWindow = { def cardsPanel = builder.getVariable("cards-panel") cardsPanel.getLayout().show(cardsPanel,"trust window") } - + def shareFiles = { def chooser = new JFileChooser() chooser.setFileHidingEnabled(false) @@ -713,7 +713,7 @@ class MainFrameView { model.core.eventBus.publish(new FileSharedEvent(file : chooser.getSelectedFile())) } } - + def watchDirectories = { def chooser = new JFileChooser() chooser.setFileHidingEnabled(false) @@ -729,7 +729,7 @@ class MainFrameView { model.core.eventBus.publish(new FileSharedEvent(file : f)) } } - + String getSelectedWatchedDirectory() { def watchedTable = builder.getVariable("watched-directories-table") int selectedRow = watchedTable.getSelectedRow() @@ -739,7 +739,7 @@ class MainFrameView { selectedRow = watchedTable.rowSorter.convertRowIndexToModel(selectedRow) model.watched[selectedRow] } - + int getSelectedTrustTablesRow(String tableName) { def table = builder.getVariable(tableName) int selectedRow = table.getSelectedRow() diff --git a/gui/griffon-app/views/com/muwire/gui/MuWireStatusView.groovy b/gui/griffon-app/views/com/muwire/gui/MuWireStatusView.groovy index 5879ab04..c6a10da1 100644 --- a/gui/griffon-app/views/com/muwire/gui/MuWireStatusView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/MuWireStatusView.groovy @@ -27,12 +27,12 @@ class MuWireStatusView { def dialog def panel def buttonsPanel - + void initUI() { mainFrame = application.windowManager.findWindow("main-frame") - + dialog = new JDialog(mainFrame, "MuWire Status", true) - + panel = builder.panel { gridBagLayout() label(text : "Incoming connections", constraints : gbc(gridx:0, gridy:0)) @@ -52,13 +52,13 @@ class MuWireStatusView { button(text : "Close", constraints : gbc(gridx : 1, gridy :0), closeAction) } } - + void mvcGroupInit(Map args) { JPanel statusPanel = new JPanel() statusPanel.setLayout(new BorderLayout()) statusPanel.add(panel, BorderLayout.CENTER) statusPanel.add(buttonsPanel, BorderLayout.SOUTH) - + dialog.getContentPane().add(statusPanel) dialog.pack() dialog.setLocationRelativeTo(mainFrame) diff --git a/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy b/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy index 46c3942b..abd58eee 100644 --- a/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/OptionsView.groovy @@ -30,12 +30,12 @@ class OptionsView { def u def bandwidth def trust - + def retryField def updateField def autoDownloadUpdateCheckbox def shareDownloadedCheckbox - + def inboundLengthField def inboundQuantityField def outboundLengthField @@ -50,18 +50,18 @@ class OptionsView { def clearFinishedDownloadsCheckbox def excludeLocalResultCheckbox def showSearchHashesCheckbox - + def inBwField def outBwField - + def allowUntrustedCheckbox def allowTrustListsCheckbox def trustListIntervalField - - def buttonsPanel - + + def buttonsPanel + def mainFrame - + void initUI() { mainFrame = application.windowManager.findWindow("main-frame") d = new JDialog(mainFrame, "Options", true) @@ -71,21 +71,21 @@ class OptionsView { label(text : "Retry failed downloads every", constraints : gbc(gridx: 0, gridy: 0)) retryField = textField(text : bind { model.downloadRetryInterval }, columns : 2, constraints : gbc(gridx: 1, gridy: 0)) label(text : "minutes", constraints : gbc(gridx : 2, gridy: 0)) - + label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1)) updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1)) label(text : "hours", constraints : gbc(gridx: 2, gridy : 1)) - + label(text : "Download updates automatically", constraints: gbc(gridx :0, gridy : 2)) autoDownloadUpdateCheckbox = checkBox(selected : bind {model.autoDownloadUpdate}, constraints : gbc(gridx:1, gridy : 2)) label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3)) shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3)) - + label(text : "Save downloaded files to:", constraints: gbc(gridx:0, gridy:4)) button(text : "Choose", constraints : gbc(gridx : 1, gridy:4), downloadLocationAction) label(text : bind {model.downloadLocation}, constraints: gbc(gridx:0, gridy:5, gridwidth:2)) - + } i = builder.panel { gridBagLayout() @@ -98,7 +98,7 @@ class OptionsView { outboundLengthField = textField(text : bind {model.outboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:3)) label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4)) outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:4)) - + Core core = application.context.get("core") if (core.router != null) { label(text : "TCP Port", constraints : gbc(gridx :0, gridy: 5)) @@ -106,7 +106,7 @@ class OptionsView { label(text : "UDP Port", constraints : gbc(gridx :0, gridy: 6)) i2pUDPPortField = textField(text : bind {model.i2pUDPPort}, columns : 4, constraints : gbc(gridx:1, gridy:6)) } - + } u = builder.panel { gridBagLayout() @@ -144,15 +144,15 @@ class OptionsView { trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:2)) label(text : "hours", constraints : gbc(gridx: 2, gridy:2)) } - - + + buttonsPanel = builder.panel { gridBagLayout() button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction) button(text : "Cancel", constraints : gbc(gridx : 2, gridy: 2), cancelAction) } } - + void mvcGroupInit(Map args) { def tabbedPane = new JTabbedPane() tabbedPane.addTab("MuWire", p) @@ -163,12 +163,12 @@ class OptionsView { tabbedPane.addTab("Bandwidth", bandwidth) } tabbedPane.addTab("Trust", trust) - + JPanel panel = new JPanel() panel.setLayout(new BorderLayout()) panel.add(tabbedPane, BorderLayout.CENTER) panel.add(buttonsPanel, BorderLayout.SOUTH) - + d.getContentPane().add(panel) d.pack() d.setLocationRelativeTo(mainFrame) diff --git a/gui/griffon-app/views/com/muwire/gui/SearchTabView.groovy b/gui/griffon-app/views/com/muwire/gui/SearchTabView.groovy index deb94685..4bbbf5c2 100644 --- a/gui/griffon-app/views/com/muwire/gui/SearchTabView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/SearchTabView.groovy @@ -34,12 +34,12 @@ class SearchTabView { @MVCMember @Nonnull SearchTabModel model - def pane + def pane def parent def searchTerms def resultsTable def lastSortEvent - + void initUI() { builder.with { def resultsTable @@ -57,13 +57,13 @@ class SearchTabView { } } } - + this.pane = pane this.pane.putClientProperty("mvc-group", mvcGroup) this.pane.putClientProperty("results-table",resultsTable) this.resultsTable = resultsTable - + def selectionModel = resultsTable.getSelectionModel() selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) selectionModel.addListSelectionListener( { @@ -77,16 +77,16 @@ class SearchTabView { }) } } - + void mvcGroupInit(Map args) { searchTerms = args["search-terms"] parent = mvcGroup.parentGroup.view.builder.getVariable("result-tabs") parent.addTab(searchTerms, pane) int index = parent.indexOfComponent(pane) parent.setSelectedIndex(index) - + def tabPanel - builder.with { + builder.with { tabPanel = panel { borderLayout() panel { @@ -96,22 +96,22 @@ class SearchTabView { actionPerformed : closeTab ) } } - + parent.setTabComponentAt(index, tabPanel) - + def centerRenderer = new DefaultTableCellRenderer() centerRenderer.setHorizontalAlignment(JLabel.CENTER) resultsTable.columnModel.getColumn(1).setCellRenderer(centerRenderer) resultsTable.setDefaultRenderer(Integer.class,centerRenderer) resultsTable.columnModel.getColumn(4).setCellRenderer(centerRenderer) - + resultsTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer()) - - + + resultsTable.rowSorter.addRowSorterListener({ evt -> lastSortEvent = evt}) resultsTable.rowSorter.setSortsOnUpdates(true) - - + + resultsTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -127,7 +127,7 @@ class SearchTabView { } }) } - + def closeTab = { int index = parent.indexOfTab(searchTerms) parent.removeTabAt(index) @@ -135,7 +135,7 @@ class SearchTabView { mvcGroup.parentGroup.model.downloadActionEnabled = false mvcGroup.destroy() } - + def showPopupMenu(MouseEvent e) { JPopupMenu menu = new JPopupMenu() if (mvcGroup.parentGroup.model.downloadActionEnabled) { @@ -148,7 +148,7 @@ class SearchTabView { menu.add(copyHashToClipboard) menu.show(e.getComponent(), e.getX(), e.getY()) } - + def copyHashToClipboard() { int selected = resultsTable.getSelectedRow() if (selected < 0) diff --git a/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy b/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy index 443ae5cc..da500796 100644 --- a/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy @@ -25,8 +25,8 @@ class TrustListView { def mainFrame def mainPanel - def sortEvents = [:] - + def sortEvents = [:] + void initUI() { mainFrame = application.windowManager.findWindow("main-frame") dialog = new JDialog(mainFrame, model.trustList.persona.getHumanReadableName(), true) @@ -78,19 +78,19 @@ class TrustListView { } } } - + void mvcGroupInit(Map args) { - + def trustedTable = builder.getVariable("trusted-table") trustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["trusted-table"] = evt}) trustedTable.rowSorter.setSortsOnUpdates(true) trustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION) - + def distrustedTable = builder.getVariable("distrusted-table") distrustedTable.rowSorter.addRowSorterListener({evt -> sortEvents["distrusted-table"] = evt}) distrustedTable.rowSorter.setSortsOnUpdates(true) distrustedTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION) - + dialog.getContentPane().add(mainPanel) dialog.pack() dialog.setLocationRelativeTo(mainFrame) @@ -102,7 +102,7 @@ class TrustListView { }) dialog.show() } - + int getSelectedRow(String tableName) { def table = builder.getVariable(tableName) int selectedRow = table.getSelectedRow() @@ -112,7 +112,7 @@ class TrustListView { selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow) selectedRow } - + void fireUpdate(String tableName) { def table = builder.getVariable(tableName) table.model.fireTableDataChanged() diff --git a/gui/src/main/groovy/com/muwire/gui/Launcher.groovy b/gui/src/main/groovy/com/muwire/gui/Launcher.groovy index 4e3630d7..6661434f 100644 --- a/gui/src/main/groovy/com/muwire/gui/Launcher.groovy +++ b/gui/src/main/groovy/com/muwire/gui/Launcher.groovy @@ -3,7 +3,7 @@ package com.muwire.gui import griffon.swing.SwingGriffonApplication class Launcher { - + public static void main(String[] args) { SwingGriffonApplication.main(args) } diff --git a/gui/src/main/groovy/com/muwire/gui/UISettings.groovy b/gui/src/main/groovy/com/muwire/gui/UISettings.groovy index 76b2e3fa..f4bc0fab 100644 --- a/gui/src/main/groovy/com/muwire/gui/UISettings.groovy +++ b/gui/src/main/groovy/com/muwire/gui/UISettings.groovy @@ -1,7 +1,7 @@ package com.muwire.gui class UISettings { - + String lnf boolean showMonitor String font @@ -9,7 +9,7 @@ class UISettings { boolean clearFinishedDownloads boolean excludeLocalResult boolean showSearchHashes - + UISettings(Properties props) { lnf = props.getProperty("lnf", "system") showMonitor = Boolean.parseBoolean(props.getProperty("showMonitor", "false")) @@ -19,7 +19,7 @@ class UISettings { excludeLocalResult = Boolean.parseBoolean(props.getProperty("excludeLocalResult","true")) showSearchHashes = Boolean.parseBoolean(props.getProperty("showSearchHashes","true")) } - + void write(OutputStream out) throws IOException { Properties props = new Properties() props.setProperty("lnf", lnf) @@ -30,8 +30,8 @@ class UISettings { props.setProperty("showSearchHashes", String.valueOf(showSearchHashes)) if (font != null) props.setProperty("font", font) - - + + props.store(out, "UI Properties") } } diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/Crawler.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/Crawler.groovy index c8741f9b..7749be26 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/Crawler.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/Crawler.groovy @@ -12,30 +12,30 @@ class Crawler { final def pinger final def hostPool final int parallel - + final Map inFlight = new HashMap<>() - + UUID currentUUID - + Crawler(pinger, hostPool, int parallel) { this.pinger = pinger this.hostPool = hostPool this.parallel = parallel } - + synchronized def handleCrawlerPong(pong, Destination source) { if (!inFlight.containsKey(source)) { log.info("response from host that hasn't been crawled") return } Host host = inFlight.remove(source) - + if (pong.uuid == null || pong.leafSlots == null || pong.peerSlots == null || pong.peers == null) { hostPool.fail(host) log.info("invalid crawler pong") return } - + UUID uuid; try { uuid = UUID.fromString(pong.uuid) @@ -43,16 +43,16 @@ class Crawler { hostPool.fail(host) return } - + if (!uuid.equals(currentUUID)) { log.info("uuid mismatch") hostPool.fail(host) return } - + host.leafSlots = parseBoolean(pong.leafSlots) host.peerSlots = parseBoolean(pong.peerSlots) - + def peers try { peers = pong.peers.stream().map({b64 -> new Destination(b64)}).collect(Collectors.toSet()) @@ -69,18 +69,18 @@ class Crawler { } hostPool.verify(host) } - + private static boolean parseBoolean(value) { return Boolean.parseBoolean(value.toString()) } - + synchronized def startCrawl() { if (!inFlight.isEmpty()) { inFlight.values().each { hostPool.fail(it) } inFlight.clear() } currentUUID = UUID.randomUUID() - hostPool.getUnverified(parallel).each { + hostPool.getUnverified(parallel).each { inFlight.put(it.destination, it) pinger.ping(it, currentUUID) } diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy index 641154a1..b04b3b4a 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/Host.groovy @@ -7,11 +7,11 @@ class Host { boolean leafSlots boolean peerSlots int verificationFailures - + public int hashCode() { return destination.hashCode() } - + public boolean equals(other) { return destination.equals(other.destination) } diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy index 30690045..d2fe7d3a 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/HostCache.groovy @@ -23,23 +23,23 @@ public class HostCache { println "This will likely not run on windows" System.exit(1) } - + def home = System.getProperty("user.home") + "/.MuWireHostCache" home = new File(home) if (home.exists() && !home.isDirectory()) { println "${home} exists but not a directory? Delete it or make it a directory" System.exit(1) } - + if (!home.exists()) { home.mkdir() } - + def keyfile = new File(home, "key.dat") - + def i2pClientFactory = new I2PClientFactory() def i2pClient = i2pClientFactory.createClient() - + def myDest def session if (!keyfile.exists()) { @@ -49,39 +49,39 @@ public class HostCache { println "No key.dat file was found, so creating a new destination." println "This is the destination you want to give out for your new HostCache" println myDest.toBase64() - } - + } + def props = System.getProperties().clone() props.putAt("inbound.nickname", "MuWire HostCache") session = i2pClient.createSession(new FileInputStream(keyfile), props) myDest = session.getMyDestination() - + // initialize hostpool and crawler HostPool hostPool = new HostPool(3, 60 * 60 * 1000) Pinger pinger = new Pinger(session) Crawler crawler = new Crawler(pinger, hostPool, 5) - + Timer timer = new Timer("timer", true) timer.schedule({hostPool.age()} as TimerTask, 1000,1000) timer.schedule({crawler.startCrawl()} as TimerTask, 10000, 10000) File verified = new File("verified.json") File unverified = new File("unverified.json") timer.schedule({hostPool.serialize(verified, unverified)} as TimerTask, 10000, 60 * 60 * 1000) - - session.addMuxedSessionListener(new Listener(hostPool: hostPool, toReturn: 2, crawler: crawler), + + session.addMuxedSessionListener(new Listener(hostPool: hostPool, toReturn: 2, crawler: crawler), I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY) session.connect() log.info("connected, going to sleep") Thread.sleep(Integer.MAX_VALUE) - + } - + static class Listener implements I2PSessionMuxedListener { final def json = new JsonSlurper() def hostPool int toReturn def crawler - + void reportAbuse(I2PSession sesison, int severity) {} void disconnected(I2PSession session) { log.severe("session disconnected, exiting") @@ -103,7 +103,7 @@ public class HostCache { dissector.loadI2PDatagram(payload) def sender = dissector.getSender() def b32 = sender.toBase32() - + payload = dissector.getPayload() payload = json.parse(payload) if (payload.type == null) { @@ -112,7 +112,7 @@ public class HostCache { } switch(payload.type) { case "Ping" : - log.info("ping from $b32") + log.info("ping from $b32") if (payload.leaf == null) { log.warning("ping didn't specify if leaf from $b32") return @@ -131,16 +131,16 @@ public class HostCache { } } catch (Exception dfe) { log.log(Level.WARNING,"invalid datagram", dfe) - } + } } void messageAvailable(I2PSession session, int msgId, long size) { } - + def respond(session, destination, ping) { def pongs = hostPool.getVerified(toReturn, ping.leaf) pongs = pongs.stream().map({ x -> x.destination.toBase64() }).collect(Collectors.toList()) - + def pong = [type:"Pong", version: 1, pongs: pongs] pong = JsonOutput.toJson(pong) def maker = new I2PDatagramMaker(session) diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy index 3deba328..754d87aa 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/HostPool.groovy @@ -5,19 +5,19 @@ import java.util.stream.Collectors import groovy.json.JsonOutput class HostPool { - + final def maxFailures final def maxAge - + def verified = new HashMap() def unverified = new HashMap() - + HostPool() {} HostPool(maxFailures, maxAge) { this.maxAge = maxAge this.maxFailures = maxFailures } - + synchronized def getVerified(int max, boolean leaf) { if (verified.isEmpty()) { return Collections.emptyList() @@ -27,13 +27,13 @@ class HostPool { return asList[0..Math.min(max, asList.size()) -1] } - + synchronized def addUnverified(host) { if (!verified.containsKey(host.destination)) { unverified.put(host.destination, host) } } - + synchronized def getUnverified(int max) { if (unverified.isEmpty()) { return Collections.emptyList() @@ -42,7 +42,7 @@ class HostPool { Collections.shuffle(asList) return asList[0..(Math.min(max, asList.size())-1)] } - + synchronized def verify(host) { if (!unverified.remove(host.destination)) throw new IllegalArgumentException() @@ -50,13 +50,13 @@ class HostPool { host.verificationFailures = 0 verified.put(host.destination, host) } - + synchronized def fail(host) { if (!unverified.containsKey(host.destination)) return host.verificationFailures++ } - + synchronized def age() { final long now = System.currentTimeMillis() for (Iterator iter = verified.keySet().iterator(); iter.hasNext();) { @@ -67,7 +67,7 @@ class HostPool { unverified.put(host.destination, host) } } - + for (Iterator iter = unverified.keySet().iterator(); iter.hasNext();) { def destination = iter.next() def host = unverified.get(destination) @@ -76,16 +76,16 @@ class HostPool { } } } - + synchronized void serialize(File verifiedFile, File unverifiedFile) { write(verifiedFile, verified.values()) write(unverifiedFile, unverified.values()) } - + private void write(File target, Collection hosts) { JsonOutput jsonOutput = new JsonOutput() target.withPrintWriter { writer -> - hosts.each { + hosts.each { def json = [:] json.destination = it.destination.toBase64() json.verifyTime = it.verifyTime @@ -94,7 +94,7 @@ class HostPool { json.verificationFailures = it.verificationFailures def str = jsonOutput.toJson(json) writer.println(str) - } + } } } } diff --git a/host-cache/src/main/groovy/com/muwire/hostcache/Pinger.groovy b/host-cache/src/main/groovy/com/muwire/hostcache/Pinger.groovy index b36d9865..8c03def1 100644 --- a/host-cache/src/main/groovy/com/muwire/hostcache/Pinger.groovy +++ b/host-cache/src/main/groovy/com/muwire/hostcache/Pinger.groovy @@ -5,12 +5,12 @@ import net.i2p.client.I2PSession import net.i2p.client.datagram.I2PDatagramMaker class Pinger { - + final def session Pinger(session) { this.session = session } - + def ping(host, uuid) { def maker = new I2PDatagramMaker(session) def payload = new HashMap() diff --git a/host-cache/src/test/groovy/com/muwire/hostcache/CrawlerTest.groovy b/host-cache/src/test/groovy/com/muwire/hostcache/CrawlerTest.groovy index 645ea404..1a6f0681 100644 --- a/host-cache/src/test/groovy/com/muwire/hostcache/CrawlerTest.groovy +++ b/host-cache/src/test/groovy/com/muwire/hostcache/CrawlerTest.groovy @@ -12,45 +12,45 @@ class CrawlerTest { def pingerMock def pinger - + def hostPoolMock def hostPool - + def crawler - + Destinations destinations = new Destinations() final Host host = new Host(destination: new Destination()) final Host host1 = new Host(destination: destinations.dest1) final Host host2 = new Host(destination: destinations.dest2) - + final int parallel = 5 - + @Before void before() { pingerMock = new MockFor(Pinger) hostPoolMock = new MockFor(HostPool) } - + @After void after() { hostPoolMock.verify hostPool pingerMock.verify pinger } - + private def initCrawler() { pinger = pingerMock.proxyInstance() hostPool = hostPoolMock.proxyInstance() crawler = new Crawler(pinger, hostPool, parallel) - + } - + @Test void testBadJson() { initCrawler() def unpingedHost = new Host(destination : new Destination()) crawler.handleCrawlerPong(null, new Destination()) } - + @Test void testStartCrawl() { hostPoolMock.demand.getUnverified { n -> @@ -58,12 +58,12 @@ class CrawlerTest { [host] } pingerMock.demand.ping { h,uuid -> assert h == host } - + initCrawler() crawler.startCrawl() - + } - + @Test void testFailsUnanswered() { hostPoolMock.demand.getUnverified {n -> [host]} @@ -71,40 +71,40 @@ class CrawlerTest { hostPoolMock.demand.getUnverified {n -> [:]} pingerMock.demand.ping {h,uuid -> } initCrawler() - + crawler.startCrawl() crawler.startCrawl() } - + @Test void testVerifiesAnswered() { def currentUUID hostPoolMock.demand.getUnverified { n -> [host1] } hostPoolMock.demand.verify { h -> assert h == host1 } pingerMock.demand.ping { h, uuid -> currentUUID = uuid } - + initCrawler() - + crawler.startCrawl() - + def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [] ] crawler.handleCrawlerPong(pong, host1.destination) } - + @Test void testWrongSourceIgnored() { def currentUUID hostPoolMock.demand.getUnverified { n -> [host1] } pingerMock.demand.ping { h, uuid -> currentUUID = uuid } - + initCrawler() - + crawler.startCrawl() - + def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [] ] crawler.handleCrawlerPong(pong, host2.destination) } - + @Test void testHost1CarriesHost2() { def currentUUID @@ -112,26 +112,26 @@ class CrawlerTest { hostPoolMock.demand.addUnverified { h -> assert h == host2 } hostPoolMock.demand.verify { h -> assert h == host1 } pingerMock.demand.ping { h, uuid -> currentUUID = uuid } - + initCrawler() - + crawler.startCrawl() - + def pong = [uuid : currentUUID.toString(), leafSlots : "false", peerSlots: "false", peers: [destinations.dest2.toBase64()] ] crawler.handleCrawlerPong(pong, host1.destination) } - + @Test void testWrongUUID() { def currentUUID hostPoolMock.demand.getUnverified { n -> [host1] } hostPoolMock.demand.fail { h -> assert h == host1 } pingerMock.demand.ping { h, uuid -> currentUUID = uuid } - + initCrawler() - + crawler.startCrawl() - + def pong = [uuid : UUID.randomUUID().toString(), leafSlots : "false", peerSlots: "false", peers: [] ] crawler.handleCrawlerPong(pong, host1.destination) } diff --git a/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy b/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy index 7ffc21eb..f53d8252 100644 --- a/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy +++ b/host-cache/src/test/groovy/com/muwire/hostcache/HostPoolTest.groovy @@ -3,65 +3,65 @@ package com.muwire.hostcache import org.junit.* class HostPoolTest { - + def hp Host freeLeafs, freePeers, freeBoth, freeNone - + @Before void before() { hp = new HostPool(3, 10) - + freeLeafs = new Host() freeLeafs.destination = "freeLeafs" freeLeafs.leafSlots = true - + freePeers = new Host() freePeers.destination = "freePeers" freePeers.peerSlots = true - + freeBoth = new Host() freeBoth.destination = "freeBoth" freeBoth.leafSlots = true freeBoth.peerSlots = true - + freeNone = new Host() freeNone.destination = "freeNone" } - + def addAllUnverified() { hp.addUnverified(freeLeafs) hp.addUnverified(freePeers) hp.addUnverified(freeBoth) hp.addUnverified(freeNone) } - + def verifyAll() { hp.verify(freeBoth) hp.verify(freeLeafs) hp.verify(freePeers) hp.verify(freeNone) } - + @Test - void testNoVerified() { + void testNoVerified() { assert hp.getVerified(1, true).isEmpty() assert hp.getVerified(1, false).isEmpty() - addAllUnverified() - + addAllUnverified() + assert hp.getVerified(1, true).isEmpty() assert hp.getVerified(1, false).isEmpty() } - + @Test void testOneVerified() { addAllUnverified() hp.verify(freeBoth) - + assert hp.getVerified(10, true).contains(freeBoth) assert hp.getVerified(10, false).contains(freeBoth) } - + @Test void testFilterByType() { addAllUnverified() @@ -70,13 +70,13 @@ class HostPoolTest { assert verifiedLeafSlots.size() == 2 assert verifiedLeafSlots.contains(freeBoth) assert verifiedLeafSlots.contains(freeLeafs) - + def verifiedPeerSlots = hp.getVerified(10, false) assert verifiedPeerSlots.size() == 2 assert verifiedPeerSlots.contains(freeBoth) assert verifiedPeerSlots.contains(freePeers) } - + @Test void getFewerThanAvailable() { addAllUnverified() @@ -85,88 +85,88 @@ class HostPoolTest { assert verifiedLeafSlots.size() == 1 assert verifiedLeafSlots.contains(freeBoth) || verifiedLeafSlots.contains(freeLeafs) } - + @Test void getUnverified() { assert hp.getUnverified().isEmpty() - + addAllUnverified() - + def allUnverified = hp.getUnverified(10) assert allUnverified.size() == 4 assert allUnverified.contains(freeLeafs) assert allUnverified.contains(freePeers) assert allUnverified.contains(freeBoth) assert allUnverified.contains(freeNone) - + def twoUnverified = hp.getUnverified(2) assert twoUnverified.size() == 2 - + def oneUnverified = hp.getUnverified(1) assert oneUnverified.size() == 1 assert oneUnverified.contains(freeLeafs) || oneUnverified.contains(freePeers) || oneUnverified.contains(freeBoth) || oneUnverified.contains(freeNone) } - + @Test void testFailHost() { hp.addUnverified(freeBoth) assert hp.getUnverified(10).size() == 1 - + hp.fail(freeBoth) hp.age() assert hp.getUnverified(10).size() == 1 - + hp.fail(freeBoth) hp.age() assert hp.getUnverified(10).size() == 1 - + hp.fail(freeBoth) hp.age() assert hp.getUnverified(10).isEmpty() assert hp.getVerified(10, true).isEmpty() } - + @Test void verifyResetsFailures() { hp.addUnverified(freeBoth) assert hp.getUnverified(10).size() == 1 - - hp.fail(freeBoth) - hp.age() - assert hp.getUnverified(10).size() == 1 - + hp.fail(freeBoth) hp.age() assert hp.getUnverified(10).size() == 1 - hp.verify(freeBoth) + hp.fail(freeBoth) + hp.age() + assert hp.getUnverified(10).size() == 1 + + hp.verify(freeBoth) hp.age() assert hp.getUnverified(10).isEmpty() assert hp.getVerified(10, true).size() == 1 } - + @Test void ageHost() { hp.addUnverified(freeBoth) hp.verify(freeBoth) - + hp.age() assert hp.getVerified(10,true).size() == 1 assert hp.getUnverified(10).isEmpty() - + Thread.sleep(20) hp.age() assert hp.getVerified(10,true).isEmpty() assert hp.getUnverified(10).size() == 1 } - + @Test void doNotAddIfVerified() { hp.addUnverified(freeBoth) hp.verify(freeBoth) assert hp.getUnverified(1).isEmpty() - + hp.addUnverified(freeBoth) assert hp.getUnverified(1).isEmpty() } diff --git a/pinger/src/main/groovy/com/muwire/pinger/Pinger.groovy b/pinger/src/main/groovy/com/muwire/pinger/Pinger.groovy index f6254b4e..eeb24bb5 100644 --- a/pinger/src/main/groovy/com/muwire/pinger/Pinger.groovy +++ b/pinger/src/main/groovy/com/muwire/pinger/Pinger.groovy @@ -18,38 +18,38 @@ public class Pinger { println "Pass b64 destination as argument 1 and file with contents to send as argument 2" System.exit(1) } - + def target = new Destination(args[0]) def payload = (new File(args[1])).getBytes() - + def i2pClientFactory = new I2PClientFactory() def i2pClient = i2pClientFactory.createClient() - + def props = System.getProperties().clone() props.putAt("outbound.nickname", "MuWire Pinger") - + def baos = new ByteArrayOutputStream() def myDest = i2pClient.createDestination(baos) def bais = new ByteArrayInputStream(baos.toByteArray()) - + def session = i2pClient.createSession(bais, props) - + session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY) session.connect() - + def maker = new I2PDatagramMaker(session) payload = maker.makeI2PDatagram(payload) session.sendMessage(target, payload, I2PSession.PROTO_DATAGRAM, 0, 0) println "Sent message, going to sleep" Thread.sleep(Integer.MAX_VALUE) } - + static class Listener implements I2PSessionMuxedListener { @Override public void messageAvailable(I2PSession session, int msgId, long size) { - + } @Override @@ -80,6 +80,6 @@ public class Pinger { public void errorOccurred(I2PSession session, String message, Throwable error) { println "Error $message $error" } - + } } diff --git a/update-server/src/main/groovy/com/muwire/update/UpdateServer.groovy b/update-server/src/main/groovy/com/muwire/update/UpdateServer.groovy index 28718d24..4293b0c2 100644 --- a/update-server/src/main/groovy/com/muwire/update/UpdateServer.groovy +++ b/update-server/src/main/groovy/com/muwire/update/UpdateServer.groovy @@ -18,12 +18,12 @@ class UpdateServer { home = new File(home) if (!home.exists()) home.mkdirs() - + def keyFile = new File(home, "key.dat") - + def i2pClientFactory = new I2PClientFactory() def i2pClient = i2pClientFactory.createClient() - + def myDest def session if (!keyFile.exists()) { @@ -34,27 +34,27 @@ class UpdateServer { log.info "This is the destination you want to give out for your new UpdateServer" log.info myDest.toBase64() } - + def update = new File(home, "update.json") if (!update.exists()) { log.warning("update file doesn't exist, exiting") System.exit(1) } - + def props = System.getProperties().clone() props.putAt("inbound.nickname", "MuWire UpdateServer") session = i2pClient.createSession(new FileInputStream(keyFile), props) myDest = session.getMyDestination() - + session.addMuxedSessionListener(new Listener(update), I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY) session.connect() log.info("Connected, going to sleep") Thread.sleep(Integer.MAX_VALUE) - + } - + static class Listener implements I2PSessionMuxedListener { - + private final File json private final def slurper = new JsonSlurper() Listener(File json) { @@ -71,7 +71,7 @@ class UpdateServer { log.warning("received uknown protocol $proto") return } - + def payload = session.receiveMessage(msgId) def dissector = new I2PDatagramDissector() try { @@ -79,7 +79,7 @@ class UpdateServer { def sender = dissector.getSender() payload = slurper.parse(dissector.getPayload()) log.info("Got an update ping from "+sender.toBase32() + " reported version "+payload?.myVersion) - + def maker = new I2PDatagramMaker(session) def response = maker.makeI2PDatagram(json.bytes) session.sendMessage(sender, response, I2PSession.PROTO_DATAGRAM, 0, 2) @@ -102,6 +102,6 @@ class UpdateServer { public void errorOccurred(I2PSession session, String message, Throwable error) { log.log(Level.SEVERE, message, error) } - + } }