Compare commits

..

5 Commits

Author SHA1 Message Date
Zlatin Balevsky
e829e09b13 fix piece count calculation 2019-06-24 18:22:46 +01:00
Zlatin Balevsky
120980100b log mesh creation 2019-06-24 18:11:55 +01:00
Zlatin Balevsky
e64ed2c89d fix method name 2019-06-24 18:03:22 +01:00
Zlatin Balevsky
81436aaa66 fix name 2019-06-24 17:59:39 +01:00
Zlatin Balevsky
3bb530e34f more logging 2019-06-24 17:50:07 +01:00
182 changed files with 4797 additions and 7359 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.4.6 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder. The current stable release - 0.2.5 is avaiable for download at http://muwire.com. You can find technical documentation in the "doc" folder.
### Building ### Building
@@ -23,13 +23,34 @@ Some of the UI tests will fail because they haven't been written yet :-/
### Running ### Running
After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt. You need to have an I2P router up and running on the same machine. After you build the application, look inside "gui/build/distributions". Untar/unzip one of the "shadow" files and then run the jar contained inside by typing "java -jar MuWire-x.y.z.jar" in a terminal or command prompt. If you use a custom I2CP host and port, create a file $HOME/.MuWire/i2p.properties and put "i2cp.tcp.host=<host>" and "i2cp.tcp.port=<port>" in there.
If you have an I2P router running on the same machine that is all you need to do. If you use a custom I2CP host and port, create a file `i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there. On Windows that file should go into `%HOME%\AppData\Roaming\MuWire`, on Mac into `$HOME/Library/Application Support/MuWire` and on Linux `$HOME/.MuWire` The first time you run MuWire it will ask you to select a nickname. This nickname will be displayed with search results, so that others can verify the file was shared by you. It is best to leave MuWire running all the time, just like I2P.
If you do not have an I2P router, pass the following switch to the Java process: `-DembeddedRouter=true`. This will launch MuWire's embedded router. Be aware that this causes startup to take a lot longer.
### GPG Fingerprint ### Known bugs and limitations
471B 9FD4 5517 A5ED 101F C57D A728 3207 2D52 5E41
You can find the full key at https://keybase.io/zlatinb * Many UI features you would expect are not there yet
### Quick FAQ
* why is MuWire slow ?
- too few sources you're downloading from
- you can increase the number of tunnels by using more tunnels via Options->I2P Inbound/Outbound Quantity
the default is 4 and you could raise up to as high as 16 ( Caution !!!!)
* my search is not returning (enough) results !
- search is keyword or hash based
- keywords and hash(es) are NOT regexed or wildcarded so they have to be complete
so searching for 'musi' will not return results with 'music' - you have to search for 'music'
- ALL keywords have to match
- only use space for keyword separation
- if you already have the file in question it is not displayed ( can be changed via Options )
* what's this right click -> 'Copy hash to clipboard' for ?
- if you have a specific file you wish to share or download you can use the hash as a unique identifier
to make sure you have exactly the right file.
- you can share this hash with others to ensure they are getting the right file

View File

@@ -12,10 +12,18 @@ This reduces query traffic by not sending last hop queries to peers that definit
This helps with scalability This helps with scalability
##### Trust List Sharing
For helping users make better decisions whom to trust
##### Content Control Panel ##### Content Control Panel
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
##### Packaging With JRE, Embedded Router
For ease of deployment for new users, and so that users do not need to run a separate I2P router
##### Web UI, REST Interface, etc. ##### Web UI, REST Interface, etc.
Basically any non-gui non-cli user interface Basically any non-gui non-cli user interface
@@ -28,4 +36,5 @@ To enable parsing of metadata from known file types and the user editing it or a
* Wrapper of some kind for in-place upgrades * Wrapper of some kind for in-place upgrades
* Download file sequentially * Download file sequentially
* Unsharing of files
* Multiple-selection download, Ctrl-A * Multiple-selection download, Ctrl-A

View File

@@ -2,7 +2,7 @@ subprojects {
apply plugin: 'groovy' apply plugin: 'groovy'
dependencies { dependencies {
compile 'net.i2p:i2p:0.9.41' compile 'net.i2p:i2p:0.9.40'
compile 'org.codehaus.groovy:groovy-all:2.4.15' compile 'org.codehaus.groovy:groovy-all:2.4.15'
} }

View File

@@ -35,7 +35,7 @@ class Cli {
Core core Core core
try { try {
core = new Core(props, home, "0.4.9") core = new Core(props, home, "0.3.8")
} 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.4.9") core = new Core(props, home, "0.3.8")
} 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,9 +2,8 @@ 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.41' compile 'net.i2p.client:mstreaming:0.9.40'
compile 'net.i2p.client:mstreaming:0.9.41' compile 'net.i2p.client:streaming:0.9.40'
compile 'net.i2p.client:streaming:0.9.41'
testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2' testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'

View File

@@ -9,5 +9,7 @@ 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 String SPLIT_PATTERN = "[\\*\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|\\[\\]\\{\\}\\?]" public static final float DOWNLOAD_SEQUENTIAL_RATIO = 0.8f
public static final String SPLIT_PATTERN = "[\\+\\-,\\.:;\\(\\)=_/\\\\\\!\\\"\\\'\\\$%\\|]"
} }

View File

@@ -20,7 +20,6 @@ import com.muwire.core.download.UIDownloadPausedEvent
import com.muwire.core.download.UIDownloadResumedEvent import com.muwire.core.download.UIDownloadResumedEvent
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.FileHashingEvent
import com.muwire.core.files.FileHasher import com.muwire.core.files.FileHasher
import com.muwire.core.files.FileLoadedEvent import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileManager import com.muwire.core.files.FileManager
@@ -28,8 +27,6 @@ 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.DirectoryUnsharedEvent
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
@@ -40,16 +37,11 @@ import com.muwire.core.search.ResultsEvent
import com.muwire.core.search.ResultsSender import com.muwire.core.search.ResultsSender
import com.muwire.core.search.SearchEvent import com.muwire.core.search.SearchEvent
import com.muwire.core.search.SearchManager import com.muwire.core.search.SearchManager
import com.muwire.core.search.UIResultBatchEvent
import com.muwire.core.trust.TrustEvent import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustService 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.update.UpdateClient
import com.muwire.core.upload.UploadManager import com.muwire.core.upload.UploadManager
import com.muwire.core.util.MuWireLogManager import com.muwire.core.util.MuWireLogManager
import com.muwire.core.content.ContentControlEvent
import com.muwire.core.content.ContentManager
import groovy.util.logging.Log import groovy.util.logging.Log
import net.i2p.I2PAppContext import net.i2p.I2PAppContext
@@ -66,9 +58,6 @@ 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 {
@@ -79,7 +68,6 @@ public class Core {
final MuWireSettings muOptions final MuWireSettings muOptions
private final TrustService trustService private final TrustService trustService
private final TrustSubscriber trustSubscriber
private final PersisterService persisterService private final PersisterService persisterService
private final HostCache hostCache private final HostCache hostCache
private final ConnectionManager connectionManager private final ConnectionManager connectionManager
@@ -91,16 +79,25 @@ public class Core {
private final DownloadManager downloadManager private final DownloadManager downloadManager
private final DirectoryWatcher directoryWatcher private final DirectoryWatcher directoryWatcher
final FileManager fileManager final FileManager fileManager
final UploadManager uploadManager
final ContentManager contentManager
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()
I2PAppContext.getGlobalContext()._logManager = new MuWireLogManager()
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)
}
}
i2pOptions = new Properties() i2pOptions = new Properties()
def i2pOptionsFile = new File(home,"i2p.properties") def i2pOptionsFile = new File(home,"i2p.properties")
@@ -120,47 +117,8 @@ public class Core {
i2pOptions["outbound.quantity"] = "4" i2pOptions["outbound.quantity"] = "4"
i2pOptions["i2cp.tcp.host"] = "127.0.0.1" i2pOptions["i2cp.tcp.host"] = "127.0.0.1"
i2pOptions["i2cp.tcp.port"] = "7654" 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 // options like tunnel length and quantity
I2PSession i2pSession I2PSession i2pSession
I2PSocketManager socketManager I2PSocketManager socketManager
@@ -213,10 +171,9 @@ public class Core {
eventBus.register(FileDownloadedEvent.class, fileManager) eventBus.register(FileDownloadedEvent.class, fileManager)
eventBus.register(FileUnsharedEvent.class, fileManager) eventBus.register(FileUnsharedEvent.class, fileManager)
eventBus.register(SearchEvent.class, fileManager) eventBus.register(SearchEvent.class, fileManager)
eventBus.register(DirectoryUnsharedEvent.class, fileManager)
log.info("initializing mesh manager") log.info("initializing mesh manager")
MeshManager meshManager = new MeshManager(fileManager, home, props) MeshManager meshManager = new MeshManager(fileManager, home)
eventBus.register(SourceDiscoveredEvent.class, meshManager) eventBus.register(SourceDiscoveredEvent.class, meshManager)
log.info "initializing persistence service" log.info "initializing persistence service"
@@ -242,9 +199,7 @@ public class Core {
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000) cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
log.info("initializing update client") log.info("initializing update client")
updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props, fileManager, me) updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props)
eventBus.register(FileDownloadedEvent.class, updateClient)
eventBus.register(UIResultBatchEvent.class, updateClient)
log.info("initializing connector") log.info("initializing connector")
I2PConnector i2pConnector = new I2PConnector(socketManager) I2PConnector i2pConnector = new I2PConnector(socketManager)
@@ -268,7 +223,7 @@ public class Core {
eventBus.register(UIDownloadResumedEvent.class, downloadManager) eventBus.register(UIDownloadResumedEvent.class, downloadManager)
log.info("initializing upload manager") log.info("initializing upload manager")
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager) UploadManager uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager)
log.info("initializing connection establisher") log.info("initializing connection establisher")
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache) connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
@@ -281,26 +236,15 @@ 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)
eventBus.register(DirectoryUnsharedEvent.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)
eventBus.register(FileSharedEvent.class, hasherService) 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)
log.info("initializing content manager")
contentManager = new ContentManager()
eventBus.register(ContentControlEvent.class, contentManager)
eventBus.register(QueryEvent.class, contentManager)
} }
public void startServices() { public void startServices() {
hasherService.start() hasherService.start()
directoryWatcher.start()
trustService.start() trustService.start()
trustService.waitForLoad() trustService.waitForLoad()
hostCache.start() hostCache.start()
@@ -317,8 +261,6 @@ public class Core {
log.info("already shutting down") log.info("already shutting down")
return return
} }
log.info("shutting down trust subscriber")
trustSubscriber.stop()
log.info("shutting down download manageer") log.info("shutting down download manageer")
downloadManager.shutdown() downloadManager.shutdown()
log.info("shutting down connection acceeptor") log.info("shutting down connection acceeptor")
@@ -327,14 +269,8 @@ public class Core {
connectionEstablisher.stop() connectionEstablisher.stop()
log.info("shutting down directory watcher") log.info("shutting down directory watcher")
directoryWatcher.stop() directoryWatcher.stop()
log.info("shutting down cache client")
cacheClient.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 main(args) { static main(args) {
@@ -361,7 +297,7 @@ public class Core {
} }
} }
Core core = new Core(props, home, "0.4.9") Core core = new Core(props, home, "0.3.8")
core.startServices() core.startServices()
// ... at the end, sleep or execute script // ... at the end, sleep or execute script

View File

@@ -48,9 +48,4 @@ class EventBus {
} }
currentHandlers.add handler currentHandlers.add handler
} }
synchronized void unregister(Class<? extends Event> eventType, def handler) {
log.info("Unregistering $handler for type $eventType")
handlers[eventType]?.remove(handler)
}
} }

View File

@@ -11,25 +11,13 @@ class MuWireSettings {
final boolean isLeaf final boolean isLeaf
boolean allowUntrusted boolean allowUntrusted
boolean allowTrustLists
int trustListInterval
Set<Persona> trustSubscriptions
int downloadRetryInterval int downloadRetryInterval
int updateCheckInterval int updateCheckInterval
boolean autoDownloadUpdate
String updateType
String nickname String nickname
File downloadLocation File downloadLocation
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
Set<String> watchedKeywords
Set<String> watchedRegexes
MuWireSettings() { MuWireSettings() {
this(new Properties()) this(new Properties())
@@ -37,36 +25,20 @@ class MuWireSettings {
MuWireSettings(Properties props) { MuWireSettings(Properties props) {
isLeaf = Boolean.valueOf(props.get("leaf","false")) isLeaf = Boolean.valueOf(props.get("leaf","false"))
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true")) allowUntrusted = Boolean.valueOf(props.get("allowUntrusted","true"))
allowTrustLists = Boolean.valueOf(props.getProperty("allowTrustLists","true"))
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED")) crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
nickname = props.getProperty("nickname","MuWireUser") nickname = props.getProperty("nickname","MuWireUser")
downloadLocation = new File((String)props.getProperty("downloadLocation", downloadLocation = new File((String)props.getProperty("downloadLocation",
System.getProperty("user.home"))) System.getProperty("user.home")))
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"))
autoDownloadUpdate = Boolean.parseBoolean(props.getProperty("autoDownloadUpdate","true"))
updateType = props.getProperty("updateType","jar")
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 = readEncodedSet(props, "watchedDirectories") watchedDirectories = new HashSet<>()
watchedKeywords = readEncodedSet(props, "watchedKeywords") if (props.containsKey("watchedDirectories")) {
watchedRegexes = readEncodedSet(props, "watchedRegexes") 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 {
trustSubscriptions.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
} }
}
} }
@@ -74,55 +46,23 @@ class MuWireSettings {
Properties props = new Properties() Properties props = new Properties()
props.setProperty("leaf", isLeaf.toString()) props.setProperty("leaf", isLeaf.toString())
props.setProperty("allowUntrusted", allowUntrusted.toString()) props.setProperty("allowUntrusted", allowUntrusted.toString())
props.setProperty("allowTrustLists", String.valueOf(allowTrustLists))
props.setProperty("trustListInterval", String.valueOf(trustListInterval))
props.setProperty("crawlerResponse", crawlerResponse.toString()) props.setProperty("crawlerResponse", crawlerResponse.toString())
props.setProperty("nickname", nickname) props.setProperty("nickname", nickname)
props.setProperty("downloadLocation", downloadLocation.getAbsolutePath()) props.setProperty("downloadLocation", downloadLocation.getAbsolutePath())
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval)) props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval)) props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
props.setProperty("autoDownloadUpdate", String.valueOf(autoDownloadUpdate))
props.setProperty("updateType",String.valueOf(updateType))
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))
writeEncodedSet(watchedDirectories, "watchedDirectories", props) if (!watchedDirectories.isEmpty()) {
writeEncodedSet(watchedKeywords, "watchedKeywords", props) String encoded = watchedDirectories.stream().
writeEncodedSet(watchedRegexes, "watchedRegexes", props) map({Base64.encode(DataUtil.encodei18nString(it))}).
if (!trustSubscriptions.isEmpty()) {
String encoded = trustSubscriptions.stream().
map({it.toBase64()}).
collect(Collectors.joining(",")) collect(Collectors.joining(","))
props.setProperty("trustSubscriptions", encoded) props.setProperty("watchedDirectories", encoded)
} }
props.store(out, "") props.store(out, "")
} }
private static Set<String> readEncodedSet(Properties props, String property) {
Set<String> rv = new HashSet<>()
if (props.containsKey(property)) {
String[] encoded = props.getProperty(property).split(",")
encoded.each { rv << DataUtil.readi18nString(Base64.decode(it)) }
}
rv
}
private static void writeEncodedSet(Set<String> set, String property, Properties props) {
if (set.isEmpty())
return
String encoded = set.stream().
map({Base64.encode(DataUtil.encodei18nString(it))}).
collect(Collectors.joining(","))
props.setProperty(property, encoded)
}
boolean isLeaf() { boolean isLeaf() {
isLeaf isLeaf
} }

View File

@@ -82,13 +82,4 @@ public class Persona {
Persona other = (Persona)o Persona other = (Persona)o
name.equals(other.name) && destination.equals(other.destination) 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"
System.exit(1)
}
Persona p = new Persona(new ByteArrayInputStream(Base64.decode(args[0])))
println p.getHumanReadableName()
}
} }

View File

@@ -22,9 +22,6 @@ import net.i2p.data.Destination
@Log @Log
abstract class Connection implements Closeable { abstract class Connection implements Closeable {
private static final int SEARCHES = 10
private static final long INTERVAL = 1000
final EventBus eventBus final EventBus eventBus
final Endpoint endpoint final Endpoint endpoint
final boolean incoming final boolean incoming
@@ -35,7 +32,6 @@ abstract class Connection implements Closeable {
private final AtomicBoolean running = new AtomicBoolean() private final AtomicBoolean running = new AtomicBoolean()
private final BlockingQueue messages = new LinkedBlockingQueue() private final BlockingQueue messages = new LinkedBlockingQueue()
private final Thread reader, writer private final Thread reader, writer
private final LinkedList<Long> searchTimestamps = new LinkedList<>()
protected final String name protected final String name
@@ -160,25 +156,7 @@ abstract class Connection implements Closeable {
} }
} }
private boolean throttleSearch() {
final long now = System.currentTimeMillis()
if (searchTimestamps.size() < SEARCHES) {
searchTimestamps.addLast(now)
return false
}
Long oldest = searchTimestamps.getFirst()
if (now - oldest.longValue() < INTERVAL)
return true
searchTimestamps.addLast(now)
searchTimestamps.removeFirst()
false
}
protected void handleSearch(def search) { protected void handleSearch(def search) {
if (throttleSearch()) {
log.info("dropping excessive search")
return
}
UUID uuid = UUID.fromString(search.uuid) UUID uuid = UUID.fromString(search.uuid)
byte [] infohash = null byte [] infohash = null
if (search.infohash != null) { if (search.infohash != null) {

View File

@@ -14,7 +14,6 @@ import com.muwire.core.hostcache.HostCache
import com.muwire.core.trust.TrustLevel import com.muwire.core.trust.TrustLevel
import com.muwire.core.trust.TrustService import com.muwire.core.trust.TrustService
import com.muwire.core.upload.UploadManager import com.muwire.core.upload.UploadManager
import com.muwire.core.util.DataUtil
import com.muwire.core.search.InvalidSearchResultException import com.muwire.core.search.InvalidSearchResultException
import com.muwire.core.search.ResultsParser import com.muwire.core.search.ResultsParser
import com.muwire.core.search.SearchManager import com.muwire.core.search.SearchManager
@@ -126,9 +125,6 @@ class ConnectionAcceptor {
case (byte)'P': case (byte)'P':
processPOST(e) processPOST(e)
break break
case (byte)'T':
processTRUST(e)
break
default: default:
throw new Exception("Invalid read $read") throw new Exception("Invalid read $read")
} }
@@ -247,43 +243,4 @@ class ConnectionAcceptor {
} }
} }
private void processTRUST(Endpoint e) {
byte[] RUST = new byte[6]
DataInputStream dis = new DataInputStream(e.getInputStream())
dis.readFully(RUST)
if (RUST != "RUST\r\n".getBytes(StandardCharsets.US_ASCII))
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))
os.flush()
e.close()
return
}
os.write("200 OK\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
List<Persona> good = new ArrayList<>(trustService.good.values())
int size = Math.min(Short.MAX_VALUE * 2, good.size())
good = good.subList(0, size)
DataOutputStream dos = new DataOutputStream(os)
dos.writeShort(size)
good.each {
it.write(dos)
}
List<Persona> bad = new ArrayList<>(trustService.bad.values())
size = Math.min(Short.MAX_VALUE * 2, bad.size())
bad = bad.subList(0, size)
dos.writeShort(size)
bad.each {
it.write(dos)
}
dos.flush()
e.close()
}
} }

View File

@@ -1,9 +0,0 @@
package com.muwire.core.content
import com.muwire.core.Event
class ContentControlEvent extends Event {
String term
boolean regex
boolean add
}

View File

@@ -1,30 +0,0 @@
package com.muwire.core.content
import java.util.concurrent.ConcurrentHashMap
import com.muwire.core.search.QueryEvent
import net.i2p.util.ConcurrentHashSet
class ContentManager {
Set<Matcher> matchers = new ConcurrentHashSet()
void onContentControlEvent(ContentControlEvent e) {
Matcher m
if (e.regex)
m = new RegexMatcher(e.term)
else
m = new KeywordMatcher(e.term)
if (e.add)
matchers.add(m)
else
matchers.remove(m)
}
void onQueryEvent(QueryEvent e) {
if (e.searchEvent.searchTerms == null)
return
matchers.each { it.process(e) }
}
}

View File

@@ -1,36 +0,0 @@
package com.muwire.core.content
class KeywordMatcher extends Matcher {
private final String keyword
KeywordMatcher(String keyword) {
this.keyword = keyword
}
@Override
protected boolean match(List<String> searchTerms) {
boolean found = false
searchTerms.each {
if (keyword == it)
found = true
}
found
}
@Override
public String getTerm() {
keyword
}
@Override
public int hashCode() {
keyword.hashCode()
}
@Override
public boolean equals(Object o) {
if (!(o instanceof KeywordMatcher))
return false
KeywordMatcher other = (KeywordMatcher) o
keyword.equals(other.keyword)
}
}

View File

@@ -1,9 +0,0 @@
package com.muwire.core.content
import com.muwire.core.Persona
class Match {
Persona persona
String [] keywords
long timestamp
}

View File

@@ -1,20 +0,0 @@
package com.muwire.core.content
import com.muwire.core.search.QueryEvent
abstract class Matcher {
final List<Match> matches = Collections.synchronizedList(new ArrayList<>())
final Set<UUID> uuids = new HashSet<>()
protected abstract boolean match(List<String> searchTerms);
public abstract String getTerm();
public void process(QueryEvent qe) {
def terms = qe.searchEvent.searchTerms
if (match(terms) && uuids.add(qe.searchEvent.uuid)) {
long now = System.currentTimeMillis()
matches << new Match(persona : qe.originator, keywords : terms, timestamp : now)
}
}
}

View File

@@ -1,35 +0,0 @@
package com.muwire.core.content
import java.util.regex.Pattern
import java.util.stream.Collectors
class RegexMatcher extends Matcher {
private final Pattern pattern
RegexMatcher(String pattern) {
this.pattern = Pattern.compile(pattern)
}
@Override
protected boolean match(List<String> keywords) {
String combined = keywords.join(" ")
return pattern.matcher(combined).find()
}
@Override
public String getTerm() {
pattern.pattern()
}
@Override
public int hashCode() {
pattern.pattern().hashCode()
}
@Override
public boolean equals(Object o) {
if (!(o instanceof RegexMatcher))
return false
RegexMatcher other = (RegexMatcher) o
pattern.pattern() == other.pattern.pattern()
}
}

View File

@@ -14,7 +14,6 @@ import static com.muwire.core.util.DataUtil.readTillRN
import groovy.util.logging.Log import groovy.util.logging.Log
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel import java.nio.channels.FileChannel
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.Files import java.nio.file.Files
@@ -26,6 +25,8 @@ import java.util.logging.Level
@Log @Log
class DownloadSession { class DownloadSession {
private static int SAMPLES = 10
private final EventBus eventBus private final EventBus eventBus
private final String meB64 private final String meB64
private final Pieces pieces private final Pieces pieces
@@ -37,10 +38,10 @@ class DownloadSession {
private final Set<Integer> available private final Set<Integer> available
private final MessageDigest digest private final MessageDigest digest
private long lastSpeedRead = System.currentTimeMillis() private final LinkedList<Long> timestamps = new LinkedList<>()
private long dataSinceLastRead private final LinkedList<Integer> reads = new LinkedList<>()
private MappedByteBuffer mapped private ByteBuffer mapped
DownloadSession(EventBus eventBus, String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file, DownloadSession(EventBus eventBus, String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file,
int pieceSize, long fileLength, Set<Integer> available) { int pieceSize, long fileLength, Set<Integer> available) {
@@ -70,23 +71,21 @@ class DownloadSession {
OutputStream os = endpoint.getOutputStream() OutputStream os = endpoint.getOutputStream()
InputStream is = endpoint.getInputStream() InputStream is = endpoint.getInputStream()
int[] pieceAndPosition int piece
if (available.isEmpty()) if (available.isEmpty())
pieceAndPosition = pieces.claim() piece = pieces.claim()
else else
pieceAndPosition = pieces.claim(new HashSet<>(available)) piece = pieces.claim(new HashSet<>(available))
if (pieceAndPosition == null) if (piece == -1)
return false return false
int piece = pieceAndPosition[0]
int position = pieceAndPosition[1]
boolean steal = pieceAndPosition[2] == 1
boolean unclaim = true boolean unclaim = true
log.info("will download piece $piece from position $position steal $steal") log.info("will download piece $piece")
long start = piece * pieceSize
long end = Math.min(fileLength, start + pieceSize) - 1
long length = end - start + 1
long pieceStart = piece * ((long)pieceSize)
long end = Math.min(fileLength, pieceStart + pieceSize) - 1
long start = pieceStart + position
String root = Base64.encode(infoHash.getRoot()) String root = Base64.encode(infoHash.getRoot())
try { try {
@@ -175,9 +174,8 @@ class DownloadSession {
FileChannel channel FileChannel channel
try { try {
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE, channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE,
StandardOpenOption.SPARSE, StandardOpenOption.CREATE)) StandardOpenOption.SPARSE, StandardOpenOption.CREATE)) // TODO: double-check, maybe CREATE_NEW
mapped = channel.map(FileChannel.MapMode.READ_WRITE, pieceStart, end - pieceStart + 1) mapped = channel.map(FileChannel.MapMode.READ_WRITE, start, end - start + 1)
mapped.position(position)
byte[] tmp = new byte[0x1 << 13] byte[] tmp = new byte[0x1 << 13]
while(mapped.hasRemaining()) { while(mapped.hasRemaining()) {
@@ -188,8 +186,13 @@ class DownloadSession {
throw new IOException() throw new IOException()
synchronized(this) { synchronized(this) {
mapped.put(tmp, 0, read) mapped.put(tmp, 0, read)
dataSinceLastRead += read
pieces.markPartial(piece, mapped.position()) if (timestamps.size() == SAMPLES) {
timestamps.removeFirst()
reads.removeFirst()
}
timestamps.addLast(System.currentTimeMillis())
reads.addLast(read)
} }
} }
@@ -198,18 +201,15 @@ class DownloadSession {
byte [] hash = digest.digest() byte [] hash = digest.digest()
byte [] expected = new byte[32] byte [] expected = new byte[32]
System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32) System.arraycopy(infoHash.getHashList(), piece * 32, expected, 0, 32)
if (hash != expected) { if (hash != expected)
pieces.markPartial(piece, 0) throw new BadHashException()
throw new BadHashException("bad hash on piece $piece")
}
} finally { } finally {
try { channel?.close() } catch (IOException ignore) {} try { channel?.close() } catch (IOException ignore) {}
DataUtil.tryUnmap(mapped)
} }
pieces.markDownloaded(piece) pieces.markDownloaded(piece)
unclaim = false unclaim = false
} finally { } finally {
if (unclaim && !steal) if (unclaim)
pieces.unclaim(piece) pieces.unclaim(piece)
} }
return true return true
@@ -222,11 +222,24 @@ class DownloadSession {
} }
synchronized int speed() { synchronized int speed() {
if (timestamps.size() < SAMPLES)
return 0
int totalRead = 0
int idx = 0
final long now = System.currentTimeMillis() final long now = System.currentTimeMillis()
long interval = Math.max(1000, now - lastSpeedRead)
lastSpeedRead = now; while(idx < SAMPLES && timestamps.get(idx) < now - 1000)
int rv = (int) (dataSinceLastRead * 1000.0 / interval) idx++
dataSinceLastRead = 0 if (idx == SAMPLES)
rv return 0
if (idx == SAMPLES - 1)
return reads[idx]
long interval = timestamps.last - timestamps[idx]
if (interval == 0)
interval = 1
for (int i = idx; i < SAMPLES; i++)
totalRead += reads[idx]
(int)(totalRead * 1000.0 / interval)
} }
} }

View File

@@ -7,7 +7,6 @@ import com.muwire.core.connection.Endpoint
import java.nio.file.AtomicMoveNotSupportedException import java.nio.file.AtomicMoveNotSupportedException
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import java.time.Instant
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
@@ -59,11 +58,6 @@ public class Downloader {
private final AtomicBoolean eventFired = new AtomicBoolean() private final AtomicBoolean eventFired = new AtomicBoolean()
private boolean piecesFileClosed private boolean piecesFileClosed
private ArrayList speedArr = new ArrayList<Integer>()
private int speedPos = 0
private int speedAvg = 0
private long timestamp = Instant.now().toEpochMilli()
public Downloader(EventBus eventBus, DownloadManager downloadManager, public Downloader(EventBus eventBus, DownloadManager downloadManager,
Persona me, File file, long length, InfoHash infoHash, Persona me, File file, long length, InfoHash infoHash,
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations, int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
@@ -82,10 +76,6 @@ public class Downloader {
this.pieceSize = 1 << pieceSizePow2 this.pieceSize = 1 << pieceSizePow2
this.pieces = pieces this.pieces = pieces
this.nPieces = pieces.nPieces this.nPieces = pieces.nPieces
// default size suitable for an average of 5 seconds / 5 elements / 5 interval units
// it's easily adjustable by resizing the size of speedArr
this.speedArr = [ 0, 0, 0, 0, 0 ]
} }
public synchronized InfoHash getInfoHash() { public synchronized InfoHash getInfoHash() {
@@ -111,14 +101,8 @@ public class Downloader {
if (!piecesFile.exists()) if (!piecesFile.exists())
return return
piecesFile.eachLine { piecesFile.eachLine {
String [] split = it.split(",") int piece = Integer.parseInt(it)
int piece = Integer.parseInt(split[0])
if (split.length == 1)
pieces.markDownloaded(piece) pieces.markDownloaded(piece)
else {
int position = Integer.parseInt(split[1])
pieces.markPartial(piece, position)
}
} }
} }
@@ -127,7 +111,9 @@ public class Downloader {
if (piecesFileClosed) if (piecesFileClosed)
return return
piecesFile.withPrintWriter { writer -> piecesFile.withPrintWriter { writer ->
pieces.write(writer) pieces.getDownloaded().each { piece ->
writer.println(piece)
}
} }
} }
} }
@@ -138,35 +124,14 @@ public class Downloader {
public int speed() { public int speed() {
int currSpeed = 0 int total = 0
if (getCurrentState() == DownloadState.DOWNLOADING) { if (getCurrentState() == DownloadState.DOWNLOADING) {
activeWorkers.values().each { activeWorkers.values().each {
if (it.currentState == WorkerState.DOWNLOADING) if (it.currentState == WorkerState.DOWNLOADING)
currSpeed += it.speed() total += it.speed()
} }
} }
total
// normalize to speedArr.size
currSpeed /= speedArr.size()
// compute new speedAvg and update speedArr
if ( speedArr[speedPos] > speedAvg ) {
speedAvg = 0
} else {
speedAvg -= speedArr[speedPos]
}
speedAvg += currSpeed
speedArr[speedPos] = currSpeed
// this might be necessary due to rounding errors
if (speedAvg < 0)
speedAvg = 0
// rolling index over the speedArr
speedPos++
if (speedPos >= speedArr.size())
speedPos=0
speedAvg
} }
public DownloadState getCurrentState() { public DownloadState getCurrentState() {
@@ -307,17 +272,12 @@ public class Downloader {
} catch (Exception bad) { } catch (Exception bad) {
log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad)) log.log(Level.WARNING,"Exception while downloading",DataUtil.findRoot(bad))
} finally { } finally {
writePieces()
currentState = WorkerState.FINISHED currentState = WorkerState.FINISHED
if (pieces.isComplete() && eventFired.compareAndSet(false, true)) { if (pieces.isComplete() && eventFired.compareAndSet(false, true)) {
synchronized(piecesFile) { synchronized(piecesFile) {
piecesFileClosed = true piecesFileClosed = true
piecesFile.delete() piecesFile.delete()
} }
activeWorkers.values().each {
if (it.destination != destination)
it.cancel()
}
try { try {
Files.move(incompleteFile.toPath(), file.toPath(), StandardCopyOption.ATOMIC_MOVE) Files.move(incompleteFile.toPath(), file.toPath(), StandardCopyOption.ATOMIC_MOVE)
} catch (AtomicMoveNotSupportedException e) { } catch (AtomicMoveNotSupportedException e) {

View File

@@ -5,7 +5,6 @@ class Pieces {
private final int nPieces private final int nPieces
private final float ratio private final float ratio
private final Random random = new Random() private final Random random = new Random()
private final Map<Integer,Integer> partials = new HashMap<>()
Pieces(int nPieces) { Pieces(int nPieces) {
this(nPieces, 1.0f) this(nPieces, 1.0f)
@@ -18,22 +17,16 @@ class Pieces {
claimed = new BitSet(nPieces) claimed = new BitSet(nPieces)
} }
synchronized int[] claim() { synchronized int claim() {
int claimedCardinality = claimed.cardinality() int claimedCardinality = claimed.cardinality()
if (claimedCardinality == nPieces) { if (claimedCardinality == nPieces)
// steal return -1
int downloadedCardinality = done.cardinality()
if (downloadedCardinality == nPieces)
return null
int rv = done.nextClearBit(0)
return [rv, partials.getOrDefault(rv, 0), 1]
}
// if fuller than ratio just do sequential // if fuller than ratio just do sequential
if ( (1.0f * claimedCardinality) / nPieces > ratio) { if ( (1.0f * claimedCardinality) / nPieces > ratio) {
int rv = claimed.nextClearBit(0) int rv = claimed.nextClearBit(0)
claimed.set(rv) claimed.set(rv)
return [rv, partials.getOrDefault(rv, 0), 0] return rv
} }
while(true) { while(true) {
@@ -41,28 +34,20 @@ class Pieces {
if (claimed.get(start)) if (claimed.get(start))
continue continue
claimed.set(start) claimed.set(start)
return [start, partials.getOrDefault(start,0), 0] return start
} }
} }
synchronized int[] claim(Set<Integer> available) { synchronized int claim(Set<Integer> available) {
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1)) for (int i = claimed.nextSetBit(0); i >= 0; i = claimed.nextSetBit(i+1))
available.remove(i) available.remove(i)
if (available.isEmpty()) if (available.isEmpty())
return null return -1
Set<Integer> availableCopy = new HashSet<>(available) List<Integer> toList = available.toList()
for (int i = claimed.nextSetBit(0); i >= 0; i = claimed.nextSetBit(i+1))
availableCopy.remove(i)
if (availableCopy.isEmpty()) {
// steal
int rv = available.first()
return [rv, partials.getOrDefault(rv, 0), 1]
}
List<Integer> toList = availableCopy.toList()
Collections.shuffle(toList) Collections.shuffle(toList)
int rv = toList[0] int rv = toList[0]
claimed.set(rv) claimed.set(rv)
[rv, partials.getOrDefault(rv, 0), 0] rv
} }
synchronized def getDownloaded() { synchronized def getDownloaded() {
@@ -76,11 +61,6 @@ class Pieces {
synchronized void markDownloaded(int piece) { synchronized void markDownloaded(int piece) {
done.set(piece) done.set(piece)
claimed.set(piece) claimed.set(piece)
partials.remove(piece)
}
synchronized void markPartial(int piece, int position) {
partials.put(piece, position)
} }
synchronized void unclaim(int piece) { synchronized void unclaim(int piece) {
@@ -102,15 +82,5 @@ class Pieces {
synchronized void clearAll() { synchronized void clearAll() {
done.clear() done.clear()
claimed.clear() claimed.clear()
partials.clear()
}
synchronized void write(PrintWriter writer) {
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1)) {
writer.println(i)
}
partials.each { piece, position ->
writer.println("$piece,$position")
}
} }
} }

View File

@@ -1,7 +0,0 @@
package com.muwire.core.files
import com.muwire.core.Event
class DirectoryUnsharedEvent extends Event {
File directory
}

View File

@@ -35,7 +35,6 @@ class DirectoryWatcher {
private final FileManager fileManager private final FileManager fileManager
private final Thread watcherThread, publisherThread private final Thread watcherThread, publisherThread
private final Map<File, Long> waitingFiles = new ConcurrentHashMap<>() private final Map<File, Long> waitingFiles = new ConcurrentHashMap<>()
private final Map<File, WatchKey> watchedDirectories = new ConcurrentHashMap<>()
private WatchService watchService private WatchService watchService
private volatile boolean shutdown private volatile boolean shutdown
@@ -48,7 +47,7 @@ class DirectoryWatcher {
publisherThread.setDaemon(true) publisherThread.setDaemon(true)
} }
void onAllFilesLoadedEvent(AllFilesLoadedEvent e) { void start() {
watchService = FileSystems.getDefault().newWatchService() watchService = FileSystems.getDefault().newWatchService()
watcherThread.start() watcherThread.start()
publisherThread.start() publisherThread.start()
@@ -56,25 +55,19 @@ 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) {
if (!e.file.isDirectory()) if (!e.file.isDirectory())
return return
Path path = e.file.getCanonicalFile().toPath() Path path = e.file.getCanonicalFile().toPath()
WatchKey wk = path.register(watchService, kinds) path.register(watchService, kinds)
watchedDirectories.put(e.file, wk)
} }
void onDirectoryUnsharedEvent(DirectoryUnsharedEvent e) {
WatchKey wk = watchedDirectories.remove(e.directory)
wk?.cancel()
}
private void watch() { private void watch() {
try { try {
while(!shutdown) { while(!shutdown) {
@@ -120,7 +113,7 @@ class DirectoryWatcher {
private static File join(Path parent, Path path) { private static File join(Path parent, Path path) {
File parentFile = parent.toFile().getCanonicalFile() File parentFile = parent.toFile().getCanonicalFile()
new File(parentFile, path.toFile().getName()).getCanonicalFile() new File(parentFile, path.toFile().getName())
} }
private void publish() { private void publish() {

View File

@@ -7,10 +7,4 @@ 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

@@ -1,7 +1,6 @@
package com.muwire.core.files package com.muwire.core.files
import com.muwire.core.InfoHash import com.muwire.core.InfoHash
import com.muwire.core.util.DataUtil
import net.i2p.data.Base64 import net.i2p.data.Base64
@@ -19,8 +18,6 @@ class FileHasher {
/** /**
* @param size of the file to be shared * @param size of the file to be shared
* @return the size of each piece in power of 2 * @return the size of each piece in power of 2
* piece size is minimum 128 KBytees and maximum 16 MBytes in power of 2 steps (2^17 - 2^24)
* there can be up to 8192 pieces maximum per file
*/ */
static int getPieceSize(long size) { static int getPieceSize(long size) {
if (size <= 0x1 << 30) if (size <= 0x1 << 30)
@@ -60,7 +57,6 @@ class FileHasher {
for (int i = 0; i < numPieces - 1; i++) { for (int i = 0; i < numPieces - 1; i++) {
buf = raf.getChannel().map(MapMode.READ_ONLY, ((long)size) * i, size) buf = raf.getChannel().map(MapMode.READ_ONLY, ((long)size) * i, size)
digest.update buf digest.update buf
DataUtil.tryUnmap(buf)
output.write(digest.digest(), 0, 32) output.write(digest.digest(), 0, 32)
} }
def lastPieceLength = length - (numPieces - 1) * ((long)size) def lastPieceLength = length - (numPieces - 1) * ((long)size)

View File

@@ -1,15 +0,0 @@
package com.muwire.core.files
import com.muwire.core.Event
import com.muwire.core.SharedFile
class FileHashingEvent extends Event {
File hashingFile
@Override
public String toString() {
super.toString() + " hashingFile " + hashingFile.getAbsolutePath()
}
}

View File

@@ -135,16 +135,4 @@ class FileManager {
} }
rv rv
} }
void onDirectoryUnsharedEvent(DirectoryUnsharedEvent e) {
e.directory.listFiles().each {
if (it.isDirectory())
eventBus.publish(new DirectoryUnsharedEvent(directory : it))
else {
SharedFile sf = fileToSharedFile.get(it)
if (sf != null)
eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
}
}
}
} }

View File

@@ -5,9 +5,4 @@ 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

@@ -24,7 +24,7 @@ class HasherService {
} }
void onFileSharedEvent(FileSharedEvent evt) { void onFileSharedEvent(FileSharedEvent evt) {
if (fileManager.fileToSharedFile.containsKey(evt.file.getCanonicalFile())) if (fileManager.fileToSharedFile.containsKey(evt.file))
return return
executor.execute( { -> process(evt.file) } as Runnable) executor.execute( { -> process(evt.file) } as Runnable)
} }
@@ -39,7 +39,6 @@ class HasherService {
} else if (f.length() > FileHasher.MAX_SIZE) { } else if (f.length() > FileHasher.MAX_SIZE) {
eventBus.publish new FileHashedEvent(error: "$f is too large to be shared ${f.length()}") eventBus.publish new FileHashedEvent(error: "$f is too large to be shared ${f.length()}")
} else { } else {
eventBus.publish new FileHashingEvent(hashingFile: f)
def hash = hasher.hashFile f def hash = hasher.hashFile f
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length()))) eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length())))
} }

View File

@@ -6,8 +6,7 @@ class CacheServers {
private static final int TO_GIVE = 3 private static final int TO_GIVE = 3
private static Set<Destination> CACHES = [ private static Set<Destination> CACHES = [
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA"), new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA")
new Destination("JC63wJNOqSJmymkj4~UJWywBTvDGikKMoYP0HX2Wz9c5l3otXSkwnxWAFL4cKr~Ygh3BNNi2t93vuLIiI1W8AsE42kR~PwRx~Y-WvIHXR6KUejRmOp-n8WidtjKg9k4aDy428uSOedqXDxys5mpoeQXwDsv1CoPTTwnmb1GWFy~oTGIsCguCl~aJWGnqiKarPO3GJQ~ev-NbvAQzUfC3HeP1e6pdI5CGGjExahTCID5UjpJw8GaDXWlGmYWWH303Xu4x-vAHQy1dJLsOBCn8dZravsn5BKJk~j0POUon45CCx-~NYtaPe0Itt9cMdD2ciC76Rep1D0X0sm1SjlSs8sZ52KmF3oaLZ6OzgI9QLMIyBUrfi41sK5I0qTuUVBAkvW1xr~L-20dYJ9TrbOaOb2-vDIfKaxVi6xQOuhgQDiSBhd3qv2m0xGu-BM9DQYfNA0FdMjnZmqjmji9RMavzQSsVFIbQGLbrLepiEFlb7TseCK5UtRp8TxnG7L4gbYevBQAEAAcAAA==")
] ]
static List<Destination> getCacheServers() { static List<Destination> getCacheServers() {

View File

@@ -5,15 +5,14 @@ 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, int clearInterval) { public Host(Destination destination) {
this.destination = destination this.destination = destination
this.clearInterval = clearInterval
} }
synchronized void onConnect() { synchronized void onConnect() {
@@ -41,6 +40,6 @@ class Host {
} }
synchronized void canTryAgain() { synchronized void canTryAgain() {
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000) System.currentTimeMillis() - lastAttempt > CLEAR_INTERVAL
} }
} }

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, settings.hostClearInterval) Host host = new Host(e.destination)
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, settings.hostClearInterval) host = new Host(dest)
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, settings.hostClearInterval) Host host = new Host(dest)
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,10 +1,10 @@
package com.muwire.core.mesh package com.muwire.core.mesh
import java.util.logging.Level
import java.util.stream.Collectors 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.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
@@ -13,19 +13,21 @@ import com.muwire.core.util.DataUtil
import groovy.json.JsonOutput import groovy.json.JsonOutput
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import groovy.util.logging.Log
import net.i2p.data.Base64 import net.i2p.data.Base64
@Log
class MeshManager { class MeshManager {
private static final int EXPIRATION = 60 * 60 * 1000
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 File home
private final MuWireSettings settings
MeshManager(FileManager fileManager, File home, MuWireSettings settings) { MeshManager(FileManager fileManager, File home) {
this.fileManager = fileManager this.fileManager = fileManager
this.home = home this.home = home
this.settings = settings
load() load()
} }
@@ -37,7 +39,8 @@ 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, settings.downloadSequentialRatio) log.log(Level.INFO,"creating mesh with pieces:$nPieces", new Exception())
Pieces pieces = new Pieces(nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO)
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)
@@ -81,10 +84,10 @@ class MeshManager {
JsonSlurper slurper = new JsonSlurper() JsonSlurper slurper = new JsonSlurper()
meshFile.eachLine { meshFile.eachLine {
def json = slurper.parseText(it) def json = slurper.parseText(it)
if (now - json.timestamp > settings.meshExpiration * 60 * 1000) if (now - json.timestamp > EXPIRATION)
return return
InfoHash infoHash = new InfoHash(Base64.decode(json.infoHash)) InfoHash infoHash = new InfoHash(Base64.decode(json.infoHash))
Pieces pieces = new Pieces(json.nPieces, settings.downloadSequentialRatio) Pieces pieces = new Pieces(json.nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO)
Mesh mesh = new Mesh(infoHash, pieces) Mesh mesh = new Mesh(infoHash, pieces)
json.sources.each { source -> json.sources.each { source ->

View File

@@ -1,31 +0,0 @@
package com.muwire.core.trust
import java.util.concurrent.ConcurrentHashMap
import com.muwire.core.Persona
import net.i2p.util.ConcurrentHashSet
class RemoteTrustList {
public enum Status { NEW, UPDATING, UPDATED, UPDATE_FAILED }
private final Persona persona
private final Set<Persona> good, bad
volatile long timestamp
volatile boolean forceUpdate
Status status = Status.NEW
RemoteTrustList(Persona persona) {
this.persona = persona
good = new ConcurrentHashSet<>()
bad = new ConcurrentHashSet<>()
}
@Override
public boolean equals(Object o) {
if (!(o instanceof RemoteTrustList))
return false
RemoteTrustList other = (RemoteTrustList)o
persona == other.persona
}
}

View File

@@ -1,161 +0,0 @@
package com.muwire.core.trust
import java.nio.charset.StandardCharsets
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.logging.Level
import com.muwire.core.EventBus
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona
import com.muwire.core.UILoadedEvent
import com.muwire.core.connection.Endpoint
import com.muwire.core.connection.I2PConnector
import com.muwire.core.util.DataUtil
import groovy.util.logging.Log
import net.i2p.data.Destination
@Log
class TrustSubscriber {
private final EventBus eventBus
private final I2PConnector i2pConnector
private final MuWireSettings settings
private final Map<Destination, RemoteTrustList> remoteTrustLists = new ConcurrentHashMap<>()
private final Object waitLock = new Object()
private volatile boolean shutdown
private volatile Thread thread
private 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)
} else {
RemoteTrustList trustList = remoteTrustLists.putIfAbsent(e.persona.destination, new RemoteTrustList(e.persona))
trustList?.forceUpdate = true
synchronized(waitLock) {
waitLock.notify()
}
}
}
private void checkLoop() {
try {
while(!shutdown) {
synchronized(waitLock) {
waitLock.wait(60 * 1000)
}
final long now = System.currentTimeMillis()
remoteTrustLists.values().each { trustList ->
if (trustList.status == RemoteTrustList.Status.UPDATING)
return
if (!trustList.forceUpdate &&
now - trustList.timestamp < settings.trustListInterval * 60 * 60 * 1000)
return
trustList.forceUpdate = false
updateThreads.submit(new UpdateJob(trustList))
}
}
} catch (InterruptedException e) {
if (!shutdown)
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))
if (check(trustList, System.currentTimeMillis()))
trustList.status = RemoteTrustList.Status.UPDATED
else
trustList.status = RemoteTrustList.Status.UPDATE_FAILED
eventBus.publish(new TrustSubscriptionUpdatedEvent(trustList : trustList))
}
}
private boolean check(RemoteTrustList trustList, long now) {
log.info("fetching trust list from ${trustList.persona.getHumanReadableName()}")
Endpoint endpoint = null
try {
endpoint = i2pConnector.connect(trustList.persona.destination)
OutputStream os = endpoint.getOutputStream()
InputStream is = endpoint.getInputStream()
os.write("TRUST\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
os.flush()
String codeString = DataUtil.readTillRN(is)
int space = codeString.indexOf(' ')
if (space > 0)
codeString = codeString.substring(0,space)
int code = Integer.parseInt(codeString.trim())
if (code != 200) {
log.info("couldn't fetch trust list, code $code")
return false
}
// swallow any headers
String header
while (( header = DataUtil.readTillRN(is)) != "");
DataInputStream dis = new DataInputStream(is)
Set<Persona> good = new HashSet<>()
int nGood = dis.readUnsignedShort()
for (int i = 0; i < nGood; i++) {
Persona p = new Persona(dis)
good.add(p)
}
Set<Persona> bad = new HashSet<>()
int nBad = dis.readUnsignedShort()
for (int i = 0; i < nBad; i++) {
Persona p = new Persona(dis)
bad.add(p)
}
trustList.timestamp = now
trustList.good.clear()
trustList.good.addAll(good)
trustList.bad.clear()
trustList.bad.addAll(bad)
return true
} catch (Exception e) {
log.log(Level.WARNING,"exception fetching trust list from ${trustList.persona.getHumanReadableName()}",e)
return false
} finally {
endpoint?.close()
}
}
}

View File

@@ -1,9 +0,0 @@
package com.muwire.core.trust
import com.muwire.core.Event
import com.muwire.core.Persona
class TrustSubscriptionEvent extends Event {
Persona persona
boolean subscribe
}

View File

@@ -1,7 +0,0 @@
package com.muwire.core.trust
import com.muwire.core.Event
class TrustSubscriptionUpdatedEvent extends Event {
RemoteTrustList trustList
}

View File

@@ -3,15 +3,7 @@ package com.muwire.core.update
import java.util.logging.Level import java.util.logging.Level
import com.muwire.core.EventBus import com.muwire.core.EventBus
import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings import com.muwire.core.MuWireSettings
import com.muwire.core.Persona
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileManager
import com.muwire.core.search.QueryEvent
import com.muwire.core.search.SearchEvent
import com.muwire.core.search.UIResultBatchEvent
import groovy.json.JsonOutput import groovy.json.JsonOutput
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
@@ -21,7 +13,6 @@ import net.i2p.client.I2PSessionMuxedListener
import net.i2p.client.SendMessageOptions import net.i2p.client.SendMessageOptions
import net.i2p.client.datagram.I2PDatagramDissector import net.i2p.client.datagram.I2PDatagramDissector
import net.i2p.client.datagram.I2PDatagramMaker import net.i2p.client.datagram.I2PDatagramMaker
import net.i2p.data.Base64
import net.i2p.util.VersionComparator import net.i2p.util.VersionComparator
@Log @Log
@@ -30,24 +21,16 @@ class UpdateClient {
final I2PSession session final I2PSession session
final String myVersion final String myVersion
final MuWireSettings settings final MuWireSettings settings
final FileManager fileManager
final Persona me
private final Timer timer private final Timer timer
private long lastUpdateCheckTime private long lastUpdateCheckTime
private volatile InfoHash updateInfoHash UpdateClient(EventBus eventBus, I2PSession session, String myVersion, MuWireSettings settings) {
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.eventBus = eventBus
this.session = session this.session = session
this.myVersion = myVersion this.myVersion = myVersion
this.settings = settings this.settings = settings
this.fileManager = fileManager
this.me = me
timer = new Timer("update-client",true) timer = new Timer("update-client",true)
} }
@@ -60,24 +43,6 @@ class UpdateClient {
timer.cancel() timer.cancel()
} }
void onUIResultBatchEvent(UIResultBatchEvent results) {
if (results.results[0].infohash != updateInfoHash)
return
if (updateDownloading)
return
updateDownloading = true
def file = new File(settings.downloadLocation, results.results[0].name)
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() { private void checkUpdate() {
final long now = System.currentTimeMillis() final long now = System.currentTimeMillis()
if (lastUpdateCheckTime > 0) { if (lastUpdateCheckTime > 0) {
@@ -141,32 +106,8 @@ class UpdateClient {
return 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") log.info("new version $payload.version available, publishing event")
eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : infoHash)) eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : payload.infoHash))
} else {
log.info("new version $payload.version available")
updateInfoHash = new InfoHash(Base64.decode(infoHash))
if (fileManager.rootToFiles.containsKey(updateInfoHash))
eventBus.publish(new UpdateDownloadedEvent(version : payload.version, signer : payload.signer))
else {
updateDownloading = false
version = payload.version
signer = payload.signer
log.info("starting search for new version hash $payload.infoHash")
def searchEvent = new SearchEvent(searchHash : updateInfoHash.getRoot(), uuid : UUID.randomUUID(), oobInfohash : true)
def queryEvent = new QueryEvent(searchEvent : searchEvent, firstHop : true, replyTo : me.destination,
receivedOn : me.destination, originator : me)
eventBus.publish(queryEvent)
}
}
} catch (Exception e) { } catch (Exception e) {
log.log(Level.WARNING,"Invalid datagram",e) log.log(Level.WARNING,"Invalid datagram",e)

View File

@@ -1,8 +0,0 @@
package com.muwire.core.update
import com.muwire.core.Event
class UpdateDownloadedEvent extends Event {
String version
String signer
}

View File

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

View File

@@ -12,8 +12,10 @@ import com.muwire.core.connection.Endpoint
import com.muwire.core.mesh.Mesh import com.muwire.core.mesh.Mesh
import com.muwire.core.util.DataUtil import com.muwire.core.util.DataUtil
import groovy.util.logging.Log
import net.i2p.data.Destination import net.i2p.data.Destination
@Log
class ContentUploader extends Uploader { class ContentUploader extends Uploader {
private final File file private final File file
@@ -42,8 +44,10 @@ class ContentUploader extends Uploader {
int endPiece = range.end / (0x1 << pieceSize) int endPiece = range.end / (0x1 << pieceSize)
for (int i = startPiece; i <= endPiece; i++) for (int i = startPiece; i <= endPiece; i++)
satisfiable &= mesh.pieces.isDownloaded(i) satisfiable &= mesh.pieces.isDownloaded(i)
log.info("requested range $range.start-$range.end startPiece:$startPiece endPiece:$endPiece satisfiable:$satisfiable my pieces: ${mesh.pieces.getDownloaded()}")
} }
if (!satisfiable) { if (!satisfiable) {
log.info("416 range not satisfiable")
os.write("416 Range Not Satisfiable\r\n".getBytes(StandardCharsets.US_ASCII)) os.write("416 Range Not Satisfiable\r\n".getBytes(StandardCharsets.US_ASCII))
writeMesh(request.downloader) writeMesh(request.downloader)
os.write("\r\n".getBytes(StandardCharsets.US_ASCII)) os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
@@ -51,12 +55,14 @@ class ContentUploader extends Uploader {
return return
} }
log.info("200 ok")
os.write("200 OK\r\n".getBytes(StandardCharsets.US_ASCII)) os.write("200 OK\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("Content-Range: $range.start-$range.end\r\n".getBytes(StandardCharsets.US_ASCII)) os.write("Content-Range: $range.start-$range.end\r\n".getBytes(StandardCharsets.US_ASCII))
writeMesh(request.downloader) writeMesh(request.downloader)
os.write("\r\n".getBytes(StandardCharsets.US_ASCII)) os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
FileChannel channel = null FileChannel channel
try { try {
channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ)) channel = Files.newByteChannel(file.toPath(), EnumSet.of(StandardOpenOption.READ))
mapped = channel.map(FileChannel.MapMode.READ_ONLY, range.start, range.end - range.start + 1) mapped = channel.map(FileChannel.MapMode.READ_ONLY, range.start, range.end - range.start + 1)
@@ -72,10 +78,6 @@ class ContentUploader extends Uploader {
} finally { } finally {
try {channel?.close() } catch (IOException ignored) {} try {channel?.close() } catch (IOException ignored) {}
endpoint.getOutputStream().flush() endpoint.getOutputStream().flush()
synchronized(this) {
DataUtil.tryUnmap(mapped)
mapped = null
}
} }
} }
@@ -83,7 +85,7 @@ class ContentUploader extends Uploader {
String xHave = DataUtil.encodeXHave(mesh.pieces.getDownloaded(), mesh.pieces.nPieces) String xHave = DataUtil.encodeXHave(mesh.pieces.getDownloaded(), mesh.pieces.nPieces)
endpoint.getOutputStream().write("X-Have: $xHave\r\n".getBytes(StandardCharsets.US_ASCII)) endpoint.getOutputStream().write("X-Have: $xHave\r\n".getBytes(StandardCharsets.US_ASCII))
Set<Persona> sources = mesh.getRandom(9, toExclude) Set<Persona> sources = mesh.getRandom(3, toExclude)
if (!sources.isEmpty()) { if (!sources.isEmpty()) {
String xAlts = sources.stream().map({ it.toBase64() }).collect(Collectors.joining(",")) String xAlts = sources.stream().map({ it.toBase64() }).collect(Collectors.joining(","))
endpoint.getOutputStream().write("X-Alt: $xAlts\r\n".getBytes(StandardCharsets.US_ASCII)) endpoint.getOutputStream().write("X-Alt: $xAlts\r\n".getBytes(StandardCharsets.US_ASCII))
@@ -109,18 +111,4 @@ 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;
}
@Override
public long getTotalSize() {
return file.length();
}
} }

View File

@@ -51,18 +51,5 @@ class HashListUploader extends Uploader {
request.downloader.getHumanReadableName() request.downloader.getHumanReadableName()
} }
@Override
public int getDonePieces() {
return 0;
}
@Override
public int getTotalPieces() {
return 1;
}
@Override
public long getTotalSize() {
return -1;
}
} }

View File

@@ -50,10 +50,10 @@ class Request {
downloader = new Persona(new ByteArrayInputStream(decoded)) downloader = new Persona(new ByteArrayInputStream(decoded))
} }
int have = 0 boolean have = false
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() have = DataUtil.decodeXHave(encoded).size() > 0
} }
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 > 0) if (request.have)
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 > 0) if (request.have)
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,10 +32,4 @@ abstract class Uploader {
abstract int getProgress(); abstract int getProgress();
abstract String getDownloader(); abstract String getDownloader();
abstract int getDonePieces();
abstract int getTotalPieces();
abstract long getTotalSize();
} }

View File

@@ -1,8 +1,5 @@
package com.muwire.core.util package com.muwire.core.util
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import com.muwire.core.Constants import com.muwire.core.Constants
@@ -118,39 +115,4 @@ class DataUtil {
e = e.getCause() e = e.getCause()
e 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 {
if (isOldJDK) {
Method cleaner = cb.getClass().getMethod("cleaner");
cleaner.setAccessible(true);
Method clean = Class.forName("sun.misc.Cleaner").getMethod("clean");
clean.setAccessible(true);
clean.invoke(cleaner.invoke(cb));
} else {
Class unsafeClass;
try {
unsafeClass = Class.forName("sun.misc.Unsafe");
} catch(Exception ex) {
// jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method,
// but that method should be added if sun.misc.Unsafe is removed.
unsafeClass = Class.forName("jdk.internal.misc.Unsafe");
}
Method clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer.class);
clean.setAccessible(true);
Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Object theUnsafe = theUnsafeField.get(null);
clean.invoke(theUnsafe, cb);
}
} catch(Exception ex) { }
cb = null;
}
} }

View File

@@ -1,7 +1,6 @@
package com.muwire.core; package com.muwire.core;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.Set; import java.util.Set;
import net.i2p.data.Destination; import net.i2p.data.Destination;
@@ -10,8 +9,7 @@ public class DownloadedFile extends SharedFile {
private final Set<Destination> sources; private final Set<Destination> sources;
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources) public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources) {
throws IOException {
super(file, infoHash, pieceSize); super(file, infoHash, pieceSize);
this.sources = sources; this.sources = sources;
} }

View File

@@ -1,7 +1,6 @@
package com.muwire.core; package com.muwire.core;
import java.io.File; import java.io.File;
import java.io.IOException;
public class SharedFile { public class SharedFile {
@@ -9,15 +8,10 @@ public class SharedFile {
private final InfoHash infoHash; private final InfoHash infoHash;
private final int pieceSize; private final int pieceSize;
private final String cachedPath; public SharedFile(File file, InfoHash infoHash, int pieceSize) {
private final long cachedLength;
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
this.file = file; this.file = file;
this.infoHash = infoHash; this.infoHash = infoHash;
this.pieceSize = pieceSize; this.pieceSize = pieceSize;
this.cachedPath = file.getAbsolutePath();
this.cachedLength = file.length();
} }
public File getFile() { public File getFile() {
@@ -41,14 +35,6 @@ public class SharedFile {
return rv; return rv;
} }
public String getCachedPath() {
return cachedPath;
}
public long getCachedLength() {
return cachedLength;
}
@Override @Override
public int hashCode() { public int hashCode() {
return file.hashCode() ^ infoHash.hashCode(); return file.hashCode() ^ infoHash.hashCode();

View File

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

34
gradlew vendored
View File

@@ -11,21 +11,21 @@
PRG="$0" PRG="$0"
# Need this for relative symlinks. # Need this for relative symlinks.
while [ -h "$PRG" ] ; do while [ -h "$PRG" ] ; do
ls=$(ls -ld "$PRG") ls=`ls -ld "$PRG"`
link=$(expr "$ls" : '.*-> \(.*\)$') link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then if expr "$link" : '/.*' > /dev/null; then
PRG="$link" PRG="$link"
else else
PRG=$(dirname "$PRG")"/$link" PRG=`dirname "$PRG"`"/$link"
fi fi
done done
SAVED="$(pwd)" SAVED="`pwd`"
cd "$(dirname "$PRG")/" >/dev/null cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="$(pwd -P)" APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=$(basename "$0") APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS="" DEFAULT_JVM_OPTS=""
@@ -49,7 +49,7 @@ cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "$(uname)" in case "`uname`" in
CYGWIN* ) CYGWIN* )
cygwin=true cygwin=true
;; ;;
@@ -90,7 +90,7 @@ fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=$(ulimit -H -n) MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT" MAX_FD="$MAX_FD_LIMIT"
@@ -111,12 +111,12 @@ fi
# For Cygwin, switch paths to Windows format before running java # For Cygwin, switch paths to Windows format before running java
if $cygwin ; then if $cygwin ; then
APP_HOME=$(cygpath --path --mixed "$APP_HOME") APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=$(cygpath --path --mixed "$CLASSPATH") CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=$(cygpath --unix "$JAVACMD") JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath # We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null) ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP="" SEP=""
for dir in $ROOTDIRSRAW ; do for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir" ROOTDIRS="$ROOTDIRS$SEP$dir"
@@ -130,13 +130,13 @@ if $cygwin ; then
# Now convert the arguments - kludge to limit ourselves to /bin/sh # Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0 i=0
for arg in "$@" ; do for arg in "$@" ; do
CHECK=$(echo "$arg"|egrep -c "$OURCYGPATTERN" -) CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=$(echo "$arg"|egrep -c "^-") ### Determine if an option CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg") eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else else
eval $(echo args$i)="\"$arg\"" eval `echo args$i`="\"$arg\""
fi fi
i=$((i+1)) i=$((i+1))
done done

View File

@@ -26,24 +26,4 @@ mvcGroups {
view = 'com.muwire.gui.OptionsView' view = 'com.muwire.gui.OptionsView'
controller = 'com.muwire.gui.OptionsController' controller = 'com.muwire.gui.OptionsController'
} }
"mu-wire-status" {
model = 'com.muwire.gui.MuWireStatusModel'
view = 'com.muwire.gui.MuWireStatusView'
controller = 'com.muwire.gui.MuWireStatusController'
}
'i-2-p-status' {
model = 'com.muwire.gui.I2PStatusModel'
view = 'com.muwire.gui.I2PStatusView'
controller = 'com.muwire.gui.I2PStatusController'
}
'trust-list' {
model = 'com.muwire.gui.TrustListModel'
view = 'com.muwire.gui.TrustListView'
controller = 'com.muwire.gui.TrustListController'
}
'content-panel' {
model = 'com.muwire.gui.ContentPanelModel'
view = 'com.muwire.gui.ContentPanelView'
controller = 'com.muwire.gui.ContentPanelController'
}
} }

View File

@@ -1,95 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull
import com.muwire.core.Core
import com.muwire.core.EventBus
import com.muwire.core.content.ContentControlEvent
import com.muwire.core.content.Match
import com.muwire.core.content.Matcher
import com.muwire.core.content.RegexMatcher
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel
@ArtifactProviderFor(GriffonController)
class ContentPanelController {
@MVCMember @Nonnull
ContentPanelModel model
@MVCMember @Nonnull
ContentPanelView view
Core core
@ControllerAction
void addRule() {
def term = view.ruleTextField.text
if (model.regex)
core.muOptions.watchedRegexes.add(term)
else
core.muOptions.watchedKeywords.add(term)
saveMuWireSettings()
core.eventBus.publish(new ContentControlEvent(term : term, regex : model.regex, add:true))
}
@ControllerAction
void deleteRule() {
int rule = view.getSelectedRule()
if (rule < 0)
return
Matcher matcher = model.rules[rule]
String term = matcher.getTerm()
if (matcher instanceof RegexMatcher)
core.muOptions.watchedRegexes.remove(term)
else
core.muOptions.watchedKeywords.remove(term)
saveMuWireSettings()
core.eventBus.publish(new ContentControlEvent(term : term, regex : (matcher instanceof RegexMatcher), add: false))
}
@ControllerAction
void keyword() {
model.regex = false
}
@ControllerAction
void regex() {
model.regex = true
}
@ControllerAction
void refresh() {
model.refresh()
}
@ControllerAction
void trust() {
int selectedHit = view.getSelectedHit()
if (selectedHit < 0)
return
Match m = model.hits[selectedHit]
core.eventBus.publish(new TrustEvent(persona : m.persona, level : TrustLevel.TRUSTED))
}
@ControllerAction
void distrust() {
int selectedHit = view.getSelectedHit()
if (selectedHit < 0)
return
Match m = model.hits[selectedHit]
core.eventBus.publish(new TrustEvent(persona : m.persona, level : TrustLevel.DISTRUSTED))
}
void saveMuWireSettings() {
File f = new File(core.home, "MuWire.properties")
f.withOutputStream {
core.muOptions.write(it)
}
}
}

View File

@@ -1,41 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import net.i2p.router.Router
import javax.annotation.Nonnull
import com.muwire.core.Core
@ArtifactProviderFor(GriffonController)
class I2PStatusController {
@MVCMember @Nonnull
I2PStatusModel model
@MVCMember @Nonnull
I2PStatusView view
@ControllerAction
void refresh() {
Core core = application.context.get("core")
Router router = core.router
model.networkStatus = router._context.commSystem().status.toStatusString()
model.floodfill = router._context.netDb().floodfillEnabled()
model.ntcpConnections = router._context.commSystem().getTransports()["NTCP"].countPeers()
model.ssuConnections = router._context.commSystem().getTransports()["SSU"].countPeers()
model.participatingTunnels = router._context.tunnelManager().getParticipatingCount()
model.activePeers = router._context.profileOrganizer().countActivePeers()
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)
mvcGroup.destroy()
}
}

View File

@@ -11,25 +11,18 @@ import net.i2p.data.Base64
import javax.annotation.Nonnull import javax.annotation.Nonnull
import javax.inject.Inject import javax.inject.Inject
import javax.swing.JTable
import com.muwire.core.Constants import com.muwire.core.Constants
import com.muwire.core.Core import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.SharedFile
import com.muwire.core.download.DownloadStartedEvent import com.muwire.core.download.DownloadStartedEvent
import com.muwire.core.download.UIDownloadCancelledEvent import com.muwire.core.download.UIDownloadCancelledEvent
import com.muwire.core.download.UIDownloadEvent import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.download.UIDownloadPausedEvent import com.muwire.core.download.UIDownloadPausedEvent
import com.muwire.core.download.UIDownloadResumedEvent import com.muwire.core.download.UIDownloadResumedEvent
import com.muwire.core.files.DirectoryUnsharedEvent
import com.muwire.core.files.FileUnsharedEvent
import com.muwire.core.search.QueryEvent import com.muwire.core.search.QueryEvent
import com.muwire.core.search.SearchEvent import com.muwire.core.search.SearchEvent
import com.muwire.core.trust.RemoteTrustList
import com.muwire.core.trust.TrustEvent import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel import com.muwire.core.trust.TrustLevel
import com.muwire.core.trust.TrustSubscriptionEvent
@ArtifactProviderFor(GriffonController) @ArtifactProviderFor(GriffonController)
class MainFrameController { class MainFrameController {
@@ -39,8 +32,6 @@ class MainFrameController {
@MVCMember @Nonnull @MVCMember @Nonnull
MainFrameModel model MainFrameModel model
@MVCMember @Nonnull
MainFrameView view
private volatile Core core private volatile Core core
@@ -53,13 +44,10 @@ 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
params["uuid"] = uuid.toString() params["uuid"] = uuid.toString()
params["core"] = core
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params) def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
model.results[uuid.toString()] = group model.results[uuid.toString()] = group
@@ -97,7 +85,6 @@ class MainFrameController {
Map<String, Object> params = new HashMap<>() Map<String, Object> params = new HashMap<>()
params["search-terms"] = tabTitle params["search-terms"] = tabTitle
params["uuid"] = uuid.toString() params["uuid"] = uuid.toString()
params["core"] = core
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params) def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
model.results[uuid.toString()] = group model.results[uuid.toString()] = group
@@ -108,6 +95,20 @@ class MainFrameController {
originator : core.me)) originator : core.me))
} }
private def selectedResult() {
def selected = builder.getVariable("result-tabs").getSelectedComponent()
def group = selected.getClientProperty("mvc-group")
def table = selected.getClientProperty("results-table")
int row = table.getSelectedRow()
if (row == -1)
return
def sortEvt = group.view.lastSortEvent
if (sortEvt != null) {
row = group.view.resultsTable.rowSorter.convertRowIndexToModel(row)
}
group.model.results[row]
}
private int selectedDownload() { private int selectedDownload() {
def downloadsTable = builder.getVariable("downloads-table") def downloadsTable = builder.getVariable("downloads-table")
def selected = downloadsTable.getSelectedRow() def selected = downloadsTable.getSelectedRow()
@@ -118,21 +119,39 @@ class MainFrameController {
} }
@ControllerAction @ControllerAction
void trustPersonaFromSearch() { void download() {
int selected = builder.getVariable("searches-table").getSelectedRow() def result = selectedResult()
if (selected < 0) if (result == null)
return return
Persona p = model.searches[selected].originator
core.eventBus.publish( new TrustEvent(persona : p, level : TrustLevel.TRUSTED) ) if (!model.canDownload(result.infohash))
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 @ControllerAction
void distrustPersonaFromSearch() { void trust() {
int selected = builder.getVariable("searches-table").getSelectedRow() def result = selectedResult()
if (selected < 0) if (result == null)
return return // TODO disable button
Persona p = model.searches[selected].originator core.eventBus.publish( new TrustEvent(persona : result.sender, level : TrustLevel.TRUSTED))
core.eventBus.publish( new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED) ) }
@ControllerAction
void distrust() {
def result = selectedResult()
if (result == null)
return // TODO disable button
core.eventBus.publish( new TrustEvent(persona : result.sender, level : TrustLevel.DISTRUSTED))
} }
@ControllerAction @ControllerAction
@@ -158,115 +177,34 @@ class MainFrameController {
} }
private void markTrust(String tableName, TrustLevel level, def list) { private void markTrust(String tableName, TrustLevel level, def list) {
int row = view.getSelectedTrustTablesRow(tableName) int row = builder.getVariable(tableName).getSelectedRow()
if (row < 0) if (row < 0)
return return
builder.getVariable(tableName).model.fireTableDataChanged()
core.eventBus.publish(new TrustEvent(persona : list[row], level : level)) core.eventBus.publish(new TrustEvent(persona : list[row], level : level))
} }
@ControllerAction @ControllerAction
void markTrusted() { void markTrusted() {
markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted) markTrust("distrusted-table", TrustLevel.TRUSTED, model.distrusted)
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
} }
@ControllerAction @ControllerAction
void markNeutralFromDistrusted() { void markNeutralFromDistrusted() {
markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted) markTrust("distrusted-table", TrustLevel.NEUTRAL, model.distrusted)
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
} }
@ControllerAction @ControllerAction
void markDistrusted() { void markDistrusted() {
markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted) markTrust("trusted-table", TrustLevel.DISTRUSTED, model.trusted)
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
} }
@ControllerAction @ControllerAction
void markNeutralFromTrusted() { void markNeutralFromTrusted() {
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted) markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
} }
@ControllerAction void unshareSelectedFiles() {
void subscribe() { println "unsharing selected files"
int row = view.getSelectedTrustTablesRow("trusted-table")
if (row < 0)
return
Persona p = model.trusted[row]
core.muOptions.trustSubscriptions.add(p)
saveMuWireSettings()
core.eventBus.publish(new TrustSubscriptionEvent(persona : p, subscribe : true))
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
}
@ControllerAction
void review() {
RemoteTrustList list = getSelectedTrustList()
if (list == null)
return
Map<String,Object> env = new HashMap<>()
env["trustList"] = list
env["trustService"] = core.trustService
env["eventBus"] = core.eventBus
mvcGroup.createMVCGroup("trust-list", env)
}
@ControllerAction
void update() {
RemoteTrustList list = getSelectedTrustList()
if (list == null)
return
core.eventBus.publish(new TrustSubscriptionEvent(persona : list.persona, subscribe : true))
}
@ControllerAction
void unsubscribe() {
RemoteTrustList list = getSelectedTrustList()
if (list == null)
return
core.muOptions.trustSubscriptions.remove(list.persona)
saveMuWireSettings()
model.subscriptions.remove(list)
JTable table = builder.getVariable("subscription-table")
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()
core.eventBus.publish(new DirectoryUnsharedEvent(directory : new File(directory)))
model.watched.remove(directory)
builder.getVariable("watched-directories-table").model.fireTableDataChanged()
} }
void saveMuWireSettings() { void saveMuWireSettings() {

View File

@@ -1,45 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull
import com.muwire.core.Core
@ArtifactProviderFor(GriffonController)
class MuWireStatusController {
@MVCMember @Nonnull
MuWireStatusModel model
@MVCMember @Nonnull
MuWireStatusView view
@ControllerAction
void refresh() {
Core core = application.context.get("core")
int incoming = 0
int outgoing = 0
core.connectionManager.getConnections().each {
if (it.incoming)
incoming++
else
outgoing++
}
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)
mvcGroup.destroy()
}
}

View File

@@ -4,15 +4,9 @@ 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 {
@@ -25,7 +19,6 @@ class OptionsController {
void save() { void save() {
String text String text
Core core = application.context.get("core") Core core = application.context.get("core")
MuWireSettings settings = application.context.get("muwire-settings")
def i2pProps = core.i2pOptions def i2pProps = core.i2pOptions
@@ -45,17 +38,6 @@ class OptionsController {
model.outboundLength = text model.outboundLength = text
i2pProps["outbound.length"] = 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") File i2pSettingsFile = new File(core.home, "i2p.properties")
i2pSettingsFile.withOutputStream { i2pSettingsFile.withOutputStream {
i2pProps.store(it,"") i2pProps.store(it,"")
@@ -64,45 +46,20 @@ class OptionsController {
text = view.retryField.text text = view.retryField.text
model.downloadRetryInterval = text model.downloadRetryInterval = text
def settings = application.context.get("muwire-settings")
settings.downloadRetryInterval = Integer.valueOf(text) settings.downloadRetryInterval = Integer.valueOf(text)
text = view.updateField.text text = view.updateField.text
model.updateCheckInterval = text model.updateCheckInterval = text
settings.updateCheckInterval = Integer.valueOf(text) settings.updateCheckInterval = Integer.valueOf(text)
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)
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)
}
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected() boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
model.onlyTrusted = onlyTrusted model.onlyTrusted = onlyTrusted
settings.setAllowUntrusted(!onlyTrusted) settings.setAllowUntrusted(!onlyTrusted)
boolean trustLists = view.allowTrustListsCheckbox.model.isSelected() boolean shareDownloaded = view.shareDownloadedCheckbox.model.isSelected()
model.trustLists = trustLists model.shareDownloadedFiles = shareDownloaded
settings.allowTrustLists = trustLists settings.shareDownloadedFiles = shareDownloaded
String trustListInterval = view.trustListIntervalField.text
model.trustListInterval = trustListInterval
settings.trustListInterval = Integer.parseInt(trustListInterval)
File settingsFile = new File(core.home, "MuWire.properties") File settingsFile = new File(core.home, "MuWire.properties")
settingsFile.withOutputStream { settingsFile.withOutputStream {
@@ -153,15 +110,4 @@ 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

@@ -6,65 +6,6 @@ import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull import javax.annotation.Nonnull
import com.muwire.core.Core
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel
@ArtifactProviderFor(GriffonController) @ArtifactProviderFor(GriffonController)
class SearchTabController { class SearchTabController {
@MVCMember @Nonnull
SearchTabModel model
@MVCMember @Nonnull
SearchTabView view
Core core
private def selectedResult() {
int row = view.resultsTable.getSelectedRow()
if (row == -1)
return null
def sortEvt = view.lastSortEvent
if (sortEvt != null) {
row = view.resultsTable.rowSorter.convertRowIndexToModel(row)
}
model.results[row]
}
@ControllerAction
void download() {
def result = selectedResult()
if (result == null)
return
if (!mvcGroup.parentGroup.model.canDownload(result.infohash))
return
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
def resultsBucket = model.hashBucket[result.infohash]
def sources = model.sourcesBucket[result.infohash]
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources, target : file))
mvcGroup.parentGroup.view.showDownloadsWindow.call()
}
@ControllerAction
void trust() {
int row = view.selectedSenderRow()
if (row < 0)
return
def sender = model.senders[row]
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.TRUSTED))
}
@ControllerAction
void distrust() {
int row = view.selectedSenderRow()
if (row < 0)
return
def sender = model.senders[row]
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.DISTRUSTED))
}
} }

View File

@@ -1,62 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonController
import griffon.core.controller.ControllerAction
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.annotation.Nonnull
import com.muwire.core.EventBus
import com.muwire.core.Persona
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel
@ArtifactProviderFor(GriffonController)
class TrustListController {
@MVCMember @Nonnull
TrustListModel model
@MVCMember @Nonnull
TrustListView view
EventBus eventBus
@ControllerAction
void trustFromTrusted() {
int selectedRow = view.getSelectedRow("trusted-table")
if (selectedRow < 0)
return
Persona p = model.trusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
view.fireUpdate("trusted-table")
}
@ControllerAction
void trustFromDistrusted() {
int selectedRow = view.getSelectedRow("distrusted-table")
if (selectedRow < 0)
return
Persona p = model.distrusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED))
view.fireUpdate("distrusted-table")
}
@ControllerAction
void distrustFromTrusted() {
int selectedRow = view.getSelectedRow("trusted-table")
if (selectedRow < 0)
return
Persona p = model.trusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
view.fireUpdate("trusted-table")
}
@ControllerAction
void distrustFromDistrusted() {
int selectedRow = view.getSelectedRow("distrusted-table")
if (selectedRow < 0)
return
Persona p = model.distrusted[selectedRow]
eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED))
view.fireUpdate("distrusted-table")
}
}

View File

@@ -43,8 +43,6 @@ 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

@@ -48,8 +48,6 @@ class Ready extends AbstractLifecycleHandler {
} else { } else {
log.info("creating new properties") log.info("creating new properties")
props = new MuWireSettings() props = new MuWireSettings()
props.embeddedRouter = Boolean.parseBoolean(System.getProperties().getProperty("embeddedRouter"))
props.updateType = System.getProperty("updateType","jar")
def nickname def nickname
while (true) { while (true) {
nickname = JOptionPane.showInputDialog(null, nickname = JOptionPane.showInputDialog(null,
@@ -76,7 +74,6 @@ 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

@@ -1,55 +0,0 @@
package com.muwire.gui
import javax.annotation.Nonnull
import com.muwire.core.Core
import com.muwire.core.EventBus
import com.muwire.core.content.ContentControlEvent
import com.muwire.core.content.ContentManager
import griffon.core.artifact.GriffonModel
import griffon.inject.MVCMember
import griffon.transform.Observable
import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
class ContentPanelModel {
@MVCMember @Nonnull
ContentPanelView view
Core core
private ContentManager contentManager
def rules = []
def hits = []
@Observable boolean regex
@Observable boolean deleteButtonEnabled
@Observable boolean trustButtonsEnabled
void mvcGroupInit(Map<String,String> args) {
contentManager = application.context.get("core").contentManager
rules.addAll(contentManager.matchers)
core.eventBus.register(ContentControlEvent.class, this)
}
void mvcGroupDestroy() {
core.eventBus.unregister(ContentControlEvent.class, this)
}
void refresh() {
rules.clear()
rules.addAll(contentManager.matchers)
hits.clear()
view.rulesTable.model.fireTableDataChanged()
view.hitsTable.model.fireTableDataChanged()
}
void onContentControlEvent(ContentControlEvent e) {
runInsideUIAsync {
refresh()
}
}
}

View File

@@ -1,28 +0,0 @@
package com.muwire.gui
import javax.annotation.Nonnull
import griffon.core.artifact.GriffonModel
import griffon.inject.MVCMember
import griffon.transform.Observable
import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
class I2PStatusModel {
@MVCMember @Nonnull
I2PStatusController controller
@Observable int ntcpConnections
@Observable int ssuConnections
@Observable String networkStatus
@Observable boolean floodfill
@Observable int participatingTunnels
@Observable int activePeers
@Observable int receiveBps
@Observable int sendBps
@Observable int participatingBW
void mvcGroupInit(Map<String,String> args) {
controller.refresh()
}
}

View File

@@ -1,8 +1,6 @@
package com.muwire.gui package com.muwire.gui
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.Calendar
import java.util.UUID
import javax.annotation.Nonnull import javax.annotation.Nonnull
import javax.inject.Inject import javax.inject.Inject
@@ -17,13 +15,10 @@ import com.muwire.core.RouterDisconnectedEvent
import com.muwire.core.connection.ConnectionAttemptStatus import com.muwire.core.connection.ConnectionAttemptStatus
import com.muwire.core.connection.ConnectionEvent import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.connection.DisconnectionEvent import com.muwire.core.connection.DisconnectionEvent
import com.muwire.core.content.ContentControlEvent
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.FileHashingEvent
import com.muwire.core.files.FileLoadedEvent import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileSharedEvent import com.muwire.core.files.FileSharedEvent
import com.muwire.core.files.FileUnsharedEvent import com.muwire.core.files.FileUnsharedEvent
@@ -32,10 +27,7 @@ import com.muwire.core.search.UIResultBatchEvent
import com.muwire.core.search.UIResultEvent import com.muwire.core.search.UIResultEvent
import com.muwire.core.trust.TrustEvent import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustService import com.muwire.core.trust.TrustService
import com.muwire.core.trust.TrustSubscriptionEvent
import com.muwire.core.trust.TrustSubscriptionUpdatedEvent
import com.muwire.core.update.UpdateAvailableEvent import com.muwire.core.update.UpdateAvailableEvent
import com.muwire.core.update.UpdateDownloadedEvent
import com.muwire.core.upload.UploadEvent import com.muwire.core.upload.UploadEvent
import com.muwire.core.upload.UploadFinishedEvent import com.muwire.core.upload.UploadFinishedEvent
@@ -59,7 +51,6 @@ class MainFrameModel {
MainFrameController controller MainFrameController controller
@Inject @Nonnull GriffonApplication application @Inject @Nonnull GriffonApplication application
@Observable boolean coreInitialized = false @Observable boolean coreInitialized = false
@Observable boolean routerPresent
def results = new ConcurrentHashMap<>() def results = new ConcurrentHashMap<>()
def downloads = [] def downloads = []
@@ -70,36 +61,21 @@ class MainFrameModel {
def searches = new LinkedList() def searches = new LinkedList()
def trusted = [] def trusted = []
def distrusted = [] def distrusted = []
def subscriptions = []
@Observable int connections @Observable int connections
@Observable String me @Observable String me
@Observable int loadedFiles @Observable boolean downloadActionEnabled
@Observable File hashingFile @Observable boolean trustButtonsEnabled
@Observable boolean cancelButtonEnabled @Observable boolean cancelButtonEnabled
@Observable boolean retryButtonEnabled @Observable boolean retryButtonEnabled
@Observable boolean pauseButtonEnabled @Observable boolean pauseButtonEnabled
@Observable String resumeButtonText @Observable String resumeButtonText
@Observable boolean subscribeButtonEnabled
@Observable boolean markNeutralFromTrustedButtonEnabled
@Observable boolean markDistrustedButtonEnabled
@Observable boolean markNeutralFromDistrustedButtonEnabled
@Observable boolean markTrustedButtonEnabled
@Observable boolean reviewButtonEnabled
@Observable boolean updateButtonEnabled
@Observable boolean unsubscribeButtonEnabled
@Observable boolean searchesPaneButtonEnabled
@Observable boolean downloadsPaneButtonEnabled
@Observable boolean uploadsPaneButtonEnabled
@Observable boolean monitorPaneButtonEnabled
@Observable boolean trustPaneButtonEnabled
private final Set<InfoHash> infoHashes = new HashSet<>() private final Set<InfoHash> infoHashes = new HashSet<>()
private final Set<InfoHash> downloadInfoHashes = new HashSet<>() private final Set<InfoHash> downloadInfoHashes = new HashSet<>()
@Observable volatile Core core volatile Core core
private long lastRetryTime = System.currentTimeMillis() private long lastRetryTime = System.currentTimeMillis()
@@ -147,7 +123,6 @@ class MainFrameModel {
application.addPropertyChangeListener("core", {e -> application.addPropertyChangeListener("core", {e ->
coreInitialized = (e.getNewValue() != null) coreInitialized = (e.getNewValue() != null)
core = e.getNewValue() core = e.getNewValue()
routerPresent = core.router != null
me = core.me.getHumanReadableName() me = core.me.getHumanReadableName()
core.eventBus.register(UIResultEvent.class, this) core.eventBus.register(UIResultEvent.class, this)
core.eventBus.register(UIResultBatchEvent.class, this) core.eventBus.register(UIResultBatchEvent.class, this)
@@ -155,7 +130,6 @@ class MainFrameModel {
core.eventBus.register(ConnectionEvent.class, this) core.eventBus.register(ConnectionEvent.class, this)
core.eventBus.register(DisconnectionEvent.class, this) core.eventBus.register(DisconnectionEvent.class, this)
core.eventBus.register(FileHashedEvent.class, this) core.eventBus.register(FileHashedEvent.class, this)
core.eventBus.register(FileHashingEvent.class, this)
core.eventBus.register(FileLoadedEvent.class, this) core.eventBus.register(FileLoadedEvent.class, this)
core.eventBus.register(UploadEvent.class, this) core.eventBus.register(UploadEvent.class, this)
core.eventBus.register(UploadFinishedEvent.class, this) core.eventBus.register(UploadFinishedEvent.class, this)
@@ -165,16 +139,6 @@ 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)
core.eventBus.register(UpdateDownloadedEvent.class, this)
core.eventBus.register(TrustSubscriptionUpdatedEvent.class, this)
core.muOptions.watchedKeywords.each {
core.eventBus.publish(new ContentControlEvent(term : it, regex: false, add: true))
}
core.muOptions.watchedRegexes.each {
core.eventBus.publish(new ContentControlEvent(term : it, regex: true, add: true))
}
timer.schedule({ timer.schedule({
if (core.shutdown.get()) if (core.shutdown.get())
@@ -203,35 +167,14 @@ 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())
resumeButtonText = "Retry"
searchesPaneButtonEnabled = false
downloadsPaneButtonEnabled = true
uploadsPaneButtonEnabled = true
monitorPaneButtonEnabled = true
trustPaneButtonEnabled = true
}
})
}
void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
runInsideUIAsync {
watched.addAll(core.muOptions.watchedDirectories) watched.addAll(core.muOptions.watchedDirectories)
builder.getVariable("watched-directories-table").model.fireTableDataChanged() builder.getVariable("watched-directories-table").model.fireTableDataChanged()
watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) } watched.each { core.eventBus.publish(new FileSharedEvent(file : new File(it))) }
core.muOptions.trustSubscriptions.each { resumeButtonText = "Retry"
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",
"Update Downloaded", JOptionPane.INFORMATION_MESSAGE)
}
} }
void onUIResultEvent(UIResultEvent e) { void onUIResultEvent(UIResultEvent e) {
@@ -241,7 +184,7 @@ class MainFrameModel {
void onUIResultBatchEvent(UIResultBatchEvent e) { void onUIResultBatchEvent(UIResultBatchEvent e) {
MVCGroup resultsGroup = results.get(e.uuid) MVCGroup resultsGroup = results.get(e.uuid)
resultsGroup?.model?.handleResultBatch(e.results) resultsGroup?.model.handleResultBatch(e.results)
} }
void onDownloadStartedEvent(DownloadStartedEvent e) { void onDownloadStartedEvent(DownloadStartedEvent e) {
@@ -285,17 +228,7 @@ class MainFrameModel {
} }
} }
void onFileHashingEvent(FileHashingEvent e) {
runInsideUIAsync {
loadedFiles = shared.size()
hashingFile = e.hashingFile
}
}
void onFileHashedEvent(FileHashedEvent e) { void onFileHashedEvent(FileHashedEvent e) {
runInsideUIAsync {
hashingFile = null
}
if (e.error != null) if (e.error != null)
return // TODO do something return // TODO do something
if (infoHashes.contains(e.sharedFile.infoHash)) if (infoHashes.contains(e.sharedFile.infoHash))
@@ -303,7 +236,6 @@ class MainFrameModel {
infoHashes.add(e.sharedFile.infoHash) infoHashes.add(e.sharedFile.infoHash)
runInsideUIAsync { runInsideUIAsync {
shared << e.sharedFile shared << e.sharedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table") JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged() table.model.fireTableDataChanged()
} }
@@ -315,7 +247,6 @@ class MainFrameModel {
infoHashes.add(e.loadedFile.infoHash) infoHashes.add(e.loadedFile.infoHash)
runInsideUIAsync { runInsideUIAsync {
shared << e.loadedFile shared << e.loadedFile
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table") JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged() table.model.fireTableDataChanged()
} }
@@ -327,7 +258,6 @@ class MainFrameModel {
return return
runInsideUIAsync { runInsideUIAsync {
shared.remove(e.unsharedFile) shared.remove(e.unsharedFile)
loadedFiles = shared.size()
JTable table = builder.getVariable("shared-files-table") JTable table = builder.getVariable("shared-files-table")
table.model.fireTableDataChanged() table.model.fireTableDataChanged()
} }
@@ -368,14 +298,6 @@ class MainFrameModel {
} }
} }
void onTrustSubscriptionUpdatedEvent(TrustSubscriptionUpdatedEvent e) {
runInsideUIAsync {
if (!subscriptions.contains(e.trustList))
subscriptions << e.trustList
updateTablePreservingSelection("subscription-table")
}
}
void onQueryEvent(QueryEvent e) { void onQueryEvent(QueryEvent e) {
if (e.replyTo == core.me.destination) if (e.replyTo == core.me.destination)
return return
@@ -397,32 +319,10 @@ class MainFrameModel {
return return
} }
runInsideUIAsync { runInsideUIAsync {
JTable table = builder.getVariable("searches-table") searches.addFirst(new IncomingSearch(search : search, replyTo : e.replyTo, originator : e.originator))
Boolean searchFound = false
Iterator searchIter = searches.iterator()
while ( searchIter.hasNext() ) {
IncomingSearch searchEle = searchIter.next()
if ( searchEle.search == search
&& searchEle.originator == e.originator
&& searchEle.uuid == e.searchEvent.getUuid() ) {
searchIter.remove()
table.model.fireTableDataChanged()
searchFound = true
searchEle.count++
searchEle.timestamp = Calendar.getInstance()
searches.addFirst(searchEle)
break
}
}
if (!searchFound) {
searches.addFirst(new IncomingSearch(search, e.replyTo, e.originator, e.searchEvent.getUuid()))
}
while(searches.size() > 200) while(searches.size() > 200)
searches.removeLast() searches.removeLast()
JTable table = builder.getVariable("searches-table")
table.model.fireTableDataChanged() table.model.fireTableDataChanged()
} }
} }
@@ -431,18 +331,6 @@ class MainFrameModel {
String search String search
Destination replyTo Destination replyTo
Persona originator Persona originator
long count
UUID uuid
Calendar timestamp
IncomingSearch( String search, Destination replyTo, Persona originator, UUID uuid ) {
this.search = search
this.replyTo = replyTo
this.originator = originator
this.uuid = uuid
this.count = 1
this.timestamp = Calendar.getInstance()
}
} }
void onUpdateAvailableEvent(UpdateAvailableEvent e) { void onUpdateAvailableEvent(UpdateAvailableEvent e) {

View File

@@ -1,25 +0,0 @@
package com.muwire.gui
import javax.annotation.Nonnull
import griffon.core.artifact.GriffonModel
import griffon.inject.MVCMember
import griffon.transform.Observable
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<String,String> args) {
controller.refresh()
}
}

View File

@@ -11,17 +11,14 @@ import griffon.metadata.ArtifactProviderFor
class OptionsModel { class OptionsModel {
@Observable String downloadRetryInterval @Observable String downloadRetryInterval
@Observable String updateCheckInterval @Observable String updateCheckInterval
@Observable boolean autoDownloadUpdate @Observable boolean onlyTrusted
@Observable boolean shareDownloadedFiles @Observable boolean shareDownloadedFiles
@Observable String downloadLocation
// i2p options // i2p options
@Observable String inboundLength @Observable String inboundLength
@Observable String inboundQuantity @Observable String inboundQuantity
@Observable String outboundLength @Observable String outboundLength
@Observable String outboundQuantity @Observable String outboundQuantity
@Observable String i2pUDPPort
@Observable String i2pNTCPPort
// gui options // gui options
@Observable boolean showMonitor @Observable boolean showMonitor
@@ -32,31 +29,18 @@ class OptionsModel {
@Observable boolean excludeLocalResult @Observable boolean excludeLocalResult
@Observable boolean showSearchHashes @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<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
autoDownloadUpdate = settings.autoDownloadUpdate 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"]
inboundQuantity = core.i2pOptions["inbound.quantity"] inboundQuantity = core.i2pOptions["inbound.quantity"]
outboundLength = core.i2pOptions["outbound.length"] outboundLength = core.i2pOptions["outbound.length"]
outboundQuantity = core.i2pOptions["outbound.quantity"] outboundQuantity = core.i2pOptions["outbound.quantity"]
i2pUDPPort = core.i2pOptions["i2np.udp.port"]
i2pNTCPPort = core.i2pOptions["i2np.ntcp.port"]
UISettings uiSettings = application.context.get("ui-settings") UISettings uiSettings = application.context.get("ui-settings")
showMonitor = uiSettings.showMonitor showMonitor = uiSettings.showMonitor
@@ -66,14 +50,5 @@ 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)
}
onlyTrusted = !settings.allowUntrusted()
trustLists = settings.allowTrustLists
trustListInterval = String.valueOf(settings.trustListInterval)
} }
} }

View File

@@ -5,7 +5,6 @@ import javax.inject.Inject
import javax.swing.JTable import javax.swing.JTable
import com.muwire.core.Core import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.search.UIResultEvent import com.muwire.core.search.UIResultEvent
import griffon.core.artifact.GriffonModel import griffon.core.artifact.GriffonModel
@@ -19,17 +18,12 @@ class SearchTabModel {
@MVCMember @Nonnull @MVCMember @Nonnull
FactoryBuilderSupport builder FactoryBuilderSupport builder
@Observable boolean downloadActionEnabled
@Observable boolean trustButtonsEnabled
Core core Core core
UISettings uiSettings UISettings uiSettings
String uuid String uuid
def senders = []
def results = [] def results = []
def hashBucket = [:] def hashBucket = [:]
def sourcesBucket = [:] def sourcesBucket = [:]
def sendersBucket = new LinkedHashMap<>()
void mvcGroupInit(Map<String, String> args) { void mvcGroupInit(Map<String, String> args) {
@@ -54,15 +48,6 @@ class SearchTabModel {
} }
bucket << e bucket << e
def senderBucket = sendersBucket.get(e.sender)
if (senderBucket == null) {
senderBucket = []
sendersBucket[e.sender] = senderBucket
senders.clear()
senders.addAll(sendersBucket.keySet())
}
senderBucket << e
Set sourceBucket = sourcesBucket.get(e.infohash) Set sourceBucket = sourcesBucket.get(e.infohash)
if (sourceBucket == null) { if (sourceBucket == null) {
sourceBucket = new HashSet() sourceBucket = new HashSet()
@@ -70,7 +55,8 @@ class SearchTabModel {
} }
sourceBucket.addAll(e.sources) sourceBucket.addAll(e.sources)
JTable table = builder.getVariable("senders-table") results << e
JTable table = builder.getVariable("results-table")
table.model.fireTableDataChanged() table.model.fireTableDataChanged()
} }
} }
@@ -87,14 +73,6 @@ class SearchTabModel {
hashBucket[it.infohash] = bucket hashBucket[it.infohash] = bucket
} }
def senderBucket = sendersBucket.get(it.sender)
if (senderBucket == null) {
senderBucket = []
sendersBucket[it.sender] = senderBucket
senders.clear()
senders.addAll(sendersBucket.keySet())
}
Set sourceBucket = sourcesBucket.get(it.infohash) Set sourceBucket = sourcesBucket.get(it.infohash)
if (sourceBucket == null) { if (sourceBucket == null) {
sourceBucket = new HashSet() sourceBucket = new HashSet()
@@ -103,9 +81,9 @@ class SearchTabModel {
sourceBucket.addAll(it.sources) sourceBucket.addAll(it.sources)
bucket << it bucket << it
senderBucket << it results << it
} }
JTable table = builder.getVariable("senders-table") JTable table = builder.getVariable("results-table")
table.model.fireTableDataChanged() table.model.fireTableDataChanged()
} }
} }

View File

@@ -1,22 +0,0 @@
package com.muwire.gui
import com.muwire.core.trust.RemoteTrustList
import com.muwire.core.trust.TrustService
import griffon.core.artifact.GriffonModel
import griffon.transform.Observable
import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
class TrustListModel {
RemoteTrustList trustList
TrustService trustService
def trusted
def distrusted
void mvcGroupInit(Map<String,String> args) {
trusted = new ArrayList<>(trustList.good)
distrusted = new ArrayList<>(trustList.bad)
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -1,154 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.swing.JDialog
import javax.swing.JLabel
import javax.swing.ListSelectionModel
import javax.swing.SwingConstants
import javax.swing.table.DefaultTableCellRenderer
import com.muwire.core.content.Matcher
import com.muwire.core.content.RegexMatcher
import java.awt.BorderLayout
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonView)
class ContentPanelView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
ContentPanelModel model
def dialog
def mainFrame
def mainPanel
def rulesTable
def ruleTextField
def lastRulesSortEvent
def hitsTable
def lastHitsSortEvent
void initUI() {
mainFrame = application.windowManager.findWindow("main-frame")
dialog = new JDialog(mainFrame, "Content Control Panel", true)
mainPanel = builder.panel {
gridLayout(rows:1, cols:2)
panel {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label(text : "Rules")
}
scrollPane (constraints : BorderLayout.CENTER) {
rulesTable = table(id : "rules-table", autoCreateRowSorter : true) {
tableModel(list : model.rules) {
closureColumn(header: "Term", type:String, read: {row -> row.getTerm()})
closureColumn(header: "Regex?", type:Boolean, read: {row -> row instanceof RegexMatcher})
closureColumn(header: "Hits", type:Integer, read : {row -> row.matches.size()})
}
}
}
panel (constraints : BorderLayout.SOUTH) {
borderLayout()
ruleTextField = textField(constraints: BorderLayout.CENTER, action: addRuleAction)
panel (constraints: BorderLayout.EAST) {
buttonGroup(id : "ruleType")
radioButton(text: "Keyword", selected : true, buttonGroup: ruleType, keywordAction)
radioButton(text: "Regex", selected : false, buttonGroup: ruleType, regexAction)
button(text : "Add Rule", addRuleAction)
button(text : "Delete Rule", enabled : bind {model.deleteButtonEnabled}, deleteRuleAction)
}
}
}
panel (border : etchedBorder()){
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label(text : "Hits")
}
scrollPane(constraints : BorderLayout.CENTER) {
hitsTable = table(id : "hits-table", autoCreateRowSorter : true) {
tableModel(list : model.hits) {
closureColumn(header : "Searcher", type : String, read : {row -> row.persona.getHumanReadableName()})
closureColumn(header : "Keywords", type : String, read : {row -> row.keywords.join(" ")})
closureColumn(header : "Date", type : String, read : {row -> String.valueOf(new Date(row.timestamp))})
}
}
}
panel (constraints : BorderLayout.SOUTH) {
button(text : "Refresh", refreshAction)
button(text : "Trust", enabled : bind {model.trustButtonsEnabled}, trustAction)
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
}
}
}
}
int getSelectedRule() {
int selectedRow = rulesTable.getSelectedRow()
if (selectedRow < 0)
return -1
if (lastRulesSortEvent != null)
selectedRow = rulesTable.rowSorter.convertRowIndexToModel(selectedRow)
selectedRow
}
int getSelectedHit() {
int selectedRow = hitsTable.getSelectedRow()
if (selectedRow < 0)
return -1
if (lastHitsSortEvent != null)
selectedRow = hitsTable.rowSorter.convertRowIndexToModel(selectedRow)
selectedRow
}
void mvcGroupInit(Map<String,String> args) {
def centerRenderer = new DefaultTableCellRenderer()
centerRenderer.setHorizontalAlignment(JLabel.CENTER)
rulesTable.setDefaultRenderer(Integer.class, centerRenderer)
rulesTable.rowSorter.addRowSorterListener({evt -> lastRulesSortEvent = evt})
rulesTable.rowSorter.setSortsOnUpdates(true)
def selectionModel = rulesTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int selectedRow = getSelectedRule()
if (selectedRow < 0) {
model.deleteButtonEnabled = false
return
} else {
model.deleteButtonEnabled = true
model.hits.clear()
Matcher matcher = model.rules[selectedRow]
model.hits.addAll(matcher.matches)
hitsTable.model.fireTableDataChanged()
}
})
hitsTable.rowSorter.addRowSorterListener({evt -> lastHitsSortEvent = evt})
hitsTable.rowSorter.setSortsOnUpdates(true)
selectionModel = hitsTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int selectedRow = getSelectedHit()
model.trustButtonsEnabled = selectedRow >= 0
})
dialog.getContentPane().add(mainPanel)
dialog.pack()
dialog.setLocationRelativeTo(mainFrame)
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
dialog.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
mvcGroup.destroy()
}
})
dialog.show()
}
}

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('/MuWire-48x48.png').image, iconImage: imageIcon('/griffon-icon-48x48.png').image,
iconImages: [imageIcon('/MuWire-48x48.png').image, iconImages: [imageIcon('/griffon-icon-48x48.png').image,
imageIcon('/MuWire-32x32.png').image, imageIcon('/griffon-icon-32x32.png').image,
imageIcon('/MuWire-16x16.png').image], imageIcon('/griffon-icon-16x16.png').image],
visible: bind { !model.coreInitialized} ) { visible: bind { !model.coreInitialized} ) {
panel { panel {
vbox { vbox {

View File

@@ -1,80 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.swing.JDialog
import javax.swing.JPanel
import javax.swing.SwingConstants
import java.awt.BorderLayout
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonView)
class I2PStatusView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
I2PStatusModel model
def mainFrame
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))
label(text : bind {model.networkStatus}, constraints : gbc(gridx: 1, gridy:0))
label(text: "Floodfill", constraints : gbc(gridx: 0, gridy : 1))
label(text : bind {model.floodfill}, constraints : gbc(gridx:1, gridy:1))
label(text : "NTCP Connections", constraints : gbc(gridx:0, gridy:2))
label(text : bind {model.ntcpConnections}, constraints : gbc(gridx: 1, gridy:2))
label(text : "SSU Connections", constraints : gbc(gridx:0, gridy:3))
label(text : bind {model.ssuConnections}, constraints : gbc(gridx: 1, gridy:3))
label(text : "Participating Tunnels", constraints : gbc(gridx:0, gridy:4))
label(text : bind {model.participatingTunnels}, constraints : gbc(gridx: 1, gridy:4))
label(text : "Participating Bandwidth", constraints : gbc(gridx:0, gridy:5))
label(text : bind {model.participatingBW}, constraints : gbc(gridx: 1, gridy:5))
label(text : "Active Peers", constraints : gbc(gridx:0, gridy:6))
label(text : bind {model.activePeers}, constraints : gbc(gridx: 1, gridy:6))
label(text : "Receive Bps (15 seconds)", constraints : gbc(gridx:0, gridy:7))
label(text : bind {model.receiveBps}, constraints : gbc(gridx: 1, gridy:7))
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<String,String> 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)
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
dialog.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
mvcGroup.destroy()
}
})
dialog.show()
}
}

View File

@@ -22,10 +22,9 @@ import javax.swing.border.Border
import javax.swing.table.DefaultTableCellRenderer import javax.swing.table.DefaultTableCellRenderer
import com.muwire.core.Constants import com.muwire.core.Constants
import com.muwire.core.MuWireSettings
import com.muwire.core.download.Downloader import com.muwire.core.download.Downloader
import com.muwire.core.files.FileSharedEvent import com.muwire.core.files.FileSharedEvent
import com.muwire.core.trust.RemoteTrustList
import java.awt.BorderLayout import java.awt.BorderLayout
import java.awt.CardLayout import java.awt.CardLayout
import java.awt.FlowLayout import java.awt.FlowLayout
@@ -53,8 +52,6 @@ class MainFrameView {
def downloadsTable def downloadsTable
def lastDownloadSortEvent def lastDownloadSortEvent
def lastSharedSortEvent def lastSharedSortEvent
def lastWatchedSortEvent
def trustTablesSortEvents = [:]
void initUI() { void initUI() {
UISettings settings = application.context.get("ui-settings") UISettings settings = application.context.get("ui-settings")
@@ -63,25 +60,15 @@ 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('/MuWire-48x48.png').image, iconImage: imageIcon('/griffon-icon-48x48.png').image,
iconImages: [imageIcon('/MuWire-48x48.png').image, iconImages: [imageIcon('/griffon-icon-48x48.png').image,
imageIcon('/MuWire-32x32.png').image, imageIcon('/griffon-icon-32x32.png').image,
imageIcon('/MuWire-16x16.png').image], imageIcon('/griffon-icon-16x16.png').image],
pack : false, pack : false,
visible : bind { model.coreInitialized }) { visible : bind { model.coreInitialized }) {
menuBar { menuBar {
menu (text : "Options") { menu (text : "Options") {
menuItem("Configuration", actionPerformed : {mvcGroup.createMVCGroup("Options")}) menuItem("Configuration", actionPerformed : {mvcGroup.createMVCGroup("Options")})
menuItem("Content Control", actionPerformed : {
def env = [:]
env["core"] = model.core
mvcGroup.createMVCGroup("content-panel", env)
})
}
menu (text : "Status") {
menuItem("MuWire", actionPerformed : {mvcGroup.createMVCGroup("mu-wire-status")})
MuWireSettings muSettings = application.context.get("muwire-settings")
menuItem("I2P", enabled : bind {model.routerPresent}, actionPerformed: {mvcGroup.createMVCGroup("i-2-p-status")})
} }
} }
borderLayout() borderLayout()
@@ -89,12 +76,11 @@ class MainFrameView {
borderLayout() borderLayout()
panel (constraints: BorderLayout.WEST) { panel (constraints: BorderLayout.WEST) {
gridLayout(rows:1, cols: 2) gridLayout(rows:1, cols: 2)
button(text: "Searches", enabled : bind{model.searchesPaneButtonEnabled},actionPerformed : showSearchWindow) button(text: "Searches", actionPerformed : showSearchWindow)
button(text: "Downloads", enabled : bind{model.downloadsPaneButtonEnabled}, actionPerformed : showDownloadsWindow) button(text: "Uploads", actionPerformed : showUploadsWindow)
button(text: "Uploads", enabled : bind{model.uploadsPaneButtonEnabled}, actionPerformed : showUploadsWindow)
if (settings.showMonitor) if (settings.showMonitor)
button(text: "Monitor", enabled: bind{model.monitorPaneButtonEnabled},actionPerformed : showMonitorWindow) button(text: "Monitor", actionPerformed : showMonitorWindow)
button(text: "Trust", enabled:bind{model.trustPaneButtonEnabled},actionPerformed : showTrustWindow) button(text: "Trust", actionPerformed : showTrustWindow)
} }
panel(id: "top-panel", constraints: BorderLayout.CENTER) { panel(id: "top-panel", constraints: BorderLayout.CENTER) {
cardLayout() cardLayout()
@@ -117,19 +103,30 @@ class MainFrameView {
panel (id: "cards-panel", constraints : BorderLayout.CENTER) { panel (id: "cards-panel", constraints : BorderLayout.CENTER) {
cardLayout() cardLayout()
panel (constraints : "search window") { panel (constraints : "search window") {
borderLayout()
splitPane( orientation : JSplitPane.VERTICAL_SPLIT, dividerLocation : 500,
continuousLayout : true, constraints : BorderLayout.CENTER) {
panel (constraints : JSplitPane.TOP) {
borderLayout() borderLayout()
tabbedPane(id : "result-tabs", constraints: BorderLayout.CENTER) tabbedPane(id : "result-tabs", constraints: BorderLayout.CENTER)
panel(constraints : BorderLayout.SOUTH) {
button(text : "Download", enabled : bind {model.downloadActionEnabled}, downloadAction)
button(text : "Trust", enabled: bind {model.trustButtonsEnabled }, trustAction)
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
} }
panel (constraints: "downloads window") { }
gridLayout(rows : 2, cols: 1) panel (constraints : JSplitPane.BOTTOM) {
panel {
borderLayout() borderLayout()
scrollPane (constraints : BorderLayout.CENTER) { scrollPane (constraints : BorderLayout.CENTER) {
downloadsTable = table(id : "downloads-table", autoCreateRowSorter : true) { downloadsTable = table(id : "downloads-table", autoCreateRowSorter : true) {
tableModel(list: model.downloads) { tableModel(list: model.downloads) {
closureColumn(header: "Name", preferredWidth: 300, type: String, read : {row -> row.downloader.file.getName()}) closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.downloader.file.getName()})
closureColumn(header: "Status", preferredWidth: 50, type: String, read : {row -> row.downloader.getCurrentState().toString()}) closureColumn(header: "Status", preferredWidth: 50, type: String, read : {row -> row.downloader.getCurrentState().toString()})
closureColumn(header: "Progress", preferredWidth: 70, type: Downloader, read: { row -> row.downloader }) closureColumn(header: "Progress", preferredWidth: 20, type: String, read: { row ->
int pieces = row.downloader.nPieces
int done = row.downloader.donePieces()
"$done/$pieces pieces".toString()
})
closureColumn(header: "Sources", preferredWidth : 10, type: Integer, read : {row -> row.downloader.activeWorkers()}) 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" DataHelper.formatSize2Decimal(row.downloader.speed(), false) + "B/sec"
@@ -143,33 +140,17 @@ class MainFrameView {
button(text: bind { model.resumeButtonText }, enabled : bind {model.retryButtonEnabled}, resumeAction) button(text: bind { model.resumeButtonText }, enabled : bind {model.retryButtonEnabled}, resumeAction)
} }
} }
panel {
borderLayout()
panel(constraints : BorderLayout.NORTH) {
label(text : "Download Details")
}
panel(constraints : BorderLayout.CENTER) {
label(text : "Details go here...")
}
} }
} }
panel (constraints: "uploads window"){ panel (constraints: "uploads window"){
gridLayout(cols : 1, rows : 2) gridLayout(cols : 1, rows : 2)
panel { panel {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label(text : bind {
if (model.hashingFile == null) {
""
} else {
"hashing: " + model.hashingFile.getAbsolutePath() + " (" + DataHelper.formatSize2Decimal(model.hashingFile.length(), false).toString() + "B)"
}
})
}
panel (border : etchedBorder(), constraints : BorderLayout.CENTER) {
gridLayout(cols : 2, rows : 1) gridLayout(cols : 2, rows : 1)
panel { panel {
borderLayout() borderLayout()
panel (constraints : BorderLayout.NORTH) {
button(text : "Add directories to watch", actionPerformed : watchDirectories)
}
scrollPane (constraints : BorderLayout.CENTER) { scrollPane (constraints : BorderLayout.CENTER) {
table(id : "watched-directories-table", autoCreateRowSorter: true) { table(id : "watched-directories-table", autoCreateRowSorter: true) {
tableModel(list : model.watched) { tableModel(list : model.watched) {
@@ -180,29 +161,20 @@ class MainFrameView {
} }
panel { panel {
borderLayout() borderLayout()
panel (constraints : BorderLayout.NORTH) {
button(text : "Share files", actionPerformed : shareFiles)
}
scrollPane(constraints : BorderLayout.CENTER) { scrollPane(constraints : BorderLayout.CENTER) {
table(id : "shared-files-table", autoCreateRowSorter: true) { table(id : "shared-files-table", autoCreateRowSorter: true) {
tableModel(list : model.shared) { tableModel(list : model.shared) {
closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.getCachedPath()}) closureColumn(header : "Name", preferredWidth : 500, type : String, read : {row -> row.file.getAbsolutePath()})
closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.getCachedLength() }) closureColumn(header : "Size", preferredWidth : 100, type : Long, read : {row -> row.file.length() })
} }
} }
} }
} }
} }
panel (constraints : BorderLayout.SOUTH) {
gridLayout(rows:1, cols:2)
panel {
button(text : "Add directories to watch", actionPerformed : watchDirectories)
button(text : "Share files", actionPerformed : shareFiles)
}
panel { panel {
label("Shared:")
label(text : bind {model.loadedFiles.toString()})
}
}
}
panel (border : etchedBorder()) {
borderLayout() borderLayout()
panel (constraints : BorderLayout.NORTH){ panel (constraints : BorderLayout.NORTH){
label("Uploads") label("Uploads")
@@ -213,25 +185,11 @@ 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% of piece".toString() "$percent%"
}) })
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 ->
int pieces = row.getTotalPieces()
int done = row.getDonePieces()
int percent = -1
if ( pieces != 0 ) {
percent = (done * 100) / pieces
}
long size = row.getTotalSize()
String totalSize = ""
if (size >= 0 ) {
totalSize = " of " + DataHelper.formatSize2Decimal(size, false) + "B"
}
String.format("%02d", percent) + "% ${totalSize} ($done/$pieces pcs)".toString()
})
} }
} }
} }
@@ -277,22 +235,12 @@ class MainFrameView {
return it.replyTo.toBase32() return it.replyTo.toBase32()
} }
}) })
closureColumn(header : "Count", type : String, read : {
it.count.toString()
})
closureColumn(header : "Timestamp", type : String, read : {
String.format("%02d", it.timestamp.get(Calendar.HOUR_OF_DAY)) + ":" +
String.format("%02d", it.timestamp.get(Calendar.MINUTE)) + ":" +
String.format("%02d", it.timestamp.get(Calendar.SECOND))
})
} }
} }
} }
} }
} }
panel(constraints : "trust window") { panel(constraints : "trust window") {
gridLayout(rows : 2, cols : 1)
panel {
gridLayout(rows: 1, cols :2) gridLayout(rows: 1, cols :2)
panel (border : etchedBorder()){ panel (border : etchedBorder()){
borderLayout() borderLayout()
@@ -303,11 +251,10 @@ class MainFrameView {
} }
} }
} }
panel (constraints : BorderLayout.SOUTH) { panel (constraints : BorderLayout.EAST) {
gridBagLayout() gridBagLayout()
button(text : "Subscribe", enabled : bind {model.subscribeButtonEnabled}, constraints : gbc(gridx: 0, gridy : 0), subscribeAction) button(text : "Mark Neutral", constraints : gbc(gridx: 0, gridy: 0), markNeutralFromTrustedAction)
button(text : "Mark Neutral", enabled : bind {model.markNeutralFromTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy: 0), markNeutralFromTrustedAction) button(text : "Mark Distrusted", constraints : gbc(gridx: 0, gridy:1), markDistrustedAction)
button(text : "Mark Distrusted", enabled : bind {model.markDistrustedButtonEnabled}, constraints : gbc(gridx: 2, gridy:0), markDistrustedAction)
} }
} }
panel (border : etchedBorder()){ panel (border : etchedBorder()){
@@ -319,38 +266,10 @@ class MainFrameView {
} }
} }
} }
panel(constraints : BorderLayout.SOUTH) { panel(constraints : BorderLayout.WEST) {
gridBagLayout() gridBagLayout()
button(text: "Mark Neutral", enabled : bind {model.markNeutralFromDistrustedButtonEnabled}, constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction) button(text: "Mark Neutral", constraints: gbc(gridx: 0, gridy: 0), markNeutralFromDistrustedAction)
button(text: "Mark Trusted", enabled : bind {model.markTrustedButtonEnabled}, constraints : gbc(gridx: 1, gridy : 0), markTrustedAction) button(text: "Mark Trusted", constraints : gbc(gridx: 0, gridy : 1), markTrustedAction)
}
}
}
panel {
borderLayout()
panel (constraints : BorderLayout.NORTH){
label(text : "Trust List Subscriptions")
}
scrollPane(constraints : BorderLayout.CENTER) {
table(id : "subscription-table", autoCreateRowSorter : true) {
tableModel(list : model.subscriptions) {
closureColumn(header : "Name", preferredWidth: 200, type: String, read : {it.persona.getHumanReadableName()})
closureColumn(header : "Trusted", preferredWidth : 20, type: Integer, read : {it.good.size()})
closureColumn(header : "Distrusted", preferredWidth: 20, type: Integer, read : {it.bad.size()})
closureColumn(header : "Status", preferredWidth: 30, type: String, read : {it.status.toString()})
closureColumn(header : "Last Updated", preferredWidth: 200, type : String, read : {
if (it.timestamp == 0)
return "Never"
else
return String.valueOf(new Date(it.timestamp))
})
}
}
}
panel(constraints : BorderLayout.SOUTH) {
button(text : "Review", enabled : bind {model.reviewButtonEnabled}, reviewAction)
button(text : "Update", enabled : bind {model.updateButtonEnabled}, updateAction)
button(text : "Unsubscribe", enabled : bind {model.unsubscribeButtonEnabled}, unsubscribeAction)
} }
} }
} }
@@ -413,11 +332,9 @@ class MainFrameView {
def centerRenderer = new DefaultTableCellRenderer() def centerRenderer = new DefaultTableCellRenderer()
centerRenderer.setHorizontalAlignment(JLabel.CENTER) centerRenderer.setHorizontalAlignment(JLabel.CENTER)
downloadsTable.setDefaultRenderer(Integer.class, centerRenderer) downloadsTable.setDefaultRenderer(Integer.class, centerRenderer)
downloadsTable.setDefaultRenderer(Downloader.class, new DownloadProgressRenderer())
downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt}) downloadsTable.rowSorter.addRowSorterListener({evt -> lastDownloadSortEvent = evt})
downloadsTable.rowSorter.setSortsOnUpdates(true) downloadsTable.rowSorter.setSortsOnUpdates(true)
downloadsTable.rowSorter.setComparator(2, new DownloaderComparator())
downloadsTable.addMouseListener(new MouseAdapter() { downloadsTable.addMouseListener(new MouseAdapter() {
@Override @Override
@@ -440,12 +357,11 @@ class MainFrameView {
sharedFilesTable.rowSorter.setSortsOnUpdates(true) sharedFilesTable.rowSorter.setSortsOnUpdates(true)
JPopupMenu sharedFilesMenu = new JPopupMenu() JPopupMenu sharedFilesMenu = new JPopupMenu()
// JMenuItem unshareSelectedFiles = new JMenuItem("Unshare selected files")
// unshareSelectedFiles.addActionListener({mvcGroup.controller.unshareSelectedFiles()})
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard") JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard()}) copyHashToClipboard.addActionListener({mvcGroup.view.copyHashToClipboard(sharedFilesTable)})
sharedFilesMenu.add(copyHashToClipboard) sharedFilesMenu.add(copyHashToClipboard)
JMenuItem unshareSelectedFiles = new JMenuItem("Unshare selected files")
unshareSelectedFiles.addActionListener({mvcGroup.controller.unshareSelectedFile()})
sharedFilesMenu.add(unshareSelectedFiles)
sharedFilesTable.addMouseListener(new MouseAdapter() { sharedFilesTable.addMouseListener(new MouseAdapter() {
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
@@ -462,18 +378,9 @@ class MainFrameView {
// searches table // searches table
def searchesTable = builder.getVariable("searches-table") def searchesTable = builder.getVariable("searches-table")
JPopupMenu searchTableMenu = new JPopupMenu() JPopupMenu searchTableMenu = new JPopupMenu()
JMenuItem copySearchToClipboard = new JMenuItem("Copy search to clipboard") JMenuItem copySearchToClipboard = new JMenuItem("Copy search to clipboard")
copySearchToClipboard.addActionListener({mvcGroup.view.copySearchToClipboard(searchesTable)}) copySearchToClipboard.addActionListener({mvcGroup.view.copySearchToClipboard(searchesTable)})
JMenuItem trustSearcher = new JMenuItem("Trust searcher")
trustSearcher.addActionListener({mvcGroup.controller.trustPersonaFromSearch()})
JMenuItem distrustSearcher = new JMenuItem("Distrust searcher")
distrustSearcher.addActionListener({mvcGroup.controller.distrustPersonaFromSearch()})
searchTableMenu.add(copySearchToClipboard) searchTableMenu.add(copySearchToClipboard)
searchTableMenu.add(trustSearcher)
searchTableMenu.add(distrustSearcher)
searchesTable.addMouseListener(new MouseAdapter() { searchesTable.addMouseListener(new MouseAdapter() {
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
@@ -487,121 +394,19 @@ class MainFrameView {
} }
}) })
// watched directories table
def watchedTable = builder.getVariable("watched-directories-table")
watchedTable.rowSorter.addRowSorterListener({evt -> lastWatchedSortEvent = evt})
watchedTable.rowSorter.setSortsOnUpdates(true)
JPopupMenu watchedMenu = new JPopupMenu()
JMenuItem stopWatching = new JMenuItem("Stop sharing")
stopWatching.addActionListener({mvcGroup.controller.stopWatchingDirectory()})
watchedMenu.add(stopWatching)
watchedTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger())
showPopupMenu(watchedMenu, e)
}
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger())
showPopupMenu(watchedMenu, e)
}
})
// subscription table
def subscriptionTable = builder.getVariable("subscription-table")
subscriptionTable.setDefaultRenderer(Integer.class, centerRenderer)
subscriptionTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["subscription-table"] = evt})
subscriptionTable.rowSorter.setSortsOnUpdates(true)
selectionModel = subscriptionTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int selectedRow = getSelectedTrustTablesRow("subscription-table")
if (selectedRow < 0) {
model.reviewButtonEnabled = false
model.updateButtonEnabled = false
model.unsubscribeButtonEnabled = false
return
}
def trustList = model.subscriptions[selectedRow]
if (trustList == null)
return
switch(trustList.status) {
case RemoteTrustList.Status.NEW:
case RemoteTrustList.Status.UPDATING:
model.reviewButtonEnabled = false
model.updateButtonEnabled = false
model.unsubscribeButtonEnabled = false
break
case RemoteTrustList.Status.UPDATED:
model.reviewButtonEnabled = true
model.updateButtonEnabled = true
model.unsubscribeButtonEnabled = true
break
case RemoteTrustList.Status.UPDATE_FAILED:
model.reviewButtonEnabled = false
model.updateButtonEnabled = true
model.unsubscribeButtonEnabled = true
break
}
})
// trusted table
def trustedTable = builder.getVariable("trusted-table")
trustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["trusted-table"] = evt})
trustedTable.rowSorter.setSortsOnUpdates(true)
selectionModel = trustedTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int selectedRow = getSelectedTrustTablesRow("trusted-table")
if (selectedRow < 0) {
model.subscribeButtonEnabled = false
model.markDistrustedButtonEnabled = false
model.markNeutralFromTrustedButtonEnabled = false
} else {
model.subscribeButtonEnabled = true
model.markDistrustedButtonEnabled = true
model.markNeutralFromTrustedButtonEnabled = true
}
})
// distrusted table
def distrustedTable = builder.getVariable("distrusted-table")
distrustedTable.rowSorter.addRowSorterListener({evt -> trustTablesSortEvents["distrusted-table"] = evt})
distrustedTable.rowSorter.setSortsOnUpdates(true)
selectionModel = distrustedTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int selectedRow = getSelectedTrustTablesRow("distrusted-table")
if (selectedRow < 0) {
model.markTrustedButtonEnabled = false
model.markNeutralFromDistrustedButtonEnabled = false
} else {
model.markTrustedButtonEnabled = true
model.markNeutralFromDistrustedButtonEnabled = true
}
})
} }
private static void showPopupMenu(JPopupMenu menu, MouseEvent event) { private static void showPopupMenu(JPopupMenu menu, MouseEvent event) {
menu.show(event.getComponent(), event.getX(), event.getY()) menu.show(event.getComponent(), event.getX(), event.getY())
} }
def selectedSharedFile() { def copyHashToClipboard(JTable sharedFilesTable) {
def sharedFilesTable = builder.getVariable("shared-files-table")
int selected = sharedFilesTable.getSelectedRow() int selected = sharedFilesTable.getSelectedRow()
if (selected < 0) if (selected < 0)
return null return
if (lastSharedSortEvent != null) if (lastSharedSortEvent != null)
selected = sharedFilesTable.rowSorter.convertRowIndexToModel(selected) selected = sharedFilesTable.rowSorter.convertRowIndexToModel(selected)
model.shared[selected] String root = Base64.encode(model.shared[selected].infoHash.getRoot())
}
def copyHashToClipboard() {
def selected = selectedSharedFile()
if (selected == null)
return
String root = Base64.encode(selected.infoHash.getRoot())
StringSelection selection = new StringSelection(root) StringSelection selection = new StringSelection(root)
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard() def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
clipboard.setContents(selection, null) clipboard.setContents(selection, null)
@@ -692,56 +497,25 @@ class MainFrameView {
def showSearchWindow = { def showSearchWindow = {
def cardsPanel = builder.getVariable("cards-panel") def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, "search window") cardsPanel.getLayout().show(cardsPanel, "search window")
model.searchesPaneButtonEnabled = false
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
}
def showDownloadsWindow = {
def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, "downloads window")
model.searchesPaneButtonEnabled = true
model.downloadsPaneButtonEnabled = false
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
} }
def showUploadsWindow = { def showUploadsWindow = {
def cardsPanel = builder.getVariable("cards-panel") def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, "uploads window") cardsPanel.getLayout().show(cardsPanel, "uploads window")
model.searchesPaneButtonEnabled = true
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = false
model.monitorPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
} }
def showMonitorWindow = { def showMonitorWindow = {
def cardsPanel = builder.getVariable("cards-panel") def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel,"monitor window") cardsPanel.getLayout().show(cardsPanel,"monitor window")
model.searchesPaneButtonEnabled = true
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = false
model.trustPaneButtonEnabled = true
} }
def showTrustWindow = { def showTrustWindow = {
def cardsPanel = builder.getVariable("cards-panel") def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel,"trust window") cardsPanel.getLayout().show(cardsPanel,"trust window")
model.searchesPaneButtonEnabled = true
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.trustPaneButtonEnabled = false
} }
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)
@@ -752,7 +526,6 @@ 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)
@@ -765,24 +538,4 @@ class MainFrameView {
model.core.eventBus.publish(new FileSharedEvent(file : f)) model.core.eventBus.publish(new FileSharedEvent(file : f))
} }
} }
String getSelectedWatchedDirectory() {
def watchedTable = builder.getVariable("watched-directories-table")
int selectedRow = watchedTable.getSelectedRow()
if (selectedRow < 0)
return null
if (lastWatchedSortEvent != null)
selectedRow = watchedTable.rowSorter.convertRowIndexToModel(selectedRow)
model.watched[selectedRow]
}
int getSelectedTrustTablesRow(String tableName) {
def table = builder.getVariable(tableName)
int selectedRow = table.getSelectedRow()
if (selectedRow < 0)
return -1
if (trustTablesSortEvents.get(tableName) != null)
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
selectedRow
}
} }

View File

@@ -1,73 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.swing.JDialog
import javax.swing.JPanel
import javax.swing.SwingConstants
import com.muwire.core.Core
import java.awt.BorderLayout
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonView)
class MuWireStatusView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
MuWireStatusModel model
def mainFrame
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))
label(text : bind {model.incomingConnections}, constraints : gbc(gridx:1, gridy:0))
label(text : "Outgoing connections", constraints : gbc(gridx:0, gridy:1))
label(text : bind {model.outgoingConnections}, constraints : gbc(gridx:1, gridy:1))
label(text : "Known hosts", constraints : gbc(gridx:0, gridy:2))
label(text : bind {model.knownHosts}, constraints : gbc(gridx:1, gridy:2))
label(text : "Shared files", constraints : gbc(gridx:0, gridy:3))
label(text : bind {model.sharedFiles}, constraints : gbc(gridx:1, gridy:3))
label(text : "Downloads", constraints : gbc(gridx:0, gridy:4))
label(text : bind {model.downloads}, constraints : gbc(gridx:1, gridy:4))
}
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<String,String> 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)
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
dialog.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
mvcGroup.destroy()
}
})
dialog.show()
}
}

View File

@@ -9,8 +9,6 @@ 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
@@ -28,20 +26,16 @@ class OptionsView {
def p def p
def i def i
def u def u
def bandwidth
def trust
def retryField def retryField
def updateField def updateField
def autoDownloadUpdateCheckbox def allowUntrustedCheckbox
def shareDownloadedCheckbox def shareDownloadedCheckbox
def inboundLengthField def inboundLengthField
def inboundQuantityField def inboundQuantityField
def outboundLengthField def outboundLengthField
def outboundQuantityField def outboundQuantityField
def i2pUDPPortField
def i2pNTCPPortField
def lnfField def lnfField
def monitorCheckbox def monitorCheckbox
@@ -51,13 +45,6 @@ class OptionsView {
def excludeLocalResultCheckbox def excludeLocalResultCheckbox
def showSearchHashesCheckbox def showSearchHashesCheckbox
def inBwField
def outBwField
def allowUntrustedCheckbox
def allowTrustListsCheckbox
def trustListIntervalField
def buttonsPanel def buttonsPanel
def mainFrame def mainFrame
@@ -76,16 +63,12 @@ class OptionsView {
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1)) updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
label(text : "hours", constraints : gbc(gridx: 2, gridy : 1)) label(text : "hours", constraints : gbc(gridx: 2, gridy : 1))
label(text : "Download updates automatically", constraints: gbc(gridx :0, gridy : 2)) label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 2))
autoDownloadUpdateCheckbox = checkBox(selected : bind {model.autoDownloadUpdate}, constraints : gbc(gridx:1, gridy : 2)) allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 2))
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3)) 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 {
gridBagLayout() gridBagLayout()
@@ -98,15 +81,6 @@ class OptionsView {
outboundLengthField = textField(text : bind {model.outboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:3)) outboundLengthField = textField(text : bind {model.outboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:3))
label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4)) label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4))
outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, 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))
i2pNTCPPortField = textField(text : bind {model.i2pNTCPPort}, columns : 4, constraints : gbc(gridx:1, gridy:5))
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 { u = builder.panel {
gridBagLayout() gridBagLayout()
@@ -126,26 +100,6 @@ 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))
}
trust = builder.panel {
gridBagLayout()
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 0))
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 0))
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 1))
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 1))
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:2))
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:2))
label(text : "hours", constraints : gbc(gridx: 2, 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)
@@ -158,11 +112,6 @@ 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)
}
tabbedPane.addTab("Trust", trust)
JPanel panel = new JPanel() JPanel panel = new JPanel()
panel.setLayout(new BorderLayout()) panel.setLayout(new BorderLayout())

View File

@@ -16,7 +16,6 @@ import javax.swing.ListSelectionModel
import javax.swing.SwingConstants import javax.swing.SwingConstants
import javax.swing.table.DefaultTableCellRenderer import javax.swing.table.DefaultTableCellRenderer
import com.muwire.core.Persona
import com.muwire.core.util.DataUtil import com.muwire.core.util.DataUtil
import java.awt.BorderLayout import java.awt.BorderLayout
@@ -38,49 +37,23 @@ class SearchTabView {
def pane def pane
def parent def parent
def searchTerms def searchTerms
def sendersTable
def lastSendersSortEvent
def resultsTable def resultsTable
def lastSortEvent def lastSortEvent
void initUI() { void initUI() {
builder.with { builder.with {
def resultsTable def resultsTable
def sendersTable def pane = scrollPane {
def pane = panel {
gridLayout(rows : 2, cols: 1)
panel {
borderLayout()
scrollPane (constraints : BorderLayout.CENTER) {
sendersTable = table(id : "senders-table", autoCreateRowSorter : true) {
tableModel(list : model.senders) {
closureColumn(header : "Sender", preferredWidth : 500, type: String, read : {row -> row.getHumanReadableName()})
closureColumn(header : "Results", preferredWidth : 20, type: Integer, read : {row -> model.sendersBucket[row].size()})
closureColumn(header : "Trust", preferredWidth : 50, type: String, read : { row ->
model.core.trustService.getLevel(row.destination).toString()
})
}
}
}
panel(constraints : BorderLayout.SOUTH) {
button(text : "Trust", enabled: bind {model.trustButtonsEnabled }, trustAction)
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
}
}
panel {
borderLayout()
scrollPane (constraints : BorderLayout.CENTER) {
resultsTable = table(id : "results-table", autoCreateRowSorter : true) { resultsTable = table(id : "results-table", autoCreateRowSorter : true) {
tableModel(list: model.results) { tableModel(list: model.results) {
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')}) closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')})
closureColumn(header: "Size", preferredWidth: 20, type: Long, read : {row -> row.size}) closureColumn(header: "Size", preferredWidth: 20, type: Long, read : {row -> row.size})
closureColumn(header: "Direct Sources", preferredWidth: 50, type : Integer, read : { row -> model.hashBucket[row.infohash].size()}) closureColumn(header: "Direct Sources", preferredWidth: 50, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
closureColumn(header: "Possible Sources", preferredWidth : 50, type : Integer, read : {row -> model.sourcesBucket[row.infohash].size()}) closureColumn(header: "Possible Sources", preferredWidth : 50, type : Integer, read : {row -> model.sourcesBucket[row.infohash].size()})
} closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
} closureColumn(header: "Trust", preferredWidth: 50, type: String, read : {row ->
} model.core.trustService.getLevel(row.sender.destination).toString()
panel(constraints : BorderLayout.SOUTH) { })
button(text : "Download", enabled : bind {model.downloadActionEnabled}, downloadAction)
} }
} }
} }
@@ -90,19 +63,17 @@ class SearchTabView {
this.pane.putClientProperty("results-table",resultsTable) this.pane.putClientProperty("results-table",resultsTable)
this.resultsTable = resultsTable this.resultsTable = resultsTable
this.sendersTable = sendersTable
def selectionModel = resultsTable.getSelectionModel() def selectionModel = resultsTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION) selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener( { selectionModel.addListSelectionListener( {
int row = resultsTable.getSelectedRow() int row = resultsTable.getSelectedRow()
if (row < 0) { if (row < 0)
model.downloadActionEnabled = false
return return
}
if (lastSortEvent != null) if (lastSortEvent != null)
row = resultsTable.rowSorter.convertRowIndexToModel(row) row = resultsTable.rowSorter.convertRowIndexToModel(row)
model.downloadActionEnabled = mvcGroup.parentGroup.model.canDownload(model.results[row].infohash) mvcGroup.parentGroup.model.trustButtonsEnabled = true
mvcGroup.parentGroup.model.downloadActionEnabled = mvcGroup.parentGroup.model.canDownload(model.results[row].infohash)
}) })
} }
} }
@@ -127,11 +98,12 @@ class SearchTabView {
} }
parent.setTabComponentAt(index, tabPanel) parent.setTabComponentAt(index, tabPanel)
mvcGroup.parentGroup.view.showSearchWindow.call()
def centerRenderer = new DefaultTableCellRenderer() def centerRenderer = new DefaultTableCellRenderer()
centerRenderer.setHorizontalAlignment(JLabel.CENTER) centerRenderer.setHorizontalAlignment(JLabel.CENTER)
resultsTable.columnModel.getColumn(1).setCellRenderer(centerRenderer)
resultsTable.setDefaultRenderer(Integer.class,centerRenderer) resultsTable.setDefaultRenderer(Integer.class,centerRenderer)
resultsTable.columnModel.getColumn(4).setCellRenderer(centerRenderer)
resultsTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer()) resultsTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
@@ -146,7 +118,7 @@ class SearchTabView {
if (e.button == MouseEvent.BUTTON3) if (e.button == MouseEvent.BUTTON3)
showPopupMenu(e) showPopupMenu(e)
else if (e.button == MouseEvent.BUTTON1 && e.clickCount == 2) else if (e.button == MouseEvent.BUTTON1 && e.clickCount == 2)
mvcGroup.controller.download() mvcGroup.parentGroup.controller.download()
} }
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
@@ -154,42 +126,21 @@ class SearchTabView {
showPopupMenu(e) showPopupMenu(e)
} }
}) })
// senders table
sendersTable.setDefaultRenderer(Integer.class, centerRenderer)
sendersTable.rowSorter.addRowSorterListener({evt -> lastSendersSortEvent = evt})
sendersTable.rowSorter.setSortsOnUpdates(true)
def selectionModel = sendersTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
int row = selectedSenderRow()
if (row < 0) {
model.trustButtonsEnabled = false
return
} else {
model.trustButtonsEnabled = true
model.results.clear()
Persona p = model.senders[row]
model.results.addAll(model.sendersBucket[p])
resultsTable.model.fireTableDataChanged()
}
})
} }
def closeTab = { def closeTab = {
int index = parent.indexOfTab(searchTerms) int index = parent.indexOfTab(searchTerms)
parent.removeTabAt(index) parent.removeTabAt(index)
model.trustButtonsEnabled = false mvcGroup.parentGroup.model.trustButtonsEnabled = false
model.downloadActionEnabled = false mvcGroup.parentGroup.model.downloadActionEnabled = false
mvcGroup.destroy() mvcGroup.destroy()
} }
def showPopupMenu(MouseEvent e) { def showPopupMenu(MouseEvent e) {
JPopupMenu menu = new JPopupMenu() JPopupMenu menu = new JPopupMenu()
if (model.downloadActionEnabled) { if (mvcGroup.parentGroup.model.downloadActionEnabled) {
JMenuItem download = new JMenuItem("Download") JMenuItem download = new JMenuItem("Download")
download.addActionListener({mvcGroup.controller.download()}) download.addActionListener({mvcGroup.parentGroup.controller.download()})
menu.add(download) menu.add(download)
} }
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard") JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
@@ -209,13 +160,4 @@ class SearchTabView {
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard() def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
clipboard.setContents(selection, null) clipboard.setContents(selection, null)
} }
int selectedSenderRow() {
int row = sendersTable.getSelectedRow()
if (row < 0)
return -1
if (lastSendersSortEvent != null)
row = sendersTable.rowSorter.convertRowIndexToModel(row)
row
}
} }

View File

@@ -1,120 +0,0 @@
package com.muwire.gui
import griffon.core.artifact.GriffonView
import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor
import javax.swing.JDialog
import javax.swing.ListSelectionModel
import javax.swing.SwingConstants
import java.awt.BorderLayout
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonView)
class TrustListView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
TrustListModel model
def dialog
def mainFrame
def mainPanel
def sortEvents = [:]
void initUI() {
mainFrame = application.windowManager.findWindow("main-frame")
dialog = new JDialog(mainFrame, model.trustList.persona.getHumanReadableName(), true)
mainPanel = builder.panel {
borderLayout()
panel(constraints : BorderLayout.NORTH) {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label(text: "Trust List of "+model.trustList.persona.getHumanReadableName())
}
panel (constraints: BorderLayout.SOUTH) {
label(text : "Last updated "+ new Date(model.trustList.timestamp))
}
}
panel(constraints : BorderLayout.CENTER) {
gridLayout(rows : 1, cols : 2)
panel {
borderLayout()
scrollPane (constraints : BorderLayout.CENTER){
table(id : "trusted-table", autoCreateRowSorter : true) {
tableModel(list : model.trusted) {
closureColumn(header: "Trusted Users", type : String, read : {it.getHumanReadableName()})
closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.destination).toString()})
}
}
}
panel (constraints : BorderLayout.SOUTH) {
gridBagLayout()
button(text : "Trust", constraints : gbc(gridx : 0, gridy : 0), trustFromTrustedAction)
button(text : "Distrust", constraints : gbc(gridx : 1, gridy : 0), distrustFromTrustedAction)
}
}
panel {
borderLayout()
scrollPane (constraints : BorderLayout.CENTER ){
table(id : "distrusted-table", autoCreateRowSorter : true) {
tableModel(list : model.distrusted) {
closureColumn(header: "Distrusted Users", type : String, read : {it.getHumanReadableName()})
closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.destination).toString()})
}
}
}
panel(constraints : BorderLayout.SOUTH) {
gridBagLayout()
button(text : "Trust", constraints : gbc(gridx : 0, gridy : 0), trustFromDistrustedAction)
button(text : "Distrust", constraints : gbc(gridx : 1, gridy : 0), distrustFromDistrustedAction)
}
}
}
}
}
void mvcGroupInit(Map<String,String> 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)
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
dialog.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent e) {
mvcGroup.destroy()
}
})
dialog.show()
}
int getSelectedRow(String tableName) {
def table = builder.getVariable(tableName)
int selectedRow = table.getSelectedRow()
if (selectedRow < 0)
return -1
if (sortEvents.get(tableName) != null)
selectedRow = table.rowSorter.convertRowIndexToModel(selectedRow)
selectedRow
}
void fireUpdate(String tableName) {
def table = builder.getVariable(tableName)
table.model.fireTableDataChanged()
}
}

View File

@@ -1,25 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonFestRule
import org.fest.swing.fixture.FrameFixture
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
class ContentPanelIntegrationTest {
static {
System.setProperty('griffon.swing.edt.violations.check', 'true')
System.setProperty('griffon.swing.edt.hang.monitor', 'true')
}
@Rule
public final GriffonFestRule fest = new GriffonFestRule()
private FrameFixture window
@Test
void smokeTest() {
fail('Not implemented yet!')
}
}

View File

@@ -1,25 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonFestRule
import org.fest.swing.fixture.FrameFixture
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
class I2PStatusIntegrationTest {
static {
System.setProperty('griffon.swing.edt.violations.check', 'true')
System.setProperty('griffon.swing.edt.hang.monitor', 'true')
}
@Rule
public final GriffonFestRule fest = new GriffonFestRule()
private FrameFixture window
@Test
void smokeTest() {
fail('Not implemented yet!')
}
}

View File

@@ -1,25 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonFestRule
import org.fest.swing.fixture.FrameFixture
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
class MuWireStatusIntegrationTest {
static {
System.setProperty('griffon.swing.edt.violations.check', 'true')
System.setProperty('griffon.swing.edt.hang.monitor', 'true')
}
@Rule
public final GriffonFestRule fest = new GriffonFestRule()
private FrameFixture window
@Test
void smokeTest() {
fail('Not implemented yet!')
}
}

View File

@@ -1,25 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonFestRule
import org.fest.swing.fixture.FrameFixture
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
class TrustListIntegrationTest {
static {
System.setProperty('griffon.swing.edt.violations.check', 'true')
System.setProperty('griffon.swing.edt.hang.monitor', 'true')
}
@Rule
public final GriffonFestRule fest = new GriffonFestRule()
private FrameFixture window
@Test
void smokeTest() {
fail('Not implemented yet!')
}
}

View File

@@ -1,39 +0,0 @@
package com.muwire.gui
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JTable
import javax.swing.table.DefaultTableCellRenderer
import com.muwire.core.download.Downloader
import net.i2p.data.DataHelper
class DownloadProgressRenderer extends DefaultTableCellRenderer {
DownloadProgressRenderer() {
setHorizontalAlignment(JLabel.CENTER)
}
@Override
JComponent getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
Downloader d = (Downloader) value
int pieces = d.nPieces
int done = d.donePieces()
int percent = -1
if (pieces != 0)
percent = (done * 100 / pieces)
String totalSize = DataHelper.formatSize2Decimal(d.length, false) + "B"
setText(String.format("%2d", percent) + "% of ${totalSize} ($done/$pieces pcs)".toString())
if (isSelected) {
setForeground(table.getSelectionForeground())
setBackground(table.getSelectionBackground())
} else {
setForeground(table.getForeground())
setBackground(table.getBackground())
}
this
}
}

View File

@@ -1,13 +0,0 @@
package com.muwire.gui
import com.muwire.core.download.Downloader
class DownloaderComparator implements Comparator<Downloader>{
@Override
public int compare(Downloader o1, Downloader o2) {
double d1 = o1.donePieces() * 1.0 / o1.nPieces
double d2 = o2.donePieces() * 1.0 / o2.nPieces
return Double.compare(d1, d2);
}
}

View File

@@ -1,21 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonUnitRule
import griffon.core.test.TestFor
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
@TestFor(ContentPanelController)
class ContentPanelControllerTest {
private ContentPanelController controller
@Rule
public final GriffonUnitRule griffon = new GriffonUnitRule()
@Test
void smokeTest() {
fail('Not yet implemented!')
}
}

View File

@@ -1,21 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonUnitRule
import griffon.core.test.TestFor
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
@TestFor(I2PStatusController)
class I2PStatusControllerTest {
private I2PStatusController controller
@Rule
public final GriffonUnitRule griffon = new GriffonUnitRule()
@Test
void smokeTest() {
fail('Not yet implemented!')
}
}

View File

@@ -1,21 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonUnitRule
import griffon.core.test.TestFor
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
@TestFor(MuWireStatusController)
class MuWireStatusControllerTest {
private MuWireStatusController controller
@Rule
public final GriffonUnitRule griffon = new GriffonUnitRule()
@Test
void smokeTest() {
fail('Not yet implemented!')
}
}

View File

@@ -1,21 +0,0 @@
package com.muwire.gui
import griffon.core.test.GriffonUnitRule
import griffon.core.test.TestFor
import org.junit.Rule
import org.junit.Test
import static org.junit.Assert.fail
@TestFor(TrustListController)
class TrustListControllerTest {
private TrustListController controller
@Rule
public final GriffonUnitRule griffon = new GriffonUnitRule()
@Test
void smokeTest() {
fail('Not yet implemented!')
}
}

View File

@@ -11,7 +11,6 @@ import net.i2p.client.I2PSession
import net.i2p.client.I2PSessionMuxedListener import net.i2p.client.I2PSessionMuxedListener
import net.i2p.client.datagram.I2PDatagramDissector import net.i2p.client.datagram.I2PDatagramDissector
import net.i2p.client.datagram.I2PDatagramMaker import net.i2p.client.datagram.I2PDatagramMaker
import net.i2p.crypto.SigType
import net.i2p.util.SystemVersion import net.i2p.util.SystemVersion
import net.i2p.data.* import net.i2p.data.*
@@ -44,7 +43,7 @@ public class HostCache {
def session def session
if (!keyfile.exists()) { if (!keyfile.exists()) {
def os = new FileOutputStream(keyfile); def os = new FileOutputStream(keyfile);
myDest = i2pClient.createDestination(os, SigType.EdDSA_SHA512_Ed25519) myDest = i2pClient.createDestination(os)
os.close() os.close()
println "No key.dat file was found, so creating a new destination." 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 "This is the destination you want to give out for your new HostCache"
@@ -64,9 +63,6 @@ public class HostCache {
Timer timer = new Timer("timer", true) Timer timer = new Timer("timer", true)
timer.schedule({hostPool.age()} as TimerTask, 1000,1000) timer.schedule({hostPool.age()} as TimerTask, 1000,1000)
timer.schedule({crawler.startCrawl()} as TimerTask, 10000, 10000) 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) I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)

View File

@@ -2,8 +2,6 @@ package com.muwire.hostcache
import java.util.stream.Collectors import java.util.stream.Collectors
import groovy.json.JsonOutput
class HostPool { class HostPool {
final def maxFailures final def maxFailures
@@ -76,25 +74,4 @@ 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 {
def json = [:]
json.destination = it.destination.toBase64()
json.verifyTime = it.verifyTime
json.leafSlots = it.leafSlots
json.peerSlots = it.peerSlots
json.verificationFailures = it.verificationFailures
def str = jsonOutput.toJson(json)
writer.println(str)
}
}
}
} }

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