Compare commits

..

4 Commits

65 changed files with 627 additions and 1820 deletions

View File

@@ -35,9 +35,8 @@ This helps with scalability
### Web UI/Plugin
* HTML 5 media players
* Minimal dependency (break up groovy-all.jar)
* Remove versions from jar names
* Security: POST nonces, CSP headers
* Upload files from browser to plugin via drag-and-drop
* Check permissions, display better errors when sharing local folders

View File

@@ -32,7 +32,7 @@ import com.muwire.core.UILoadedEvent
import com.muwire.core.files.AllFilesLoadedEvent
class CliLanterna {
private static final String MW_VERSION = "0.6.10"
private static final String MW_VERSION = "0.6.8"
private static volatile Core core

View File

@@ -3,7 +3,6 @@ package com.muwire.clilanterna
import com.googlecode.lanterna.gui2.TextGUIThread
import com.googlecode.lanterna.gui2.table.TableModel
import com.muwire.core.Core
import com.muwire.core.InfoHash
import com.muwire.core.SharedFile
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.DirectoryWatchedEvent
@@ -73,7 +72,7 @@ class FilesModel {
sharedFiles.each {
long size = it.getCachedLength()
boolean comment = it.comment != null
boolean certified = core.certificateManager.hasLocalCertificate(new InfoHash(it.getRoot()))
boolean certified = core.certificateManager.hasLocalCertificate(it.getInfoHash())
String hits = String.valueOf(it.getHits())
String downloaders = String.valueOf(it.getDownloaders().size())
model.addRow(new SharedFileWrapper(it), DataHelper.formatSize2(size, false)+"B", comment, certified, hits, downloaders)

View File

@@ -32,16 +32,6 @@ import com.muwire.core.filecert.CertificateManager
import com.muwire.core.filecert.UICreateCertificateEvent
import com.muwire.core.filecert.UIFetchCertificatesEvent
import com.muwire.core.filecert.UIImportCertificateEvent
import com.muwire.core.filefeeds.FeedClient
import com.muwire.core.filefeeds.FeedFetchEvent
import com.muwire.core.filefeeds.FeedItemFetchedEvent
import com.muwire.core.filefeeds.FeedManager
import com.muwire.core.filefeeds.UIDownloadFeedItemEvent
import com.muwire.core.filefeeds.UIFilePublishedEvent
import com.muwire.core.filefeeds.UIFeedConfigurationEvent
import com.muwire.core.filefeeds.UIFeedDeletedEvent
import com.muwire.core.filefeeds.UIFeedUpdateEvent
import com.muwire.core.filefeeds.UIFileUnpublishedEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHashedEvent
import com.muwire.core.files.FileHasher
@@ -126,8 +116,6 @@ public class Core {
final CertificateManager certificateManager
final ChatServer chatServer
final ChatManager chatManager
final FeedManager feedManager
private final FeedClient feedClient
private final Router router
@@ -206,7 +194,7 @@ public class Core {
// options like tunnel length and quantity
I2PSocketManager socketManager
keyDat.withInputStream {
socketManager = new I2PSocketManagerFactory().createDisconnectedManager(it, i2pOptions["i2cp.tcp.host"], i2pOptions["i2cp.tcp.port"].toInteger(), i2pOptions)
socketManager = new I2PSocketManagerFactory().createManager(it, i2pOptions["i2cp.tcp.host"], i2pOptions["i2cp.tcp.port"].toInteger(), i2pOptions)
}
socketManager.getDefaultOptions().setReadTimeout(60000)
socketManager.getDefaultOptions().setConnectTimeout(30000)
@@ -280,9 +268,6 @@ public class Core {
eventBus.register(FileLoadedEvent.class, persisterFolderService)
eventBus.register(FileHashedEvent.class, persisterFolderService)
eventBus.register(FileUnsharedEvent.class, persisterFolderService)
eventBus.register(UICommentEvent.class, persisterFolderService)
eventBus.register(UIFilePublishedEvent.class, persisterFolderService)
eventBus.register(UIFileUnpublishedEvent.class, persisterFolderService)
log.info("initializing host cache")
File hostStorage = new File(home, "hosts.json")
@@ -325,19 +310,6 @@ public class Core {
register(TrustEvent.class, chatServer)
}
log.info("initializing feed manager")
feedManager = new FeedManager(eventBus, home)
eventBus.with {
register(FeedItemFetchedEvent.class, feedManager)
register(FeedFetchEvent.class, feedManager)
register(UIFeedConfigurationEvent.class, feedManager)
register(UIFeedDeletedEvent.class, feedManager)
}
log.info("initializing feed client")
feedClient = new FeedClient(i2pConnector, eventBus, me, feedManager)
eventBus.register(UIFeedUpdateEvent.class, feedClient)
log.info "initializing results sender"
ResultsSender resultsSender = new ResultsSender(eventBus, i2pConnector, me, props, certificateManager, chatServer)
@@ -349,7 +321,6 @@ public class Core {
log.info("initializing download manager")
downloadManager = new DownloadManager(eventBus, trustService, meshManager, props, i2pConnector, home, me)
eventBus.register(UIDownloadEvent.class, downloadManager)
eventBus.register(UIDownloadFeedItemEvent.class, downloadManager)
eventBus.register(UILoadedEvent.class, downloadManager)
eventBus.register(FileDownloadedEvent.class, downloadManager)
eventBus.register(UIDownloadCancelledEvent.class, downloadManager)
@@ -358,7 +329,7 @@ public class Core {
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
log.info("initializing upload manager")
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager, persisterFolderService, props)
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager, props)
log.info("initializing connection establisher")
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
@@ -408,7 +379,6 @@ public class Core {
}
public void startServices() {
i2pSession.connect()
hasherService.start()
trustService.start()
trustService.waitForLoad()
@@ -419,8 +389,6 @@ public class Core {
connectionEstablisher.start()
hostCache.waitForLoad()
updateClient?.start()
feedManager.start()
feedClient.start()
}
public void shutdown() {
@@ -454,10 +422,6 @@ public class Core {
chatServer.stop()
log.info("shutting down chat manager")
chatManager.shutdown()
log.info("shutting down feed manager")
feedManager.stop()
log.info("shutting down feed client")
feedClient.stop()
log.info("shutting down connection manager")
connectionManager.shutdown()
log.info("killing i2p session")
@@ -505,7 +469,7 @@ public class Core {
}
}
Core core = new Core(props, home, "0.6.10")
Core core = new Core(props, home, "0.6.8")
core.startServices()
// ... at the end, sleep or execute script

View File

@@ -31,16 +31,6 @@ class MuWireSettings {
boolean shareHiddenFiles
boolean searchComments
boolean browseFiles
boolean fileFeed
boolean advertiseFeed
boolean autoPublishSharedFiles
boolean defaultFeedAutoDownload
int defaultFeedUpdateInterval
int defaultFeedItemsToKeep
boolean defaultFeedSequential
boolean startChatServer
int maxChatConnections
boolean advertiseChat
@@ -92,16 +82,6 @@ class MuWireSettings {
outBw = Integer.valueOf(props.getProperty("outBw","128"))
searchComments = Boolean.valueOf(props.getProperty("searchComments","true"))
browseFiles = Boolean.valueOf(props.getProperty("browseFiles","true"))
// feed settings
fileFeed = Boolean.valueOf(props.getProperty("fileFeed","true"))
advertiseFeed = Boolean.valueOf(props.getProperty("advertiseFeed","true"))
autoPublishSharedFiles = Boolean.valueOf(props.getProperty("autoPublishSharedFiles", "false"))
defaultFeedAutoDownload = Boolean.valueOf(props.getProperty("defaultFeedAutoDownload", "false"))
defaultFeedItemsToKeep = Integer.valueOf(props.getProperty("defaultFeedItemsToKeep", "1000"))
defaultFeedSequential = Boolean.valueOf(props.getProperty("defaultFeedSequential", "false"))
defaultFeedUpdateInterval = Integer.valueOf(props.getProperty("defaultFeedUpdateInterval", "60"))
speedSmoothSeconds = Integer.valueOf(props.getProperty("speedSmoothSeconds","60"))
totalUploadSlots = Integer.valueOf(props.getProperty("totalUploadSlots","-1"))
uploadSlotsPerUser = Integer.valueOf(props.getProperty("uploadSlotsPerUser","-1"))
@@ -157,16 +137,6 @@ class MuWireSettings {
props.setProperty("outBw", String.valueOf(outBw))
props.setProperty("searchComments", String.valueOf(searchComments))
props.setProperty("browseFiles", String.valueOf(browseFiles))
// feed settings
props.setProperty("fileFeed", String.valueOf(fileFeed))
props.setProperty("advertiseFeed", String.valueOf(advertiseFeed))
props.setProperty("autoPublishSharedFiles", String.valueOf(autoPublishSharedFiles))
props.setProperty("defaultFeedAutoDownload", String.valueOf(defaultFeedAutoDownload))
props.setProperty("defaultFeedItemsToKeep", String.valueOf(defaultFeedItemsToKeep))
props.setProperty("defaultFeedSequential", String.valueOf(defaultFeedSequential))
props.setProperty("defaultFeedUpdateInterval", String.valueOf(defaultFeedUpdateInterval))
props.setProperty("speedSmoothSeconds", String.valueOf(speedSmoothSeconds))
props.setProperty("totalUploadSlots", String.valueOf(totalUploadSlots))
props.setProperty("uploadSlotsPerUser", String.valueOf(uploadSlotsPerUser))

View File

@@ -255,6 +255,7 @@ abstract class Connection implements Closeable {
return
}
// TODO: make this mandatory at some point
byte[] sig2 = null
long queryTime = 0
if (search.sig2 != null) {
@@ -277,10 +278,8 @@ abstract class Connection implements Closeable {
return
}
}
} else {
} else
log.info("no extended signature in query")
return
}
SearchEvent searchEvent = new SearchEvent(searchTerms : search.keywords,
searchHash : infohash,

View File

@@ -15,11 +15,9 @@ import com.muwire.core.EventBus
import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings
import com.muwire.core.Persona
import com.muwire.core.SharedFile
import com.muwire.core.chat.ChatServer
import com.muwire.core.filecert.Certificate
import com.muwire.core.filecert.CertificateManager
import com.muwire.core.filefeeds.FeedItems
import com.muwire.core.files.FileManager
import com.muwire.core.hostcache.HostCache
import com.muwire.core.trust.TrustLevel
@@ -163,9 +161,6 @@ class ConnectionAcceptor {
case (byte)'I':
processIRC(e)
break
case (byte)'F':
processFEED(e)
break
default:
throw new Exception("Invalid read $read")
}
@@ -315,9 +310,6 @@ class ConnectionAcceptor {
boolean chat = false
if (headers.containsKey('Chat'))
chat = Boolean.parseBoolean(headers['Chat'])
boolean feed = false
if (headers.containsKey('Feed'))
feed = Boolean.parseBoolean(headers['Feed'])
byte [] personaBytes = Base64.decode(headers['Sender'])
Persona sender = new Persona(new ByteArrayInputStream(personaBytes))
@@ -337,7 +329,6 @@ class ConnectionAcceptor {
def json = slurper.parse(payload)
results[i] = ResultsParser.parse(sender, resultsUUID, json)
results[i].chat = chat
results[i].feed = feed
}
eventBus.publish(new UIResultBatchEvent(uuid: resultsUUID, results: results))
} catch (IOException bad) {
@@ -383,16 +374,13 @@ class ConnectionAcceptor {
boolean chat = chatServer.running.get() && settings.advertiseChat
os.write("Chat: ${chat}\r\n".getBytes(StandardCharsets.US_ASCII))
boolean feed = settings.fileFeed && settings.advertiseFeed
os.write("Feed: ${feed}\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(os))
JsonOutput jsonOutput = new JsonOutput()
sharedFiles.each {
it.hit(browser, System.currentTimeMillis(), "Browse Host");
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
def obj = ResultsSender.sharedFileToObj(it, false, certificates)
def json = jsonOutput.toJson(obj)
dos.writeShort((short)json.length())
@@ -536,56 +524,5 @@ class ConnectionAcceptor {
throw new Exception("Invalid IRC connection")
chatServer.handle(e)
}
private void processFEED(Endpoint e) {
try {
byte[] EED = new byte[5];
DataInputStream dis = new DataInputStream(e.getInputStream())
dis.readFully(EED);
if (EED != "EED\r\n".getBytes(StandardCharsets.US_ASCII))
throw new Exception("Invalid FEED connection")
OutputStream os = e.getOutputStream()
Map<String, String> headers = DataUtil.readAllHeaders(dis)
if (!headers.containsKey("Persona"))
throw new Exception("Persona header missing")
Persona requestor = new Persona(new ByteArrayInputStream(Base64.decode(headers['Persona'])))
if (requestor.destination != e.destination)
throw new Exception("Requestor persona mismatch")
if (!settings.fileFeed) {
os.write("403 Not Allowed\r\n\r\n".getBytes(StandardCharsets.US_ASCII))
os.flush()
e.close()
return
}
long timestamp = 0
if (headers.containsKey("Timestamp")) {
timestamp = Long.parseLong(headers['Timestamp'])
}
List<SharedFile> published = fileManager.getPublishedSince(timestamp)
os.write("200 OK\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("Count: ${published.size()}\r\n".getBytes(StandardCharsets.US_ASCII));
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(os))
JsonOutput jsonOutput = new JsonOutput()
published.each {
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
def obj = FeedItems.sharedFileToObj(it, certificates)
def json = jsonOutput.toJson(obj)
dos.writeShort((short)json.length())
dos.write(json.getBytes(StandardCharsets.US_ASCII))
}
dos.flush()
dos.close()
} finally {
e.close()
}
}
}

View File

@@ -1,7 +1,6 @@
package com.muwire.core.download
import com.muwire.core.connection.I2PConnector
import com.muwire.core.filefeeds.UIDownloadFeedItemEvent
import com.muwire.core.files.FileDownloadedEvent
import com.muwire.core.files.FileHasher
import com.muwire.core.mesh.Mesh
@@ -63,6 +62,11 @@ public class DownloadManager {
public void onUIDownloadEvent(UIDownloadEvent e) {
File incompletes = muSettings.incompleteLocation
if (incompletes == null)
incompletes = new File(home, "incompletes")
incompletes.mkdirs()
def size = e.result[0].size
def infohash = e.result[0].infohash
@@ -75,29 +79,12 @@ public class DownloadManager {
destinations.addAll(e.sources)
destinations.remove(me.destination)
doDownload(infohash, e.target, size, pieceSize, e.sequential, destinations)
Pieces pieces = getPieces(infohash, size, pieceSize, e.sequential)
}
public void onUIDownloadFeedItemEvent(UIDownloadFeedItemEvent e) {
Set<Destination> singleSource = new HashSet<>()
singleSource.add(e.item.getPublisher().getDestination())
doDownload(e.item.getInfoHash(), e.target, e.item.getSize(), e.item.getPieceSize(),
e.sequential, singleSource)
}
private void doDownload(InfoHash infoHash, File target, long size, int pieceSize,
boolean sequential, Set<Destination> destinations) {
File incompletes = muSettings.incompleteLocation
if (incompletes == null)
incompletes = new File(home, "incompletes")
incompletes.mkdirs()
Pieces pieces = getPieces(infoHash, size, pieceSize, sequential)
def downloader = new Downloader(eventBus, this, me, target, size,
infoHash, pieceSize, connector, destinations,
incompletes, pieces)
downloaders.put(infoHash, downloader)
def downloader = new Downloader(eventBus, this, me, e.target, size,
infohash, pieceSize, connector, destinations,
incompletes, pieces)
downloaders.put(infohash, downloader)
persistDownloaders()
executor.execute({downloader.download()} as Runnable)
eventBus.publish(new DownloadStartedEvent(downloader : downloader))

View File

@@ -405,9 +405,8 @@ public class Downloader {
}
eventBus.publish(
new FileDownloadedEvent(
downloadedFile : new DownloadedFile(file.getCanonicalFile(), getInfoHash().getRoot(), pieceSizePow2, successfulDestinations),
downloader : Downloader.this,
infoHash: getInfoHash()))
downloadedFile : new DownloadedFile(file.getCanonicalFile(), getInfoHash(), pieceSizePow2, successfulDestinations),
downloader : Downloader.this))
}
endpoint?.close()

View File

@@ -70,7 +70,7 @@ class CertificateManager {
}
void onUICreateCertificateEvent(UICreateCertificateEvent e) {
InfoHash infoHash = new InfoHash(e.sharedFile.getRoot())
InfoHash infoHash = e.sharedFile.getInfoHash()
String name = e.sharedFile.getFile().getName()
long timestamp = System.currentTimeMillis()

View File

@@ -1,110 +0,0 @@
package com.muwire.core.filefeeds
import java.util.logging.Level
import java.nio.charset.StandardCharsets
import java.util.concurrent.Executor
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.zip.GZIPInputStream
import com.muwire.core.EventBus
import com.muwire.core.Persona
import com.muwire.core.connection.Endpoint
import com.muwire.core.connection.I2PConnector
import com.muwire.core.util.DataUtil
import groovy.json.JsonSlurper
import groovy.util.logging.Log
@Log
class FeedClient {
private final I2PConnector connector
private final EventBus eventBus
private final Persona me
private final FeedManager feedManager
private final ExecutorService feedFetcher = Executors.newCachedThreadPool()
private final Timer feedUpdater = new Timer("feed-updater", true)
FeedClient(I2PConnector connector, EventBus eventBus, Persona me, FeedManager feedManager) {
this.connector = connector
this.eventBus = eventBus
this.me = me
this.feedManager = feedManager
}
private void start() {
feedUpdater.schedule({updateAnyFeeds()} as TimerTask, 60000, 60000)
}
private void stop() {
feedUpdater.cancel()
feedFetcher.shutdown()
}
private void updateAnyFeeds() {
feedManager.getFeedsToUpdate().each { feed ->
feedFetcher.execute({updateFeed(feed)} as Runnable)
}
}
void onUIFeedUpdateEvent(UIFeedUpdateEvent e) {
Feed feed = feedManager.getFeed(e.host)
if (feed == null) {
log.severe("UI request to update non-existent feed " + e.host.getHumanReadableName())
return
}
feedFetcher.execute({updateFeed(feed)} as Runnable)
}
private void updateFeed(Feed feed) {
log.info("updating feed " + feed.getPublisher().getHumanReadableName())
Endpoint endpoint = null
try {
eventBus.publish(new FeedFetchEvent(host : feed.getPublisher(), status : FeedFetchStatus.CONNECTING))
feed.setLastUpdateAttempt(System.currentTimeMillis())
endpoint = connector.connect(feed.getPublisher().getDestination())
OutputStream os = endpoint.getOutputStream()
os.write("FEED\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("Persona:${me.toBase64()}\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("Timestamp:${feed.getLastUpdated()}\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
os.flush()
InputStream is = endpoint.getInputStream()
String code = DataUtil.readTillRN(is)
if (!code.startsWith("200"))
throw new IOException("Invalid code $code")
// parse all headers
Map<String,String> headers = DataUtil.readAllHeaders(is)
if (!headers.containsKey("Count"))
throw new IOException("No count header")
int items = Integer.parseInt(headers['Count'])
eventBus.publish(new FeedFetchEvent(host : feed.getPublisher(), status : FeedFetchStatus.FETCHING, totalItems: items))
JsonSlurper slurper = new JsonSlurper()
DataInputStream dis = new DataInputStream(new GZIPInputStream(is))
for (int i = 0; i < items; i++) {
int size = dis.readUnsignedShort()
byte [] tmp = new byte[size]
dis.readFully(tmp)
def json = slurper.parse(tmp)
FeedItem item = FeedItems.objToFeedItem(json, feed.getPublisher())
eventBus.publish(new FeedItemFetchedEvent(item: item))
}
eventBus.publish(new FeedFetchEvent(host : feed.getPublisher(), status : FeedFetchStatus.FINISHED))
} catch (Exception bad) {
log.log(Level.WARNING, "Feed update failed", bad)
eventBus.publish(new FeedFetchEvent(host : feed.getPublisher(), status : FeedFetchStatus.FAILED))
} finally {
endpoint?.close()
}
}
}

View File

@@ -1,10 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
import com.muwire.core.Persona
class FeedFetchEvent extends Event {
Persona host
FeedFetchStatus status
int totalItems
}

View File

@@ -1,7 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
class FeedItemFetchedEvent extends Event {
FeedItem item
}

View File

@@ -1,7 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
class FeedItemLoadedEvent extends Event {
FeedItem item
}

View File

@@ -1,79 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.InfoHash
import com.muwire.core.Persona
import com.muwire.core.SharedFile
import com.muwire.core.files.FileHasher
import com.muwire.core.util.DataUtil
import net.i2p.data.Base64
class FeedItems {
public static def sharedFileToObj(SharedFile sf, int certificates) {
def json = [:]
json.type = "FeedItem"
json.version = 1
json.name = Base64.encode(DataUtil.encodei18nString(sf.getFile().getName()))
json.infoHash = Base64.encode(sf.getRoot())
json.size = sf.getCachedLength()
json.pieceSize = sf.getPieceSize()
if (sf.getComment() != null)
json.comment = sf.getComment()
json.certificates = certificates
json.timestamp = sf.getPublishedTimestamp()
json
}
public static FeedItem objToFeedItem(def obj, Persona publisher) throws InvalidFeedItemException {
if (obj.timestamp == null)
throw new InvalidFeedItemException("No timestamp");
if (obj.name == null)
throw new InvalidFeedItemException("No name");
if (obj.size == null || obj.size <= 0 || obj.size > FileHasher.MAX_SIZE)
throw new InvalidFeedItemException("length missing or invalid ${obj.size}")
if (obj.pieceSize == null || obj.pieceSize < FileHasher.MIN_PIECE_SIZE_POW2 || obj.pieceSize > FileHasher.MAX_PIECE_SIZE_POW2)
throw new InvalidFeedItemException("piece size missing or invalid ${obj.pieceSize}")
if (obj.infoHash == null)
throw new InvalidFeedItemException("Infohash missing")
InfoHash infoHash
try {
infoHash = new InfoHash(Base64.decode(obj.infoHash))
} catch (Exception bad) {
throw new InvalidFeedItemException("Invalid infohash", bad)
}
String name
try {
name = DataUtil.readi18nString(Base64.decode(obj.name))
} catch (Exception bad) {
throw new InvalidFeedItemException("Invalid name", bad)
}
int certificates = 0
if (obj.certificates != null)
certificates = obj.certificates
new FeedItem(publisher, obj.timestamp, name, obj.size, obj.pieceSize, infoHash, certificates, obj.comment)
}
public static def feedItemToObj(FeedItem item) {
def json = [:]
json.type = "FeedItem"
json.version = 1
json.name = Base64.encode(DataUtil.encodei18nString(item.getName()))
json.infoHash = Base64.encode(item.getInfoHash().getRoot())
json.size = item.getSize()
json.pieceSize = item.getPieceSize()
json.timestamp = item.getTimestamp()
json.certificates = item.getCertificates()
json.comment = item.getComment()
json
}
}

View File

@@ -1,7 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
class FeedLoadedEvent extends Event {
Feed feed
}

View File

@@ -1,225 +0,0 @@
package com.muwire.core.filefeeds
import java.nio.file.Files
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.ThreadFactory
import java.util.stream.Collectors
import com.muwire.core.EventBus
import com.muwire.core.Persona
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.util.logging.Log
import net.i2p.data.Base64
import net.i2p.util.ConcurrentHashSet
@Log
class FeedManager {
private final EventBus eventBus
private final File metadataFolder, itemsFolder
private final Map<Persona, Feed> feeds = new ConcurrentHashMap<>()
private final Map<Persona, Set<FeedItem>> feedItems = new ConcurrentHashMap<>()
private final ExecutorService persister = Executors.newSingleThreadExecutor({r ->
new Thread(r, "feed persister")
} as ThreadFactory)
FeedManager(EventBus eventBus, File home) {
this.eventBus = eventBus
File feedsFolder = new File(home, "filefeeds")
if (!feedsFolder.exists())
feedsFolder.mkdir()
this.metadataFolder = new File(feedsFolder, "metadata")
if (!metadataFolder.exists())
metadataFolder.mkdir()
this.itemsFolder = new File(feedsFolder, "items")
if (!itemsFolder.exists())
itemsFolder.mkdir()
}
public Feed getFeed(Persona persona) {
feeds.get(persona)
}
public Set<FeedItem> getFeedItems(Persona persona) {
feedItems.getOrDefault(persona, Collections.emptySet())
}
public List<Feed> getFeedsToUpdate() {
long now = System.currentTimeMillis()
feeds.values().stream().
filter({Feed f -> !f.getStatus().isActive()}).
filter({Feed f -> f.getLastUpdateAttempt() + f.getUpdateInterval() <= now})
.collect(Collectors.toList())
}
void start() {
log.info("starting feed manager")
persister.submit({loadFeeds()} as Runnable)
persister.submit({loadItems()} as Runnable)
}
void stop() {
persister.shutdown()
}
private void loadFeeds() {
def slurper = new JsonSlurper()
Files.walk(metadataFolder.toPath()).
filter( { it.getFileName().toString().endsWith(".json")}).
forEach( {
def parsed = slurper.parse(it.toFile())
Persona publisher = new Persona(new ByteArrayInputStream(Base64.decode(parsed.publisher)))
Feed feed = new Feed(publisher)
feed.setUpdateInterval(parsed.updateInterval)
feed.setLastUpdated(parsed.lastUpdated)
feed.setLastUpdateAttempt(parsed.lastUpdateAttempt)
feed.setItemsToKeep(parsed.itemsToKeep)
feed.setAutoDownload(parsed.autoDownload)
feed.setSequential(parsed.sequential)
feed.setStatus(FeedFetchStatus.IDLE)
feeds.put(feed.getPublisher(), feed)
eventBus.publish(new FeedLoadedEvent(feed : feed))
})
}
private void loadItems() {
def slurper = new JsonSlurper()
feeds.keySet().each { persona ->
File itemsFile = getItemsFile(feeds[persona])
if (!itemsFile.exists())
return // no items yet?
itemsFile.eachLine { line ->
def parsed = slurper.parseText(line)
FeedItem item = FeedItems.objToFeedItem(parsed, persona)
Set<FeedItem> items = feedItems.get(persona)
if (items == null) {
items = new ConcurrentHashSet<>()
feedItems.put(persona, items)
}
items.add(item)
eventBus.publish(new FeedItemLoadedEvent(item : item))
}
}
}
void onFeedItemFetchedEvent(FeedItemFetchedEvent e) {
Set<FeedItem> set = feedItems.get(e.item.getPublisher())
if (set == null) {
set = new ConcurrentHashSet<>()
feedItems.put(e.getItem().getPublisher(), set)
}
set.add(e.item)
}
void onFeedFetchEvent(FeedFetchEvent e) {
Feed feed = feeds.get(e.host)
if (feed == null) {
log.severe("Fetching non-existent feed " + e.host.getHumanReadableName())
return
}
feed.setStatus(e.status)
if (e.status.isActive())
return
if (e.status == FeedFetchStatus.FINISHED) {
feed.setStatus(FeedFetchStatus.IDLE)
feed.setLastUpdated(e.getTimestamp())
}
// save feed items, then save feed. This will save partial fetches too
// which is ok because the items are stored in a Set
persister.submit({saveFeedItems(e.host)} as Runnable)
persister.submit({saveFeedMetadata(feed)} as Runnable)
}
void onUIFeedConfigurationEvent(UIFeedConfigurationEvent e) {
feeds.put(e.feed.getPublisher(), e.feed)
persister.submit({saveFeedMetadata(e.feed)} as Runnable)
}
void onUIFeedDeletedEvent(UIFeedDeletedEvent e) {
Feed f = feeds.get(e.host)
if (f == null) {
log.severe("Deleting a non-existing feed " + e.host.getHumanReadableName())
return
}
persister.submit({deleteFeed(f)} as Runnable)
}
private void saveFeedItems(Persona publisher) {
Set<FeedItem> set = feedItems.get(publisher)
if (set == null)
return // can happen if nothing was published
Feed feed = feeds[publisher]
if (feed == null) {
log.severe("Persisting items for non-existing feed " + publisher.getHumanReadableName())
return
}
if (feed.getItemsToKeep() == 0)
return
List<FeedItem> list = new ArrayList<>(set)
if (feed.getItemsToKeep() > 0 && list.size() > feed.getItemsToKeep()) {
log.info("will persist ${feed.getItemsToKeep()}/${list.size()} items")
list.sort({l, r ->
Long.compare(r.getTimestamp(), l.getTimestamp())
} as Comparator<FeedItem>)
list = list[0..feed.getItemsToKeep() - 1]
}
File itemsFile = getItemsFile(feed)
itemsFile.withPrintWriter { writer ->
list.each { item ->
def obj = FeedItems.feedItemToObj(item)
def json = JsonOutput.toJson(obj)
writer.println(json)
}
}
}
private void saveFeedMetadata(Feed feed) {
File metadataFile = getMetadataFile(feed)
metadataFile.withPrintWriter { writer ->
def json = [:]
json.publisher = feed.getPublisher().toBase64()
json.itemsToKeep = feed.getItemsToKeep()
json.lastUpdated = feed.getLastUpdated()
json.updateInterval = feed.getUpdateInterval()
json.autoDownload = feed.isAutoDownload()
json.sequential = feed.isSequential()
json.lastUpdateAttempt = feed.getLastUpdateAttempt()
json = JsonOutput.toJson(json)
writer.println(json)
}
}
private void deleteFeed(Feed feed) {
feeds.remove(feed.getPublisher())
feedItems.remove(feed.getPublisher())
getItemsFile(feed).delete()
getMetadataFile(feed).delete()
}
private File getItemsFile(Feed feed) {
return new File(itemsFolder, feed.getPublisher().destination.toBase32() + ".json")
}
private File getMetadataFile(Feed feed) {
return new File(metadataFolder, feed.getPublisher().destination.toBase32() + ".json")
}
}

View File

@@ -1,9 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
class UIDownloadFeedItemEvent extends Event {
FeedItem item
File target
boolean sequential
}

View File

@@ -1,12 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
/**
* Emitted when configuration of a feed changes.
* The object should already contain the updated values.
*/
class UIFeedConfigurationEvent extends Event {
Feed feed
boolean newFeed
}

View File

@@ -1,8 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
import com.muwire.core.Persona
class UIFeedDeletedEvent extends Event {
Persona host
}

View File

@@ -1,8 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
import com.muwire.core.Persona
class UIFeedUpdateEvent extends Event {
Persona host
}

View File

@@ -1,8 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
import com.muwire.core.SharedFile
class UIFilePublishedEvent extends Event {
SharedFile sf
}

View File

@@ -1,8 +0,0 @@
package com.muwire.core.filefeeds
import com.muwire.core.Event
import com.muwire.core.SharedFile
class UIFileUnpublishedEvent extends Event {
SharedFile sf
}

View File

@@ -47,17 +47,17 @@ abstract class BasePersisterService extends Service{
int pieceSize = 0
if (json.pieceSize != null)
pieceSize = json.pieceSize
if (json.sources != null) {
List sources = (List)json.sources
Set<Destination> sourceSet = sources.stream().map({ d -> new Destination(d.toString())}).collect Collectors.toSet()
DownloadedFile df = new DownloadedFile(file, ih.getRoot(), pieceSize, sourceSet)
DownloadedFile df = new DownloadedFile(file, ih, pieceSize, sourceSet)
df.setComment(json.comment)
return new FileLoadedEvent(loadedFile : df, infoHash: ih)
return new FileLoadedEvent(loadedFile : df)
}
SharedFile sf = new SharedFile(file, ih.getRoot(), pieceSize)
SharedFile sf = new SharedFile(file, ih, pieceSize)
sf.setComment(json.comment)
if (json.downloaders != null)
sf.getDownloaders().addAll(json.downloaders)
@@ -71,72 +71,18 @@ abstract class BasePersisterService extends Service{
sf.hit(searcher, timestamp, query)
}
}
return new FileLoadedEvent(loadedFile: sf, infoHash: ih)
return new FileLoadedEvent(loadedFile: sf)
}
protected static FileLoadedEvent fromJsonLite(json) {
if (json.file == null || json.length == null || json.root == null)
throw new IllegalArgumentException()
def file = new File(DataUtil.readi18nString(Base64.decode(json.file)))
file = file.getCanonicalFile()
if (!file.exists() || file.isDirectory())
return null
long length = Long.valueOf(json.length)
if (length != file.length())
return null
byte[] root = Base64.decode(json.root)
InfoHash ih = new InfoHash(root)
int pieceSize = 0
if (json.pieceSize != null)
pieceSize = json.pieceSize
boolean published = false
long publishedTimestamp = -1
if (json.published != null && json.published) {
published = true
publishedTimestamp = json.publishedTimestamp
}
if (json.sources != null) {
List sources = (List)json.sources
Set<Destination> sourceSet = sources.stream().map({ d -> new Destination(d.toString())}).collect Collectors.toSet()
DownloadedFile df = new DownloadedFile(file, ih.getRoot(), pieceSize, sourceSet)
if (published)
df.publish(publishedTimestamp)
df.setComment(json.comment)
return new FileLoadedEvent(loadedFile : df, infoHash: ih)
}
SharedFile sf = new SharedFile(file, ih.getRoot(), pieceSize)
sf.setComment(json.comment)
if (published)
sf.publish(publishedTimestamp)
if (json.downloaders != null)
sf.getDownloaders().addAll(json.downloaders)
if (json.searchers != null) {
json.searchers.each {
Persona searcher = null
if (it.searcher != null)
searcher = new Persona(new ByteArrayInputStream(Base64.decode(it.searcher)))
long timestamp = it.timestamp
String query = it.query
sf.hit(searcher, timestamp, query)
}
}
return new FileLoadedEvent(loadedFile: sf, infoHash: ih)
}
protected static toJson(SharedFile sf) {
def json = [:]
json.file = sf.getB64EncodedFileName()
json.length = sf.getCachedLength()
json.root = Base64.encode(sf.getRoot())
InfoHash ih = sf.getInfoHash()
json.infoHash = sf.getB64EncodedHashRoot()
json.pieceSize = sf.getPieceSize()
json.hashList = sf.getB64EncodedHashList()
json.comment = sf.getComment()
json.hits = sf.getHits()
json.downloaders = sf.getDownloaders()
@@ -157,11 +103,6 @@ abstract class BasePersisterService extends Service{
if (sf instanceof DownloadedFile) {
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
}
if (sf.isPublished()) {
json.published = true
json.publishedTimestamp = sf.getPublishedTimestamp()
}
json
}

View File

@@ -2,7 +2,6 @@ package com.muwire.core.files
import com.muwire.core.DownloadedFile
import com.muwire.core.Event
import com.muwire.core.InfoHash
import com.muwire.core.download.Downloader
import net.i2p.data.Destination
@@ -10,5 +9,4 @@ import net.i2p.data.Destination
class FileDownloadedEvent extends Event {
Downloader downloader
DownloadedFile downloadedFile
InfoHash infoHash
}

View File

@@ -1,18 +1,16 @@
package com.muwire.core.files
import com.muwire.core.Event
import com.muwire.core.InfoHash
import com.muwire.core.SharedFile
class FileHashedEvent extends Event {
SharedFile sharedFile
InfoHash infoHash
String error
@Override
public String toString() {
super.toString() + " sharedFile " + sharedFile?.file?.getAbsolutePath() + " error: $error"
super.toString() + " sharedFile " + sharedFile?.file.getAbsolutePath() + " error: $error"
}
}

View File

@@ -1,12 +1,10 @@
package com.muwire.core.files
import com.muwire.core.Event
import com.muwire.core.InfoHash
import com.muwire.core.SharedFile
class FileLoadedEvent extends Event {
SharedFile loadedFile
InfoHash infoHash
String source
}

View File

@@ -1,8 +1,5 @@
package com.muwire.core.files
import java.util.stream.Collectors
import java.util.stream.Stream
import com.muwire.core.EventBus
import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings
@@ -78,7 +75,7 @@ class FileManager {
private void addToIndex(SharedFile sf) {
log.info("Adding shared file " + sf.getFile())
InfoHash infoHash = new InfoHash(sf.getRoot())
InfoHash infoHash = sf.getInfoHash()
Set<SharedFile> existing = rootToFiles.get(infoHash)
if (existing == null) {
log.info("adding new root")
@@ -120,7 +117,7 @@ class FileManager {
void onFileUnsharedEvent(FileUnsharedEvent e) {
SharedFile sf = e.unsharedFile
InfoHash infoHash = new InfoHash(sf.getRoot())
InfoHash infoHash = sf.getInfoHash()
Set<SharedFile> existing = rootToFiles.get(infoHash)
if (existing != null) {
existing.remove(sf)
@@ -193,10 +190,6 @@ class FileManager {
Set<SharedFile> getSharedFiles(byte []root) {
return rootToFiles.get(new InfoHash(root))
}
boolean isShared(InfoHash infoHash) {
rootToFiles.containsKey(infoHash)
}
void onSearchEvent(SearchEvent e) {
// hash takes precedence
@@ -261,13 +254,4 @@ class FileManager {
settings.negativeFileTree.clear()
settings.negativeFileTree.addAll(negativeTree.fileToNode.keySet().collect { it.getAbsolutePath() })
}
public List<SharedFile> getPublishedSince(long timestamp) {
synchronized(fileToSharedFile) {
fileToSharedFile.values().stream().
filter({sf -> sf.isPublished()}).
filter({sf -> sf.getPublishedTimestamp() >= timestamp}).
collect(Collectors.toList())
}
}
}

View File

@@ -65,8 +65,7 @@ class HasherService {
} else {
eventBus.publish new FileHashingEvent(hashingFile: f)
def hash = hasher.hashFile f
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash.getRoot(), FileHasher.getPieceSize(f.length())),
infoHash : hash)
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length())))
}
}
}

View File

@@ -1,9 +1,6 @@
package com.muwire.core.files
import com.muwire.core.*
import com.muwire.core.filefeeds.UIFilePublishedEvent
import com.muwire.core.filefeeds.UIFileUnpublishedEvent
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.util.logging.Log
@@ -59,21 +56,17 @@ class PersisterFolderService extends BasePersisterService {
}
void onFileHashedEvent(FileHashedEvent hashedEvent) {
if (core.getMuOptions().getAutoPublishSharedFiles() && hashedEvent.sharedFile != null)
hashedEvent.sharedFile.publish(System.currentTimeMillis())
persistFile(hashedEvent.sharedFile, hashedEvent.infoHash)
persistFile(hashedEvent.sharedFile)
}
void onFileDownloadedEvent(FileDownloadedEvent downloadedEvent) {
if (core.getMuOptions().getShareDownloadedFiles()) {
if (core.getMuOptions().getAutoPublishSharedFiles())
downloadedEvent.downloadedFile.publish(System.currentTimeMillis())
persistFile(downloadedEvent.downloadedFile, downloadedEvent.infoHash)
persistFile(downloadedEvent.downloadedFile)
}
}
/**
* Get rid of the json and hashlists of unshared files
* Get rid of the json of unshared files
* @param unsharedEvent
*/
void onFileUnsharedEvent(FileUnsharedEvent unsharedEvent) {
@@ -82,31 +75,14 @@ class PersisterFolderService extends BasePersisterService {
if(jsonFile.isFile()){
jsonFile.delete()
}
def hashListPath = getHashListPath(unsharedEvent.unsharedFile)
def hashListFile = hashListPath.toFile()
if (hashListFile.isFile())
hashListFile.delete()
}
void onFileLoadedEvent(FileLoadedEvent loadedEvent) {
if(loadedEvent.source == "PersisterService"){
log.info("Migrating persisted file from PersisterService: "
+ loadedEvent.loadedFile.file.absolutePath.toString())
persistFile(loadedEvent.loadedFile, loadedEvent.infoHash)
persistFile(loadedEvent.loadedFile)
}
}
void onUICommentEvent(UICommentEvent e) {
persistFile(e.sharedFile,null)
}
void onUIFilePublishedEvent(UIFilePublishedEvent e) {
persistFile(e.sf, null)
}
void onUIFileUnpublishedEvent(UIFileUnpublishedEvent e) {
persistFile(e.sf, null)
}
void load() {
log.fine("Loading...")
@@ -133,12 +109,10 @@ class PersisterFolderService extends BasePersisterService {
int loaded = 0
def slurper = new JsonSlurper()
Files.walk(location.toPath())
.filter({
it.getFileName().toString().endsWith(".json")
})
.filter({ it.fileName.endsWith(".json") })
.forEach({
def parsed = slurper.parse it.toFile()
def event = fromJsonLite parsed
def event = fromJson parsed
if (event == null) return
log.fine("loaded file $event.loadedFile.file")
@@ -151,7 +125,7 @@ class PersisterFolderService extends BasePersisterService {
listener.publish(new AllFilesLoadedEvent())
}
private void persistFile(SharedFile sf, InfoHash ih) {
private void persistFile(SharedFile sf) {
persisterExecutor.submit({
def jsonPath = getJsonPath(sf)
@@ -162,12 +136,7 @@ class PersisterFolderService extends BasePersisterService {
json = JsonOutput.toJson(json)
writer.println json
}
if (ih != null) {
def hashListPath = getHashListPath(sf)
hashListPath.toFile().bytes = ih.hashList
}
log.fine("Time(ms) to write json+hashList: " + (System.currentTimeMillis() - startTime))
log.fine("Time(ms) to write json: " + (System.currentTimeMillis() - startTime))
} as Runnable)
}
private Path getJsonPath(SharedFile sf){
@@ -178,18 +147,4 @@ class PersisterFolderService extends BasePersisterService {
pathHash.substring(CUT_LENGTH) + ".json"
)
}
private Path getHashListPath(SharedFile sf) {
def pathHash = sf.getB64PathHash()
return Paths.get(
location.getAbsolutePath(),
pathHash.substring(0, CUT_LENGTH),
pathHash.substring(CUT_LENGTH) + ".hashlist"
)
}
InfoHash loadInfoHash(SharedFile sf) {
def path = getHashListPath(sf)
InfoHash.fromHashList(path.toFile().bytes)
}
}

View File

@@ -77,19 +77,18 @@ class ResultsSender {
if (it.getComment() != null) {
comment = DataUtil.readi18nString(Base64.decode(it.getComment()))
}
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
def uiResultEvent = new UIResultEvent( sender : me,
name : it.getFile().getName(),
size : length,
infohash : new InfoHash(it.getRoot()),
infohash : it.getInfoHash(),
pieceSize : pieceSize,
uuid : uuid,
browse : settings.browseFiles,
sources : suggested,
comment : comment,
certificates : certificates,
chat : chatServer.running.get() && settings.advertiseChat,
feed : settings.fileFeed && settings.advertiseFeed
chat : chatServer.running.get() && settings.advertiseChat
)
uiResultEvents << uiResultEvent
}
@@ -120,7 +119,7 @@ class ResultsSender {
me.write(os)
os.writeShort((short)results.length)
results.each {
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
def obj = sharedFileToObj(it, settings.browseFiles, certificates)
def json = jsonOutput.toJson(obj)
os.writeShort((short)json.length())
@@ -139,12 +138,10 @@ class ResultsSender {
os.write("Count: $results.length\r\n".getBytes(StandardCharsets.US_ASCII))
boolean chat = chatServer.running.get() && settings.advertiseChat
os.write("Chat: $chat\r\n".getBytes(StandardCharsets.US_ASCII))
boolean feed = settings.fileFeed && settings.advertiseFeed
os.write("Feed: $feed\r\n".getBytes(StandardCharsets.US_ASCII))
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(os))
results.each {
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
def obj = sharedFileToObj(it, settings.browseFiles, certificates)
def json = jsonOutput.toJson(obj)
dos.writeShort((short)json.length())
@@ -173,7 +170,7 @@ class ResultsSender {
obj.type = "Result"
obj.version = 2
obj.name = encodedName
obj.infohash = Base64.encode(sf.getRoot())
obj.infohash = Base64.encode(sf.getInfoHash().getRoot())
obj.size = sf.getCachedLength()
obj.pieceSize = sf.getPieceSize()

View File

@@ -18,8 +18,7 @@ class UIResultEvent extends Event {
boolean browse
int certificates
boolean chat
boolean feed
@Override
public String toString() {
super.toString() + "name:$name size:$size sender:${sender.getHumanReadableName()} pieceSize $pieceSize"

View File

@@ -83,7 +83,7 @@ class UpdateClient {
}
void onFileDownloadedEvent(FileDownloadedEvent e) {
if (e.infoHash != updateInfoHash)
if (e.downloadedFile.infoHash != updateInfoHash)
return
updateDownloading = false
eventBus.publish(new UpdateDownloadedEvent(version : version, signer : signer, text : text))

View File

@@ -12,7 +12,6 @@ import com.muwire.core.download.DownloadManager
import com.muwire.core.download.Downloader
import com.muwire.core.download.SourceDiscoveredEvent
import com.muwire.core.files.FileManager
import com.muwire.core.files.PersisterFolderService
import com.muwire.core.mesh.Mesh
import com.muwire.core.mesh.MeshManager
@@ -23,7 +22,6 @@ import net.i2p.data.Base64
public class UploadManager {
private final EventBus eventBus
private final FileManager fileManager
private final PersisterFolderService persisterService
private final MeshManager meshManager
private final DownloadManager downloadManager
private final MuWireSettings props
@@ -36,11 +34,9 @@ public class UploadManager {
public UploadManager(EventBus eventBus, FileManager fileManager,
MeshManager meshManager, DownloadManager downloadManager,
PersisterFolderService persisterService,
MuWireSettings props) {
this.eventBus = eventBus
this.fileManager = fileManager
this.persisterService = persisterService
this.meshManager = meshManager
this.downloadManager = downloadManager
this.props = props
@@ -166,7 +162,7 @@ public class UploadManager {
InfoHash fullInfoHash
if (downloader == null) {
fullInfoHash = persisterService.loadInfoHash(sharedFiles.iterator().next())
fullInfoHash = sharedFiles.iterator().next().infoHash
} else {
byte [] hashList = downloader.getInfoHash().getHashList()
if (hashList != null && hashList.length > 0)

View File

@@ -10,9 +10,9 @@ public class DownloadedFile extends SharedFile {
private final Set<Destination> sources;
public DownloadedFile(File file, byte[] root, int pieceSize, Set<Destination> sources)
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources)
throws IOException {
super(file, root, pieceSize);
super(file, infoHash, pieceSize);
this.sources = sources;
}

View File

@@ -5,7 +5,6 @@ import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -19,7 +18,7 @@ import net.i2p.data.Base64;
public class SharedFile {
private final File file;
private final byte[] root;
private final InfoHash infoHash;
private final int pieceSize;
private final String cachedPath;
@@ -27,20 +26,29 @@ public class SharedFile {
private String b64PathHash;
private final String b64EncodedFileName;
private final String b64EncodedHashRoot;
private final List<String> b64EncodedHashList;
private volatile String comment;
private final Set<String> downloaders = Collections.synchronizedSet(new HashSet<>());
private final Set<SearchEntry> searches = Collections.synchronizedSet(new HashSet<>());
private volatile boolean published;
private volatile long publishedTimestamp;
public SharedFile(File file, byte[] root, int pieceSize) throws IOException {
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
this.file = file;
this.root = root;
this.infoHash = infoHash;
this.pieceSize = pieceSize;
this.cachedPath = file.getAbsolutePath();
this.cachedLength = file.length();
this.b64EncodedFileName = Base64.encode(DataUtil.encodei18nString(file.toString()));
this.b64EncodedHashRoot = Base64.encode(infoHash.getRoot());
List<String> b64List = new ArrayList<String>();
byte[] tmp = new byte[32];
for (int i = 0; i < infoHash.getHashList().length / 32; i++) {
System.arraycopy(infoHash.getHashList(), i * 32, tmp, 0, 32);
b64List.add(Base64.encode(tmp));
}
this.b64EncodedHashList = b64List;
}
public File getFile() {
@@ -48,7 +56,7 @@ public class SharedFile {
}
public byte[] getPathHash() throws NoSuchAlgorithmException {
MessageDigest digester = MessageDigest.getInstance("SHA-256");
var digester = MessageDigest.getInstance("SHA-256");
digester.update(file.getAbsolutePath().getBytes());
return digester.digest();
}
@@ -60,8 +68,8 @@ public class SharedFile {
return b64PathHash;
}
public byte[] getRoot() {
return root;
public InfoHash getInfoHash() {
return infoHash;
}
public int getPieceSize() {
@@ -81,6 +89,14 @@ public class SharedFile {
return b64EncodedFileName;
}
public String getB64EncodedHashRoot() {
return b64EncodedHashRoot;
}
public List<String> getB64EncodedHashList() {
return b64EncodedHashList;
}
public String getCachedPath() {
return cachedPath;
}
@@ -116,28 +132,10 @@ public class SharedFile {
public void addDownloader(String name) {
downloaders.add(name);
}
public void publish(long timestamp) {
published = true;
publishedTimestamp = timestamp;
}
public void unpublish() {
published = false;
publishedTimestamp = 0;
}
public boolean isPublished() {
return published;
}
public long getPublishedTimestamp() {
return publishedTimestamp;
}
@Override
public int hashCode() {
return file.hashCode() ^ Arrays.hashCode(root);
return file.hashCode() ^ infoHash.hashCode();
}
@Override
@@ -145,7 +143,7 @@ public class SharedFile {
if (!(o instanceof SharedFile))
return false;
SharedFile other = (SharedFile)o;
return file.equals(other.file) && Arrays.equals(root, other.root);
return file.equals(other.file) && infoHash.equals(other.infoHash);
}
public static class SearchEntry {

View File

@@ -1,81 +0,0 @@
package com.muwire.core.filefeeds;
import com.muwire.core.Persona;
public class Feed {
private final Persona publisher;
private int updateInterval;
private long lastUpdated;
private volatile long lastUpdateAttempt;
private int itemsToKeep;
private boolean autoDownload;
private boolean sequential;
private FeedFetchStatus status;
public Feed(Persona publisher) {
this.publisher = publisher;
this.status = FeedFetchStatus.IDLE;
}
public int getUpdateInterval() {
return updateInterval;
}
public void setUpdateInterval(int updateInterval) {
this.updateInterval = updateInterval;
}
public long getLastUpdated() {
return lastUpdated;
}
public void setLastUpdated(long lastUpdated) {
this.lastUpdated = lastUpdated;
}
public int getItemsToKeep() {
return itemsToKeep;
}
public void setItemsToKeep(int itemsToKeep) {
this.itemsToKeep = itemsToKeep;
}
public boolean isAutoDownload() {
return autoDownload;
}
public void setAutoDownload(boolean autoDownload) {
this.autoDownload = autoDownload;
}
public Persona getPublisher() {
return publisher;
}
public void setStatus(FeedFetchStatus status) {
this.status = status;
}
public FeedFetchStatus getStatus() {
return status;
}
public void setSequential(boolean sequential) {
this.sequential = sequential;
}
public boolean isSequential() {
return sequential;
}
public void setLastUpdateAttempt(long lastUpdateAttempt) {
this.lastUpdateAttempt = lastUpdateAttempt;
}
public long getLastUpdateAttempt() {
return lastUpdateAttempt;
}
}

View File

@@ -1,19 +0,0 @@
package com.muwire.core.filefeeds;
public enum FeedFetchStatus {
IDLE(false),
CONNECTING(true),
FETCHING(true),
FINISHED(false),
FAILED(false);
private final boolean active;
FeedFetchStatus(boolean active) {
this.active = active;
}
public boolean isActive() {
return active;
}
}

View File

@@ -1,79 +0,0 @@
package com.muwire.core.filefeeds;
import java.util.Objects;
import com.muwire.core.InfoHash;
import com.muwire.core.Persona;
public class FeedItem {
private final Persona publisher;
private final long timestamp;
private final String name;
private final long size;
private final int pieceSize;
private final InfoHash infoHash;
private final int certificates;
private final String comment;
public FeedItem(Persona publisher, long timestamp, String name, long size, int pieceSize, InfoHash infoHash,
int certificates, String comment) {
super();
this.publisher = publisher;
this.timestamp = timestamp;
this.name = name;
this.size = size;
this.pieceSize = pieceSize;
this.infoHash = infoHash;
this.certificates = certificates;
this.comment = comment;
}
public Persona getPublisher() {
return publisher;
}
public long getTimestamp() {
return timestamp;
}
public String getName() {
return name;
}
public long getSize() {
return size;
}
public int getPieceSize() {
return pieceSize;
}
public InfoHash getInfoHash() {
return infoHash;
}
public int getCertificates() {
return certificates;
}
public String getComment() {
return comment;
}
@Override
public int hashCode() {
return Objects.hash(publisher, timestamp, name, infoHash);
}
@Override
public boolean equals(Object o) {
if (!(o instanceof FeedItem))
return false;
FeedItem other = (FeedItem)o;
return Objects.equals(publisher, other.publisher) &&
timestamp == other.timestamp &&
Objects.equals(name, other.name) &&
Objects.equals(infoHash, other.infoHash);
}
}

View File

@@ -1,30 +0,0 @@
package com.muwire.core.filefeeds;
public class InvalidFeedItemException extends Exception {
public InvalidFeedItemException() {
super();
}
public InvalidFeedItemException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
// TODO Auto-generated constructor stub
}
public InvalidFeedItemException(String message, Throwable cause) {
super(message, cause);
// TODO Auto-generated constructor stub
}
public InvalidFeedItemException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
public InvalidFeedItemException(Throwable cause) {
super(cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -1,5 +1,5 @@
group = com.muwire
version = 0.6.10
version = 0.6.8
i2pVersion = 0.9.44
groovyVersion = 2.4.15
slf4jVersion = 1.7.25

View File

@@ -126,9 +126,4 @@ mvcGroups {
view = 'com.muwire.gui.ChatMonitorView'
controller = 'com.muwire.gui.ChatMonitorController'
}
'feed-configuration' {
model = 'com.muwire.gui.FeedConfigurationModel'
view = 'com.muwire.gui.FeedConfigurationView'
controller = 'com.muwire.gui.FeedConfigurationController'
}
}

View File

@@ -113,9 +113,7 @@ class BrowseController {
return
def params = [:]
params['host'] = result.getSender()
params['infoHash'] = result.getInfohash()
params['name'] = result.getName()
params['result'] = result
params['core'] = core
mvcGroup.createMVCGroup("fetch-certificates", params)
}

View File

@@ -1,36 +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.filefeeds.UIFeedConfigurationEvent
@ArtifactProviderFor(GriffonController)
class FeedConfigurationController {
@MVCMember @Nonnull
FeedConfigurationModel model
@MVCMember @Nonnull
FeedConfigurationView view
@ControllerAction
void save() {
model.feed.setAutoDownload(view.autoDownloadCheckbox.model.isSelected())
model.feed.setSequential(view.sequentialCheckbox.model.isSelected())
model.feed.setItemsToKeep(Integer.parseInt(view.itemsToKeepField.text))
model.feed.setUpdateInterval(Integer.parseInt(view.updateIntervalField.text) * 60000)
model.core.eventBus.publish(new UIFeedConfigurationEvent(feed : model.feed))
cancel()
}
@ControllerAction
void cancel() {
view.dialog.setVisible(false)
mvcGroup.destroy()
}
}

View File

@@ -28,7 +28,7 @@ class FetchCertificatesController {
core.eventBus.with {
register(CertificateFetchEvent.class, this)
register(CertificateFetchedEvent.class, this)
publish(new UIFetchCertificatesEvent(host : model.host, infoHash : model.infoHash))
publish(new UIFetchCertificatesEvent(host : model.result.sender, infoHash : model.result.infohash))
}
}

View File

@@ -30,13 +30,6 @@ import com.muwire.core.download.UIDownloadCancelledEvent
import com.muwire.core.download.UIDownloadPausedEvent
import com.muwire.core.download.UIDownloadResumedEvent
import com.muwire.core.filecert.UICreateCertificateEvent
import com.muwire.core.filefeeds.Feed
import com.muwire.core.filefeeds.FeedItem
import com.muwire.core.filefeeds.UIDownloadFeedItemEvent
import com.muwire.core.filefeeds.UIFeedDeletedEvent
import com.muwire.core.filefeeds.UIFeedUpdateEvent
import com.muwire.core.filefeeds.UIFilePublishedEvent
import com.muwire.core.filefeeds.UIFileUnpublishedEvent
import com.muwire.core.files.FileUnsharedEvent
import com.muwire.core.search.QueryEvent
import com.muwire.core.search.SearchEvent
@@ -512,105 +505,6 @@ class MainFrameController {
clipboard.setContents(selection, null)
}
@ControllerAction
void publish() {
def selectedFiles = view.selectedSharedFiles()
if (selectedFiles == null || selectedFiles.isEmpty())
return
if (model.publishButtonText == "Unpublish") {
selectedFiles.each {
it.unpublish()
model.core.eventBus.publish(new UIFileUnpublishedEvent(sf : it))
}
} else {
long now = System.currentTimeMillis()
selectedFiles.stream().filter({!it.isPublished()}).forEach({
it.publish(now)
model.core.eventBus.publish(new UIFilePublishedEvent(sf : it))
})
}
view.refreshSharedFiles()
}
@ControllerAction
void updateFileFeed() {
Feed feed = view.selectedFeed()
if (feed == null)
return
model.core.eventBus.publish(new UIFeedUpdateEvent(host: feed.getPublisher()))
}
@ControllerAction
void unsubscribeFileFeed() {
Feed feed = view.selectedFeed()
if (feed == null)
return
model.core.eventBus.publish(new UIFeedDeletedEvent(host : feed.getPublisher()))
runInsideUIAsync {
model.feeds.remove(feed)
model.feedItems.clear()
view.refreshFeeds()
}
}
@ControllerAction
void configureFileFeed() {
Feed feed = view.selectedFeed()
if (feed == null)
return
def params = [:]
params['core'] = core
params['feed'] = feed
mvcGroup.createMVCGroup("feed-configuration", params)
}
@ControllerAction
void downloadFeedItem() {
List<FeedItem> items = view.selectedFeedItems()
if (items == null || items.isEmpty())
return
Feed f = model.core.getFeedManager().getFeed(items.get(0).getPublisher())
items.each {
if (!model.canDownload(it.getInfoHash()))
return
File target = new File(application.context.get("muwire-settings").downloadLocation, it.getName())
model.core.eventBus.publish(new UIDownloadFeedItemEvent(item : it, target : target, sequential : f.isSequential()))
}
view.showDownloadsWindow.call()
}
@ControllerAction
void viewFeedItemComment() {
List<FeedItem> items = view.selectedFeedItems()
if (items == null || items.size() != 1)
return
FeedItem item = items.get(0)
String groupId = Base64.encode(item.getInfoHash().getRoot())
Map<String, Object> params = new HashMap<>()
params['text'] = DataUtil.readi18nString(Base64.decode(item.getComment()))
params['name'] = item.getName()
mvcGroup.createMVCGroup("show-comment", groupId, params)
}
@ControllerAction
void viewFeedItemCertificates() {
List<FeedItem> items = view.selectedFeedItems()
if (items == null || items.size() != 1)
return
FeedItem item = items.get(0)
def params = [:]
params['core'] = core
params['host'] = item.getPublisher()
params['infoHash'] = item.getInfoHash()
params['name'] = item.getName()
mvcGroup.createMVCGroup("fetch-certificates", params)
}
void startChat(Persona p) {
if (!mvcGroup.getChildrenGroups().containsKey(p.getHumanReadableName())) {
def params = [:]

View File

@@ -122,38 +122,7 @@ class OptionsController {
model.outBw = text
settings.outBw = Integer.valueOf(text)
}
// feed saving
boolean fileFeed = view.fileFeedCheckbox.model.isSelected()
model.fileFeed = fileFeed
settings.fileFeed = fileFeed
boolean advertiseFeed = view.advertiseFeedCheckbox.model.isSelected()
model.advertiseFeed = advertiseFeed
settings.advertiseFeed = advertiseFeed
boolean autoPublishSharedFiles = view.autoPublishSharedFilesCheckbox.model.isSelected()
model.autoPublishSharedFiles = autoPublishSharedFiles
settings.autoPublishSharedFiles = autoPublishSharedFiles
boolean defaultFeedAutoDownload = view.defaultFeedAutoDownloadCheckbox.model.isSelected()
model.defaultFeedAutoDownload = defaultFeedAutoDownload
settings.defaultFeedAutoDownload = defaultFeedAutoDownload
boolean defaultFeedSequential = view.defaultFeedSequentialCheckbox.model.isSelected()
model.defaultFeedSequential = defaultFeedSequential
settings.defaultFeedSequential = defaultFeedSequential
String defaultFeedItemsToKeep = view.defaultFeedItemsToKeepField.text
model.defaultFeedItemsToKeep = defaultFeedItemsToKeep
settings.defaultFeedItemsToKeep = Integer.parseInt(defaultFeedItemsToKeep)
String defaultFeedUpdateInterval = view.defaultFeedUpdateIntervalField.text
model.defaultFeedUpdateInterval = defaultFeedUpdateInterval
settings.defaultFeedUpdateInterval = Integer.parseInt(defaultFeedUpdateInterval)
// trust saving
boolean onlyTrusted = view.allowUntrustedCheckbox.model.isSelected()
model.onlyTrusted = onlyTrusted

View File

@@ -12,8 +12,6 @@ import javax.swing.JOptionPane
import com.muwire.core.Core
import com.muwire.core.Persona
import com.muwire.core.download.UIDownloadEvent
import com.muwire.core.filefeeds.Feed
import com.muwire.core.filefeeds.UIFeedConfigurationEvent
import com.muwire.core.search.UIResultEvent
import com.muwire.core.trust.TrustEvent
import com.muwire.core.trust.TrustLevel
@@ -109,22 +107,6 @@ class SearchTabController {
mvcGroup.createMVCGroup("browse", groupId, params)
}
@ControllerAction
void subscribe() {
def sender = view.selectedSender()
if (sender == null)
return
Feed feed = new Feed(sender)
feed.setAutoDownload(core.muOptions.defaultFeedAutoDownload)
feed.setSequential(core.muOptions.defaultFeedSequential)
feed.setItemsToKeep(core.muOptions.defaultFeedItemsToKeep)
feed.setUpdateInterval(core.muOptions.defaultFeedUpdateInterval * 60 * 1000)
core.eventBus.publish(new UIFeedConfigurationEvent(feed : feed, newFeed: true))
mvcGroup.parentGroup.view.showFeedsWindow.call()
}
@ControllerAction
void chat() {
def sender = view.selectedSender()
@@ -157,9 +139,7 @@ class SearchTabController {
return
def params = [:]
params['host'] = event.getSender()
params['infoHash'] = event.getInfohash()
params['name'] = event.getName()
params['result'] = event
params['core'] = core
mvcGroup.createMVCGroup("fetch-certificates", params)
}

View File

@@ -82,23 +82,23 @@ class Ready extends AbstractLifecycleHandler {
Core core
try {
core = new Core(props, home, metadata["application.version"])
Runtime.getRuntime().addShutdownHook({
core.shutdown()
})
core.startServices()
application.context.put("muwire-settings", props)
application.context.put("core",core)
application.getPropertyChangeListeners("core").each {
it.propertyChange(new PropertyChangeEvent(this, "core", null, core))
}
core.eventBus.publish(new UILoadedEvent())
} catch (Exception bad) {
log.log(Level.SEVERE,"couldn't initialize core",bad)
JOptionPane.showMessageDialog(null, "Couldn't connect to I2P router. Make sure I2P is running and restart MuWire",
"Can't connect to I2P router", JOptionPane.WARNING_MESSAGE)
"Can't connect to I2P router", JOptionPane.WARNING_MESSAGE)
System.exit(0)
}
Runtime.getRuntime().addShutdownHook({
core.shutdown()
})
core.startServices()
application.context.put("muwire-settings", props)
application.context.put("core",core)
application.getPropertyChangeListeners("core").each {
it.propertyChange(new PropertyChangeEvent(this, "core", null, core))
}
core.eventBus.publish(new UILoadedEvent())
}
private String selectNickname() {

View File

@@ -1,26 +0,0 @@
package com.muwire.gui
import com.muwire.core.Core
import com.muwire.core.filefeeds.Feed
import griffon.core.artifact.GriffonModel
import griffon.transform.Observable
import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
class FeedConfigurationModel {
Core core
Feed feed
@Observable boolean autoDownload
@Observable boolean sequential
@Observable int updateInterval
@Observable int itemsToKeep
void mvcGroupInit(Map<String, String> args) {
autoDownload = feed.isAutoDownload()
sequential = feed.isSequential()
updateInterval = feed.getUpdateInterval() / 60000
itemsToKeep = feed.getItemsToKeep()
}
}

View File

@@ -1,9 +1,6 @@
package com.muwire.gui
import com.muwire.core.InfoHash
import com.muwire.core.Persona
import com.muwire.core.filecert.CertificateFetchStatus
import com.muwire.core.filefeeds.FeedItem
import com.muwire.core.search.UIResultEvent
import griffon.core.artifact.GriffonModel
@@ -12,9 +9,7 @@ import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
class FetchCertificatesModel {
Persona host
InfoHash infoHash
String name
UIResultEvent result
@Observable CertificateFetchStatus status
@Observable int totalCertificates

View File

@@ -28,12 +28,6 @@ import com.muwire.core.content.ContentControlEvent
import com.muwire.core.download.DownloadStartedEvent
import com.muwire.core.download.Downloader
import com.muwire.core.filecert.CertificateCreatedEvent
import com.muwire.core.filefeeds.Feed
import com.muwire.core.filefeeds.FeedFetchEvent
import com.muwire.core.filefeeds.FeedItemFetchedEvent
import com.muwire.core.filefeeds.FeedLoadedEvent
import com.muwire.core.filefeeds.UIDownloadFeedItemEvent
import com.muwire.core.filefeeds.UIFeedConfigurationEvent
import com.muwire.core.files.AllFilesLoadedEvent
import com.muwire.core.files.DirectoryUnsharedEvent
import com.muwire.core.files.DirectoryWatchedEvent
@@ -67,7 +61,6 @@ import griffon.transform.FXObservable
import griffon.transform.Observable
import net.i2p.data.Base64
import net.i2p.data.Destination
import net.i2p.util.ConcurrentHashSet
import griffon.metadata.ArtifactProviderFor
@ArtifactProviderFor(GriffonModel)
@@ -96,8 +89,6 @@ class MainFrameModel {
def trusted = []
def distrusted = []
def subscriptions = []
def feeds = []
def feedItems = []
boolean sessionRestored
@@ -112,14 +103,6 @@ class MainFrameModel {
@Observable boolean previewButtonEnabled
@Observable String resumeButtonText
@Observable boolean addCommentButtonEnabled
@Observable boolean publishButtonEnabled
@Observable String publishButtonText
@Observable boolean updateFileFeedButtonEnabled
@Observable boolean unsubscribeFileFeedButtonEnabled
@Observable boolean configureFileFeedButtonEnabled
@Observable boolean downloadFeedItemButtonEnabled
@Observable boolean viewFeedItemCommentButtonEnabled
@Observable boolean viewFeedItemCertificatesButtonEnabled
@Observable boolean subscribeButtonEnabled
@Observable boolean markNeutralFromTrustedButtonEnabled
@Observable boolean markDistrustedButtonEnabled
@@ -135,7 +118,6 @@ class MainFrameModel {
@Observable boolean downloadsPaneButtonEnabled
@Observable boolean uploadsPaneButtonEnabled
@Observable boolean monitorPaneButtonEnabled
@Observable boolean feedsPaneButtonEnabled
@Observable boolean trustPaneButtonEnabled
@Observable boolean chatPaneButtonEnabled
@@ -143,7 +125,7 @@ class MainFrameModel {
@Observable Downloader downloader
private final Set<InfoHash> downloadInfoHashes = new ConcurrentHashSet<>()
private final Set<InfoHash> downloadInfoHashes = new HashSet<>()
@Observable volatile Core core
@@ -233,10 +215,6 @@ class MainFrameModel {
core.eventBus.register(TrustSubscriptionUpdatedEvent.class, this)
core.eventBus.register(SearchEvent.class, this)
core.eventBus.register(CertificateCreatedEvent.class, this)
core.eventBus.register(FeedLoadedEvent.class, this)
core.eventBus.register(FeedFetchEvent.class, this)
core.eventBus.register(FeedItemFetchedEvent.class, this)
core.eventBus.register(UIFeedConfigurationEvent.class, this)
core.muOptions.watchedKeywords.each {
core.eventBus.publish(new ContentControlEvent(term : it, regex: false, add: true))
@@ -275,13 +253,11 @@ class MainFrameModel {
distrusted.addAll(core.trustService.bad.values())
resumeButtonText = "Retry"
publishButtonText = "Publish"
searchesPaneButtonEnabled = false
downloadsPaneButtonEnabled = true
uploadsPaneButtonEnabled = true
monitorPaneButtonEnabled = true
feedsPaneButtonEnabled = true
trustPaneButtonEnabled = true
chatPaneButtonEnabled = true
@@ -387,8 +363,6 @@ class MainFrameModel {
}
void onFileLoadedEvent(FileLoadedEvent e) {
if (e.source == "PersisterService")
return
runInsideUIAsync {
shared << e.loadedFile
loadedFiles = shared.size()
@@ -675,41 +649,4 @@ class MainFrameModel {
int requests
boolean finished
}
void onFeedLoadedEvent(FeedLoadedEvent e) {
runInsideUIAsync {
feeds << e.feed
view.refreshFeeds()
}
}
void onFeedFetchEvent(FeedFetchEvent e) {
runInsideUIAsync {
view.refreshFeeds()
}
}
void onUIFeedConfigurationEvent(UIFeedConfigurationEvent e) {
if (!e.newFeed)
return
runInsideUIAsync {
if (feeds.contains(e.feed))
return
feeds << e.feed
view.refreshFeeds()
}
}
void onFeedItemFetchedEvent(FeedItemFetchedEvent e) {
Feed feed = core.feedManager.getFeed(e.item.getPublisher())
if (feed == null || !feed.isAutoDownload())
return
if (!canDownload(e.item.getInfoHash()))
return
if (core.fileManager.isShared(e.item.getInfoHash()))
return
File target = new File(core.getMuOptions().getDownloadLocation(), e.item.getName())
core.eventBus.publish(new UIDownloadFeedItemEvent(item : e.item, target : target, sequential : feed.isSequential()))
}
}

View File

@@ -50,15 +50,6 @@ class OptionsModel {
@Observable String inBw
@Observable String outBw
// feed options
@Observable boolean fileFeed
@Observable boolean advertiseFeed
@Observable boolean autoPublishSharedFiles
@Observable boolean defaultFeedAutoDownload
@Observable String defaultFeedItemsToKeep
@Observable boolean defaultFeedSequential
@Observable String defaultFeedUpdateInterval
// trust options
@Observable boolean onlyTrusted
@Observable boolean searchExtraHop
@@ -114,14 +105,6 @@ class OptionsModel {
inBw = String.valueOf(settings.inBw)
outBw = String.valueOf(settings.outBw)
}
fileFeed = settings.fileFeed
advertiseFeed = settings.advertiseFeed
autoPublishSharedFiles = settings.autoPublishSharedFiles
defaultFeedAutoDownload = settings.defaultFeedAutoDownload
defaultFeedItemsToKeep = String.valueOf(settings.defaultFeedItemsToKeep)
defaultFeedSequential = settings.defaultFeedSequential
defaultFeedUpdateInterval = String.valueOf(settings.defaultFeedUpdateInterval)
onlyTrusted = !settings.allowUntrusted()
searchExtraHop = settings.searchExtraHop

View File

@@ -25,7 +25,6 @@ class SearchTabModel {
@Observable boolean viewCommentActionEnabled
@Observable boolean viewCertificatesActionEnabled
@Observable boolean chatActionEnabled
@Observable boolean subscribeActionEnabled
@Observable boolean groupedByFile
Core core

View File

@@ -1,7 +1,6 @@
package com.muwire.gui
import com.muwire.core.Core
import com.muwire.core.InfoHash
import com.muwire.core.SharedFile
import griffon.core.artifact.GriffonModel
@@ -22,6 +21,6 @@ class SharedFileModel {
public void mvcGroupInit(Map<String,String> args) {
searchers.addAll(sf.getSearches())
downloaders.addAll(sf.getDownloaders())
certificates.addAll(core.certificateManager.byInfoHash.getOrDefault(new InfoHash(sf.getRoot()),[]))
certificates.addAll(core.certificateManager.byInfoHash.getOrDefault(sf.infoHash,[]))
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 37 KiB

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.SwingConstants
import java.awt.BorderLayout
import java.awt.GridBagConstraints
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import javax.annotation.Nonnull
@ArtifactProviderFor(GriffonView)
class FeedConfigurationView {
@MVCMember @Nonnull
FactoryBuilderSupport builder
@MVCMember @Nonnull
FeedConfigurationModel model
def dialog
def p
def mainFrame
def autoDownloadCheckbox
def sequentialCheckbox
def itemsToKeepField
def updateIntervalField
void initUI() {
mainFrame = application.windowManager.findWindow("main-frame")
dialog = new JDialog(mainFrame, "Feed Configuration", true)
dialog.setResizable(false)
p = builder.panel {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label("Configuration for feed " + model.feed.getPublisher().getHumanReadableName())
}
panel (constraints : BorderLayout.CENTER) {
gridBagLayout()
label(text : "Automatically download files from feed", constraints : gbc(gridx: 0, gridy : 0, anchor : GridBagConstraints.LINE_START, weightx: 100))
autoDownloadCheckbox = checkBox(selected : bind {model.autoDownload}, constraints : gbc(gridx: 1, gridy : 0, anchor : GridBagConstraints.LINE_END))
label(text : "Download files from feed sequentially", constraints : gbc(gridx: 0, gridy : 1, anchor : GridBagConstraints.LINE_START, weightx: 100))
sequentialCheckbox = checkBox(selected : bind {model.sequential}, constraints : gbc(gridx: 1, gridy : 1, anchor : GridBagConstraints.LINE_END))
label(text : "Feed items to store on disk (-1 means unlimited)", constraints : gbc(gridx: 0, gridy : 2, anchor : GridBagConstraints.LINE_START, weightx: 100))
itemsToKeepField = textField(text : bind {model.itemsToKeep}, constraints:gbc(gridx :1, gridy:2, anchor : GridBagConstraints.LINE_END))
label(text : "Feed refresh frequency in minutes", constraints : gbc(gridx: 0, gridy : 3, anchor : GridBagConstraints.LINE_START, weightx: 100))
updateIntervalField = textField(text : bind {model.updateInterval}, constraints:gbc(gridx :1, gridy:3, anchor : GridBagConstraints.LINE_END))
}
panel (constraints : BorderLayout.SOUTH) {
button(text : "Save", saveAction)
button(text : "Cancel", cancelAction)
}
}
}
void mvcGroupInit(Map<String,String> args) {
dialog.getContentPane().add(p)
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

@@ -38,7 +38,7 @@ class FetchCertificatesView {
void initUI() {
int rowHeight = application.context.get("row-height")
mainFrame = application.windowManager.findWindow("main-frame")
dialog = new JDialog(mainFrame, model.name, true)
dialog = new JDialog(mainFrame, model.result.name, true)
dialog.setResizable(true)
p = builder.panel {

View File

@@ -35,13 +35,9 @@ import javax.swing.tree.TreePath
import com.muwire.core.Constants
import com.muwire.core.Core
import com.muwire.core.InfoHash
import com.muwire.core.MuWireSettings
import com.muwire.core.SharedFile
import com.muwire.core.download.Downloader
import com.muwire.core.filefeeds.Feed
import com.muwire.core.filefeeds.FeedFetchStatus
import com.muwire.core.filefeeds.FeedItem
import com.muwire.core.files.FileSharedEvent
import com.muwire.core.trust.RemoteTrustList
import java.awt.BorderLayout
@@ -79,8 +75,6 @@ class MainFrameView {
def lastSharedSortEvent
def trustTablesSortEvents = [:]
def expansionListener = new TreeExpansions()
def lastFeedsSortEvent
def lastFeedItemsSortEvent
UISettings settings
@@ -156,7 +150,6 @@ class MainFrameView {
button(text: "Uploads", enabled : bind{model.uploadsPaneButtonEnabled}, actionPerformed : showUploadsWindow)
if (settings.showMonitor)
button(text: "Monitor", enabled: bind{model.monitorPaneButtonEnabled},actionPerformed : showMonitorWindow)
button(text: "Feeds", enabled: bind {model.feedsPaneButtonEnabled}, actionPerformed : showFeedsWindow)
button(text: "Trust", enabled:bind{model.trustPaneButtonEnabled},actionPerformed : showTrustWindow)
button(text: "Chat", enabled : bind{model.chatPaneButtonEnabled}, actionPerformed : showChatWindow)
}
@@ -296,9 +289,8 @@ class MainFrameView {
closureColumn(header : "Comments", preferredWidth : 50, type : Boolean, read : {it.getComment() != null})
closureColumn(header : "Certified", preferredWidth : 50, type : Boolean, read : {
Core core = application.context.get("core")
core.certificateManager.hasLocalCertificate(new InfoHash(it.getRoot()))
core.certificateManager.hasLocalCertificate(it.getInfoHash())
})
closureColumn(header : "Published", preferredWidth : 50, type : Boolean, read : {row -> row.isPublished()})
closureColumn(header : "Search Hits", preferredWidth: 50, type : Integer, read : {it.getHits()})
closureColumn(header : "Downloaders", preferredWidth: 50, type : Integer, read : {it.getDownloaders().size()})
}
@@ -323,11 +315,9 @@ class MainFrameView {
radioButton(text : "Table", selected : false, buttonGroup : sharedViewType, actionPerformed : showSharedFilesTable)
}
panel {
gridBagLayout()
button(text : "Share", constraints : gbc(gridx: 0), actionPerformed : shareFiles)
button(text : "Add Comment", enabled : bind {model.addCommentButtonEnabled}, constraints : gbc(gridx: 1), addCommentAction)
button(text : "Certify", enabled : bind {model.addCommentButtonEnabled}, constraints : gbc(gridx: 2), issueCertificateAction)
button(text : bind {model.publishButtonText}, enabled : bind {model.publishButtonEnabled}, constraints : gbc(gridx:3), publishAction)
button(text : "Share", actionPerformed : shareFiles)
button(text : "Add Comment", enabled : bind {model.addCommentButtonEnabled}, addCommentAction)
button(text : "Certify", enabled : bind {model.addCommentButtonEnabled}, issueCertificateAction)
}
panel {
panel {
@@ -434,56 +424,6 @@ class MainFrameView {
}
}
}
panel(constraints : "feeds window") {
gridLayout(rows : 2, cols : 1)
panel {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label(text: "Subscriptions")
}
scrollPane(constraints : BorderLayout.CENTER) {
table(id : "feeds-table", autoCreateRowSorter : true, rowHeight : rowHeight) {
tableModel(list : model.feeds) {
closureColumn(header : "Publisher", preferredWidth: 350, type : String, read : {it.getPublisher().getHumanReadableName()})
closureColumn(header : "Files", preferredWidth: 10, type : Integer, read : {model.core.feedManager.getFeedItems(it.getPublisher()).size()})
closureColumn(header : "Last Updated", type : Long, read : {it.getLastUpdated()})
closureColumn(header : "Status", preferredWidth: 10, type : String, read : {it.getStatus()})
}
}
}
panel (constraints : BorderLayout.SOUTH) {
button(text : "Update", enabled : bind {model.updateFileFeedButtonEnabled}, updateFileFeedAction)
button(text : "Unsubscribe", enabled : bind {model.unsubscribeFileFeedButtonEnabled}, unsubscribeFileFeedAction)
button(text : "Configure", enabled : bind {model.configureFileFeedButtonEnabled}, configureFileFeedAction)
}
}
panel {
borderLayout()
panel (constraints : BorderLayout.NORTH) {
label(text : "Published Files")
}
scrollPane(constraints : BorderLayout.CENTER) {
table(id : "feed-items-table", autoCreateRowSorter : true, rowHeight : rowHeight) {
tableModel(list : model.feedItems) {
closureColumn(header : "Name", preferredWidth: 350, type : String, read : {it.getName()})
closureColumn(header : "Size", preferredWidth: 10, type : Long, read : {it.getSize()})
closureColumn(header : "Comment", preferredWidth: 10, type : Boolean, read : {it.getComment() != null})
closureColumn(header : "Certificates", preferredWidth: 10, type : Integer, read : {it.getCertificates()})
closureColumn(header : "Downloaded", preferredWidth: 10, type : Boolean, read : {
InfoHash ih = it.getInfoHash()
model.core.fileManager.isShared(ih)
})
closureColumn(header: "Date", type : Long, read : {it.getTimestamp()})
}
}
}
panel(constraints : BorderLayout.SOUTH) {
button(text : "Download", enabled : bind {model.downloadFeedItemButtonEnabled}, downloadFeedItemAction)
button(text : "View Comment", enabled : bind {model.viewFeedItemCommentButtonEnabled}, viewFeedItemCommentAction)
button(text : "View Certificates", enabled : bind {model.viewFeedItemCertificatesButtonEnabled}, viewFeedItemCertificatesAction )
}
}
}
panel(constraints : "trust window") {
gridLayout(rows : 2, cols : 1)
panel {
@@ -741,30 +681,14 @@ class MainFrameView {
if (selectedFiles == null || selectedFiles.isEmpty())
return
model.addCommentButtonEnabled = true
model.publishButtonEnabled = true
boolean unpublish = true
selectedFiles.each {
unpublish &= it.isPublished()
}
model.publishButtonText = unpublish ? "Unpublish" : "Publish"
})
def sharedFilesTree = builder.getVariable("shared-files-tree")
sharedFilesTree.addMouseListener(sharedFilesMouseListener)
sharedFilesTree.addTreeSelectionListener({
def selectedNode = sharedFilesTree.getLastSelectedPathComponent()
model.addCommentButtonEnabled = selectedNode != null
model.publishButtonEnabled = selectedNode != null
def selectedFiles = selectedSharedFiles()
if (selectedFiles == null || selectedFiles.isEmpty())
return
boolean unpublish = true
selectedFiles.each {
unpublish &= it.isPublished()
}
model.publishButtonText = unpublish ? "Unpublish" : "Publish"
})
sharedFilesTree.addTreeExpansionListener(expansionListener)
@@ -820,98 +744,6 @@ class MainFrameView {
}
})
// feeds table
def feedsTable = builder.getVariable("feeds-table")
feedsTable.rowSorter.addRowSorterListener({evt -> lastFeedsSortEvent = evt})
feedsTable.rowSorter.setSortsOnUpdates(true)
feedsTable.setDefaultRenderer(Integer.class, centerRenderer)
feedsTable.setDefaultRenderer(Long.class, new DateRenderer())
selectionModel = feedsTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
selectionModel.addListSelectionListener({
Feed selectedFeed = selectedFeed()
if (selectedFeed == null) {
model.updateFileFeedButtonEnabled = false
model.unsubscribeFileFeedButtonEnabled = false
model.configureFileFeedButtonEnabled = false
return
}
model.unsubscribeFileFeedButtonEnabled = true
model.configureFileFeedButtonEnabled = true
model.updateFileFeedButtonEnabled = !selectedFeed.getStatus().isActive()
def items = model.core.feedManager.getFeedItems(selectedFeed.getPublisher())
model.feedItems.clear()
model.feedItems.addAll(items)
def feedItemsTable = builder.getVariable("feed-items-table")
int selectedItemRow = feedItemsTable.getSelectedRow()
feedItemsTable.model.fireTableDataChanged()
if (selectedItemRow >= 0 && selectedItemRow < items.size())
feedItemsTable.selectionModel.setSelectionInterval(selectedItemRow, selectedItemRow)
})
feedsTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3)
showFeedsPopupMenu(e)
}
@Override
public void mouseReleased(MouseEvent e) {
if(e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3)
showFeedsPopupMenu(e)
}
})
// feed items table
def feedItemsTable = builder.getVariable("feed-items-table")
feedItemsTable.rowSorter.addRowSorterListener({evt -> lastFeedItemsSortEvent = evt})
feedItemsTable.rowSorter.setSortsOnUpdates(true)
feedItemsTable.setDefaultRenderer(Integer.class, centerRenderer)
feedItemsTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
feedItemsTable.columnModel.getColumn(5).setCellRenderer(new DateRenderer())
selectionModel = feedItemsTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
selectionModel.addListSelectionListener({
List<FeedItem> selectedItems = selectedFeedItems()
if (selectedItems == null || selectedItems.isEmpty()) {
model.downloadFeedItemButtonEnabled = false
model.viewFeedItemCommentButtonEnabled = false
model.viewFeedItemCertificatesButtonEnabled = false
return
}
model.downloadFeedItemButtonEnabled = true
model.viewFeedItemCommentButtonEnabled = false
model.viewFeedItemCertificatesButtonEnabled = false
if (selectedItems.size() == 1) {
FeedItem item = selectedItems.get(0)
model.viewFeedItemCommentButtonEnabled = item.getComment() != null
model.viewFeedItemCertificatesButtonEnabled = item.getCertificates() > 0
}
})
feedItemsTable.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
List<FeedItem> selectedItems = selectedFeedItems()
if (e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3)
showFeedItemsPopupMenu(e)
else if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2 &&
selectedItems != null && selectedItems.size() == 1 &&
model.canDownload(selectedItems.get(0).getInfoHash())) {
mvcGroup.controller.downloadFeedItem()
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger() || e.getButton() == MouseEvent.BUTTON3)
showFeedItemsPopupMenu(e)
}
})
// subscription table
def subscriptionTable = builder.getVariable("subscription-table")
subscriptionTable.setDefaultRenderer(Integer.class, centerRenderer)
@@ -1079,7 +911,7 @@ class MainFrameView {
String roots = ""
for (Iterator<SharedFile> iterator = selectedFiles.iterator(); iterator.hasNext(); ) {
SharedFile selected = iterator.next()
String root = Base64.encode(selected.getRoot())
String root = Base64.encode(selected.infoHash.getRoot())
roots += root
if (iterator.hasNext())
roots += "\n"
@@ -1174,52 +1006,6 @@ class MainFrameView {
showPopupMenu(menu, e)
}
void showFeedsPopupMenu(MouseEvent e) {
Feed feed = selectedFeed()
if (feed == null)
return
JPopupMenu menu = new JPopupMenu()
if (model.updateFileFeedButtonEnabled) {
JMenuItem update = new JMenuItem("Update")
update.addActionListener({mvcGroup.controller.updateFileFeed()})
menu.add(update)
}
JMenuItem unsubscribe = new JMenuItem("Unsubscribe")
unsubscribe.addActionListener({mvcGroup.controller.unsubscribeFileFeed()})
menu.add(unsubscribe)
JMenuItem configure = new JMenuItem("Configure")
configure.addActionListener({mvcGroup.controller.configureFileFeed()})
menu.add(configure)
showPopupMenu(menu,e)
}
void showFeedItemsPopupMenu(MouseEvent e) {
List<FeedItem> items = selectedFeedItems()
if (items == null || items.isEmpty())
return
// TODO: finish
JPopupMenu menu = new JPopupMenu()
if (model.downloadFeedItemButtonEnabled) {
JMenuItem download = new JMenuItem("Download")
download.addActionListener({mvcGroup.controller.downloadFeedItem()})
menu.add(download)
}
if (model.viewFeedItemCommentButtonEnabled) {
JMenuItem viewComment = new JMenuItem("View Comment")
viewComment.addActionListener({mvcGroup.controller.viewFeedItemComment()})
menu.add(viewComment)
}
if (model.viewFeedItemCertificatesButtonEnabled) {
JMenuItem viewCertificates = new JMenuItem("View Certificates")
viewCertificates.addActionListener({mvcGroup.controller.viewFeedItemCertificates()})
menu.add(viewCertificates)
}
showPopupMenu(menu, e)
}
def selectedUploader() {
def uploadsTable = builder.getVariable("uploads-table")
int selectedRow = uploadsTable.getSelectedRow()
@@ -1270,7 +1056,6 @@ class MainFrameView {
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.feedsPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
model.chatPaneButtonEnabled = true
chatNotificator.mainWindowDeactivated()
@@ -1283,7 +1068,6 @@ class MainFrameView {
model.downloadsPaneButtonEnabled = false
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.feedsPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
model.chatPaneButtonEnabled = true
chatNotificator.mainWindowDeactivated()
@@ -1296,7 +1080,6 @@ class MainFrameView {
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = false
model.monitorPaneButtonEnabled = true
model.feedsPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
model.chatPaneButtonEnabled = true
chatNotificator.mainWindowDeactivated()
@@ -1309,20 +1092,6 @@ class MainFrameView {
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = false
model.feedsPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
model.chatPaneButtonEnabled = true
chatNotificator.mainWindowDeactivated()
}
def showFeedsWindow = {
def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel,"feeds window")
model.searchesPaneButtonEnabled = true
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.feedsPaneButtonEnabled = false
model.trustPaneButtonEnabled = true
model.chatPaneButtonEnabled = true
chatNotificator.mainWindowDeactivated()
@@ -1335,7 +1104,6 @@ class MainFrameView {
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.feedsPaneButtonEnabled = true
model.trustPaneButtonEnabled = false
model.chatPaneButtonEnabled = true
chatNotificator.mainWindowDeactivated()
@@ -1348,7 +1116,6 @@ class MainFrameView {
model.downloadsPaneButtonEnabled = true
model.uploadsPaneButtonEnabled = true
model.monitorPaneButtonEnabled = true
model.feedsPaneButtonEnabled = true
model.trustPaneButtonEnabled = true
model.chatPaneButtonEnabled = false
chatNotificator.mainWindowActivated()
@@ -1405,43 +1172,6 @@ class MainFrameView {
builder.getVariable("shared-files-table").model.fireTableDataChanged()
}
public void refreshFeeds() {
JTable feedsTable = builder.getVariable("feeds-table")
int selectedFeed = feedsTable.getSelectedRow()
feedsTable.model.fireTableDataChanged()
if (selectedFeed >= 0)
feedsTable.selectionModel.setSelectionInterval(selectedFeed, selectedFeed)
JTable feedItemsTable = builder.getVariable("feed-items-table")
feedItemsTable.model.fireTableDataChanged()
}
Feed selectedFeed() {
JTable feedsTable = builder.getVariable("feeds-table")
int row = feedsTable.getSelectedRow()
if (row < 0)
return null
if (lastFeedsSortEvent != null)
row = feedsTable.rowSorter.convertRowIndexToModel(row)
model.feeds[row]
}
List<FeedItem> selectedFeedItems() {
JTable feedItemsTable = builder.getVariable("feed-items-table")
int [] selectedRows = feedItemsTable.getSelectedRows()
if (selectedRows.length == 0)
return null
List<FeedItem> rv = new ArrayList<>()
if (lastFeedItemsSortEvent != null) {
for (int i = 0; i < selectedRows.length; i++) {
selectedRows[i] = feedItemsTable.rowSorter.convertRowIndexToModel(selectedRows[i])
}
}
for (int selectedRow : selectedRows)
rv.add(model.feedItems[selectedRow])
rv
}
private void closeApplication() {
Core core = application.getContext().get("core")

View File

@@ -32,7 +32,6 @@ class OptionsView {
def i
def u
def bandwidth
def feed
def trust
def chat
@@ -67,14 +66,6 @@ class OptionsView {
def inBwField
def outBwField
def fileFeedCheckbox
def advertiseFeedCheckbox
def autoPublishSharedFilesCheckbox
def defaultFeedAutoDownloadCheckbox
def defaultFeedItemsToKeepField
def defaultFeedSequentialCheckbox
def defaultFeedUpdateIntervalField
def allowUntrustedCheckbox
def searchExtraHopCheckbox
@@ -266,32 +257,6 @@ class OptionsView {
}
panel(constraints : gbc(gridx: 0, gridy: 1, weighty: 100))
}
feed = builder.panel {
gridBagLayout()
panel (border : titledBorder(title : "General Feed Settings", border : etchedBorder(), titlePosition : TitledBorder.TOP),
constraints : gbc(gridx : 0, gridy : 0, fill : GridBagConstraints.HORIZONTAL, weightx: 100)) {
gridBagLayout()
label(text : "Enable file feed", constraints : gbc(gridx: 0, gridy : 0, anchor : GridBagConstraints.LINE_START, weightx: 100))
fileFeedCheckbox = checkBox(selected : bind {model.fileFeed}, constraints : gbc(gridx: 1, gridy : 0, anchor : GridBagConstraints.LINE_END))
label(text : "Advertise feed in search results", constraints : gbc(gridx: 0, gridy : 1, anchor : GridBagConstraints.LINE_START, weightx: 100))
advertiseFeedCheckbox = checkBox(selected : bind {model.advertiseFeed}, constraints : gbc(gridx: 1, gridy : 1, anchor : GridBagConstraints.LINE_END))
label(text : "Automatically publish shared files", constraints : gbc(gridx: 0, gridy : 2, anchor : GridBagConstraints.LINE_START, weightx: 100))
autoPublishSharedFilesCheckbox = checkBox(selected : bind {model.autoPublishSharedFiles}, constraints : gbc(gridx: 1, gridy : 2, anchor : GridBagConstraints.LINE_END))
}
panel (border : titledBorder(title : "Default Settings For New Feeds", border : etchedBorder(), titlePosition : TitledBorder.TOP),
constraints : gbc(gridx : 0, gridy : 1, fill : GridBagConstraints.HORIZONTAL, weightx: 100)) {
gridBagLayout()
label(text : "Automatically download files from feed", constraints : gbc(gridx: 0, gridy : 0, anchor : GridBagConstraints.LINE_START, weightx: 100))
defaultFeedAutoDownloadCheckbox = checkBox(selected : bind {model.defaultFeedAutoDownload}, constraints : gbc(gridx: 1, gridy : 0, anchor : GridBagConstraints.LINE_END))
label(text : "Download files from feed sequentially", constraints : gbc(gridx: 0, gridy : 1, anchor : GridBagConstraints.LINE_START, weightx: 100))
defaultFeedSequentialCheckbox = checkBox(selected : bind {model.defaultFeedSequential}, constraints : gbc(gridx: 1, gridy : 1, anchor : GridBagConstraints.LINE_END))
label(text : "Feed items to store on disk (-1 means unlimited)", constraints : gbc(gridx: 0, gridy : 2, anchor : GridBagConstraints.LINE_START, weightx: 100))
defaultFeedItemsToKeepField = textField(text : bind {model.defaultFeedItemsToKeep}, constraints:gbc(gridx :1, gridy:2, anchor : GridBagConstraints.LINE_END))
label(text : "Feed refresh frequency in minutes", constraints : gbc(gridx: 0, gridy : 3, anchor : GridBagConstraints.LINE_START, weightx: 100))
defaultFeedUpdateIntervalField = textField(text : bind {model.defaultFeedUpdateInterval}, constraints:gbc(gridx :1, gridy:3, anchor : GridBagConstraints.LINE_END))
}
panel(constraints : gbc(gridx: 0, gridy : 2, weighty: 100))
}
trust = builder.panel {
gridBagLayout()
panel (border : titledBorder(title : "Trust Settings", border : etchedBorder(), titlePosition : TitledBorder.TOP),
@@ -346,7 +311,6 @@ class OptionsView {
if (core.router != null) {
tabbedPane.addTab("Bandwidth", bandwidth)
}
tabbedPane.addTab("Feed", feed)
tabbedPane.addTab("Trust", trust)
tabbedPane.addTab("Chat", chat)

View File

@@ -74,7 +74,6 @@ class SearchTabView {
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 : "Browse", preferredWidth : 20, type: Boolean, read : {row -> model.sendersBucket[row].first().browse})
closureColumn(header : "Feed", preferredWidth : 20, type : Boolean, read : {row -> model.sendersBucket[row].first().feed})
closureColumn(header : "Chat", preferredWidth : 20, type : Boolean, read : {row -> model.sendersBucket[row].first().chat})
closureColumn(header : "Trust", preferredWidth : 50, type: String, read : { row ->
model.core.trustService.getLevel(row.destination).toString()
@@ -86,7 +85,6 @@ class SearchTabView {
gridLayout(rows: 1, cols : 2)
panel (border : etchedBorder()){
button(text : "Browse Host", enabled : bind {model.browseActionEnabled}, browseAction)
button(text : "Subscribe", enabled : bind {model.subscribeActionEnabled}, subscribeAction)
button(text : "Chat", enabled : bind{model.chatActionEnabled}, chatAction)
}
panel (border : etchedBorder()){
@@ -158,14 +156,6 @@ class SearchTabView {
}
count
})
closureColumn(header : "Feeds", preferredWidth : 20, type : Integer, read : {
int count = 0
model.hashBucket[it].each {
if (it.feed)
count++
}
count
})
closureColumn(header : "Chat Hosts", preferredWidth : 20, type : Integer, read : {
int count = 0
model.hashBucket[it].each {
@@ -197,7 +187,6 @@ class SearchTabView {
tableModel(list : model.senders2) {
closureColumn(header : "Sender", preferredWidth : 350, type : String, read : {it.sender.getHumanReadableName()})
closureColumn(header : "Browse", preferredWidth : 20, type : Boolean, read : {it.browse})
closureColumn(header : "Feed", preferredWidth : 20, type: Boolean, read : {it.feed})
closureColumn(header : "Chat", preferredWidth : 20, type : Boolean, read : {it.chat})
closureColumn(header : "Comment", preferredWidth : 20, type : Boolean, read : {it.comment != null})
closureColumn(header : "Certificates", preferredWidth : 20, type: Integer, read : {it.certificates})
@@ -211,7 +200,6 @@ class SearchTabView {
gridLayout(rows : 1, cols : 2)
panel (border : etchedBorder()) {
button(text : "Browse Host", enabled : bind {model.browseActionEnabled}, browseAction)
button(text : "Subscribe", enabled : bind {model.subscribeActionEnabled}, subscribeAction)
button(text : "Chat", enabled : bind{model.chatActionEnabled}, chatAction)
button(text : "View Comment", enabled : bind {model.viewCommentActionEnabled}, showCommentAction)
button(text : "View Certificates", enabled : bind {model.viewCertificatesActionEnabled}, viewCertificatesAction)
@@ -320,7 +308,6 @@ class SearchTabView {
if (result == null) {
model.viewCommentActionEnabled = false
model.viewCertificatesActionEnabled = false
model.subscribeActionEnabled = false
return
} else {
model.viewCommentActionEnabled = result.comment != null
@@ -339,14 +326,12 @@ class SearchTabView {
if (row < 0) {
model.trustButtonsEnabled = false
model.browseActionEnabled = false
model.subscribeActionEnabled = false
model.chatActionEnabled = false
return
} else {
Persona sender = model.senders[row]
model.browseActionEnabled = model.sendersBucket[sender].first().browse
model.chatActionEnabled = model.sendersBucket[sender].first().chat
model.subscribeActionEnabled = model.sendersBucket[sender].first().feed &&
model.core.feedManager.getFeed(sender) == null
model.trustButtonsEnabled = true
model.results.clear()
model.results.addAll(model.sendersBucket[sender])
@@ -401,19 +386,16 @@ class SearchTabView {
if (row < 0 || model.senders2[row] == null) {
model.browseActionEnabled = false
model.chatActionEnabled = false
model.subscribeActionEnabled = false
model.viewCertificatesActionEnabled = false
model.trustButtonsEnabled = false
model.viewCommentActionEnabled = false
return
}
UIResultEvent e = model.senders2[row]
model.browseActionEnabled = e.browse
model.chatActionEnabled = e.chat
model.subscribeActionEnabled = e.feed && model.core.feedManager.getFeed(e.getSender()) == null
model.browseActionEnabled = model.senders2[row].browse
model.chatActionEnabled = model.senders2[row].chat
model.trustButtonsEnabled = true
model.viewCommentActionEnabled = e.comment != null
model.viewCertificatesActionEnabled = e.certificates > 0
model.viewCommentActionEnabled = model.senders2[row].comment != null
model.viewCertificatesActionEnabled = model.senders2[row].certificates > 0
})
if (settings.groupByFile)

View File

@@ -21,6 +21,7 @@ import com.muwire.core.files.FileTree;
import com.muwire.core.files.FileTreeCallback;
import com.muwire.core.files.FileUnsharedEvent;
import com.muwire.core.files.UICommentEvent;
import com.muwire.core.files.UIPersistFilesEvent;
import com.muwire.core.util.DataUtil;
import net.i2p.data.Base64;
@@ -131,6 +132,7 @@ public class FileManager {
core.getEventBus().publish(event);
}
}
core.getEventBus().publish(new UIPersistFilesEvent());
Util.pause();
}

View File

@@ -89,13 +89,12 @@ public class FilesServlet extends HttpServlet {
String comment = null;
if (sf.getComment() != null)
comment = DataUtil.readi18nString(Base64.decode(sf.getComment()));
InfoHash ih = new InfoHash(sf.getRoot());
FilesTableEntry entry = new FilesTableEntry(sf.getFile().getName(),
ih,
sf.getInfoHash(),
sf.getCachedPath(),
sf.getCachedLength(),
comment,
core.getCertificateManager().hasLocalCertificate(ih));
core.getCertificateManager().hasLocalCertificate(sf.getInfoHash()));
entries.add(entry);
});
@@ -261,13 +260,12 @@ public class FilesServlet extends HttpServlet {
sb.append("<Name>").append(Util.escapeHTMLinXML(sf.getFile().getName())).append("</Name>");
sb.append("<Path>").append(Util.escapeHTMLinXML(sf.getCachedPath())).append("</Path>");
sb.append("<Size>").append(DataHelper.formatSize2Decimal(sf.getCachedLength())).append("B").append("</Size>");
sb.append("<InfoHash>").append(Base64.encode(sf.getRoot())).append("</InfoHash>");
sb.append("<InfoHash>").append(Base64.encode(sf.getInfoHash().getRoot())).append("</InfoHash>");
if (sf.getComment() != null) {
String comment = DataUtil.readi18nString(Base64.decode(sf.getComment()));
sb.append("<Comment>").append(Util.escapeHTMLinXML(comment)).append("</Comment>");
}
InfoHash ih = new InfoHash(sf.getRoot());
sb.append("<Certified>").append(core.getCertificateManager().hasLocalCertificate(ih)).append("</Certified>");
sb.append("<Certified>").append(core.getCertificateManager().hasLocalCertificate(sf.getInfoHash())).append("</Certified>");
// TODO: other stuff
sb.append("</File>");
}

View File

@@ -4,7 +4,6 @@ import java.io.File;
import com.muwire.core.Core;
import com.muwire.core.MuWireSettings;
import com.muwire.core.UILoadedEvent;
class MWStarter extends Thread {
private final MuWireSettings settings;
@@ -23,8 +22,7 @@ class MWStarter extends Thread {
public void run() {
Core core = new Core(settings, home, version);
client.setCore(core);
core.startServices();
core.getEventBus().publish(new UILoadedEvent());
client.setCore(core);
}
}

View File

@@ -171,6 +171,8 @@ public class MuWireClient {
servletContext.setAttribute("trustManager", trustManager);
servletContext.setAttribute("certificateManager", certificateManager);
servletContext.setAttribute("uploadManager", uploadManager);
core.getEventBus().publish(new UILoadedEvent());
}
public String getHome() {