Compare commits

...

17 Commits

Author SHA1 Message Date
Zlatin Balevsky
a5d442d320 Release 0.0.13 for keyword search fix 2019-06-07 06:37:23 +01:00
Zlatin Balevsky
3f9ee887d6 prevent NPE in toString 2019-06-07 06:31:29 +01:00
Zlatin Balevsky
4a9e6d3b6b prevent npe in keyword searches 2019-06-07 06:14:40 +01:00
Zlatin Balevsky
80f2cc5f99 logging and toString() 2019-06-07 06:07:02 +01:00
Zlatin Balevsky
12283dba9d Release 0.0.12 for search by hash 2019-06-06 22:22:43 +01:00
Zlatin Balevsky
5c959bc8b7 name update search tab 2019-06-06 22:07:20 +01:00
Zlatin Balevsky
f3712fe7af delay initial update check a minute 2019-06-06 21:52:35 +01:00
Zlatin Balevsky
3e49b0ec66 infohash may be null 2019-06-06 21:40:44 +01:00
Zlatin Balevsky
f90beb8e3d encode infohash 2019-06-06 21:31:00 +01:00
Zlatin Balevsky
fbad7b6c7e searchHash 2019-06-06 21:27:07 +01:00
Zlatin Balevsky
ec2d89c18c serialize infohash 2019-06-06 21:21:40 +01:00
Zlatin Balevsky
c27fc0a515 update from infohash 2019-06-06 21:08:58 +01:00
Zlatin Balevsky
14681c2060 search by hash ui 2019-06-06 20:30:15 +01:00
Zlatin Balevsky
1aeb230ea8 catch exceptions in event dispatch thread 2019-06-06 19:31:10 +01:00
Zlatin Balevsky
d1dfc73f5a decode infohash 2019-06-06 19:28:29 +01:00
Zlatin Balevsky
0cebe4119c update list of limitations 2019-06-06 14:19:43 +01:00
Zlatin Balevsky
9f21120ec8 print periodic stats 2019-06-06 13:59:05 +01:00
14 changed files with 173 additions and 41 deletions

View File

@@ -32,7 +32,6 @@ At the moment there are very few nodes on the network, so you will see very few
### Known bugs and limitations ### Known bugs and limitations
* Sometimes the list of shared files gets lost
* Many UI features you would expect are not there yet * Many UI features you would expect are not there yet

View File

@@ -4,9 +4,15 @@ import java.util.concurrent.CountDownLatch
import com.muwire.core.Core import com.muwire.core.Core
import com.muwire.core.MuWireSettings import com.muwire.core.MuWireSettings
import com.muwire.core.connection.ConnectionAttemptStatus
import com.muwire.core.connection.ConnectionEvent
import com.muwire.core.connection.DisconnectionEvent
import com.muwire.core.files.AllFilesLoadedEvent import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.FileHashedEvent import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileLoadedEvent
import com.muwire.core.files.FileSharedEvent import com.muwire.core.files.FileSharedEvent
import com.muwire.core.upload.UploadEvent
import com.muwire.core.upload.UploadFinishedEvent
class Cli { class Cli {
@@ -28,28 +34,15 @@ class Cli {
Core core Core core
try { try {
core = new Core(props, home, "0.0.11") core = new Core(props, home, "0.0.13")
} catch (Exception bad) { } catch (Exception bad) {
bad.printStackTrace(System.out) bad.printStackTrace(System.out)
println "Failed to initialize core, exiting" println "Failed to initialize core, exiting"
System.exit(1) System.exit(1)
} }
def latch = new CountDownLatch(1)
def fileLoader = new Object() {
public void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
latch.countDown()
}
}
core.eventBus.register(AllFilesLoadedEvent.class, fileLoader)
core.startServices()
println "waiting for files to load"
latch.await()
// now we begin
println "MuWire is ready"
def filesList def filesList
if (args.length == 0) { if (args.length == 0) {
@@ -62,14 +55,39 @@ class Cli {
Thread.sleep(1000) Thread.sleep(1000)
println "loading shared files from $filesList" println "loading shared files from $filesList"
core.eventBus.register(FileHashedEvent.class, new Object() { // listener for shared files
void onFileHashedEvent(FileHashedEvent e) { def sharedListener = new SharedListener()
if (e.error != null) core.eventBus.register(FileHashedEvent.class, sharedListener)
println "ERROR $e.error" core.eventBus.register(FileLoadedEvent.class, sharedListener)
else
println "Shared file : $e.sharedFile.file" // for connections
def connectionsListener = new ConnectionListener()
core.eventBus.register(ConnectionEvent.class, connectionsListener)
core.eventBus.register(DisconnectionEvent.class, connectionsListener)
// for uploads
def uploadsListener = new UploadsListener()
core.eventBus.register(UploadEvent.class, uploadsListener)
core.eventBus.register(UploadFinishedEvent.class, uploadsListener)
Timer timer = new Timer("status-printer", true)
timer.schedule({
println "Connections $connectionsListener.connections Uploads $uploadsListener.uploads Shared $sharedListener.shared"
} as TimerTask, 60000, 60000)
def latch = new CountDownLatch(1)
def fileLoader = new Object() {
public void onAllFilesLoadedEvent(AllFilesLoadedEvent e) {
latch.countDown()
} }
}) }
core.eventBus.register(AllFilesLoadedEvent.class, fileLoader)
core.startServices()
println "waiting for files to load"
latch.await()
// now we begin
println "MuWire is ready"
filesList = new File(filesList) filesList = new File(filesList)
filesList.withReader { filesList.withReader {
@@ -83,4 +101,42 @@ class Cli {
}) })
Thread.sleep(Integer.MAX_VALUE) Thread.sleep(Integer.MAX_VALUE)
} }
static class ConnectionListener {
volatile int connections
public void onConnectionEvent(ConnectionEvent e) {
if (e.status == ConnectionAttemptStatus.SUCCESSFUL)
connections++
}
public void onDisconnectionEvent(DisconnectionEvent e) {
connections--
}
}
static class UploadsListener {
volatile int uploads
public void onUploadEvent(UploadEvent e) {
uploads++
println "Starting upload of ${e.uploader.file.getName()} to ${e.uploader.request.downloader.getHumanReadableName()}"
}
public void onUploadFinishedEvent(UploadFinishedEvent e) {
uploads--
println "Finished upload of ${e.uploader.file.getName()} to ${e.uploader.request.downloader.getHumanReadableName()}"
}
}
static class SharedListener {
volatile int shared
void onFileHashedEvent(FileHashedEvent e) {
if (e.error != null)
println "ERROR $e.error"
else {
println "Shared file : $e.sharedFile.file"
shared++
}
}
void onFileLoadedEvent(FileLoadedEvent e) {
shared++
}
}
} }

