Compare commits
25 Commits
docker-0.6
...
separate-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c110bbae5 | ||
|
|
2cc1e384bc | ||
|
|
9337d1b74d | ||
|
|
16ed5dd346 | ||
|
|
7b55fc9ed8 | ||
|
|
d5c8050572 | ||
|
|
83546d68d2 | ||
| a891c83518 | |||
| aa56cc23c0 | |||
| a2b37ef567 | |||
| 4bc04ae631 | |||
| 56da9a16b0 | |||
| 2935ee1a1d | |||
| 855183397b | |||
| e27704c1af | |||
| 5c18b4a141 | |||
| dcd233b7ad | |||
| 7cee8a28ba | |||
| 7446fc949a | |||
| 598ab90f63 | |||
| 043028c296 | |||
| cd1757fac3 | |||
| 9d4b365e63 | |||
|
|
b12d57e30a | ||
|
|
f33d1b6db3 |
@@ -4,7 +4,7 @@ FROM jlesage/baseimage-gui:alpine-3.10-glibc
|
|||||||
ARG DOCKER_IMAGE_VERSION=unknown
|
ARG DOCKER_IMAGE_VERSION=unknown
|
||||||
|
|
||||||
# JDK version
|
# JDK version
|
||||||
ARG JDK=9
|
ARG JDK=11
|
||||||
|
|
||||||
# Important directories
|
# Important directories
|
||||||
ARG TMP_DIR=/muwire-tmp
|
ARG TMP_DIR=/muwire-tmp
|
||||||
|
|||||||
51
README.md
51
README.md
@@ -44,53 +44,7 @@ There is a Web-based UI under development. It is intended to be run as a plugin
|
|||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
The Docker image is based on the wonderful work in [jlesage/docker-baseimage-gui].
|
MuWire is available as a Docker image. For more information see the [Docker] page.
|
||||||
You can refer to it for environment variables to pass to the container.
|
|
||||||
|
|
||||||
If you don't want to use the image on dockerhub, build an image yourself.
|
|
||||||
```bash
|
|
||||||
MUWIRE_VERSION=`awk -F "=" '/^version/ { gsub(" ","") ; print $2}' gradle.properties`
|
|
||||||
docker build -t muwire:latest,muwire:${MUWIRE_VERSION} .
|
|
||||||
```
|
|
||||||
|
|
||||||
**Necessary configuration**
|
|
||||||
|
|
||||||
Since MuWire will be running in a container, it won't have direct access to the host's localhost.
|
|
||||||
By default, it will be configured to use `172.17.0.1` as the target host.
|
|
||||||
You'll need to open the I2CP port on that interface.
|
|
||||||
If you're running I2P on the localhost, navigate to http://localhost:7657/configi2cp and make the necessary changes.
|
|
||||||
|
|
||||||
![i2cp_config.png]
|
|
||||||
|
|
||||||
Should you be using a different interface write an `i2p.properties` and then put that into the shared docker volume.
|
|
||||||
|
|
||||||
Example configuration file:
|
|
||||||
```properties
|
|
||||||
i2cp.tcp.host=112.13.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
**Running**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run \
|
|
||||||
-p 5800:5800 \
|
|
||||||
-v config:/muwire/.MuWire \
|
|
||||||
-v incompletes:/incompletes \
|
|
||||||
-v output:/output \
|
|
||||||
--name muwire \
|
|
||||||
zlatinb/muwire
|
|
||||||
```
|
|
||||||
|
|
||||||
You will then be able to access the muwire GUI over a browser at http://localhost:5800
|
|
||||||
|
|
||||||
**Options**
|
|
||||||
|
|
||||||
|
|
||||||
| Option | Description |
|
|
||||||
|--------------|--------------------------------------------|
|
|
||||||
|`-v config:/muwire/.MuWire`| This is where the `i2p.properties` and possibly other config should go |
|
|
||||||
|`-v incompletes:/incompletes`| The `/incompletes` volume should be used to store MuWire's **incomplete** download/upload data \*|
|
|
||||||
|`-v output:/output`| The `/output` volume should be used to store MuWire's download/upload data |
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
If you want to help translate MuWire, instructions are on the wiki https://github.com/zlatinb/muwire/wiki/Translate
|
If you want to help translate MuWire, instructions are on the wiki https://github.com/zlatinb/muwire/wiki/Translate
|
||||||
@@ -111,6 +65,5 @@ You can find the full key at https://keybase.io/zlatinb
|
|||||||
[cli options]: https://github.com/zlatinb/muwire/wiki/CLI-Configuration-Options
|
[cli options]: https://github.com/zlatinb/muwire/wiki/CLI-Configuration-Options
|
||||||
[I2P Github]: https://github.com/i2p/i2p.i2p
|
[I2P Github]: https://github.com/i2p/i2p.i2p
|
||||||
[Plugin]: https://github.com/zlatinb/muwire/wiki/Plugin
|
[Plugin]: https://github.com/zlatinb/muwire/wiki/Plugin
|
||||||
[i2cp_config.png]: ./images/i2cp_config.png
|
[Docker]: https://github.com/zlatinb/muwire/wiki/Docker
|
||||||
[muwire_incompletes.png]: ./images/muwire_incompletes.png
|
|
||||||
[jlesage/docker-baseimage-gui]: https://github.com/jlesage/docker-baseimage-gui
|
[jlesage/docker-baseimage-gui]: https://github.com/jlesage/docker-baseimage-gui
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import com.muwire.core.filecert.UICreateCertificateEvent
|
|||||||
import com.muwire.core.files.DirectoryUnsharedEvent
|
import com.muwire.core.files.DirectoryUnsharedEvent
|
||||||
import com.muwire.core.files.FileSharedEvent
|
import com.muwire.core.files.FileSharedEvent
|
||||||
import com.muwire.core.files.FileUnsharedEvent
|
import com.muwire.core.files.FileUnsharedEvent
|
||||||
import com.muwire.core.files.UIPersistFilesEvent
|
|
||||||
|
|
||||||
class FilesView extends BasicWindow {
|
class FilesView extends BasicWindow {
|
||||||
private final FilesModel model
|
private final FilesModel model
|
||||||
@@ -84,7 +83,6 @@ class FilesView extends BasicWindow {
|
|||||||
|
|
||||||
Button unshareButton = new Button("Unshare", {
|
Button unshareButton = new Button("Unshare", {
|
||||||
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
|
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : sf))
|
||||||
core.eventBus.publish(new UIPersistFilesEvent())
|
|
||||||
MessageDialog.showMessageDialog(textGUI, "File Unshared", "Unshared "+sf.getFile().getName(), MessageDialogButton.OK)
|
MessageDialog.showMessageDialog(textGUI, "File Unshared", "Unshared "+sf.getFile().getName(), MessageDialogButton.OK)
|
||||||
} )
|
} )
|
||||||
Button addCommentButton = new Button("Add Comment", {
|
Button addCommentButton = new Button("Add Comment", {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.muwire.core
|
package com.muwire.core
|
||||||
|
|
||||||
|
import com.muwire.core.files.PersisterDoneEvent
|
||||||
|
import com.muwire.core.files.PersisterFolderService
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
@@ -31,7 +34,6 @@ import com.muwire.core.filecert.UIFetchCertificatesEvent
|
|||||||
import com.muwire.core.filecert.UIImportCertificateEvent
|
import com.muwire.core.filecert.UIImportCertificateEvent
|
||||||
import com.muwire.core.files.FileDownloadedEvent
|
import com.muwire.core.files.FileDownloadedEvent
|
||||||
import com.muwire.core.files.FileHashedEvent
|
import com.muwire.core.files.FileHashedEvent
|
||||||
import com.muwire.core.files.FileHashingEvent
|
|
||||||
import com.muwire.core.files.FileHasher
|
import com.muwire.core.files.FileHasher
|
||||||
import com.muwire.core.files.FileLoadedEvent
|
import com.muwire.core.files.FileLoadedEvent
|
||||||
import com.muwire.core.files.FileManager
|
import com.muwire.core.files.FileManager
|
||||||
@@ -41,7 +43,7 @@ import com.muwire.core.files.HasherService
|
|||||||
import com.muwire.core.files.PersisterService
|
import com.muwire.core.files.PersisterService
|
||||||
import com.muwire.core.files.SideCarFileEvent
|
import com.muwire.core.files.SideCarFileEvent
|
||||||
import com.muwire.core.files.UICommentEvent
|
import com.muwire.core.files.UICommentEvent
|
||||||
import com.muwire.core.files.UIPersistFilesEvent
|
|
||||||
import com.muwire.core.files.AllFilesLoadedEvent
|
import com.muwire.core.files.AllFilesLoadedEvent
|
||||||
import com.muwire.core.files.DirectoryUnsharedEvent
|
import com.muwire.core.files.DirectoryUnsharedEvent
|
||||||
import com.muwire.core.files.DirectoryWatchedEvent
|
import com.muwire.core.files.DirectoryWatchedEvent
|
||||||
@@ -74,10 +76,8 @@ import net.i2p.client.I2PClientFactory
|
|||||||
import net.i2p.client.I2PSession
|
import net.i2p.client.I2PSession
|
||||||
import net.i2p.client.streaming.I2PSocketManager
|
import net.i2p.client.streaming.I2PSocketManager
|
||||||
import net.i2p.client.streaming.I2PSocketManagerFactory
|
import net.i2p.client.streaming.I2PSocketManagerFactory
|
||||||
import net.i2p.client.streaming.I2PSocketOptions
|
|
||||||
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener
|
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener
|
||||||
import net.i2p.crypto.DSAEngine
|
import net.i2p.crypto.DSAEngine
|
||||||
import net.i2p.crypto.SigType
|
|
||||||
import net.i2p.data.Destination
|
import net.i2p.data.Destination
|
||||||
import net.i2p.data.PrivateKey
|
import net.i2p.data.PrivateKey
|
||||||
import net.i2p.data.Signature
|
import net.i2p.data.Signature
|
||||||
@@ -100,6 +100,7 @@ public class Core {
|
|||||||
final TrustService trustService
|
final TrustService trustService
|
||||||
final TrustSubscriber trustSubscriber
|
final TrustSubscriber trustSubscriber
|
||||||
private final PersisterService persisterService
|
private final PersisterService persisterService
|
||||||
|
private final PersisterFolderService persisterFolderService
|
||||||
private final HostCache hostCache
|
private final HostCache hostCache
|
||||||
private final ConnectionManager connectionManager
|
private final ConnectionManager connectionManager
|
||||||
private final CacheClient cacheClient
|
private final CacheClient cacheClient
|
||||||
@@ -259,7 +260,14 @@ public class Core {
|
|||||||
log.info "initializing persistence service"
|
log.info "initializing persistence service"
|
||||||
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 60000, fileManager)
|
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 60000, fileManager)
|
||||||
eventBus.register(UILoadedEvent.class, persisterService)
|
eventBus.register(UILoadedEvent.class, persisterService)
|
||||||
eventBus.register(UIPersistFilesEvent.class, persisterService)
|
|
||||||
|
log.info "initializing folder persistence service"
|
||||||
|
persisterFolderService = new PersisterFolderService(this, new File(home, "files"), eventBus)
|
||||||
|
eventBus.register(PersisterDoneEvent.class, persisterFolderService)
|
||||||
|
eventBus.register(FileDownloadedEvent.class, persisterFolderService)
|
||||||
|
eventBus.register(FileLoadedEvent.class, persisterFolderService)
|
||||||
|
eventBus.register(FileHashedEvent.class, persisterFolderService)
|
||||||
|
eventBus.register(FileUnsharedEvent.class, persisterFolderService)
|
||||||
|
|
||||||
log.info("initializing host cache")
|
log.info("initializing host cache")
|
||||||
File hostStorage = new File(home, "hosts.json")
|
File hostStorage = new File(home, "hosts.json")
|
||||||
@@ -321,7 +329,7 @@ public class Core {
|
|||||||
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
|
eventBus.register(UIDownloadResumedEvent.class, downloadManager)
|
||||||
|
|
||||||
log.info("initializing upload manager")
|
log.info("initializing upload manager")
|
||||||
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager, props)
|
uploadManager = new UploadManager(eventBus, fileManager, meshManager, downloadManager, persisterFolderService, props)
|
||||||
|
|
||||||
log.info("initializing connection establisher")
|
log.info("initializing connection establisher")
|
||||||
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
|
connectionEstablisher = new ConnectionEstablisher(eventBus, i2pConnector, props, connectionManager, hostCache)
|
||||||
@@ -398,6 +406,8 @@ public class Core {
|
|||||||
trustService.stop()
|
trustService.stop()
|
||||||
log.info("shutting down persister service")
|
log.info("shutting down persister service")
|
||||||
persisterService.stop()
|
persisterService.stop()
|
||||||
|
log.info("shutting down persisterFolder service")
|
||||||
|
persisterFolderService.stop()
|
||||||
log.info("shutting down download manager")
|
log.info("shutting down download manager")
|
||||||
downloadManager.shutdown()
|
downloadManager.shutdown()
|
||||||
log.info("shutting down connection acceptor")
|
log.info("shutting down connection acceptor")
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ class ConnectionAcceptor {
|
|||||||
JsonOutput jsonOutput = new JsonOutput()
|
JsonOutput jsonOutput = new JsonOutput()
|
||||||
sharedFiles.each {
|
sharedFiles.each {
|
||||||
it.hit(browser, System.currentTimeMillis(), "Browse Host");
|
it.hit(browser, System.currentTimeMillis(), "Browse Host");
|
||||||
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
|
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
|
||||||
def obj = ResultsSender.sharedFileToObj(it, false, certificates)
|
def obj = ResultsSender.sharedFileToObj(it, false, certificates)
|
||||||
def json = jsonOutput.toJson(obj)
|
def json = jsonOutput.toJson(obj)
|
||||||
dos.writeShort((short)json.length())
|
dos.writeShort((short)json.length())
|
||||||
|
|||||||
@@ -405,8 +405,9 @@ public class Downloader {
|
|||||||
}
|
}
|
||||||
eventBus.publish(
|
eventBus.publish(
|
||||||
new FileDownloadedEvent(
|
new FileDownloadedEvent(
|
||||||
downloadedFile : new DownloadedFile(file.getCanonicalFile(), getInfoHash(), pieceSizePow2, successfulDestinations),
|
downloadedFile : new DownloadedFile(file.getCanonicalFile(), getInfoHash().getRoot(), pieceSizePow2, successfulDestinations),
|
||||||
downloader : Downloader.this))
|
downloader : Downloader.this,
|
||||||
|
infoHash: getInfoHash()))
|
||||||
|
|
||||||
}
|
}
|
||||||
endpoint?.close()
|
endpoint?.close()
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class CertificateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onUICreateCertificateEvent(UICreateCertificateEvent e) {
|
void onUICreateCertificateEvent(UICreateCertificateEvent e) {
|
||||||
InfoHash infoHash = e.sharedFile.getInfoHash()
|
InfoHash infoHash = new InfoHash(e.sharedFile.getRoot())
|
||||||
String name = e.sharedFile.getFile().getName()
|
String name = e.sharedFile.getFile().getName()
|
||||||
long timestamp = System.currentTimeMillis()
|
long timestamp = System.currentTimeMillis()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package com.muwire.core.files
|
||||||
|
|
||||||
|
import com.muwire.core.DownloadedFile
|
||||||
|
import com.muwire.core.InfoHash
|
||||||
|
import com.muwire.core.Persona
|
||||||
|
import com.muwire.core.Service
|
||||||
|
import com.muwire.core.SharedFile
|
||||||
|
import com.muwire.core.util.DataUtil
|
||||||
|
import net.i2p.data.Base64
|
||||||
|
import net.i2p.data.Destination
|
||||||
|
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
|
abstract class BasePersisterService extends Service{
|
||||||
|
|
||||||
|
protected static FileLoadedEvent fromJson(def json) {
|
||||||
|
if (json.file == null || json.length == null || json.infoHash == null || json.hashList == null)
|
||||||
|
throw new IllegalArgumentException()
|
||||||
|
if (!(json.hashList instanceof List))
|
||||||
|
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
|
||||||
|
|
||||||
|
List hashList = (List) json.hashList
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream()
|
||||||
|
hashList.each {
|
||||||
|
byte [] hash = Base64.decode it.toString()
|
||||||
|
if (hash == null)
|
||||||
|
throw new IllegalArgumentException()
|
||||||
|
baos.write hash
|
||||||
|
}
|
||||||
|
byte[] hashListBytes = baos.toByteArray()
|
||||||
|
|
||||||
|
InfoHash ih = InfoHash.fromHashList(hashListBytes)
|
||||||
|
byte [] root = Base64.decode(json.infoHash.toString())
|
||||||
|
if (root == null)
|
||||||
|
throw new IllegalArgumentException()
|
||||||
|
if (!Arrays.equals(root, ih.getRoot()))
|
||||||
|
return null
|
||||||
|
|
||||||
|
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)
|
||||||
|
df.setComment(json.comment)
|
||||||
|
return new FileLoadedEvent(loadedFile : df, infoHash: ih)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SharedFile sf = new SharedFile(file, ih.getRoot(), pieceSize)
|
||||||
|
sf.setComment(json.comment)
|
||||||
|
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 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
|
||||||
|
|
||||||
|
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)
|
||||||
|
df.setComment(json.comment)
|
||||||
|
return new FileLoadedEvent(loadedFile : df, infoHash: ih)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SharedFile sf = new SharedFile(file, ih.getRoot(), pieceSize)
|
||||||
|
sf.setComment(json.comment)
|
||||||
|
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())
|
||||||
|
json.pieceSize = sf.getPieceSize()
|
||||||
|
json.comment = sf.getComment()
|
||||||
|
json.hits = sf.getHits()
|
||||||
|
json.downloaders = sf.getDownloaders()
|
||||||
|
|
||||||
|
if (!sf.searches.isEmpty()) {
|
||||||
|
Set searchers = new HashSet<>()
|
||||||
|
sf.searches.each {
|
||||||
|
def search = [:]
|
||||||
|
if (it.searcher != null)
|
||||||
|
search.searcher = it.searcher.toBase64()
|
||||||
|
search.timestamp = it.timestamp
|
||||||
|
search.query = it.query
|
||||||
|
searchers.add(search)
|
||||||
|
}
|
||||||
|
json.searchers = searchers
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sf instanceof DownloadedFile) {
|
||||||
|
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
json
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.muwire.core.files
|
|||||||
|
|
||||||
import com.muwire.core.DownloadedFile
|
import com.muwire.core.DownloadedFile
|
||||||
import com.muwire.core.Event
|
import com.muwire.core.Event
|
||||||
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
|
|
||||||
import net.i2p.data.Destination
|
import net.i2p.data.Destination
|
||||||
@@ -9,4 +10,5 @@ import net.i2p.data.Destination
|
|||||||
class FileDownloadedEvent extends Event {
|
class FileDownloadedEvent extends Event {
|
||||||
Downloader downloader
|
Downloader downloader
|
||||||
DownloadedFile downloadedFile
|
DownloadedFile downloadedFile
|
||||||
|
InfoHash infoHash
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
package com.muwire.core.files
|
package com.muwire.core.files
|
||||||
|
|
||||||
import com.muwire.core.Event
|
import com.muwire.core.Event
|
||||||
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.SharedFile
|
import com.muwire.core.SharedFile
|
||||||
|
|
||||||
class FileHashedEvent extends Event {
|
class FileHashedEvent extends Event {
|
||||||
|
|
||||||
SharedFile sharedFile
|
SharedFile sharedFile
|
||||||
|
InfoHash infoHash
|
||||||
String error
|
String error
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package com.muwire.core.files
|
package com.muwire.core.files
|
||||||
|
|
||||||
import com.muwire.core.Event
|
import com.muwire.core.Event
|
||||||
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.SharedFile
|
import com.muwire.core.SharedFile
|
||||||
|
|
||||||
class FileLoadedEvent extends Event {
|
class FileLoadedEvent extends Event {
|
||||||
|
|
||||||
SharedFile loadedFile
|
SharedFile loadedFile
|
||||||
|
InfoHash infoHash
|
||||||
|
String source
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class FileManager {
|
|||||||
|
|
||||||
private void addToIndex(SharedFile sf) {
|
private void addToIndex(SharedFile sf) {
|
||||||
log.info("Adding shared file " + sf.getFile())
|
log.info("Adding shared file " + sf.getFile())
|
||||||
InfoHash infoHash = sf.getInfoHash()
|
InfoHash infoHash = new InfoHash(sf.getRoot())
|
||||||
Set<SharedFile> existing = rootToFiles.get(infoHash)
|
Set<SharedFile> existing = rootToFiles.get(infoHash)
|
||||||
if (existing == null) {
|
if (existing == null) {
|
||||||
log.info("adding new root")
|
log.info("adding new root")
|
||||||
@@ -117,7 +117,7 @@ class FileManager {
|
|||||||
|
|
||||||
void onFileUnsharedEvent(FileUnsharedEvent e) {
|
void onFileUnsharedEvent(FileUnsharedEvent e) {
|
||||||
SharedFile sf = e.unsharedFile
|
SharedFile sf = e.unsharedFile
|
||||||
InfoHash infoHash = sf.getInfoHash()
|
InfoHash infoHash = new InfoHash(sf.getRoot())
|
||||||
Set<SharedFile> existing = rootToFiles.get(infoHash)
|
Set<SharedFile> existing = rootToFiles.get(infoHash)
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
existing.remove(sf)
|
existing.remove(sf)
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ class HasherService {
|
|||||||
} else {
|
} else {
|
||||||
eventBus.publish new FileHashingEvent(hashingFile: f)
|
eventBus.publish new FileHashingEvent(hashingFile: f)
|
||||||
def hash = hasher.hashFile f
|
def hash = hasher.hashFile f
|
||||||
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash, FileHasher.getPieceSize(f.length())))
|
eventBus.publish new FileHashedEvent(sharedFile: new SharedFile(f, hash.getRoot(), FileHasher.getPieceSize(f.length())),
|
||||||
|
infoHash : hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.muwire.core.files
|
||||||
|
|
||||||
|
import com.muwire.core.Event
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be triggered by the old PersisterService
|
||||||
|
* once it has finished reading the old file
|
||||||
|
*
|
||||||
|
* @see PersisterService
|
||||||
|
*/
|
||||||
|
class PersisterDoneEvent extends Event{
|
||||||
|
}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
package com.muwire.core.files
|
||||||
|
|
||||||
|
import com.muwire.core.*
|
||||||
|
import groovy.json.JsonOutput
|
||||||
|
import groovy.json.JsonSlurper
|
||||||
|
import groovy.util.logging.Log
|
||||||
|
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.ThreadFactory
|
||||||
|
import java.util.logging.Level
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A persister that stores information about the files shared using
|
||||||
|
* individual JSON files in directories.
|
||||||
|
*
|
||||||
|
* The absolute path's 32bit hash to the shared file is used
|
||||||
|
* to build the directory and filename.
|
||||||
|
*
|
||||||
|
* This persister only starts working once the old persister has finished loading
|
||||||
|
* @see PersisterFolderService#getJsonPath
|
||||||
|
*/
|
||||||
|
@Log
|
||||||
|
class PersisterFolderService extends BasePersisterService {
|
||||||
|
|
||||||
|
final static int CUT_LENGTH = 6
|
||||||
|
|
||||||
|
private final Core core;
|
||||||
|
final File location
|
||||||
|
final EventBus listener
|
||||||
|
final int interval
|
||||||
|
final Timer timer
|
||||||
|
final ExecutorService persisterExecutor = Executors.newSingleThreadExecutor({ r ->
|
||||||
|
new Thread(r, "file persister")
|
||||||
|
} as ThreadFactory)
|
||||||
|
|
||||||
|
PersisterFolderService(Core core, File location, EventBus listener) {
|
||||||
|
this.core = core;
|
||||||
|
this.location = location
|
||||||
|
this.listener = listener
|
||||||
|
this.interval = interval
|
||||||
|
timer = new Timer("file-folder persister timer", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
timer.cancel()
|
||||||
|
persisterExecutor.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
void onPersisterDoneEvent(PersisterDoneEvent persisterDoneEvent) {
|
||||||
|
log.info("Old persister done")
|
||||||
|
load()
|
||||||
|
}
|
||||||
|
|
||||||
|
void onFileHashedEvent(FileHashedEvent hashedEvent) {
|
||||||
|
persistFile(hashedEvent.sharedFile, hashedEvent.infoHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
void onFileDownloadedEvent(FileDownloadedEvent downloadedEvent) {
|
||||||
|
if (core.getMuOptions().getShareDownloadedFiles()) {
|
||||||
|
persistFile(downloadedEvent.downloadedFile, downloadedEvent.infoHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get rid of the json and hashlists of unshared files
|
||||||
|
* @param unsharedEvent
|
||||||
|
*/
|
||||||
|
void onFileUnsharedEvent(FileUnsharedEvent unsharedEvent) {
|
||||||
|
def jsonPath = getJsonPath(unsharedEvent.unsharedFile)
|
||||||
|
def jsonFile = jsonPath.toFile()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load() {
|
||||||
|
log.fine("Loading...")
|
||||||
|
Thread.currentThread().setPriority(Thread.MIN_PRIORITY)
|
||||||
|
|
||||||
|
if (location.exists() && location.isDirectory()) {
|
||||||
|
try {
|
||||||
|
_load()
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
log.log(Level.WARNING, "couldn't load files", e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
location.mkdirs()
|
||||||
|
listener.publish(new AllFilesLoadedEvent())
|
||||||
|
}
|
||||||
|
loaded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads every JSON into memory
|
||||||
|
*/
|
||||||
|
private void _load() {
|
||||||
|
int loaded = 0
|
||||||
|
def slurper = new JsonSlurper()
|
||||||
|
Files.walk(location.toPath())
|
||||||
|
.filter({
|
||||||
|
it.getFileName().toString().endsWith(".json")
|
||||||
|
})
|
||||||
|
.forEach({
|
||||||
|
def parsed = slurper.parse it.toFile()
|
||||||
|
def event = fromJsonLite parsed
|
||||||
|
if (event == null) return
|
||||||
|
|
||||||
|
log.fine("loaded file $event.loadedFile.file")
|
||||||
|
listener.publish event
|
||||||
|
loaded++
|
||||||
|
if (loaded % 10 == 0)
|
||||||
|
Thread.sleep(20)
|
||||||
|
|
||||||
|
})
|
||||||
|
listener.publish(new AllFilesLoadedEvent())
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistFile(SharedFile sf, InfoHash ih) {
|
||||||
|
persisterExecutor.submit({
|
||||||
|
def jsonPath = getJsonPath(sf)
|
||||||
|
|
||||||
|
def startTime = System.currentTimeMillis()
|
||||||
|
jsonPath.parent.toFile().mkdirs()
|
||||||
|
jsonPath.toFile().withPrintWriter { writer ->
|
||||||
|
def json = toJson sf
|
||||||
|
json = JsonOutput.toJson(json)
|
||||||
|
writer.println json
|
||||||
|
}
|
||||||
|
|
||||||
|
def hashListPath = getHashListPath(sf)
|
||||||
|
hashListPath.toFile().bytes = ih.hashList
|
||||||
|
log.fine("Time(ms) to write json+hashList: " + (System.currentTimeMillis() - startTime))
|
||||||
|
} as Runnable)
|
||||||
|
}
|
||||||
|
private Path getJsonPath(SharedFile sf){
|
||||||
|
def pathHash = sf.getB64PathHash()
|
||||||
|
return Paths.get(
|
||||||
|
location.getAbsolutePath(),
|
||||||
|
pathHash.substring(0, CUT_LENGTH),
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,40 +1,24 @@
|
|||||||
package com.muwire.core.files
|
package com.muwire.core.files
|
||||||
|
|
||||||
import java.nio.file.CopyOption
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.StandardCopyOption
|
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.ThreadFactory
|
import java.util.concurrent.ThreadFactory
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import java.util.stream.Collectors
|
|
||||||
|
|
||||||
import com.muwire.core.DownloadedFile
|
|
||||||
import com.muwire.core.EventBus
|
import com.muwire.core.EventBus
|
||||||
import com.muwire.core.InfoHash
|
|
||||||
import com.muwire.core.Persona
|
|
||||||
import com.muwire.core.Service
|
|
||||||
import com.muwire.core.SharedFile
|
|
||||||
import com.muwire.core.UILoadedEvent
|
import com.muwire.core.UILoadedEvent
|
||||||
import com.muwire.core.util.DataUtil
|
|
||||||
|
|
||||||
import groovy.json.JsonOutput
|
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import groovy.util.logging.Log
|
import groovy.util.logging.Log
|
||||||
import net.i2p.data.Base64
|
|
||||||
import net.i2p.data.Destination
|
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
class PersisterService extends Service {
|
class PersisterService extends BasePersisterService {
|
||||||
|
|
||||||
final File location
|
final File location
|
||||||
final EventBus listener
|
final EventBus listener
|
||||||
final int interval
|
final int interval
|
||||||
final Timer timer
|
final Timer timer
|
||||||
final FileManager fileManager
|
final FileManager fileManager
|
||||||
final ExecutorService persisterExecutor = Executors.newSingleThreadExecutor({ r ->
|
|
||||||
new Thread(r, "file persister")
|
|
||||||
} as ThreadFactory)
|
|
||||||
|
|
||||||
PersisterService(File location, EventBus listener, int interval, FileManager fileManager) {
|
PersisterService(File location, EventBus listener, int interval, FileManager fileManager) {
|
||||||
this.location = location
|
this.location = location
|
||||||
@@ -52,10 +36,6 @@ class PersisterService extends Service {
|
|||||||
timer.schedule({load()} as TimerTask, 1)
|
timer.schedule({load()} as TimerTask, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
void onUIPersistFilesEvent(UIPersistFilesEvent e) {
|
|
||||||
persistFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
Thread.currentThread().setPriority(Thread.MIN_PRIORITY)
|
Thread.currentThread().setPriority(Thread.MIN_PRIORITY)
|
||||||
|
|
||||||
@@ -69,6 +49,7 @@ class PersisterService extends Service {
|
|||||||
def event = fromJson parsed
|
def event = fromJson parsed
|
||||||
if (event != null) {
|
if (event != null) {
|
||||||
log.fine("loaded file $event.loadedFile.file")
|
log.fine("loaded file $event.loadedFile.file")
|
||||||
|
event.source = "PersisterService"
|
||||||
listener.publish event
|
listener.publish event
|
||||||
loaded++
|
loaded++
|
||||||
if (loaded % 10 == 0)
|
if (loaded % 10 == 0)
|
||||||
@@ -76,126 +57,18 @@ class PersisterService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.publish(new AllFilesLoadedEvent())
|
// Backup the old hashes
|
||||||
} catch (IllegalArgumentException|NumberFormatException e) {
|
location.renameTo(
|
||||||
|
new File(location.absolutePath + ".bak")
|
||||||
|
)
|
||||||
|
listener.publish(new PersisterDoneEvent())
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
log.log(Level.WARNING, "couldn't load files",e)
|
log.log(Level.WARNING, "couldn't load files",e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
listener.publish(new AllFilesLoadedEvent())
|
listener.publish(new PersisterDoneEvent())
|
||||||
}
|
}
|
||||||
timer.schedule({persistFiles()} as TimerTask, 1000, interval)
|
|
||||||
loaded = true
|
loaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FileLoadedEvent fromJson(def json) {
|
|
||||||
if (json.file == null || json.length == null || json.infoHash == null || json.hashList == null)
|
|
||||||
throw new IllegalArgumentException()
|
|
||||||
if (!(json.hashList instanceof List))
|
|
||||||
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
|
|
||||||
|
|
||||||
List hashList = (List) json.hashList
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()
|
|
||||||
hashList.each {
|
|
||||||
byte [] hash = Base64.decode it.toString()
|
|
||||||
if (hash == null)
|
|
||||||
throw new IllegalArgumentException()
|
|
||||||
baos.write hash
|
|
||||||
}
|
|
||||||
byte[] hashListBytes = baos.toByteArray()
|
|
||||||
|
|
||||||
InfoHash ih = InfoHash.fromHashList(hashListBytes)
|
|
||||||
byte [] root = Base64.decode(json.infoHash.toString())
|
|
||||||
if (root == null)
|
|
||||||
throw new IllegalArgumentException()
|
|
||||||
if (!Arrays.equals(root, ih.getRoot()))
|
|
||||||
return null
|
|
||||||
|
|
||||||
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, pieceSize, sourceSet)
|
|
||||||
df.setComment(json.comment)
|
|
||||||
return new FileLoadedEvent(loadedFile : df)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SharedFile sf = new SharedFile(file, ih, pieceSize)
|
|
||||||
sf.setComment(json.comment)
|
|
||||||
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)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void persistFiles() {
|
|
||||||
persisterExecutor.submit( {
|
|
||||||
def sharedFiles = fileManager.getSharedFiles()
|
|
||||||
|
|
||||||
File tmp = File.createTempFile("muwire-files", "tmp")
|
|
||||||
tmp.deleteOnExit()
|
|
||||||
tmp.withPrintWriter { writer ->
|
|
||||||
sharedFiles.each { k, v ->
|
|
||||||
def json = toJson(k,v)
|
|
||||||
json = JsonOutput.toJson(json)
|
|
||||||
writer.println json
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Files.copy(tmp.toPath(), location.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
|
||||||
tmp.delete()
|
|
||||||
} as Runnable)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def toJson(File f, SharedFile sf) {
|
|
||||||
def json = [:]
|
|
||||||
json.file = sf.getB64EncodedFileName()
|
|
||||||
json.length = sf.getCachedLength()
|
|
||||||
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()
|
|
||||||
|
|
||||||
if (!sf.searches.isEmpty()) {
|
|
||||||
Set searchers = new HashSet<>()
|
|
||||||
sf.searches.each {
|
|
||||||
def search = [:]
|
|
||||||
if (it.searcher != null)
|
|
||||||
search.searcher = it.searcher.toBase64()
|
|
||||||
search.timestamp = it.timestamp
|
|
||||||
search.query = it.query
|
|
||||||
searchers.add(search)
|
|
||||||
}
|
|
||||||
json.searchers = searchers
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sf instanceof DownloadedFile) {
|
|
||||||
json.sources = sf.sources.stream().map( {d -> d.toBase64()}).collect(Collectors.toList())
|
|
||||||
}
|
|
||||||
|
|
||||||
json
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package com.muwire.core.files
|
|
||||||
|
|
||||||
import com.muwire.core.Event
|
|
||||||
|
|
||||||
class UIPersistFilesEvent extends Event {
|
|
||||||
}
|
|
||||||
@@ -77,11 +77,11 @@ class ResultsSender {
|
|||||||
if (it.getComment() != null) {
|
if (it.getComment() != null) {
|
||||||
comment = DataUtil.readi18nString(Base64.decode(it.getComment()))
|
comment = DataUtil.readi18nString(Base64.decode(it.getComment()))
|
||||||
}
|
}
|
||||||
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
|
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
|
||||||
def uiResultEvent = new UIResultEvent( sender : me,
|
def uiResultEvent = new UIResultEvent( sender : me,
|
||||||
name : it.getFile().getName(),
|
name : it.getFile().getName(),
|
||||||
size : length,
|
size : length,
|
||||||
infohash : it.getInfoHash(),
|
infohash : new InfoHash(it.getRoot()),
|
||||||
pieceSize : pieceSize,
|
pieceSize : pieceSize,
|
||||||
uuid : uuid,
|
uuid : uuid,
|
||||||
browse : settings.browseFiles,
|
browse : settings.browseFiles,
|
||||||
@@ -119,7 +119,7 @@ class ResultsSender {
|
|||||||
me.write(os)
|
me.write(os)
|
||||||
os.writeShort((short)results.length)
|
os.writeShort((short)results.length)
|
||||||
results.each {
|
results.each {
|
||||||
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
|
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
|
||||||
def obj = sharedFileToObj(it, settings.browseFiles, certificates)
|
def obj = sharedFileToObj(it, settings.browseFiles, certificates)
|
||||||
def json = jsonOutput.toJson(obj)
|
def json = jsonOutput.toJson(obj)
|
||||||
os.writeShort((short)json.length())
|
os.writeShort((short)json.length())
|
||||||
@@ -141,7 +141,7 @@ class ResultsSender {
|
|||||||
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
|
os.write("\r\n".getBytes(StandardCharsets.US_ASCII))
|
||||||
DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(os))
|
DataOutputStream dos = new DataOutputStream(new GZIPOutputStream(os))
|
||||||
results.each {
|
results.each {
|
||||||
int certificates = certificateManager.getByInfoHash(it.getInfoHash()).size()
|
int certificates = certificateManager.getByInfoHash(new InfoHash(it.getRoot())).size()
|
||||||
def obj = sharedFileToObj(it, settings.browseFiles, certificates)
|
def obj = sharedFileToObj(it, settings.browseFiles, certificates)
|
||||||
def json = jsonOutput.toJson(obj)
|
def json = jsonOutput.toJson(obj)
|
||||||
dos.writeShort((short)json.length())
|
dos.writeShort((short)json.length())
|
||||||
@@ -170,7 +170,7 @@ class ResultsSender {
|
|||||||
obj.type = "Result"
|
obj.type = "Result"
|
||||||
obj.version = 2
|
obj.version = 2
|
||||||
obj.name = encodedName
|
obj.name = encodedName
|
||||||
obj.infohash = Base64.encode(sf.getInfoHash().getRoot())
|
obj.infohash = Base64.encode(sf.getRoot())
|
||||||
obj.size = sf.getCachedLength()
|
obj.size = sf.getCachedLength()
|
||||||
obj.pieceSize = sf.getPieceSize()
|
obj.pieceSize = sf.getPieceSize()
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class UpdateClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
||||||
if (e.downloadedFile.infoHash != updateInfoHash)
|
if (e.infoHash != updateInfoHash)
|
||||||
return
|
return
|
||||||
updateDownloading = false
|
updateDownloading = false
|
||||||
eventBus.publish(new UpdateDownloadedEvent(version : version, signer : signer, text : text))
|
eventBus.publish(new UpdateDownloadedEvent(version : version, signer : signer, text : text))
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.muwire.core.download.DownloadManager
|
|||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
import com.muwire.core.download.SourceDiscoveredEvent
|
import com.muwire.core.download.SourceDiscoveredEvent
|
||||||
import com.muwire.core.files.FileManager
|
import com.muwire.core.files.FileManager
|
||||||
|
import com.muwire.core.files.PersisterFolderService
|
||||||
import com.muwire.core.mesh.Mesh
|
import com.muwire.core.mesh.Mesh
|
||||||
import com.muwire.core.mesh.MeshManager
|
import com.muwire.core.mesh.MeshManager
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ import net.i2p.data.Base64
|
|||||||
public class UploadManager {
|
public class UploadManager {
|
||||||
private final EventBus eventBus
|
private final EventBus eventBus
|
||||||
private final FileManager fileManager
|
private final FileManager fileManager
|
||||||
|
private final PersisterFolderService persisterService
|
||||||
private final MeshManager meshManager
|
private final MeshManager meshManager
|
||||||
private final DownloadManager downloadManager
|
private final DownloadManager downloadManager
|
||||||
private final MuWireSettings props
|
private final MuWireSettings props
|
||||||
@@ -34,9 +36,11 @@ public class UploadManager {
|
|||||||
|
|
||||||
public UploadManager(EventBus eventBus, FileManager fileManager,
|
public UploadManager(EventBus eventBus, FileManager fileManager,
|
||||||
MeshManager meshManager, DownloadManager downloadManager,
|
MeshManager meshManager, DownloadManager downloadManager,
|
||||||
|
PersisterFolderService persisterService,
|
||||||
MuWireSettings props) {
|
MuWireSettings props) {
|
||||||
this.eventBus = eventBus
|
this.eventBus = eventBus
|
||||||
this.fileManager = fileManager
|
this.fileManager = fileManager
|
||||||
|
this.persisterService = persisterService
|
||||||
this.meshManager = meshManager
|
this.meshManager = meshManager
|
||||||
this.downloadManager = downloadManager
|
this.downloadManager = downloadManager
|
||||||
this.props = props
|
this.props = props
|
||||||
@@ -162,7 +166,7 @@ public class UploadManager {
|
|||||||
|
|
||||||
InfoHash fullInfoHash
|
InfoHash fullInfoHash
|
||||||
if (downloader == null) {
|
if (downloader == null) {
|
||||||
fullInfoHash = sharedFiles.iterator().next().infoHash
|
fullInfoHash = persisterService.loadInfoHash(sharedFiles.iterator().next())
|
||||||
} else {
|
} else {
|
||||||
byte [] hashList = downloader.getInfoHash().getHashList()
|
byte [] hashList = downloader.getInfoHash().getHashList()
|
||||||
if (hashList != null && hashList.length > 0)
|
if (hashList != null && hashList.length > 0)
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ public class DownloadedFile extends SharedFile {
|
|||||||
|
|
||||||
private final Set<Destination> sources;
|
private final Set<Destination> sources;
|
||||||
|
|
||||||
public DownloadedFile(File file, InfoHash infoHash, int pieceSize, Set<Destination> sources)
|
public DownloadedFile(File file, byte[] root, int pieceSize, Set<Destination> sources)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
super(file, infoHash, pieceSize);
|
super(file, root, pieceSize);
|
||||||
this.sources = sources;
|
this.sources = sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ package com.muwire.core;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -16,44 +19,47 @@ import net.i2p.data.Base64;
|
|||||||
public class SharedFile {
|
public class SharedFile {
|
||||||
|
|
||||||
private final File file;
|
private final File file;
|
||||||
private final InfoHash infoHash;
|
private final byte[] root;
|
||||||
private final int pieceSize;
|
private final int pieceSize;
|
||||||
|
|
||||||
private final String cachedPath;
|
private final String cachedPath;
|
||||||
private final long cachedLength;
|
private final long cachedLength;
|
||||||
|
|
||||||
|
private String b64PathHash;
|
||||||
private final String b64EncodedFileName;
|
private final String b64EncodedFileName;
|
||||||
private final String b64EncodedHashRoot;
|
|
||||||
private final List<String> b64EncodedHashList;
|
|
||||||
|
|
||||||
private volatile String comment;
|
private volatile String comment;
|
||||||
private final Set<String> downloaders = Collections.synchronizedSet(new HashSet<>());
|
private final Set<String> downloaders = Collections.synchronizedSet(new HashSet<>());
|
||||||
private final Set<SearchEntry> searches = Collections.synchronizedSet(new HashSet<>());
|
private final Set<SearchEntry> searches = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
public SharedFile(File file, InfoHash infoHash, int pieceSize) throws IOException {
|
public SharedFile(File file, byte[] root, int pieceSize) throws IOException {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.infoHash = infoHash;
|
this.root = root;
|
||||||
this.pieceSize = pieceSize;
|
this.pieceSize = pieceSize;
|
||||||
this.cachedPath = file.getAbsolutePath();
|
this.cachedPath = file.getAbsolutePath();
|
||||||
this.cachedLength = file.length();
|
this.cachedLength = file.length();
|
||||||
this.b64EncodedFileName = Base64.encode(DataUtil.encodei18nString(file.toString()));
|
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() {
|
public File getFile() {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InfoHash getInfoHash() {
|
public byte[] getPathHash() throws NoSuchAlgorithmException {
|
||||||
return infoHash;
|
MessageDigest digester = MessageDigest.getInstance("SHA-256");
|
||||||
|
digester.update(file.getAbsolutePath().getBytes());
|
||||||
|
return digester.digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getB64PathHash() throws NoSuchAlgorithmException {
|
||||||
|
if(b64PathHash == null){
|
||||||
|
b64PathHash = Base64.encode(getPathHash());
|
||||||
|
}
|
||||||
|
return b64PathHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getRoot() {
|
||||||
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPieceSize() {
|
public int getPieceSize() {
|
||||||
@@ -73,14 +79,6 @@ public class SharedFile {
|
|||||||
return b64EncodedFileName;
|
return b64EncodedFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getB64EncodedHashRoot() {
|
|
||||||
return b64EncodedHashRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getB64EncodedHashList() {
|
|
||||||
return b64EncodedHashList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCachedPath() {
|
public String getCachedPath() {
|
||||||
return cachedPath;
|
return cachedPath;
|
||||||
}
|
}
|
||||||
@@ -119,7 +117,7 @@ public class SharedFile {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return file.hashCode() ^ infoHash.hashCode();
|
return file.hashCode() ^ Arrays.hashCode(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -127,7 +125,7 @@ public class SharedFile {
|
|||||||
if (!(o instanceof SharedFile))
|
if (!(o instanceof SharedFile))
|
||||||
return false;
|
return false;
|
||||||
SharedFile other = (SharedFile)o;
|
SharedFile other = (SharedFile)o;
|
||||||
return file.equals(other.file) && infoHash.equals(other.infoHash);
|
return file.equals(other.file) && Arrays.equals(root, other.root);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SearchEntry {
|
public static class SearchEntry {
|
||||||
|
|||||||
@@ -3,15 +3,11 @@ package com.muwire.gui
|
|||||||
import griffon.core.GriffonApplication
|
import griffon.core.GriffonApplication
|
||||||
import griffon.core.artifact.GriffonController
|
import griffon.core.artifact.GriffonController
|
||||||
import griffon.core.controller.ControllerAction
|
import griffon.core.controller.ControllerAction
|
||||||
import griffon.core.mvc.MVCGroup
|
|
||||||
import griffon.core.mvc.MVCGroupConfiguration
|
|
||||||
import griffon.inject.MVCMember
|
import griffon.inject.MVCMember
|
||||||
import griffon.metadata.ArtifactProviderFor
|
import griffon.metadata.ArtifactProviderFor
|
||||||
import groovy.json.StringEscapeUtils
|
|
||||||
import net.i2p.crypto.DSAEngine
|
import net.i2p.crypto.DSAEngine
|
||||||
import net.i2p.data.Base64
|
import net.i2p.data.Base64
|
||||||
import net.i2p.data.Signature
|
import net.i2p.data.Signature
|
||||||
import net.i2p.data.SigningPrivateKey
|
|
||||||
|
|
||||||
import java.awt.Desktop
|
import java.awt.Desktop
|
||||||
import java.awt.Toolkit
|
import java.awt.Toolkit
|
||||||
@@ -30,15 +26,11 @@ import com.muwire.core.Persona
|
|||||||
import com.muwire.core.SharedFile
|
import com.muwire.core.SharedFile
|
||||||
import com.muwire.core.SplitPattern
|
import com.muwire.core.SplitPattern
|
||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
import com.muwire.core.download.DownloadStartedEvent
|
|
||||||
import com.muwire.core.download.UIDownloadCancelledEvent
|
import com.muwire.core.download.UIDownloadCancelledEvent
|
||||||
import com.muwire.core.download.UIDownloadEvent
|
|
||||||
import com.muwire.core.download.UIDownloadPausedEvent
|
import com.muwire.core.download.UIDownloadPausedEvent
|
||||||
import com.muwire.core.download.UIDownloadResumedEvent
|
import com.muwire.core.download.UIDownloadResumedEvent
|
||||||
import com.muwire.core.filecert.UICreateCertificateEvent
|
import com.muwire.core.filecert.UICreateCertificateEvent
|
||||||
import com.muwire.core.files.DirectoryUnsharedEvent
|
|
||||||
import com.muwire.core.files.FileUnsharedEvent
|
import com.muwire.core.files.FileUnsharedEvent
|
||||||
import com.muwire.core.files.UIPersistFilesEvent
|
|
||||||
import com.muwire.core.search.QueryEvent
|
import com.muwire.core.search.QueryEvent
|
||||||
import com.muwire.core.search.SearchEvent
|
import com.muwire.core.search.SearchEvent
|
||||||
import com.muwire.core.trust.RemoteTrustList
|
import com.muwire.core.trust.RemoteTrustList
|
||||||
@@ -371,7 +363,6 @@ class MainFrameController {
|
|||||||
sf.each {
|
sf.each {
|
||||||
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : it))
|
core.eventBus.publish(new FileUnsharedEvent(unsharedFile : it))
|
||||||
}
|
}
|
||||||
core.eventBus.publish(new UIPersistFilesEvent())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ControllerAction
|
@ControllerAction
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.muwire.gui
|
package com.muwire.gui
|
||||||
|
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.SharedFile
|
import com.muwire.core.SharedFile
|
||||||
|
|
||||||
import griffon.core.artifact.GriffonModel
|
import griffon.core.artifact.GriffonModel
|
||||||
@@ -21,6 +22,6 @@ class SharedFileModel {
|
|||||||
public void mvcGroupInit(Map<String,String> args) {
|
public void mvcGroupInit(Map<String,String> args) {
|
||||||
searchers.addAll(sf.getSearches())
|
searchers.addAll(sf.getSearches())
|
||||||
downloaders.addAll(sf.getDownloaders())
|
downloaders.addAll(sf.getDownloaders())
|
||||||
certificates.addAll(core.certificateManager.byInfoHash.getOrDefault(sf.infoHash,[]))
|
certificates.addAll(core.certificateManager.byInfoHash.getOrDefault(new InfoHash(sf.getRoot()),[]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,6 +35,7 @@ import javax.swing.tree.TreePath
|
|||||||
|
|
||||||
import com.muwire.core.Constants
|
import com.muwire.core.Constants
|
||||||
import com.muwire.core.Core
|
import com.muwire.core.Core
|
||||||
|
import com.muwire.core.InfoHash
|
||||||
import com.muwire.core.MuWireSettings
|
import com.muwire.core.MuWireSettings
|
||||||
import com.muwire.core.SharedFile
|
import com.muwire.core.SharedFile
|
||||||
import com.muwire.core.download.Downloader
|
import com.muwire.core.download.Downloader
|
||||||
@@ -289,7 +290,7 @@ class MainFrameView {
|
|||||||
closureColumn(header : "Comments", preferredWidth : 50, type : Boolean, read : {it.getComment() != null})
|
closureColumn(header : "Comments", preferredWidth : 50, type : Boolean, read : {it.getComment() != null})
|
||||||
closureColumn(header : "Certified", preferredWidth : 50, type : Boolean, read : {
|
closureColumn(header : "Certified", preferredWidth : 50, type : Boolean, read : {
|
||||||
Core core = application.context.get("core")
|
Core core = application.context.get("core")
|
||||||
core.certificateManager.hasLocalCertificate(it.getInfoHash())
|
core.certificateManager.hasLocalCertificate(new InfoHash(it.getRoot()))
|
||||||
})
|
})
|
||||||
closureColumn(header : "Search Hits", preferredWidth: 50, type : Integer, read : {it.getHits()})
|
closureColumn(header : "Search Hits", preferredWidth: 50, type : Integer, read : {it.getHits()})
|
||||||
closureColumn(header : "Downloaders", preferredWidth: 50, type : Integer, read : {it.getDownloaders().size()})
|
closureColumn(header : "Downloaders", preferredWidth: 50, type : Integer, read : {it.getDownloaders().size()})
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import com.muwire.core.files.FileTree;
|
|||||||
import com.muwire.core.files.FileTreeCallback;
|
import com.muwire.core.files.FileTreeCallback;
|
||||||
import com.muwire.core.files.FileUnsharedEvent;
|
import com.muwire.core.files.FileUnsharedEvent;
|
||||||
import com.muwire.core.files.UICommentEvent;
|
import com.muwire.core.files.UICommentEvent;
|
||||||
import com.muwire.core.files.UIPersistFilesEvent;
|
|
||||||
import com.muwire.core.util.DataUtil;
|
import com.muwire.core.util.DataUtil;
|
||||||
|
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
@@ -132,7 +131,6 @@ public class FileManager {
|
|||||||
core.getEventBus().publish(event);
|
core.getEventBus().publish(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.getEventBus().publish(new UIPersistFilesEvent());
|
|
||||||
Util.pause();
|
Util.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,12 +89,13 @@ public class FilesServlet extends HttpServlet {
|
|||||||
String comment = null;
|
String comment = null;
|
||||||
if (sf.getComment() != null)
|
if (sf.getComment() != null)
|
||||||
comment = DataUtil.readi18nString(Base64.decode(sf.getComment()));
|
comment = DataUtil.readi18nString(Base64.decode(sf.getComment()));
|
||||||
|
InfoHash ih = new InfoHash(sf.getRoot());
|
||||||
FilesTableEntry entry = new FilesTableEntry(sf.getFile().getName(),
|
FilesTableEntry entry = new FilesTableEntry(sf.getFile().getName(),
|
||||||
sf.getInfoHash(),
|
ih,
|
||||||
sf.getCachedPath(),
|
sf.getCachedPath(),
|
||||||
sf.getCachedLength(),
|
sf.getCachedLength(),
|
||||||
comment,
|
comment,
|
||||||
core.getCertificateManager().hasLocalCertificate(sf.getInfoHash()));
|
core.getCertificateManager().hasLocalCertificate(ih));
|
||||||
entries.add(entry);
|
entries.add(entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -260,12 +261,13 @@ public class FilesServlet extends HttpServlet {
|
|||||||
sb.append("<Name>").append(Util.escapeHTMLinXML(sf.getFile().getName())).append("</Name>");
|
sb.append("<Name>").append(Util.escapeHTMLinXML(sf.getFile().getName())).append("</Name>");
|
||||||
sb.append("<Path>").append(Util.escapeHTMLinXML(sf.getCachedPath())).append("</Path>");
|
sb.append("<Path>").append(Util.escapeHTMLinXML(sf.getCachedPath())).append("</Path>");
|
||||||
sb.append("<Size>").append(DataHelper.formatSize2Decimal(sf.getCachedLength())).append("B").append("</Size>");
|
sb.append("<Size>").append(DataHelper.formatSize2Decimal(sf.getCachedLength())).append("B").append("</Size>");
|
||||||
sb.append("<InfoHash>").append(Base64.encode(sf.getInfoHash().getRoot())).append("</InfoHash>");
|
sb.append("<InfoHash>").append(Base64.encode(sf.getRoot())).append("</InfoHash>");
|
||||||
if (sf.getComment() != null) {
|
if (sf.getComment() != null) {
|
||||||
String comment = DataUtil.readi18nString(Base64.decode(sf.getComment()));
|
String comment = DataUtil.readi18nString(Base64.decode(sf.getComment()));
|
||||||
sb.append("<Comment>").append(Util.escapeHTMLinXML(comment)).append("</Comment>");
|
sb.append("<Comment>").append(Util.escapeHTMLinXML(comment)).append("</Comment>");
|
||||||
}
|
}
|
||||||
sb.append("<Certified>").append(core.getCertificateManager().hasLocalCertificate(sf.getInfoHash())).append("</Certified>");
|
InfoHash ih = new InfoHash(sf.getRoot());
|
||||||
|
sb.append("<Certified>").append(core.getCertificateManager().hasLocalCertificate(ih)).append("</Certified>");
|
||||||
// TODO: other stuff
|
// TODO: other stuff
|
||||||
sb.append("</File>");
|
sb.append("</File>");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user