Compare commits
9 Commits
muwire-0.6
...
muwire-0.6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
761bf0a177 | ||
![]() |
bd873211c0 | ||
![]() |
036971cfe5 | ||
![]() |
a2637570b1 | ||
![]() |
6012adbeab | ||
![]() |
8f6b6b0caa | ||
![]() |
8f3b5aea8d | ||
![]() |
ee098ace8e | ||
![]() |
5d8401e4bf |
@@ -4,7 +4,7 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
|
||||
|
||||
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
||||
|
||||
The current stable release - 0.5.9 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||
The current stable release - 0.6.0 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||
|
||||
### Building
|
||||
|
||||
|
@@ -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.0"
|
||||
private static final String MW_VERSION = "0.6.2"
|
||||
|
||||
private static volatile Core core
|
||||
|
||||
|
@@ -406,7 +406,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.6.0")
|
||||
Core core = new Core(props, home, "0.6.2")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -277,6 +277,57 @@ public class Downloader {
|
||||
executorService.submit(newWorker)
|
||||
}
|
||||
|
||||
boolean isSequential() {
|
||||
pieces.ratio == 0f
|
||||
}
|
||||
|
||||
File generatePreview() {
|
||||
int lastCompletePiece = pieces.firstIncomplete() - 1
|
||||
if (lastCompletePiece == -1)
|
||||
return null
|
||||
if (lastCompletePiece < -1)
|
||||
return file
|
||||
long previewableLength = (lastCompletePiece + 1) * ((long)pieceSize)
|
||||
|
||||
// generate name
|
||||
long now = System.currentTimeMillis()
|
||||
File previewFile
|
||||
File parentFile = file.getParentFile()
|
||||
int lastDot = file.getName().lastIndexOf('.')
|
||||
if (lastDot < 0)
|
||||
previewFile = new File(parentFile, file.getName() + "." + String.valueOf(now) + ".mwpreview")
|
||||
else {
|
||||
String name = file.getName().substring(0, lastDot)
|
||||
String extension = file.getName().substring(lastDot + 1)
|
||||
String previewName = name + "." + String.valueOf(now) + ".mwpreview."+extension
|
||||
previewFile = new File(parentFile, previewName)
|
||||
}
|
||||
|
||||
// copy
|
||||
InputStream is = null
|
||||
OutputStream os = null
|
||||
try {
|
||||
is = new BufferedInputStream(new FileInputStream(incompleteFile))
|
||||
os = new BufferedOutputStream(new FileOutputStream(previewFile))
|
||||
byte [] tmp = new byte[0x1 << 13]
|
||||
long totalCopied = 0
|
||||
while(totalCopied < previewableLength) {
|
||||
int read = is.read(tmp, 0, (int)Math.min(tmp.length, previewableLength - totalCopied))
|
||||
if (read < 0)
|
||||
throw new IOException("EOF?")
|
||||
os.write(tmp, 0, read)
|
||||
totalCopied += read
|
||||
}
|
||||
return previewFile
|
||||
} catch (IOException bad) {
|
||||
log.log(Level.WARNING,"Preview failed",bad)
|
||||
return null
|
||||
} finally {
|
||||
try {is?.close() } catch (IOException ignore) {}
|
||||
try {os?.close() } catch (IOException ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadWorker implements Runnable {
|
||||
private final Destination destination
|
||||
private volatile WorkerState currentState
|
||||
|
@@ -108,6 +108,10 @@ class Pieces {
|
||||
partials.clear()
|
||||
}
|
||||
|
||||
synchronized int firstIncomplete() {
|
||||
done.nextClearBit(0)
|
||||
}
|
||||
|
||||
synchronized void write(PrintWriter writer) {
|
||||
for (int i = done.nextSetBit(0); i >= 0; i = done.nextSetBit(i+1)) {
|
||||
writer.println(i)
|
||||
|
@@ -143,6 +143,7 @@ class FileManager {
|
||||
|
||||
String comment = sf.getComment()
|
||||
if (comment != null) {
|
||||
comment = DataUtil.readi18nString(Base64.decode(comment))
|
||||
Set<File> existingComment = commentToFile.get(comment)
|
||||
if (existingComment != null) {
|
||||
existingComment.remove(sf.getFile())
|
||||
@@ -229,7 +230,7 @@ class FileManager {
|
||||
return files
|
||||
Set<SharedFile> rv = new HashSet<>()
|
||||
files.each {
|
||||
if (it.getPieceSize() != 0)
|
||||
if (it != null && it.getPieceSize() != 0)
|
||||
rv.add(it)
|
||||
}
|
||||
rv
|
||||
|
@@ -39,10 +39,11 @@ class SearchIndex {
|
||||
split.each { if (it.length() > 0) rv << it }
|
||||
|
||||
// then just by ' '
|
||||
source.split(' ').each { if (it.length() > 0) rv << it }
|
||||
source.toLowerCase().split(' ').each { if (it.length() > 0) rv << it }
|
||||
|
||||
// and add original string
|
||||
rv << source
|
||||
rv << source.toLowerCase()
|
||||
rv.toArray(new String[0])
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package com.muwire.core.files
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll
|
||||
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
@@ -9,6 +11,9 @@ import com.muwire.core.MuWireSettings
|
||||
import com.muwire.core.SharedFile
|
||||
import com.muwire.core.search.ResultsEvent
|
||||
import com.muwire.core.search.SearchEvent
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import net.i2p.data.Base64
|
||||
|
||||
class FileManagerTest {
|
||||
|
||||
@@ -185,4 +190,39 @@ class FileManagerTest {
|
||||
assert results == null
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testComplicatedScenario() {
|
||||
// this tries to reproduce an NPE when un-sharing then sharing again and searching
|
||||
String comment = "same comment"
|
||||
comment = Base64.encode(DataUtil.encodei18nString(comment))
|
||||
File f1 = new File("MuWire-0.5.10.AppImage")
|
||||
InfoHash ih1 = InfoHash.fromHashList(new byte[32])
|
||||
SharedFile sf1 = new SharedFile(f1, ih1, 0)
|
||||
sf1.setComment(comment)
|
||||
|
||||
manager.onFileLoadedEvent(new FileLoadedEvent(loadedFile : sf1))
|
||||
manager.onFileUnsharedEvent(new FileUnsharedEvent(unsharedFile : sf1, deleted : true))
|
||||
|
||||
File f2 = new File("MuWire-0.6.0.AppImage")
|
||||
InfoHash ih2 = InfoHash.fromHashList(new byte[64])
|
||||
SharedFile sf2 = new SharedFile(f2, ih2, 0)
|
||||
sf2.setComment(comment)
|
||||
|
||||
manager.onFileLoadedEvent(new FileLoadedEvent(loadedFile : sf2))
|
||||
|
||||
manager.onSearchEvent(new SearchEvent(searchTerms : ["muwire"]))
|
||||
Thread.sleep(20)
|
||||
|
||||
assert results != null
|
||||
assert results.results.size() == 1
|
||||
assert results.results.contains(sf2)
|
||||
|
||||
results = null
|
||||
manager.onSearchEvent(new SearchEvent(searchTerms : ['comment'], searchComments : true, oobInfohash : true))
|
||||
Thread.sleep(20)
|
||||
assert results != null
|
||||
assert results.results.size() == 1
|
||||
assert results.results.contains(sf2)
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import com.muwire.core.Destinations
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.Personas
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import net.i2p.data.Base64
|
||||
import net.i2p.data.Destination
|
||||
|
||||
@@ -55,13 +56,16 @@ class TrustServiceTest {
|
||||
service.onTrustEvent new TrustEvent(level: TrustLevel.DISTRUSTED, persona: personas.persona2)
|
||||
|
||||
Thread.sleep(250)
|
||||
JsonSlurper slurper = new JsonSlurper()
|
||||
def trusted = new HashSet<>()
|
||||
persistGood.eachLine {
|
||||
trusted.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
|
||||
def json = slurper.parseText(it)
|
||||
trusted.add(new Persona(new ByteArrayInputStream(Base64.decode(json.persona))))
|
||||
}
|
||||
def distrusted = new HashSet<>()
|
||||
persistBad.eachLine {
|
||||
distrusted.add(new Persona(new ByteArrayInputStream(Base64.decode(it))))
|
||||
def json = slurper.parseText(it)
|
||||
distrusted.add(new Persona(new ByteArrayInputStream(Base64.decode(json.persona))))
|
||||
}
|
||||
|
||||
assert trusted.size() == 1
|
||||
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.6.0
|
||||
version = 0.6.2
|
||||
i2pVersion = 0.9.43
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
|
@@ -106,4 +106,9 @@ mvcGroups {
|
||||
view = 'com.muwire.gui.SharedFileView'
|
||||
controller = 'com.muwire.gui.SharedFileController'
|
||||
}
|
||||
'download-preview' {
|
||||
model = "com.muwire.gui.DownloadPreviewModel"
|
||||
view = "com.muwire.gui.DownloadPreviewView"
|
||||
controller = "com.muwire.gui.DownloadPreviewController"
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,13 @@
|
||||
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
|
||||
|
||||
@ArtifactProviderFor(GriffonController)
|
||||
class DownloadPreviewController {
|
||||
@MVCMember @Nonnull
|
||||
DownloadPreviewModel model
|
||||
}
|
@@ -205,6 +205,14 @@ class MainFrameController {
|
||||
core.eventBus.publish(new UIDownloadPausedEvent())
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void preview() {
|
||||
def downloader = model.downloads[selectedDownload()].downloader
|
||||
def params = [:]
|
||||
params['downloader'] = downloader
|
||||
mvcGroup.createMVCGroup("download-preview", params)
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void clear() {
|
||||
def toRemove = []
|
||||
|
@@ -0,0 +1,12 @@
|
||||
package com.muwire.gui
|
||||
|
||||
import com.muwire.core.download.Downloader
|
||||
|
||||
import griffon.core.artifact.GriffonModel
|
||||
import griffon.transform.Observable
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
|
||||
@ArtifactProviderFor(GriffonModel)
|
||||
class DownloadPreviewModel {
|
||||
Downloader downloader
|
||||
}
|
@@ -100,6 +100,7 @@ class MainFrameModel {
|
||||
@Observable boolean retryButtonEnabled
|
||||
@Observable boolean pauseButtonEnabled
|
||||
@Observable boolean clearButtonEnabled
|
||||
@Observable boolean previewButtonEnabled
|
||||
@Observable String resumeButtonText
|
||||
@Observable boolean addCommentButtonEnabled
|
||||
@Observable boolean subscribeButtonEnabled
|
||||
|
@@ -0,0 +1,61 @@
|
||||
package com.muwire.gui
|
||||
|
||||
import griffon.core.artifact.GriffonView
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
|
||||
import javax.swing.Box
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.JOptionPane
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
|
||||
@ArtifactProviderFor(GriffonView)
|
||||
class DownloadPreviewView {
|
||||
@MVCMember @Nonnull
|
||||
FactoryBuilderSupport builder
|
||||
@MVCMember @Nonnull
|
||||
DownloadPreviewModel model
|
||||
|
||||
def mainFrame
|
||||
def dialog
|
||||
def panel
|
||||
|
||||
void initUI() {
|
||||
mainFrame = application.windowManager.findWindow("main-frame")
|
||||
|
||||
dialog = new JDialog(mainFrame, "Generating Preview", true)
|
||||
|
||||
panel = builder.panel {
|
||||
vbox {
|
||||
label(text : "Generating preview for "+model.downloader.file.getName())
|
||||
Box.createVerticalGlue()
|
||||
progressBar(indeterminate : true)
|
||||
}
|
||||
}
|
||||
|
||||
dialog.getContentPane().add(panel)
|
||||
dialog.pack()
|
||||
dialog.setResizable(false)
|
||||
dialog.setLocationRelativeTo(mainFrame)
|
||||
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
||||
dialog.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosed(WindowEvent e) {
|
||||
mainFrame.setVisible(false)
|
||||
mvcGroup.destroy()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
if (!model.downloader.isSequential())
|
||||
JOptionPane.showMessageDialog(mainFrame, "This download is not sequential, there may not be much to preview")
|
||||
DownloadPreviewer previewer = new DownloadPreviewer(model.downloader, this)
|
||||
previewer.execute()
|
||||
dialog.show()
|
||||
}
|
||||
}
|
@@ -216,6 +216,7 @@ class MainFrameView {
|
||||
button(text: "Pause", enabled : bind {model.pauseButtonEnabled}, pauseAction)
|
||||
button(text: bind { model.resumeButtonText }, enabled : bind {model.retryButtonEnabled}, resumeAction)
|
||||
button(text: "Cancel", enabled : bind {model.cancelButtonEnabled }, cancelAction)
|
||||
button(text: "Preview", enabled : bind {model.previewButtonEnabled}, previewAction)
|
||||
button(text: "Clear Done", enabled : bind {model.clearButtonEnabled}, clearAction)
|
||||
}
|
||||
}
|
||||
@@ -536,6 +537,7 @@ class MainFrameView {
|
||||
model.cancelButtonEnabled = false
|
||||
model.retryButtonEnabled = false
|
||||
model.pauseButtonEnabled = false
|
||||
model.previewButtonEnabled = false
|
||||
model.downloader = null
|
||||
downloadDetailsPanel.getLayout().show(downloadDetailsPanel,"select-download")
|
||||
return
|
||||
@@ -544,6 +546,7 @@ class MainFrameView {
|
||||
if (downloader == null)
|
||||
return
|
||||
model.downloader = downloader
|
||||
model.previewButtonEnabled = true
|
||||
downloadDetailsPanel.getLayout().show(downloadDetailsPanel,"download-selected")
|
||||
switch(downloader.getCurrentState()) {
|
||||
case Downloader.DownloadState.CONNECTING :
|
||||
|
36
gui/src/main/groovy/com/muwire/gui/DownloadPreviewer.groovy
Normal file
36
gui/src/main/groovy/com/muwire/gui/DownloadPreviewer.groovy
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.muwire.gui
|
||||
|
||||
import java.awt.Desktop
|
||||
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.JOptionPane
|
||||
import javax.swing.SwingWorker
|
||||
|
||||
import com.muwire.core.download.Downloader
|
||||
|
||||
class DownloadPreviewer extends SwingWorker {
|
||||
|
||||
private final Downloader downloader
|
||||
private final DownloadPreviewView view
|
||||
|
||||
DownloadPreviewer(Downloader downloader, DownloadPreviewView view) {
|
||||
this.downloader = downloader
|
||||
this.view = view
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
downloader.generatePreview()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
File previewFile = get()
|
||||
view.dialog.setVisible(false)
|
||||
view.mvcGroup.destroy()
|
||||
if (previewFile == null)
|
||||
JOptionPane.showMessageDialog(null, "Generating preview file failed", "Preview Failed", JOptionPane.ERROR_MESSAGE)
|
||||
else
|
||||
Desktop.getDesktop().open(previewFile)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user