View File

@@ -248,7 +248,7 @@ public class Core {
} }
} }
Core core = new Core(props, home, "0.0.11") Core core = new Core(props, home, "0.0.13")
core.startServices() core.startServices()
// ... at the end, sleep or execute script // ... at the end, sleep or execute script

View File

@@ -3,6 +3,7 @@ package com.muwire.core
import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executor import java.util.concurrent.Executor
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.logging.Level
import com.muwire.core.files.FileSharedEvent import com.muwire.core.files.FileSharedEvent
@@ -23,14 +24,18 @@ class EventBus {
} }
private void publishInternal(Event e) { private void publishInternal(Event e) {
log.fine "publishing event $e of type ${e.getClass().getSimpleName()}" log.fine "publishing event $e of type ${e.getClass().getSimpleName()} event $e"
def currentHandlers def currentHandlers
final def clazz = e.getClass() final def clazz = e.getClass()
synchronized(this) { synchronized(this) {
currentHandlers = handlers.getOrDefault(clazz, []) currentHandlers = handlers.getOrDefault(clazz, [])
} }
currentHandlers.each { currentHandlers.each {
it."on${clazz.getSimpleName()}"(e) try {
it."on${clazz.getSimpleName()}"(e)
} catch (Exception bad) {
log.log(Level.SEVERE, "exception dispatching event",bad)
}
} }
} }

View File

