Compare commits

...

41 Commits

Author SHA1 Message Date
Zlatin Balevsky
a7aa3008c0 bandwidth settings 2019-06-27 00:42:27 +01:00
Zlatin Balevsky
485325e824 embedded router except for logs 2019-06-26 23:25:22 +01:00
Zlatin Balevsky
0df2a0e039 start work on embedded router 2019-06-26 22:39:25 +01:00
Zlatin Balevsky
fb7b4466c2 update readme 2019-06-26 22:05:04 +01:00
Zlatin Balevsky
53105245f4 Release 0.4.0 2019-06-26 21:59:28 +01:00
Zlatin Balevsky
b68eab91e0 Release 0.3.10 2019-06-25 22:39:43 +01:00
Zlatin Balevsky
f72cf91462 wait for files to be loaded before sharing watched directories 2019-06-25 22:24:32 +01:00
Zlatin Balevsky
a655c4ef50 add toString 2019-06-25 22:24:15 +01:00
Zlatin Balevsky
5d46e9b796 switch 4_ to INFO 2019-06-25 21:50:15 +01:00
Zlatin Balevsky
642e6e67b3 wait for all files loaded before watching dirs 2019-06-25 21:43:07 +01:00
Zlatin Balevsky
2b6b86f903 show how many pieces the remote side already has 2019-06-25 17:44:05 +01:00
Zlatin Balevsky
f2706a4426 clarify upload column 2019-06-25 17:24:42 +01:00
Zlatin Balevsky
1af75413aa update for brackets 2019-06-25 16:27:02 +01:00
Zlatin Balevsky
adc4077b1a filter asterix 2019-06-25 15:54:30 +01:00
Zlatin Balevsky
01f4e2453b limit search length to 128 characters 2019-06-25 15:53:53 +01:00
Zlatin Balevsky
61267374dd move button around 2019-06-25 08:10:20 +01:00
Zlatin Balevsky
970f814685 make mesh expiration configurable 2019-06-25 08:04:57 +01:00
Zlatin Balevsky
4fd9fc1991 add option to change download location 2019-06-25 07:59:30 +01:00
Zlatin Balevsky
26207ffd1b add constructor 2019-06-25 07:53:24 +01:00
Zlatin Balevsky
2614cfbe5f make host clear interval configurable 2019-06-25 07:41:20 +01:00
Zlatin Balevsky
f11d461ec0 make download sequential ratio a property 2019-06-25 07:34:26 +01:00
Zlatin Balevsky
b2eb2d2755 show hidden files in file choosers 2019-06-24 23:09:20 +01:00
Zlatin Balevsky
ea46a54f19 enable AA by default 2019-06-24 22:55:26 +01:00
Zlatin Balevsky
627add45ad remove griffon icons 2019-06-24 22:51:43 +01:00
Zlatin Balevsky
d364855459 logo 2019-06-24 22:13:03 +01:00
Zlatin Balevsky
14ee35e77a Release 0.3.9 2019-06-24 18:39:59 +01:00
Zlatin Balevsky
8773eb4ee0 fix piece size calculation 2019-06-24 18:29:00 +01:00
Zlatin Balevsky
51425bbfd9 Release 0.3.8 2019-06-24 07:38:39 +01:00
Zlatin Balevsky
6a4879bc0b always save pieces 2019-06-24 07:29:49 +01:00
Zlatin Balevsky
e7fe56439b persist X-Have, fix flickering bug 2019-06-24 07:20:53 +01:00
Zlatin Balevsky
2886feab4a do not modify the set of available pieces 2019-06-23 17:08:07 +01:00
Zlatin Balevsky
fb91194026 even noisier log 2019-06-23 16:39:38 +01:00
Zlatin Balevsky
4527478b0d even noisier 4_ 2019-06-23 12:42:44 +01:00
Zlatin Balevsky
b0062f146e log roots of download exceptions 2019-06-23 12:10:19 +01:00
Zlatin Balevsky
bf16561170 Release 0.3.7 2019-06-23 11:25:19 +01:00
Zlatin Balevsky
3b23dc29c4 if all sources are expired forget mesh 2019-06-23 11:21:39 +01:00
Zlatin Balevsky
c0645b670e no split on list 2019-06-23 10:50:19 +01:00
Zlatin Balevsky
30613fe530 update todo 2019-06-23 09:56:51 +01:00
Zlatin Balevsky
e7822f6edc expire sources, fix compilation 2019-06-23 09:43:56 +01:00
Zlatin Balevsky
7e5c9ba115 actually save 2019-06-23 09:41:20 +01:00
Zlatin Balevsky
647fa3a481 persist download mesh 2019-06-23 09:38:42 +01:00
47 changed files with 289 additions and 51 deletions

View File

@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer. It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
The current stable release - 0.2.5 is avaiable for download at http://muwire.com. You can find technical documentation in the "doc" folder. The current stable release - 0.4.0 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
### Building ### Building