@@ -127,6 +127,8 @@ abstract class Connection implements Closeable {
query.uuid = e.searchEvent.getUuid() query.uuid = e.searchEvent.getUuid()
query.firstHop = e.firstHop query.firstHop = e.firstHop
query.keywords = e.searchEvent.getSearchTerms() query.keywords = e.searchEvent.getSearchTerms()
if (e.searchEvent.searchHash != null)
query.infohash = Base64.encode(e.searchEvent.searchHash)
query.replyTo = e.replyTo.toBase64() query.replyTo = e.replyTo.toBase64()
if (e.originator != null) if (e.originator != null)
query.originator = e.originator.toBase64() query.originator = e.originator.toBase64()
@@ -155,8 +157,11 @@ abstract class Connection implements Closeable {
protected void handleSearch(def search) { protected void handleSearch(def search) {
UUID uuid = UUID.fromString(search.uuid) UUID uuid = UUID.fromString(search.uuid)
if (search.infohash != null) byte [] infohash = null
if (search.infohash != null) {
search.keywords = null search.keywords = null
infohash = Base64.decode(search.infohash)
}
Destination replyTo = new Destination(search.replyTo) Destination replyTo = new Destination(search.replyTo)
TrustLevel trustLevel = trustService.getLevel(replyTo) TrustLevel trustLevel = trustService.getLevel(replyTo)
@@ -180,7 +185,7 @@ abstract class Connection implements Closeable {
SearchEvent searchEvent = new SearchEvent(searchTerms : search.keywords, SearchEvent searchEvent = new SearchEvent(searchTerms : search.keywords,
searchHash : search.infohash, searchHash : infohash,
uuid : uuid) uuid : uuid)
QueryEvent event = new QueryEvent ( searchEvent : searchEvent, QueryEvent event = new QueryEvent ( searchEvent : searchEvent,
replyTo : replyTo, replyTo : replyTo,

View File

@@ -13,4 +13,8 @@ class QueryEvent extends Event {
Persona originator Persona originator
Destination receivedOn Destination receivedOn
String toString() {
"searchEvent: $searchEvent firstHop:$firstHop, replyTo:${replyTo.toBase32()}" +
"originator: ${originator.getHumanReadableName()} receivedOn: ${receivedOn.toBase32()}"
}
} }

View File

@@ -1,10 +1,18 @@
package com.muwire.core.search package com.muwire.core.search
import com.muwire.core.Event import com.muwire.core.Event
import com.muwire.core.InfoHash
class SearchEvent extends Event { class SearchEvent extends Event {
List<String> searchTerms List<String> searchTerms
byte [] searchHash byte [] searchHash
UUID uuid UUID uuid
String toString() {
def infoHash = null
if (searchHash != null)
infoHash = new InfoHash(searchHash)
"searchTerms: $searchTerms searchHash:$infoHash, uuid:$uuid"
}
} }

View File

@@ -1,8 +1,10 @@
package com.muwire.core.update package com.muwire.core.update
import com.muwire.core.Event import com.muwire.core.Event
import com.muwire.core.InfoHash
class UpdateAvailableEvent extends Event { class UpdateAvailableEvent extends Event {
String version String version
String signer String signer
String infoHash
} }

View File

@@ -36,7 +36,7 @@ class UpdateClient {
void start() { void start() {
session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, 2) session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, 2)
timer.schedule({checkUpdate()} as TimerTask, 30000, 60 * 60 * 1000) timer.schedule({checkUpdate()} as TimerTask, 60000, 60 * 60 * 1000)
} }
void stop() { void stop() {
@@ -107,7 +107,7 @@ class UpdateClient {
} }
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)) eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer, infoHash : payload.infoHash))
} catch (Exception e) { } catch (Exception e) {
log.log(Level.WARNING,"Invalid datagram",e) log.log(Level.WARNING,"Invalid datagram",e)

View File

@@ -77,13 +77,15 @@ public class InfoHash {
public String toString() { public String toString() {
String rv = "InfoHash[root:"+Base32.encode(root) + " hashList:"; String rv = "InfoHash[root:"+Base32.encode(root) + " hashList:";
List<String> b32HashList = new ArrayList<>(hashList.length / SIZE); List<String> b64HashList = new ArrayList<>();
byte [] tmp = new byte[SIZE]; if (hashList != null) {
for (int i = 0; i < hashList.length / SIZE; i++) { byte [] tmp = new byte[SIZE];
System.arraycopy(hashList, SIZE * i, tmp, 0, SIZE); for (int i = 0; i < hashList.length / SIZE; i++) {
b32HashList.add(Base32.encode(tmp)); System.arraycopy(hashList, SIZE * i, tmp, 0, SIZE);
b64HashList.add(Base32.encode(tmp));
}
} }
rv += b32HashList.toString(); rv += b64HashList.toString();
rv += "]"; rv += "]";
return rv; return rv;
} }

View File

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

View File

@@ -7,6 +7,8 @@ import griffon.core.mvc.MVCGroup
import griffon.core.mvc.MVCGroupConfiguration import griffon.core.mvc.MVCGroupConfiguration
import griffon.inject.MVCMember import griffon.inject.MVCMember
import griffon.metadata.ArtifactProviderFor import griffon.metadata.ArtifactProviderFor
import net.i2p.data.Base64
import javax.annotation.Nonnull import javax.annotation.Nonnull
import javax.inject.Inject import javax.inject.Inject
@@ -42,15 +44,36 @@ class MainFrameController {
params["uuid"] = uuid.toString() params["uuid"] = uuid.toString()
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
// this can be improved a lot def searchEvent
def terms = search.toLowerCase().trim().split(Constants.SPLIT_PATTERN) if (model.hashSearch) {
def searchEvent = new SearchEvent(searchTerms : terms, uuid : uuid) searchEvent = new SearchEvent(searchHash : Base64.decode(search), uuid : uuid)
} else {
// this can be improved a lot
def terms = search.toLowerCase().trim().split(Constants.SPLIT_PATTERN)
searchEvent = new SearchEvent(searchTerms : terms, uuid : uuid)
}
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true, core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
replyTo: core.me.destination, receivedOn: core.me.destination, replyTo: core.me.destination, receivedOn: core.me.destination,
originator : core.me)) originator : core.me))
} }
void search(String infoHash, String tabTitle) {
def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, "search window")
def uuid = UUID.randomUUID()
Map<String, Object> params = new HashMap<>()
params["search-terms"] = tabTitle
params["uuid"] = uuid.toString()
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
model.results[uuid.toString()] = group
def searchEvent = new SearchEvent(searchHash : Base64.decode(infoHash), uuid:uuid)
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
replyTo: core.me.destination, receivedOn: core.me.destination,
originator : core.me))
}
private def selectedResult() { private def selectedResult() {
def selected = builder.getVariable("result-tabs").getSelectedComponent() def selected = builder.getVariable("result-tabs").getSelectedComponent()
def group = selected.getClientProperty("mvc-group") def group = selected.getClientProperty("mvc-group")
@@ -137,6 +160,16 @@ class MainFrameController {
markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted) markTrust("trusted-table", TrustLevel.NEUTRAL, model.trusted)
} }
@ControllerAction
void keywordSearch() {
model.hashSearch = false
}
@ControllerAction
void hashSearch() {
model.hashSearch = true
}
void mvcGroupInit(Map<String, String> args) { void mvcGroupInit(Map<String, String> args) {
application.addPropertyChangeListener("core", {e-> application.addPropertyChangeListener("core", {e->
core = e.getNewValue() core = e.getNewValue()

View File

@@ -29,6 +29,7 @@ import com.muwire.core.upload.UploadFinishedEvent
import griffon.core.GriffonApplication import griffon.core.GriffonApplication
import griffon.core.artifact.GriffonModel import griffon.core.artifact.GriffonModel
import griffon.core.env.Metadata
import griffon.core.mvc.MVCGroup import griffon.core.mvc.MVCGroup
import griffon.inject.MVCMember import griffon.inject.MVCMember
import griffon.transform.FXObservable import griffon.transform.FXObservable
@@ -38,8 +39,11 @@ import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel) @ArtifactProviderFor(GriffonModel)
class MainFrameModel { class MainFrameModel {
@Inject Metadata metadata
@MVCMember @Nonnull @MVCMember @Nonnull
FactoryBuilderSupport builder FactoryBuilderSupport builder
@MVCMember @Nonnull
MainFrameController controller
@Inject @Nonnull GriffonApplication application @Inject @Nonnull GriffonApplication application
@Observable boolean coreInitialized = false @Observable boolean coreInitialized = false
@@ -52,6 +56,8 @@ class MainFrameModel {
def trusted = [] def trusted = []
def distrusted = [] def distrusted = []
boolean hashSearch
@Observable int connections @Observable int connections
@Observable String me @Observable String me
@Observable boolean searchButtonsEnabled @Observable boolean searchButtonsEnabled
@@ -260,7 +266,13 @@ class MainFrameModel {
void onUpdateAvailableEvent(UpdateAvailableEvent e) { void onUpdateAvailableEvent(UpdateAvailableEvent e) {
runInsideUIAsync { runInsideUIAsync {
JOptionPane.showMessageDialog(null, "A new version of MuWire is available from $e.signer. Please update to $e.version")
int option = JOptionPane.showConfirmDialog(null,
"MuWire $e.version is available from $e.signer. You have "+ metadata["application.version"]+" Update?",
"New MuWire version availble", JOptionPane.OK_CANCEL_OPTION)
if (option == JOptionPane.CANCEL_OPTION)
return
controller.search(e.infoHash,"MuWire update")
} }
} }

View File

@@ -76,6 +76,12 @@ class MainFrameView {
} }
panel( constraints: BorderLayout.EAST) { panel( constraints: BorderLayout.EAST) {
panel {
buttonGroup(id : "searchButtonGroup")
radioButton(text : "Keywords", selected : true, buttonGroup : searchButtonGroup, keywordSearchAction)
radioButton(text : "Hash", selected : false, buttonGroup : searchButtonGroup, hashSearchAction)
}
button(text: "Search", searchAction) button(text: "Search", searchAction)
} }
} }