View File

@@ -38,4 +38,3 @@ To enable parsing of metadata from known file types and the user editing it or a
* Download file sequentially * Download file sequentially
* Unsharing of files * Unsharing of files
* Multiple-selection download, Ctrl-A * Multiple-selection download, Ctrl-A
* Pause/Resume downloads

View File

@@ -35,7 +35,7 @@ class Cli {
Core core Core core
try { try {
core = new Core(props, home, "0.3.6") core = new Core(props, home, "0.4.0")
} catch (Exception bad) { } catch (Exception bad) {
bad.printStackTrace(System.out) bad.printStackTrace(System.out)
println "Failed to initialize core, exiting" println "Failed to initialize core, exiting"

View File

@@ -53,7 +53,7 @@ class CliDownloader {
Core core Core core
try { try {
core = new Core(props, home, "0.3.6") core = new Core(props, home, "0.4.0")
} catch (Exception bad) { } catch (Exception bad) {
bad.printStackTrace(System.out) bad.printStackTrace(System.out)
println "Failed to initialize core, exiting" println "Failed to initialize core, exiting"

View File

@@ -2,6 +2,7 @@ apply plugin : 'application'
mainClassName = 'com.muwire.core.Core' mainClassName = 'com.muwire.core.Core'
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties'] applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
dependencies { dependencies {
compile 'net.i2p:router:0.9.40'
compile 'net.i2p.client:mstreaming:0.9.40' compile 'net.i2p.client:mstreaming:0.9.40'
compile 'net.i2p.client:streaming:0.9.40' compile 'net.i2p.client:streaming:0.9.40'

View File

@@ -9,7 +9,5 @@ class Constants {
public static final int MAX_HEADER_SIZE = 0x1 << 14 public static final int MAX_HEADER_SIZE = 0x1 << 14
public static final int MAX_HEADERS = 16 public static final int MAX_HEADERS = 16
public static final float DOWNLOAD_SEQUENTIAL_RATIO = 0.8f public static final String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}]"
public static final String SPLIT_PATTERN = "[\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|]"
} }

View File

@@ -27,6 +27,7 @@ import com.muwire.core.files.FileSharedEvent
import com.muwire.core.files.FileUnsharedEvent import com.muwire.core.files.FileUnsharedEvent
import com.muwire.core.files.HasherService import com.muwire.core.files.HasherService
import com.muwire.core.files.PersisterService import com.muwire.core.files.PersisterService
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.DirectoryWatcher import com.muwire.core.files.DirectoryWatcher
import com.muwire.core.hostcache.CacheClient import com.muwire.core.hostcache.CacheClient
import com.muwire.core.hostcache.HostCache import com.muwire.core.hostcache.HostCache
@@ -58,6 +59,9 @@ import net.i2p.data.PrivateKey
import net.i2p.data.Signature import net.i2p.data.Signature
import net.i2p.data.SigningPrivateKey import net.i2p.data.SigningPrivateKey
import net.i2p.router.Router
import net.i2p.router.RouterContext
@Log @Log
public class Core { public class Core {
@@ -80,14 +84,31 @@ public class Core {
private final DirectoryWatcher directoryWatcher private final DirectoryWatcher directoryWatcher
final FileManager fileManager final FileManager fileManager
private final Router router
final AtomicBoolean shutdown = new AtomicBoolean() final AtomicBoolean shutdown = new AtomicBoolean()
public Core(MuWireSettings props, File home, String myVersion) { public Core(MuWireSettings props, File home, String myVersion) {
this.home = home this.home = home
this.muOptions = props this.muOptions = props
log.info "Initializing I2P context"
I2PAppContext.getGlobalContext().logManager() if (!props.embeddedRouter) {
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager() 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("i2np.inboundKBytesPerSecond", String.valueOf(props.inBw))
routerProps.setProperty("i2np.outboundKBytesPerSecond", String.valueOf(props.outBw))
router = new Router(routerProps)
I2PAppContext.getGlobalContext().metaClass = new RouterContextMetaClass()
router.runRouter()
while(!router.isRunning())
Thread.sleep(100)
}
log.info("initializing I2P socket manager") log.info("initializing I2P socket manager")
def i2pClient = new I2PClientFactory().createClient() def i2pClient = new I2PClientFactory().createClient()
@@ -173,7 +194,7 @@ public class Core {
eventBus.register(SearchEvent.class, fileManager) eventBus.register(SearchEvent.class, fileManager)
log.info("initializing mesh manager") log.info("initializing mesh manager")
MeshManager meshManager = new MeshManager(fileManager) MeshManager meshManager = new MeshManager(fileManager, home, props)
eventBus.register(SourceDiscoveredEvent.class, meshManager) eventBus.register(SourceDiscoveredEvent.class, meshManager)
log.info "initializing persistence service" log.info "initializing persistence service"
@@ -236,6 +257,7 @@ public class Core {
log.info("initializing directory watcher") log.info("initializing directory watcher")
directoryWatcher = new DirectoryWatcher(eventBus, fileManager) directoryWatcher = new DirectoryWatcher(eventBus, fileManager)
eventBus.register(FileSharedEvent.class, directoryWatcher) eventBus.register(FileSharedEvent.class, directoryWatcher)
eventBus.register(AllFilesLoadedEvent.class, directoryWatcher)
log.info("initializing hasher service") log.info("initializing hasher service")
hasherService = new HasherService(new FileHasher(), eventBus, fileManager) hasherService = new HasherService(new FileHasher(), eventBus, fileManager)
@@ -244,7 +266,6 @@ public class Core {
public void startServices() { public void startServices() {
hasherService.start() hasherService.start()
directoryWatcher.start()
trustService.start() trustService.start()
trustService.waitForLoad() trustService.waitForLoad()
hostCache.start() hostCache.start()
@@ -271,8 +292,25 @@ public class Core {
directoryWatcher.stop() directoryWatcher.stop()
log.info("shutting down connection manager") log.info("shutting down connection manager")
connectionManager.shutdown() connectionManager.shutdown()
if (router != null) {
log.info("shutting down embedded router")
router.shutdown(0)
}
} }
static class RouterContextMetaClass extends DelegatingMetaClass {
private final Object logManager = new MuWireLogManager()
RouterContextMetaClass() {
super(RouterContext.class)
}
Object invokeMethod(Object object, String name, Object[] args) {
if (name == "logManager")
return logManager
super.invokeMethod(object, name, args)
}
}
static main(args) { static main(args) {
def home = System.getProperty("user.home") + File.separator + ".MuWire" def home = System.getProperty("user.home") + File.separator + ".MuWire"
home = new File(home) home = new File(home)
@@ -297,7 +335,7 @@ public class Core {
} }
} }
Core core = new Core(props, home, "0.3.6") Core core = new Core(props, home, "0.4.0")
core.startServices() core.startServices()
// ... at the end, sleep or execute script // ... at the end, sleep or execute script

View File

@@ -18,6 +18,11 @@ class MuWireSettings {
CrawlerResponse crawlerResponse CrawlerResponse crawlerResponse
boolean shareDownloadedFiles boolean shareDownloadedFiles
Set<String> watchedDirectories Set<String> watchedDirectories
float downloadSequentialRatio
int hostClearInterval
int meshExpiration
boolean embeddedRouter
int inBw, outBw
MuWireSettings() { MuWireSettings() {
this(new Properties()) this(new Properties())
@@ -33,6 +38,12 @@ class MuWireSettings {
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1")) downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24")) updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true")) shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
downloadSequentialRatio = Float.valueOf(props.getProperty("downloadSequentialRatio","0.8"))
hostClearInterval = Integer.valueOf(props.getProperty("hostClearInterval","60"))
meshExpiration = Integer.valueOf(props.getProperty("meshExpiration","60"))
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
inBw = Integer.valueOf(props.getProperty("inBw","256"))
outBw = Integer.valueOf(props.getProperty("outBw","128"))
watchedDirectories = new HashSet<>() watchedDirectories = new HashSet<>()
if (props.containsKey("watchedDirectories")) { if (props.containsKey("watchedDirectories")) {
@@ -52,6 +63,12 @@ class MuWireSettings {
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval)) props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval)) props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles)) props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
props.setProperty("meshExpiration", String.valueOf(meshExpiration))
props.setProperty("embeddedRouter", String.valueOf(embeddedRouter))
props.setProperty("inBw", String.valueOf(inBw))
props.setProperty("outBw", String.valueOf(outBw))
if (!watchedDirectories.isEmpty()) { if (!watchedDirectories.isEmpty()) {
String encoded = watchedDirectories.stream(). String encoded = watchedDirectories.stream().

View File

@@ -75,7 +75,7 @@ class DownloadSession {
if (available.isEmpty()) if (available.isEmpty())
piece = pieces.claim() piece = pieces.claim()
else else
piece = pieces.claim(available) piece = pieces.claim(new HashSet<>(available))
if (piece == -1) if (piece == -1)
return false return false
boolean unclaim = true boolean unclaim = true

View File

@@ -18,6 +18,7 @@ import com.muwire.core.DownloadedFile
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.connection.I2PConnector import com.muwire.core.connection.I2PConnector
import com.muwire.core.files.FileDownloadedEvent import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.util.DataUtil
import groovy.util.logging.Log import groovy.util.logging.Log
import net.i2p.data.Destination import net.i2p.data.Destination
@@ -269,7 +270,7 @@ public class Downloader {
writePieces() writePieces()
} }
} catch (Exception bad) { } catch (Exception bad) {
log.log(Level.WARNING,"Exception while downloading",bad) log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad))
} finally { } finally {
currentState = WorkerState.FINISHED currentState = WorkerState.FINISHED
if (pieces.isComplete() && eventFired.compareAndSet(false, true)) { if (pieces.isComplete() && eventFired.compareAndSet(false, true)) {

View File

@@ -47,7 +47,7 @@ class DirectoryWatcher {
publisherThread.setDaemon(true) publisherThread.setDaemon(true)
} }
void start() { void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
watchService = FileSystems.getDefault().newWatchService() watchService = FileSystems.getDefault().newWatchService()
watcherThread.start() watcherThread.start()
publisherThread.start() publisherThread.start()
@@ -55,9 +55,9 @@ class DirectoryWatcher {
void stop() { void stop() {
shutdown = true shutdown = true
watcherThread.interrupt() watcherThread?.interrupt()
publisherThread.interrupt() publisherThread?.interrupt()
watchService.close() watchService?.close()
} }
void onFileSharedEvent(FileSharedEvent e) { void onFileSharedEvent(FileSharedEvent e) {

View File

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

View File

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

View File

@@ -5,14 +5,15 @@ import net.i2p.data.Destination
class Host { class Host {
private static final int MAX_FAILURES = 3 private static final int MAX_FAILURES = 3
private static final int CLEAR_INTERVAL = 60 * 60 * 1000
final Destination destination final Destination destination
private final int clearInterval
int failures,successes int failures,successes
long lastAttempt long lastAttempt
public Host(Destination destination) { public Host(Destination destination, int clearInterval) {
this.destination = destination this.destination = destination
this.clearInterval = clearInterval
} }
synchronized void onConnect() { synchronized void onConnect() {
@@ -40,6 +41,6 @@ class Host {
} }
synchronized void canTryAgain() { synchronized void canTryAgain() {
System.currentTimeMillis() - lastAttempt > CLEAR_INTERVAL System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
} }
} }

View File

@@ -52,7 +52,7 @@ class HostCache extends Service {
hosts.get(e.destination).clearFailures() hosts.get(e.destination).clearFailures()
return return
} }
Host host = new Host(e.destination) Host host = new Host(e.destination, settings.hostClearInterval)
if (allowHost(host)) { if (allowHost(host)) {
hosts.put(e.destination, host) hosts.put(e.destination, host)
} }
@@ -64,7 +64,7 @@ class HostCache extends Service {
Destination dest = e.endpoint.destination Destination dest = e.endpoint.destination
Host host = hosts.get(dest) Host host = hosts.get(dest)
if (host == null) { if (host == null) {
host = new Host(dest) host = new Host(dest, settings.hostClearInterval)
hosts.put(dest, host) hosts.put(dest, host)
} }
@@ -106,7 +106,7 @@ class HostCache extends Service {
storage.eachLine { storage.eachLine {
def entry = slurper.parseText(it) def entry = slurper.parseText(it)
Destination dest = new Destination(entry.destination) Destination dest = new Destination(entry.destination)
Host host = new Host(dest) Host host = new Host(dest, settings.hostClearInterval)
host.failures = Integer.valueOf(String.valueOf(entry.failures)) host.failures = Integer.valueOf(String.valueOf(entry.failures))
host.successes = Integer.valueOf(String.valueOf(entry.successes)) host.successes = Integer.valueOf(String.valueOf(entry.successes))
if (entry.lastAttempt != null) if (entry.lastAttempt != null)

View File

@@ -1,18 +1,32 @@
package com.muwire.core.mesh package com.muwire.core.mesh
import java.util.stream.Collectors
import com.muwire.core.Constants import com.muwire.core.Constants
import com.muwire.core.InfoHash import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona
import com.muwire.core.download.Pieces import com.muwire.core.download.Pieces
import com.muwire.core.download.SourceDiscoveredEvent import com.muwire.core.download.SourceDiscoveredEvent
import com.muwire.core.files.FileManager import com.muwire.core.files.FileManager
import com.muwire.core.util.DataUtil
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import net.i2p.data.Base64
class MeshManager { class MeshManager {
private final Map<InfoHash, Mesh> meshes = Collections.synchronizedMap(new HashMap<>()) private final Map<InfoHash, Mesh> meshes = Collections.synchronizedMap(new HashMap<>())
private final FileManager fileManager private final FileManager fileManager
private final File home
private final MuWireSettings settings
MeshManager(FileManager fileManager) { MeshManager(FileManager fileManager, File home, MuWireSettings settings) {
this.fileManager = fileManager this.fileManager = fileManager
this.home = home
this.settings = settings
load()
} }
Mesh get(InfoHash infoHash) { Mesh get(InfoHash infoHash) {
@@ -23,7 +37,7 @@ class MeshManager {
synchronized(meshes) { synchronized(meshes) {
if (meshes.containsKey(infoHash)) if (meshes.containsKey(infoHash))
return meshes.get(infoHash) return meshes.get(infoHash)
Pieces pieces = new Pieces(nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO) Pieces pieces = new Pieces(nPieces, settings.downloadSequentialRatio)
if (fileManager.rootToFiles.containsKey(infoHash)) { if (fileManager.rootToFiles.containsKey(infoHash)) {
for (int i = 0; i < nPieces; i++) for (int i = 0; i < nPieces; i++)
pieces.markDownloaded(i) pieces.markDownloaded(i)
@@ -38,6 +52,51 @@ class MeshManager {
Mesh mesh = meshes.get(e.infoHash) Mesh mesh = meshes.get(e.infoHash)
if (mesh == null) if (mesh == null)
return return
mesh.sources.add(e.source) mesh.sources.add(e.source)
save()
}
private void save() {
File meshFile = new File(home, "mesh.json")
synchronized(meshes) {
meshFile.withPrintWriter { writer ->
meshes.values().each { mesh ->
def json = [:]
json.timestamp = System.currentTimeMillis()
json.infoHash = Base64.encode(mesh.infoHash.getRoot())
json.sources = mesh.sources.stream().map({it.toBase64()}).collect(Collectors.toList())
json.nPieces = mesh.pieces.nPieces
json.xHave = DataUtil.encodeXHave(mesh.pieces.downloaded, mesh.pieces.nPieces)
writer.println(JsonOutput.toJson(json))
}
}
}
}
private void load() {
File meshFile = new File(home, "mesh.json")
if (!meshFile.exists())
return
long now = System.currentTimeMillis()
JsonSlurper slurper = new JsonSlurper()
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 ->
Persona persona = new Persona(new ByteArrayInputStream(Base64.decode(source)))
mesh.sources.add(persona)
}
if (json.xHave != null)
DataUtil.decodeXHave(json.xHave).each { pieces.markDownloaded(it) }
if (!mesh.sources.isEmpty())
meshes.put(infoHash, mesh)
}
} }
} }

View File

@@ -2,5 +2,5 @@ package com.muwire.core.upload
class ContentRequest extends Request { class ContentRequest extends Request {
Range range Range range
boolean have int have
} }

View File

@@ -105,4 +105,14 @@ class ContentUploader extends Uploader {
request.downloader.getHumanReadableName() request.downloader.getHumanReadableName()
} }
@Override
public int getDonePieces() {
return request.have;
}
@Override
public int getTotalPieces() {
return mesh.pieces.nPieces;
}
} }

View File

@@ -50,6 +50,16 @@ class HashListUploader extends Uploader {
public String getDownloader() { public String getDownloader() {
request.downloader.getHumanReadableName() request.downloader.getHumanReadableName()
} }
@Override
public int getDonePieces() {
return 0;
}
@Override
public int getTotalPieces() {
return 1;
}
} }

View File

@@ -50,10 +50,10 @@ class Request {
downloader = new Persona(new ByteArrayInputStream(decoded)) downloader = new Persona(new ByteArrayInputStream(decoded))
} }
boolean have = false int have = 0
if (headers.containsKey("X-Have")) { if (headers.containsKey("X-Have")) {
def encoded = headers["X-Have"].trim() def encoded = headers["X-Have"].trim()
have = DataUtil.decodeXHave(encoded).size() > 0 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) headers : headers, downloader : downloader, have : have)

View File

@@ -80,7 +80,7 @@ public class UploadManager {
return return
} }
if (request.have) if (request.have > 0)
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader)) eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
Mesh mesh Mesh mesh
@@ -205,7 +205,7 @@ public class UploadManager {
return return
} }
if (request.have) if (request.have > 0)
eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader)) eventBus.publish(new SourceDiscoveredEvent(infoHash : request.infoHash, source : request.downloader))
Mesh mesh Mesh mesh

View File

@@ -32,4 +32,8 @@ abstract class Uploader {
abstract int getProgress(); abstract int getProgress();
abstract String getDownloader(); abstract String getDownloader();
abstract int getDonePieces();
abstract int getTotalPieces()
} }

View File

@@ -109,4 +109,10 @@ class DataUtil {
} }
available available
} }
public static Exception findRoot(Exception e) {
while(e.getCause() != null)
e = e.getCause()
e
}
} }

View File

@@ -30,7 +30,7 @@ public class SharedFile {
long length = file.length(); long length = file.length();
int rawPieceSize = 0x1 << pieceSize; int rawPieceSize = 0x1 << pieceSize;
int rv = (int) (length / rawPieceSize); int rv = (int) (length / rawPieceSize);
if (length % pieceSize != 0) if (length % rawPieceSize != 0)
rv++; rv++;
return rv; return rv;
} }

View File

@@ -1,5 +1,5 @@
group = com.muwire group = com.muwire
version = 0.3.6 version = 0.4.0
groovyVersion = 2.4.15 groovyVersion = 2.4.15
slf4jVersion = 1.7.25 slf4jVersion = 1.7.25
spockVersion = 1.1-groovy-2.4 spockVersion = 1.1-groovy-2.4

View File

@@ -44,6 +44,8 @@ class MainFrameController {
search = search.trim() search = search.trim()
if (search.length() == 0) if (search.length() == 0)
return return
if (search.length() > 128)
search = search.substring(0,128)
def uuid = UUID.randomUUID() def uuid = UUID.randomUUID()
Map<String, Object> params = new HashMap<>() Map<String, Object> params = new HashMap<>()
params["search-terms"] = search params["search-terms"] = search

View File

@@ -4,9 +4,15 @@ import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor import griffon.metadata.ArtifactProviderFor
import groovy.util.logging.Log
import java.util.logging.Level
import javax.annotation.Nonnull import javax.annotation.Nonnull
import javax.swing.JFileChooser
import com.muwire.core.Core import com.muwire.core.Core
import com.muwire.core.MuWireSettings
@ArtifactProviderFor(GriffonController) @ArtifactProviderFor(GriffonController)
class OptionsController { class OptionsController {
@@ -46,7 +52,7 @@ class OptionsController {
text = view.retryField.text text = view.retryField.text
model.downloadRetryInterval = text model.downloadRetryInterval = text
def settings = application.context.get("muwire-settings") MuWireSettings settings = application.context.get("muwire-settings")
settings.downloadRetryInterval = Integer.valueOf(text) settings.downloadRetryInterval = Integer.valueOf(text)
text = view.updateField.text text = view.updateField.text
@@ -60,7 +66,19 @@ class OptionsController {
boolean shareDownloaded = view.shareDownloadedCheckbox.model.isSelected() boolean shareDownloaded = view.shareDownloadedCheckbox.model.isSelected()
model.shareDownloadedFiles = shareDownloaded model.shareDownloadedFiles = shareDownloaded
settings.shareDownloadedFiles = shareDownloaded settings.shareDownloadedFiles = shareDownloaded
String downloadLocation = model.downloadLocation
settings.downloadLocation = new File(downloadLocation)
if (settings.embeddedRouter) {
text = view.inBwField.text
model.inBw = text
settings.inBw = Integer.valueOf(text)
text = view.outBwField.text
model.outBw = text
settings.outBw = Integer.valueOf(text)
}
File settingsFile = new File(core.home, "MuWire.properties") File settingsFile = new File(core.home, "MuWire.properties")
settingsFile.withOutputStream { settingsFile.withOutputStream {
settings.write(it) settings.write(it)
@@ -110,4 +128,15 @@ class OptionsController {
view.d.setVisible(false) view.d.setVisible(false)
mvcGroup.destroy() mvcGroup.destroy()
} }
@ControllerAction
void downloadLocation() {
def chooser = new JFileChooser()
chooser.setFileHidingEnabled(false)
chooser.setDialogTitle("Select location for downloaded files")
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
int rv = chooser.showOpenDialog(null)
if (rv == JFileChooser.APPROVE_OPTION)
model.downloadLocation = chooser.getSelectedFile().getAbsolutePath()
}
} }

View File

@@ -43,6 +43,8 @@ class Initialize extends AbstractLifecycleHandler {
application.context.put("muwire-home", home.getAbsolutePath()) application.context.put("muwire-home", home.getAbsolutePath())
System.getProperties().setProperty("awt.useSystemAAFontSettings", "true")
def guiPropsFile = new File(home, "gui.properties") def guiPropsFile = new File(home, "gui.properties")
UISettings uiSettings UISettings uiSettings
if (guiPropsFile.exists()) { if (guiPropsFile.exists()) {

View File

@@ -74,6 +74,7 @@ class Ready extends AbstractLifecycleHandler {
props.downloadLocation = new File(portableDownloads) props.downloadLocation = new File(portableDownloads)
} else { } else {
def chooser = new JFileChooser() def chooser = new JFileChooser()
chooser.setFileHidingEnabled(false)
chooser.setDialogTitle("Select a directory where downloads will be saved") chooser.setDialogTitle("Select a directory where downloads will be saved")
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY) chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
int rv = chooser.showOpenDialog(null) int rv = chooser.showOpenDialog(null)

View File

@@ -17,6 +17,7 @@ import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.connection.DisconnectionEvent import com.muwire.core.connection.DisconnectionEvent
import com.muwire.core.download.DownloadStartedEvent import com.muwire.core.download.DownloadStartedEvent
import com.muwire.core.download.Downloader import com.muwire.core.download.Downloader
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.FileDownloadedEvent import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileLoadedEvent import com.muwire.core.files.FileLoadedEvent
@@ -139,6 +140,7 @@ class MainFrameModel {
core.eventBus.register(FileDownloadedEvent.class, this) core.eventBus.register(FileDownloadedEvent.class, this)
core.eventBus.register(FileUnsharedEvent.class, this) core.eventBus.register(FileUnsharedEvent.class, this)
core.eventBus.register(RouterDisconnectedEvent.class, this) core.eventBus.register(RouterDisconnectedEvent.class, this)
core.eventBus.register(AllFilesLoadedEvent.class, this)
timer.schedule({ timer.schedule({
if (core.shutdown.get()) if (core.shutdown.get())
@@ -167,9 +169,6 @@ class MainFrameModel {
trusted.addAll(core.trustService.good.values()) trusted.addAll(core.trustService.good.values())
distrusted.addAll(core.trustService.bad.values()) distrusted.addAll(core.trustService.bad.values())
watched.addAll(core.muOptions.watchedDirectories)
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
resumeButtonText = "Retry" resumeButtonText = "Retry"
} }
@@ -177,6 +176,13 @@ class MainFrameModel {
} }
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))) }
}
}
void onUIResultEvent(UIResultEvent e) { void onUIResultEvent(UIResultEvent e) {
MVCGroup resultsGroup = results.get(e.uuid) MVCGroup resultsGroup = results.get(e.uuid)
resultsGroup?.model.handleResult(e) resultsGroup?.model.handleResult(e)

View File

@@ -13,6 +13,7 @@ class OptionsModel {
@Observable String updateCheckInterval @Observable String updateCheckInterval
@Observable boolean onlyTrusted @Observable boolean onlyTrusted
@Observable boolean shareDownloadedFiles @Observable boolean shareDownloadedFiles
@Observable String downloadLocation
// i2p options // i2p options
@Observable String inboundLength @Observable String inboundLength
@@ -29,12 +30,17 @@ class OptionsModel {
@Observable boolean excludeLocalResult @Observable boolean excludeLocalResult
@Observable boolean showSearchHashes @Observable boolean showSearchHashes
// bw options
@Observable String inBw
@Observable String outBw
void mvcGroupInit(Map<String, String> args) { void mvcGroupInit(Map<String, String> args) {
MuWireSettings settings = application.context.get("muwire-settings") MuWireSettings settings = application.context.get("muwire-settings")
downloadRetryInterval = settings.downloadRetryInterval downloadRetryInterval = settings.downloadRetryInterval
updateCheckInterval = settings.updateCheckInterval updateCheckInterval = settings.updateCheckInterval
onlyTrusted = !settings.allowUntrusted() onlyTrusted = !settings.allowUntrusted()
shareDownloadedFiles = settings.shareDownloadedFiles shareDownloadedFiles = settings.shareDownloadedFiles
downloadLocation = settings.downloadLocation.getAbsolutePath()
Core core = application.context.get("core") Core core = application.context.get("core")
inboundLength = core.i2pOptions["inbound.length"] inboundLength = core.i2pOptions["inbound.length"]
@@ -50,5 +56,10 @@ class OptionsModel {
clearFinishedDownloads = uiSettings.clearFinishedDownloads clearFinishedDownloads = uiSettings.clearFinishedDownloads
excludeLocalResult = uiSettings.excludeLocalResult excludeLocalResult = uiSettings.excludeLocalResult
showSearchHashes = uiSettings.showSearchHashes showSearchHashes = uiSettings.showSearchHashes
if (core.router != null) {
inBw = String.valueOf(settings.inBw)
outBw = String.valueOf(settings.outBw)
}
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -21,10 +21,10 @@ class EventListView {
application(size: [320, 80], id: 'event-list', application(size: [320, 80], id: 'event-list',
locationRelativeTo : null, locationRelativeTo : null,
title: application.configuration['application.title'], title: application.configuration['application.title'],
iconImage: imageIcon('/griffon-icon-48x48.png').image, iconImage: imageIcon('/MuWire-48x48.png').image,
iconImages: [imageIcon('/griffon-icon-48x48.png').image, iconImages: [imageIcon('/MuWire-48x48.png').image,
imageIcon('/griffon-icon-32x32.png').image, imageIcon('/MuWire-32x32.png').image,
imageIcon('/griffon-icon-16x16.png').image], imageIcon('/MuWire-16x16.png').image],
visible: bind { !model.coreInitialized} ) { visible: bind { !model.coreInitialized} ) {
panel { panel {
vbox { vbox {

View File

@@ -60,10 +60,10 @@ class MainFrameView {
locationRelativeTo : null, locationRelativeTo : null,
title: application.configuration['application.title'] + " " + title: application.configuration['application.title'] + " " +
metadata["application.version"] + " revision " + metadata["build.revision"], metadata["application.version"] + " revision " + metadata["build.revision"],
iconImage: imageIcon('/griffon-icon-48x48.png').image, iconImage: imageIcon('/MuWire-48x48.png').image,
iconImages: [imageIcon('/griffon-icon-48x48.png').image, iconImages: [imageIcon('/MuWire-48x48.png').image,
imageIcon('/griffon-icon-32x32.png').image, imageIcon('/MuWire-32x32.png').image,
imageIcon('/griffon-icon-16x16.png').image], imageIcon('/MuWire-16x16.png').image],
pack : false, pack : false,
visible : bind { model.coreInitialized }) { visible : bind { model.coreInitialized }) {
menuBar { menuBar {
@@ -185,11 +185,14 @@ class MainFrameView {
closureColumn(header : "Name", type : String, read : {row -> row.getName() }) closureColumn(header : "Name", type : String, read : {row -> row.getName() })
closureColumn(header : "Progress", type : String, read : { row -> closureColumn(header : "Progress", type : String, read : { row ->
int percent = row.getProgress() int percent = row.getProgress()
"$percent%" "$percent% of piece".toString()
}) })
closureColumn(header : "Downloader", type : String, read : { row -> closureColumn(header : "Downloader", type : String, read : { row ->
row.getDownloader() row.getDownloader()
}) })
closureColumn(header : "Remote Pieces", type : String, read : { row ->
"${row.getDonePieces()}/${row.getTotalPieces()}".toString()
})
} }
} }
} }
@@ -516,6 +519,7 @@ class MainFrameView {
def shareFiles = { def shareFiles = {
def chooser = new JFileChooser() def chooser = new JFileChooser()
chooser.setFileHidingEnabled(false)
chooser.setDialogTitle("Select file to share") chooser.setDialogTitle("Select file to share")
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY) chooser.setFileSelectionMode(JFileChooser.FILES_ONLY)
int rv = chooser.showOpenDialog(null) int rv = chooser.showOpenDialog(null)
@@ -526,6 +530,7 @@ class MainFrameView {
def watchDirectories = { def watchDirectories = {
def chooser = new JFileChooser() def chooser = new JFileChooser()
chooser.setFileHidingEnabled(false)
chooser.setDialogTitle("Select directory to watch") chooser.setDialogTitle("Select directory to watch")
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY) chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)
int rv = chooser.showOpenDialog(null) int rv = chooser.showOpenDialog(null)

View File

@@ -9,6 +9,8 @@ import javax.swing.JPanel
import javax.swing.JTabbedPane import javax.swing.JTabbedPane
import javax.swing.SwingConstants import javax.swing.SwingConstants
import com.muwire.core.Core
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.event.WindowAdapter import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent import java.awt.event.WindowEvent
@@ -26,6 +28,7 @@ class OptionsView {
def p def p
def i def i
def u def u
def bandwidth
def retryField def retryField
def updateField def updateField
@@ -45,6 +48,10 @@ class OptionsView {
def excludeLocalResultCheckbox def excludeLocalResultCheckbox
def showSearchHashesCheckbox def showSearchHashesCheckbox
def inBwField
def outBwField
def buttonsPanel def buttonsPanel
def mainFrame def mainFrame
@@ -68,6 +75,10 @@ class OptionsView {
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3)) label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, 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 { i = builder.panel {
@@ -100,6 +111,16 @@ class OptionsView {
// label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7)) // label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
// showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7)) // showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
} }
bandwidth = builder.panel {
gridBagLayout()
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
label(text : "Inbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 1))
inBwField = textField(text : bind {model.inBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 1))
label(text : "Outbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 2))
outBwField = textField(text : bind {model.outBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 2))
}
buttonsPanel = builder.panel { buttonsPanel = builder.panel {
gridBagLayout() gridBagLayout()
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction) button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
@@ -112,6 +133,10 @@ class OptionsView {
tabbedPane.addTab("MuWire", p) tabbedPane.addTab("MuWire", p)
tabbedPane.addTab("I2P", i) tabbedPane.addTab("I2P", i)
tabbedPane.addTab("GUI", u) tabbedPane.addTab("GUI", u)
Core core = application.context.get("core")
if (core.router != null) {
tabbedPane.addTab("Bandwidth", bandwidth)
}
JPanel panel = new JPanel() JPanel panel = new JPanel()
panel.setLayout(new BorderLayout()) panel.setLayout(new BorderLayout())

View File

@@ -26,7 +26,7 @@ handlers= java.util.logging.FileHandler
# can be overriden by a facility specific level # can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level # Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console. # setting to limit messages printed to the console.
.level= SEVERE .level= INFO
############################################################ ############################################################
# Handler specific properties. # Handler specific properties.
@@ -35,7 +35,7 @@ handlers= java.util.logging.FileHandler
# default file output is in user's home directory. # default file output is in user's home directory.
java.util.logging.FileHandler.pattern = MuWire.log java.util.logging.FileHandler.pattern = MuWire.log
java.util.logging.FileHandler.limit = 50000000 java.util.logging.FileHandler.limit = 150000000
java.util.logging.FileHandler.count = 1 java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
@@ -60,3 +60,5 @@ java.util.logging.SimpleFormatter.format=%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$
# messages: # messages:
com.xyz.foo.level = SEVERE com.xyz.foo.level = SEVERE
com.muwire.core.level = FINE com.muwire.core.level = FINE
net.i2p.client.streaming.impl.level = FINE
net.i2p.client.impl.level = FINE