Compare commits
24 Commits
muwire-0.0
...
muwire-0.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ed3943c1af | ||
![]() |
e195141a27 | ||
![]() |
bb02fdbee9 | ||
![]() |
6e3a2c0d08 | ||
![]() |
bd5fecc19d | ||
![]() |
d5db49fa79 | ||
![]() |
f2ea8619bb | ||
![]() |
b129e79196 | ||
![]() |
404d5b60bc | ||
![]() |
de2753ac50 | ||
![]() |
2d53999c8e | ||
![]() |
5aecf72d6f | ||
![]() |
a574a67ec6 | ||
![]() |
6b5ad969b7 | ||
![]() |
617209c4e4 | ||
![]() |
16b475bd9a | ||
![]() |
3cea1870cd | ||
![]() |
e7240dcb6f | ||
![]() |
c91440cbfc | ||
![]() |
294605f5c7 | ||
![]() |
986caf3a75 | ||
![]() |
8524d5309f | ||
![]() |
48b3ac2b4a | ||
![]() |
18f21dc247 |
7
cli/build.gradle
Normal file
7
cli/build.gradle
Normal file
@@ -0,0 +1,7 @@
|
||||
apply plugin : 'application'
|
||||
|
||||
mainClassName = 'com.muwire.cli.Cli'
|
||||
|
||||
dependencies {
|
||||
compile project(":core")
|
||||
}
|
42
cli/src/main/groovy/com/muwire/cli/Cli.groovy
Normal file
42
cli/src/main/groovy/com/muwire/cli/Cli.groovy
Normal file
@@ -0,0 +1,42 @@
|
||||
package com.muwire.cli
|
||||
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.MuWireSettings
|
||||
|
||||
class Cli {
|
||||
|
||||
public static void main(String[] args) {
|
||||
def home = System.getProperty("user.home") + File.separator + ".MuWire"
|
||||
home = new File(home)
|
||||
if (!home.exists())
|
||||
home.mkdirs()
|
||||
|
||||
def propsFile = new File(home,"MuWire.properties")
|
||||
if (!propsFile.exists()) {
|
||||
println "create props file ${propsFile.getAbsoluteFile()} before launching MuWire"
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
def props = new Properties()
|
||||
propsFile.withInputStream { props.load(it) }
|
||||
props = new MuWireSettings(props)
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.0.8")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
core.startServices()
|
||||
|
||||
// now we begin
|
||||
println "MuWire is ready"
|
||||
println "Enter a file containing list of files to share"
|
||||
def reader = new BufferedReader(new InputStreamReader(System.in))
|
||||
def filesList = reader.readLine()
|
||||
Thread.sleep(Integer.MAX_VALUE)
|
||||
}
|
||||
}
|
@@ -32,6 +32,7 @@ import com.muwire.core.search.SearchEvent
|
||||
import com.muwire.core.search.SearchManager
|
||||
import com.muwire.core.trust.TrustEvent
|
||||
import com.muwire.core.trust.TrustService
|
||||
import com.muwire.core.update.UpdateClient
|
||||
import com.muwire.core.upload.UploadManager
|
||||
import com.muwire.core.util.MuWireLogManager
|
||||
|
||||
@@ -61,11 +62,12 @@ public class Core {
|
||||
private final HostCache hostCache
|
||||
private final ConnectionManager connectionManager
|
||||
private final CacheClient cacheClient
|
||||
private final UpdateClient updateClient
|
||||
private final ConnectionAcceptor connectionAcceptor
|
||||
private final ConnectionEstablisher connectionEstablisher
|
||||
private final HasherService hasherService
|
||||
|
||||
public Core(MuWireSettings props, File home) {
|
||||
public Core(MuWireSettings props, File home, String myVersion) {
|
||||
this.home = home
|
||||
log.info "Initializing I2P context"
|
||||
I2PAppContext.getGlobalContext().logManager()
|
||||
@@ -154,6 +156,9 @@ public class Core {
|
||||
log.info("initializing cache client")
|
||||
cacheClient = new CacheClient(eventBus,hostCache, connectionManager, i2pSession, props, 10000)
|
||||
|
||||
log.info("initializing update client")
|
||||
updateClient = new UpdateClient(eventBus, i2pSession, myVersion, props)
|
||||
|
||||
log.info("initializing connector")
|
||||
I2PConnector i2pConnector = new I2PConnector(socketManager)
|
||||
|
||||
@@ -197,6 +202,7 @@ public class Core {
|
||||
connectionAcceptor.start()
|
||||
connectionEstablisher.start()
|
||||
hostCache.waitForLoad()
|
||||
updateClient.start()
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
@@ -227,7 +233,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home)
|
||||
Core core = new Core(props, home, "0.0.8")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -7,6 +7,7 @@ class MuWireSettings {
|
||||
final boolean isLeaf
|
||||
boolean allowUntrusted
|
||||
int downloadRetryInterval
|
||||
int updateCheckInterval
|
||||
String nickname
|
||||
File downloadLocation
|
||||
String sharedFiles
|
||||
@@ -25,6 +26,7 @@ class MuWireSettings {
|
||||
System.getProperty("user.home")))
|
||||
sharedFiles = props.getProperty("sharedFiles")
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","15"))
|
||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","36"))
|
||||
}
|
||||
|
||||
void write(OutputStream out) throws IOException {
|
||||
@@ -35,6 +37,7 @@ class MuWireSettings {
|
||||
props.setProperty("nickname", nickname)
|
||||
props.setProperty("downloadLocation", downloadLocation.getAbsolutePath())
|
||||
props.setProperty("downloadRetryInterval", String.valueOf(downloadRetryInterval))
|
||||
props.setProperty("updateCheckInterval", String.valueOf(updateCheckInterval))
|
||||
if (sharedFiles != null)
|
||||
props.setProperty("sharedFiles", sharedFiles)
|
||||
props.store(out, "")
|
||||
|
@@ -3,6 +3,7 @@ package com.muwire.core.download
|
||||
import com.muwire.core.connection.I2PConnector
|
||||
|
||||
import net.i2p.data.Base64
|
||||
import net.i2p.data.Destination
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.Persona
|
||||
@@ -16,16 +17,13 @@ public class DownloadManager {
|
||||
private final I2PConnector connector
|
||||
private final Executor executor
|
||||
private final File incompletes
|
||||
private final String meB64
|
||||
private final Persona me
|
||||
|
||||
public DownloadManager(EventBus eventBus, I2PConnector connector, File incompletes, Persona me) {
|
||||
this.eventBus = eventBus
|
||||
this.connector = connector
|
||||
this.incompletes = incompletes
|
||||
|
||||
def baos = new ByteArrayOutputStream()
|
||||
me.write(baos)
|
||||
this.meB64 = Base64.encode(baos.toByteArray())
|
||||
this.me = me
|
||||
|
||||
incompletes.mkdir()
|
||||
|
||||
@@ -39,8 +37,18 @@ public class DownloadManager {
|
||||
|
||||
|
||||
public void onUIDownloadEvent(UIDownloadEvent e) {
|
||||
def downloader = new Downloader(this, meB64, e.target, e.result.size,
|
||||
e.result.infohash, e.result.pieceSize, connector, e.result.sender.destination,
|
||||
|
||||
def size = e.result[0].size
|
||||
def infohash = e.result[0].infohash
|
||||
def pieceSize = e.result[0].pieceSize
|
||||
|
||||
Set<Destination> destinations = new HashSet<>()
|
||||
e.result.each {
|
||||
destinations.add(it.sender.destination)
|
||||
}
|
||||
|
||||
def downloader = new Downloader(this, me, e.target, size,
|
||||
infohash, pieceSize, connector, destinations,
|
||||
incompletes)
|
||||
executor.execute({downloader.download()} as Runnable)
|
||||
eventBus.publish(new DownloadStartedEvent(downloader : downloader))
|
||||
|
@@ -23,7 +23,7 @@ class DownloadSession {
|
||||
private static int SAMPLES = 10
|
||||
|
||||
private final String meB64
|
||||
private final Pieces pieces
|
||||
private final Pieces downloaded, claimed
|
||||
private final InfoHash infoHash
|
||||
private final Endpoint endpoint
|
||||
private final File file
|
||||
@@ -36,10 +36,11 @@ class DownloadSession {
|
||||
|
||||
private ByteBuffer mapped
|
||||
|
||||
DownloadSession(String meB64, Pieces pieces, InfoHash infoHash, Endpoint endpoint, File file,
|
||||
DownloadSession(String meB64, Pieces downloaded, Pieces claimed, InfoHash infoHash, Endpoint endpoint, File file,
|
||||
int pieceSize, long fileLength) {
|
||||
this.meB64 = meB64
|
||||
this.pieces = pieces
|
||||
this.downloaded = downloaded
|
||||
this.claimed = claimed
|
||||
this.endpoint = endpoint
|
||||
this.infoHash = infoHash
|
||||
this.file = file
|
||||
@@ -53,11 +54,31 @@ class DownloadSession {
|
||||
}
|
||||
}
|
||||
|
||||
public void request() throws IOException {
|
||||
/**
|
||||
* @return if the request will proceed. The only time it may not
|
||||
* is if all the pieces have been claimed by other sessions.
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean request() throws IOException {
|
||||
OutputStream os = endpoint.getOutputStream()
|
||||
InputStream is = endpoint.getInputStream()
|
||||
|
||||
int piece = pieces.getRandomPiece()
|
||||
int piece
|
||||
while(true) {
|
||||
piece = downloaded.getRandomPiece()
|
||||
if (claimed.isMarked(piece)) {
|
||||
if (downloaded.donePieces() + claimed.donePieces() == downloaded.nPieces) {
|
||||
log.info("all pieces claimed")
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
claimed.markDownloaded(piece)
|
||||
|
||||
log.info("will download piece $piece")
|
||||
|
||||
long start = piece * pieceSize
|
||||
long end = Math.min(fileLength, start + pieceSize) - 1
|
||||
long length = end - start + 1
|
||||
@@ -145,10 +166,12 @@ class DownloadSession {
|
||||
if (hash != expected)
|
||||
throw new BadHashException()
|
||||
|
||||
pieces.markDownloaded(piece)
|
||||
downloaded.markDownloaded(piece)
|
||||
} finally {
|
||||
claimed.clear(piece)
|
||||
try { channel?.close() } catch (IOException ignore) {}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
synchronized int positionInPiece() {
|
||||
|
@@ -1,8 +1,12 @@
|
||||
package com.muwire.core.download
|
||||
|
||||
import com.muwire.core.InfoHash
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.connection.Endpoint
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.logging.Level
|
||||
|
||||
import com.muwire.core.Constants
|
||||
@@ -14,35 +18,41 @@ import net.i2p.data.Destination
|
||||
@Log
|
||||
public class Downloader {
|
||||
public enum DownloadState { CONNECTING, DOWNLOADING, FAILED, CANCELLED, FINISHED }
|
||||
private enum WorkerState { CONNECTING, DOWNLOADING, FINISHED}
|
||||
|
||||
private static final ExecutorService executorService = Executors.newCachedThreadPool({r ->
|
||||
Thread rv = new Thread(r)
|
||||
rv.setName("download worker")
|
||||
rv.setDaemon(true)
|
||||
rv
|
||||
})
|
||||
|
||||
private final DownloadManager downloadManager
|
||||
private final String meB64
|
||||
private final Persona me
|
||||
private final File file
|
||||
private final Pieces pieces
|
||||
private final Pieces downloaded, claimed
|
||||
private final long length
|
||||
private final InfoHash infoHash
|
||||
private final int pieceSize
|
||||
private final I2PConnector connector
|
||||
private final Destination destination
|
||||
private final Set<Destination> destinations
|
||||
private final int nPieces
|
||||
private final File piecesFile
|
||||
private final Map<Destination, DownloadWorker> activeWorkers = new ConcurrentHashMap<>()
|
||||
|
||||
|
||||
private Endpoint endpoint
|
||||
private volatile DownloadSession currentSession
|
||||
private volatile DownloadState currentState
|
||||
private volatile boolean cancelled
|
||||
private volatile Thread downloadThread
|
||||
|
||||
public Downloader(DownloadManager downloadManager, String meB64, File file, long length, InfoHash infoHash,
|
||||
int pieceSizePow2, I2PConnector connector, Destination destination,
|
||||
|
||||
public Downloader(DownloadManager downloadManager, Persona me, File file, long length, InfoHash infoHash,
|
||||
int pieceSizePow2, I2PConnector connector, Set<Destination> destinations,
|
||||
File incompletes) {
|
||||
this.meB64 = meB64
|
||||
this.me = me
|
||||
this.downloadManager = downloadManager
|
||||
this.file = file
|
||||
this.infoHash = infoHash
|
||||
this.length = length
|
||||
this.connector = connector
|
||||
this.destination = destination
|
||||
this.destinations = destinations
|
||||
this.piecesFile = new File(incompletes, file.getName()+".pieces")
|
||||
this.pieceSize = 1 << pieceSizePow2
|
||||
|
||||
@@ -53,32 +63,18 @@ public class Downloader {
|
||||
nPieces = length / pieceSize + 1
|
||||
this.nPieces = nPieces
|
||||
|
||||
pieces = new Pieces(nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO)
|
||||
currentState = DownloadState.CONNECTING
|
||||
downloaded = new Pieces(nPieces, Constants.DOWNLOAD_SEQUENTIAL_RATIO)
|
||||
claimed = new Pieces(nPieces)
|
||||
}
|
||||
|
||||
void download() {
|
||||
readPieces()
|
||||
downloadThread = Thread.currentThread()
|
||||
Endpoint endpoint = null
|
||||
try {
|
||||
endpoint = connector.connect(destination)
|
||||
currentState = DownloadState.DOWNLOADING
|
||||
while(!pieces.isComplete()) {
|
||||
currentSession = new DownloadSession(meB64, pieces, infoHash, endpoint, file, pieceSize, length)
|
||||
currentSession.request()
|
||||
writePieces()
|
||||
destinations.each {
|
||||
if (it != me.destination) {
|
||||
def worker = new DownloadWorker(it)
|
||||
activeWorkers.put(it, worker)
|
||||
executorService.submit(worker)
|
||||
}
|
||||
currentState = DownloadState.FINISHED
|
||||
piecesFile.delete()
|
||||
} catch (Exception bad) {
|
||||
log.log(Level.WARNING,"Exception while downloading",bad)
|
||||
if (cancelled)
|
||||
currentState = DownloadState.CANCELLED
|
||||
else if (currentState != DownloadState.FINISHED)
|
||||
currentState = DownloadState.FAILED
|
||||
} finally {
|
||||
endpoint?.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,45 +83,123 @@ public class Downloader {
|
||||
return
|
||||
piecesFile.withReader {
|
||||
int piece = Integer.parseInt(it.readLine())
|
||||
pieces.markDownloaded(piece)
|
||||
downloaded.markDownloaded(piece)
|
||||
}
|
||||
}
|
||||
|
||||
void writePieces() {
|
||||
piecesFile.withPrintWriter { writer ->
|
||||
pieces.getDownloaded().each { piece ->
|
||||
downloaded.getDownloaded().each { piece ->
|
||||
writer.println(piece)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long donePieces() {
|
||||
pieces.donePieces()
|
||||
downloaded.donePieces()
|
||||
}
|
||||
|
||||
public int positionInPiece() {
|
||||
if (currentSession == null)
|
||||
return 0
|
||||
currentSession.positionInPiece()
|
||||
}
|
||||
|
||||
public int speed() {
|
||||
if (currentSession == null)
|
||||
return 0
|
||||
currentSession.speed()
|
||||
int total = 0
|
||||
if (getCurrentState() == DownloadState.DOWNLOADING) {
|
||||
activeWorkers.values().each {
|
||||
total += it.speed()
|
||||
}
|
||||
}
|
||||
total
|
||||
}
|
||||
|
||||
public DownloadState getCurrentState() {
|
||||
currentState
|
||||
if (cancelled)
|
||||
return DownloadState.CANCELLED
|
||||
boolean allFinished = true
|
||||
activeWorkers.values().each {
|
||||
allFinished &= it.currentState == WorkerState.FINISHED
|
||||
}
|
||||
if (allFinished) {
|
||||
if (downloaded.isComplete())
|
||||
return DownloadState.FINISHED
|
||||
return DownloadState.FAILED
|
||||
}
|
||||
|
||||
// if at least one is downloading...
|
||||
boolean oneDownloading = false
|
||||
activeWorkers.values().each {
|
||||
if (it.currentState == WorkerState.DOWNLOADING) {
|
||||
oneDownloading = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (oneDownloading)
|
||||
return DownloadState.DOWNLOADING
|
||||
|
||||
return DownloadState.CONNECTING
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
cancelled = true
|
||||
downloadThread?.interrupt()
|
||||
activeWorkers.values().each {
|
||||
it.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
public int activeWorkers() {
|
||||
int active = 0
|
||||
activeWorkers.values().each {
|
||||
if (it.currentState != WorkerState.FINISHED)
|
||||
active++
|
||||
}
|
||||
active
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
currentState = DownloadState.CONNECTING
|
||||
downloadManager.resume(this)
|
||||
}
|
||||
|
||||
class DownloadWorker implements Runnable {
|
||||
private final Destination destination
|
||||
private volatile WorkerState currentState
|
||||
private volatile Thread downloadThread
|
||||
private Endpoint endpoint
|
||||
private volatile DownloadSession currentSession
|
||||
|
||||
DownloadWorker(Destination destination) {
|
||||
this.destination = destination
|
||||
}
|
||||
|
||||
public void run() {
|
||||
downloadThread = Thread.currentThread()
|
||||
currentState = WorkerState.CONNECTING
|
||||
Endpoint endpoint = null
|
||||
try {
|
||||
endpoint = connector.connect(destination)
|
||||
currentState = WorkerState.DOWNLOADING
|
||||
boolean requestPerformed
|
||||
while(!downloaded.isComplete()) {
|
||||
currentSession = new DownloadSession(me.toBase64(), downloaded, claimed, infoHash, endpoint, file, pieceSize, length)
|
||||
requestPerformed = currentSession.request()
|
||||
if (!requestPerformed)
|
||||
break
|
||||
writePieces()
|
||||
}
|
||||
} catch (Exception bad) {
|
||||
log.log(Level.WARNING,"Exception while downloading",bad)
|
||||
} finally {
|
||||
currentState = WorkerState.FINISHED
|
||||
endpoint?.close()
|
||||
}
|
||||
}
|
||||
|
||||
int speed() {
|
||||
if (currentSession == null)
|
||||
return 0
|
||||
currentSession.speed()
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
downloadThread?.interrupt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,8 @@ class Pieces {
|
||||
|
||||
while(true) {
|
||||
int start = random.nextInt(nPieces)
|
||||
while(bitSet.get(start) && ++start < nPieces);
|
||||
if (bitSet.get(start))
|
||||
continue
|
||||
return start
|
||||
}
|
||||
}
|
||||
@@ -45,10 +46,18 @@ class Pieces {
|
||||
bitSet.set(piece)
|
||||
}
|
||||
|
||||
synchronized void clear(int piece) {
|
||||
bitSet.clear(piece)
|
||||
}
|
||||
|
||||
synchronized boolean isComplete() {
|
||||
bitSet.cardinality() == nPieces
|
||||
}
|
||||
|
||||
synchronized boolean isMarked(int piece) {
|
||||
bitSet.get(piece)
|
||||
}
|
||||
|
||||
synchronized int donePieces() {
|
||||
bitSet.cardinality()
|
||||
}
|
||||
|
@@ -5,6 +5,6 @@ import com.muwire.core.search.UIResultEvent
|
||||
|
||||
class UIDownloadEvent extends Event {
|
||||
|
||||
UIResultEvent result
|
||||
UIResultEvent[] result
|
||||
File target
|
||||
}
|
||||
|
@@ -65,7 +65,7 @@ class CacheClient {
|
||||
options.setSendLeaseSet(true)
|
||||
CacheServers.getCacheServers().each {
|
||||
log.info "Querying hostcache ${it.toBase32()}"
|
||||
session.sendMessage(it, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 0, 0, options)
|
||||
session.sendMessage(it, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 1, 0, options)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package com.muwire.core.update
|
||||
|
||||
import com.muwire.core.Event
|
||||
|
||||
class UpdateAvailableEvent extends Event {
|
||||
String version
|
||||
String signer
|
||||
}
|
132
core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy
Normal file
132
core/src/main/groovy/com/muwire/core/update/UpdateClient.groovy
Normal file
@@ -0,0 +1,132 @@
|
||||
package com.muwire.core.update
|
||||
|
||||
import java.util.logging.Level
|
||||
|
||||
import com.muwire.core.EventBus
|
||||
import com.muwire.core.MuWireSettings
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.client.I2PSession
|
||||
import net.i2p.client.I2PSessionMuxedListener
|
||||
import net.i2p.client.SendMessageOptions
|
||||
import net.i2p.client.datagram.I2PDatagramDissector
|
||||
import net.i2p.client.datagram.I2PDatagramMaker
|
||||
import net.i2p.util.VersionComparator
|
||||
|
||||
@Log
|
||||
class UpdateClient {
|
||||
final EventBus eventBus
|
||||
final I2PSession session
|
||||
final String myVersion
|
||||
final MuWireSettings settings
|
||||
|
||||
private final Timer timer
|
||||
|
||||
private long lastUpdateCheckTime
|
||||
|
||||
UpdateClient(EventBus eventBus, I2PSession session, String myVersion, MuWireSettings settings) {
|
||||
this.eventBus = eventBus
|
||||
this.session = session
|
||||
this.myVersion = myVersion
|
||||
this.settings = settings
|
||||
timer = new Timer("update-client",true)
|
||||
}
|
||||
|
||||
void start() {
|
||||
session.addMuxedSessionListener(new Listener(), I2PSession.PROTO_DATAGRAM, 2)
|
||||
timer.schedule({checkUpdate()} as TimerTask, 30000, 60 * 60 * 1000)
|
||||
}
|
||||
|
||||
void stop() {
|
||||
timer.cancel()
|
||||
}
|
||||
|
||||
private void checkUpdate() {
|
||||
final long now = System.currentTimeMillis()
|
||||
if (lastUpdateCheckTime > 0) {
|
||||
if (now - lastUpdateCheckTime < settings.updateCheckInterval * 60 * 60 * 1000)
|
||||
return
|
||||
}
|
||||
lastUpdateCheckTime = now
|
||||
|
||||
log.info("checking for update")
|
||||
|
||||
def ping = [version : 1, myVersion : myVersion]
|
||||
ping = JsonOutput.toJson(ping)
|
||||
def maker = new I2PDatagramMaker(session)
|
||||
ping = maker.makeI2PDatagram(ping.bytes)
|
||||
def options = new SendMessageOptions()
|
||||
options.setSendLeaseSet(true)
|
||||
session.sendMessage(UpdateServers.UPDATE_SERVER, ping, 0, ping.length, I2PSession.PROTO_DATAGRAM, 2, 0, options)
|
||||
}
|
||||
|
||||
class Listener implements I2PSessionMuxedListener {
|
||||
|
||||
final JsonSlurper slurper = new JsonSlurper()
|
||||
|
||||
@Override
|
||||
public void messageAvailable(I2PSession session, int msgId, long size) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
|
||||
if (proto != I2PSession.PROTO_DATAGRAM) {
|
||||
log.warning "Received unexpected protocol $proto"
|
||||
return
|
||||
}
|
||||
|
||||
def payload = session.receiveMessage(msgId)
|
||||
def dissector = new I2PDatagramDissector()
|
||||
try {
|
||||
dissector.loadI2PDatagram(payload)
|
||||
def sender = dissector.getSender()
|
||||
if (sender != UpdateServers.UPDATE_SERVER) {
|
||||
log.warning("received something not from update server " + sender.toBase32())
|
||||
return
|
||||
}
|
||||
|
||||
log.info("Received something from update server")
|
||||
|
||||
payload = dissector.getPayload()
|
||||
payload = slurper.parse(payload)
|
||||
|
||||
if (payload.version == null) {
|
||||
log.warning("version missing")
|
||||
return
|
||||
}
|
||||
|
||||
if (payload.signer == null) {
|
||||
log.warning("signer missing")
|
||||
}
|
||||
|
||||
if (VersionComparator.comp(myVersion, payload.version) >= 0) {
|
||||
log.info("no new version available")
|
||||
return
|
||||
}
|
||||
|
||||
log.info("new version $payload.version available, publishing event")
|
||||
eventBus.publish(new UpdateAvailableEvent(version : payload.version, signer : payload.signer))
|
||||
|
||||
} catch (Exception e) {
|
||||
log.log(Level.WARNING,"Invalid datagram",e)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAbuse(I2PSession session, int severity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(I2PSession session) {
|
||||
log.severe("I2P session disconnected")
|
||||
}
|
||||
|
||||
@Override
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
log.log(Level.SEVERE, message, error)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package com.muwire.core.update
|
||||
|
||||
import net.i2p.data.Destination
|
||||
|
||||
class UpdateServers {
|
||||
static final Destination UPDATE_SERVER = new Destination("pSWieSRB3czCl3Zz4WpKp4Z8tjv-05zbogRDS7SEnKcSdWOupVwjzQ92GsgQh1VqgoSRk1F8dpZOnHxxz5HFy9D7ri0uFdkMyXdSKoB7IgkkvCfTAyEmeaPwSYnurF3Zk7u286E7YG2rZkQZgJ77tow7ZS0mxFB7Z0Ti-VkZ9~GeGePW~howwNm4iSQACZA0DyTpI8iv5j4I0itPCQRgaGziob~Vfvjk49nd8N4jtaDGo9cEcafikVzQ2OgBgYWL6LRbrrItwuGqsDvITUHWaElUYIDhRQYUq8gYiUA6rwAJputfhFU0J7lIxFR9vVY7YzRvcFckfr0DNI4VQVVlPnRPkUxQa--BlldMaCIppWugjgKLwqiSiHywKpSMlBWgY2z1ry4ueEBo1WEP-mEf88wRk4cFQBCKtctCQnIG2GsnATqTl-VGUAsuzeNWZiFSwXiTy~gQ094yWx-K06fFZUDt4CMiLZVhGlixiInD~34FCRC9LVMtFcqiFB2M-Ql2AAAA")
|
||||
}
|
@@ -15,7 +15,7 @@ class DownloadSessionTest {
|
||||
private File source, target
|
||||
private InfoHash infoHash
|
||||
private Endpoint endpoint
|
||||
private Pieces pieces
|
||||
private Pieces pieces, claimed
|
||||
private String rootBase64
|
||||
|
||||
private DownloadSession session
|
||||
@@ -24,7 +24,7 @@ class DownloadSessionTest {
|
||||
private InputStream fromDownloader, fromUploader
|
||||
private OutputStream toDownloader, toUploader
|
||||
|
||||
private void initSession(int size) {
|
||||
private void initSession(int size, def claimedPieces = []) {
|
||||
Random r = new Random()
|
||||
byte [] content = new byte[size]
|
||||
r.nextBytes(content)
|
||||
@@ -48,6 +48,8 @@ class DownloadSessionTest {
|
||||
else
|
||||
nPieces = size / pieceSize + 1
|
||||
pieces = new Pieces(nPieces)
|
||||
claimed = new Pieces(nPieces)
|
||||
claimedPieces.each {claimed.markDownloaded(it)}
|
||||
|
||||
fromDownloader = new PipedInputStream()
|
||||
fromUploader = new PipedInputStream()
|
||||
@@ -55,7 +57,7 @@ class DownloadSessionTest {
|
||||
toUploader = new PipedOutputStream(fromDownloader)
|
||||
endpoint = new Endpoint(null, fromUploader, toUploader, null)
|
||||
|
||||
session = new DownloadSession("",pieces, infoHash, endpoint, target, pieceSize, size)
|
||||
session = new DownloadSession("",pieces, claimed, infoHash, endpoint, target, pieceSize, size)
|
||||
downloadThread = new Thread( { session.request() } as Runnable)
|
||||
downloadThread.setDaemon(true)
|
||||
downloadThread.start()
|
||||
@@ -138,4 +140,29 @@ class DownloadSessionTest {
|
||||
assert !pieces.isComplete()
|
||||
assert 1 == pieces.donePieces()
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSmallFileClaimed() {
|
||||
initSession(20, [0])
|
||||
long now = System.currentTimeMillis()
|
||||
downloadThread.join(100)
|
||||
assert 100 > (System.currentTimeMillis() - now)
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClaimedPiecesAvoided() {
|
||||
int pieceSize = FileHasher.getPieceSize(1)
|
||||
int size = (1 << pieceSize) * 10
|
||||
initSession(size, [1,2,3,4,5,6,7,8,9])
|
||||
assert !claimed.isMarked(0)
|
||||
|
||||
assert "GET $rootBase64" == readTillRN(fromDownloader)
|
||||
String range = readTillRN(fromDownloader)
|
||||
def matcher = (range =~ /^Range: (\d+)-(\d+)$/)
|
||||
int start = Integer.parseInt(matcher[0][1])
|
||||
int end = Integer.parseInt(matcher[0][2])
|
||||
|
||||
assert claimed.isMarked(0)
|
||||
assert start == 0 && end == (1 << pieceSize) - 1
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.0.6
|
||||
version = 0.0.8
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
|
@@ -41,6 +41,7 @@ griffon {
|
||||
}
|
||||
|
||||
mainClassName = 'com.muwire.gui.Launcher'
|
||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||
|
||||
apply from: 'gradle/publishing.gradle'
|
||||
apply from: 'gradle/code-coverage.gradle'
|
||||
|
@@ -71,8 +71,15 @@ class MainFrameController {
|
||||
def result = selectedResult()
|
||||
if (result == null)
|
||||
return // TODO disable button
|
||||
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
|
||||
core.eventBus.publish(new UIDownloadEvent(result : result, target : file))
|
||||
|
||||
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
|
||||
|
||||
def selected = builder.getVariable("result-tabs").getSelectedComponent()
|
||||
def group = selected.getClientProperty("mvc-group")
|
||||
|
||||
def resultsBucket = group.model.hashBucket[result.infohash]
|
||||
|
||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, target : file))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
|
@@ -20,7 +20,11 @@ class OptionsController {
|
||||
|
||||
def settings = application.context.get("muwire-settings")
|
||||
settings.downloadRetryInterval = Integer.valueOf(text)
|
||||
|
||||
|
||||
text = view.updateField.text
|
||||
model.updateCheckInterval = text
|
||||
settings.updateCheckInterval = Integer.valueOf(text)
|
||||
|
||||
File settingsFile = new File(application.context.get("core").home, "MuWire.properties")
|
||||
settingsFile.withOutputStream {
|
||||
settings.write(it)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import griffon.core.GriffonApplication
|
||||
import griffon.core.env.Metadata
|
||||
import groovy.util.logging.Log
|
||||
|
||||
import org.codehaus.griffon.runtime.core.AbstractLifecycleHandler
|
||||
@@ -20,6 +21,9 @@ import java.util.logging.Level
|
||||
|
||||
@Log
|
||||
class Ready extends AbstractLifecycleHandler {
|
||||
|
||||
@Inject Metadata metadata
|
||||
|
||||
@Inject
|
||||
Ready(@Nonnull GriffonApplication application) {
|
||||
super(application)
|
||||
@@ -90,9 +94,10 @@ class Ready extends AbstractLifecycleHandler {
|
||||
props.write(it)
|
||||
}
|
||||
}
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home)
|
||||
core = new Core(props, home, metadata["application.version"])
|
||||
} 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",
|
||||
|
@@ -4,6 +4,7 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
import javax.inject.Inject
|
||||
import javax.swing.JOptionPane
|
||||
import javax.swing.JTable
|
||||
|
||||
import com.muwire.core.Core
|
||||
@@ -21,6 +22,7 @@ import com.muwire.core.search.QueryEvent
|
||||
import com.muwire.core.search.UIResultEvent
|
||||
import com.muwire.core.trust.TrustEvent
|
||||
import com.muwire.core.trust.TrustService
|
||||
import com.muwire.core.update.UpdateAvailableEvent
|
||||
import com.muwire.core.upload.UploadEvent
|
||||
import com.muwire.core.upload.UploadFinishedEvent
|
||||
|
||||
@@ -97,6 +99,7 @@ class MainFrameModel {
|
||||
core.eventBus.register(UploadFinishedEvent.class, this)
|
||||
core.eventBus.register(TrustEvent.class, this)
|
||||
core.eventBus.register(QueryEvent.class, this)
|
||||
core.eventBus.register(UpdateAvailableEvent.class, this)
|
||||
|
||||
timer.schedule({
|
||||
int retryInterval = application.context.get("muwire-settings").downloadRetryInterval
|
||||
@@ -250,4 +253,10 @@ class MainFrameModel {
|
||||
Destination replyTo
|
||||
Persona originator
|
||||
}
|
||||
|
||||
void onUpdateAvailableEvent(UpdateAvailableEvent e) {
|
||||
runInsideUIAsync {
|
||||
JOptionPane.showMessageDialog(null, "A new version of MuWire is available from $e.signer. Please update to $e.version")
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,8 +7,10 @@ import griffon.metadata.ArtifactProviderFor
|
||||
@ArtifactProviderFor(GriffonModel)
|
||||
class OptionsModel {
|
||||
@Observable String downloadRetryInterval
|
||||
@Observable String updateCheckInterval
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
downloadRetryInterval = application.context.get("muwire-settings").downloadRetryInterval
|
||||
updateCheckInterval = application.context.get("muwire-settings").updateCheckInterval
|
||||
}
|
||||
}
|
@@ -21,7 +21,7 @@ class SearchTabModel {
|
||||
Core core
|
||||
String uuid
|
||||
def results = []
|
||||
def hashCount = [:]
|
||||
def hashBucket = [:]
|
||||
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
@@ -35,11 +35,12 @@ class SearchTabModel {
|
||||
|
||||
void handleResult(UIResultEvent e) {
|
||||
runInsideUIAsync {
|
||||
Integer count = hashCount.get(e.infohash)
|
||||
if (count == null)
|
||||
count = 0
|
||||
count++
|
||||
hashCount[e.infohash] = count
|
||||
def bucket = hashBucket.get(e.infohash)
|
||||
if (bucket == null) {
|
||||
bucket = []
|
||||
hashBucket[e.infohash] = bucket
|
||||
}
|
||||
bucket << e
|
||||
|
||||
results << e
|
||||
JTable table = builder.getVariable("results-table")
|
||||
|
@@ -3,6 +3,7 @@ package com.muwire.gui
|
||||
import griffon.core.artifact.GriffonView
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import net.i2p.data.DataHelper
|
||||
|
||||
import javax.swing.BorderFactory
|
||||
import javax.swing.Box
|
||||
@@ -13,6 +14,7 @@ import javax.swing.ListSelectionModel
|
||||
import javax.swing.SwingConstants
|
||||
import javax.swing.border.Border
|
||||
|
||||
import com.muwire.core.Constants
|
||||
import com.muwire.core.download.Downloader
|
||||
import com.muwire.core.files.FileSharedEvent
|
||||
|
||||
@@ -97,19 +99,17 @@ class MainFrameView {
|
||||
scrollPane (constraints : BorderLayout.CENTER) {
|
||||
table(id : "downloads-table") {
|
||||
tableModel(list: model.downloads) {
|
||||
closureColumn(header: "Name", type: String, read : {row -> row.downloader.file.getName()})
|
||||
closureColumn(header: "Status", type: String, read : {row -> row.downloader.getCurrentState()})
|
||||
closureColumn(header: "Progress", type: String, read: { row ->
|
||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.downloader.file.getName()})
|
||||
closureColumn(header: "Status", preferredWidth: 50, type: String, read : {row -> row.downloader.getCurrentState()})
|
||||
closureColumn(header: "Progress", preferredWidth: 20, type: String, read: { row ->
|
||||
int pieces = row.downloader.nPieces
|
||||
int done = row.downloader.donePieces()
|
||||
"$done/$pieces pieces"
|
||||
})
|
||||
closureColumn(header: "Piece", type: String, read: { row ->
|
||||
int position = row.downloader.positionInPiece()
|
||||
int pieceSize = row.downloader.pieceSize // TODO: fix for last piece
|
||||
"$position/$pieceSize bytes"
|
||||
closureColumn(header: "Sources", preferredWidth : 10, type: Integer, read : {row -> row.downloader.activeWorkers()})
|
||||
closureColumn(header: "Speed", preferredWidth: 50, type:String, read :{row ->
|
||||
DataHelper.formatSize2Decimal(row.downloader.speed(), false) + "B/sec"
|
||||
})
|
||||
closureColumn(header: "Speed (bytes/second)", type:Integer, read :{row -> row.downloader.speed()})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,8 +130,9 @@ class MainFrameView {
|
||||
scrollPane ( constraints : BorderLayout.CENTER) {
|
||||
table(id : "shared-files-table") {
|
||||
tableModel(list : model.shared) {
|
||||
closureColumn(header : "Name", type : String, read : {row -> row.file.getAbsolutePath()})
|
||||
closureColumn(header : "Size", type : Long, read : {row -> row.file.length()})
|
||||
closureColumn(header : "Name", preferredWidth : 550, type : String, read : {row -> row.file.getAbsolutePath()})
|
||||
closureColumn(header : "Size", preferredWidth : 50, type : String,
|
||||
read : {row -> DataHelper.formatSize2Decimal(row.file.length(),false) + "B"})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,7 +184,10 @@ class MainFrameView {
|
||||
scrollPane(constraints : BorderLayout.CENTER) {
|
||||
table(id : "searches-table") {
|
||||
tableModel(list : model.searches) {
|
||||
closureColumn(header : "Keywords", type : String, read : { it.search })
|
||||
closureColumn(header : "Keywords", type : String, read : {
|
||||
sanitized = it.search.replace('<', ' ')
|
||||
sanitized
|
||||
})
|
||||
closureColumn(header : "From", type : String, read : {
|
||||
if (it.originator != null) {
|
||||
return it.originator.getHumanReadableName()
|
||||
|
@@ -22,6 +22,7 @@ class OptionsView {
|
||||
def d
|
||||
def p
|
||||
def retryField
|
||||
def updateField
|
||||
def mainFrame
|
||||
|
||||
void initUI() {
|
||||
@@ -34,8 +35,12 @@ class OptionsView {
|
||||
retryField = textField(text : bind { model.downloadRetryInterval }, columns : 2, constraints : gbc(gridx: 1, gridy: 0))
|
||||
label(text : "minutes", constraints : gbc(gridx : 2, gridy: 0))
|
||||
|
||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 1), saveAction)
|
||||
button(text : "Cancel", constraints : gbc(gridx : 2, gridy: 1), cancelAction)
|
||||
label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1))
|
||||
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
|
||||
label(text : "hours", constraints : gbc(gridx: 2, gridy : 1))
|
||||
|
||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
||||
button(text : "Cancel", constraints : gbc(gridx : 2, gridy: 2), cancelAction)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,9 +4,12 @@ import griffon.core.artifact.GriffonView
|
||||
import griffon.core.mvc.MVCGroup
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import net.i2p.data.DataHelper
|
||||
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.SwingConstants
|
||||
import javax.swing.table.DefaultTableCellRenderer
|
||||
|
||||
import java.awt.BorderLayout
|
||||
|
||||
@@ -22,6 +25,7 @@ class SearchTabView {
|
||||
def pane
|
||||
def parent
|
||||
def searchTerms
|
||||
def resultsTable
|
||||
|
||||
void initUI() {
|
||||
builder.with {
|
||||
@@ -29,20 +33,23 @@ class SearchTabView {
|
||||
def pane = scrollPane {
|
||||
resultsTable = table(id : "results-table") {
|
||||
tableModel(list: model.results) {
|
||||
closureColumn(header: "Name", type: String, read : {row -> row.name})
|
||||
closureColumn(header: "Size", preferredWidth: 150, type: Long, read : {row -> row.size})
|
||||
closureColumn(header: "Sources", type : Integer, read : { row -> model.hashCount[row.infohash]})
|
||||
closureColumn(header: "Sender", type: String, read : {row -> row.sender.getHumanReadableName()})
|
||||
closureColumn(header: "Trust", type: String, read : {row ->
|
||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name})
|
||||
closureColumn(header: "Size", preferredWidth: 50, type: String, read : {row -> DataHelper.formatSize2Decimal(row.size, false)+"B"})
|
||||
closureColumn(header: "Sources", preferredWidth: 10, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
||||
closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
|
||||
closureColumn(header: "Trust", preferredWidth: 50, type: String, read : {row ->
|
||||
model.core.trustService.getLevel(row.sender.destination)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.pane = pane
|
||||
this.pane.putClientProperty("mvc-group", mvcGroup)
|
||||
this.pane.putClientProperty("results-table",resultsTable)
|
||||
|
||||
|
||||
this.resultsTable = resultsTable
|
||||
|
||||
def selectionModel = resultsTable.getSelectionModel()
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
selectionModel.addListSelectionListener( {
|
||||
@@ -71,6 +78,12 @@ class SearchTabView {
|
||||
}
|
||||
|
||||
parent.setTabComponentAt(index, tabPanel)
|
||||
|
||||
def centerRenderer = new DefaultTableCellRenderer()
|
||||
centerRenderer.setHorizontalAlignment(JLabel.CENTER)
|
||||
resultsTable.columnModel.getColumn(1).setCellRenderer(centerRenderer)
|
||||
resultsTable.setDefaultRenderer(Integer.class,centerRenderer)
|
||||
resultsTable.columnModel.getColumn(4).setCellRenderer(centerRenderer)
|
||||
}
|
||||
|
||||
def closeTab = {
|
||||
|
@@ -1,4 +1,6 @@
|
||||
include 'pinger'
|
||||
include 'host-cache'
|
||||
include 'update-server'
|
||||
include 'core'
|
||||
include 'gui'
|
||||
include 'cli'
|
||||
|
3
update-server/build.gradle
Normal file
3
update-server/build.gradle
Normal file
@@ -0,0 +1,3 @@
|
||||
apply plugin : 'application'
|
||||
mainClassName = 'com.muwire.update.UpdateServer'
|
||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
@@ -0,0 +1,105 @@
|
||||
package com.muwire.update
|
||||
|
||||
import java.util.logging.Level
|
||||
|
||||
import groovy.util.logging.Log
|
||||
import net.i2p.client.I2PClientFactory
|
||||
import net.i2p.client.I2PSession
|
||||
import net.i2p.client.I2PSessionMuxedListener
|
||||
import net.i2p.client.datagram.I2PDatagramDissector
|
||||
import net.i2p.client.datagram.I2PDatagramMaker
|
||||
|
||||
|
||||
@Log
|
||||
class UpdateServer {
|
||||
public static void main(String[] args) {
|
||||
def home = System.getProperty("user.home") + "/.MuWireUpdateServer"
|
||||
home = new File(home)
|
||||
if (!home.exists())
|
||||
home.mkdirs()
|
||||
|
||||
def keyFile = new File(home, "key.dat")
|
||||
|
||||
def i2pClientFactory = new I2PClientFactory()
|
||||
def i2pClient = i2pClientFactory.createClient()
|
||||
|
||||
def myDest
|
||||
def session
|
||||
if (!keyFile.exists()) {
|
||||
def os = new FileOutputStream(keyFile);
|
||||
myDest = i2pClient.createDestination(os)
|
||||
os.close()
|
||||
log.info "No key.dat file was found, so creating a new destination."
|
||||
log.info "This is the destination you want to give out for your new UpdateServer"
|
||||
log.info myDest.toBase64()
|
||||
}
|
||||
|
||||
def update = new File(home, "update.json")
|
||||
if (!update.exists()) {
|
||||
log.warning("update file doesn't exist, exiting")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
def props = System.getProperties().clone()
|
||||
props.putAt("inbound.nickname", "MuWire UpdateServer")
|
||||
session = i2pClient.createSession(new FileInputStream(keyFile), props)
|
||||
myDest = session.getMyDestination()
|
||||
|
||||
session.addMuxedSessionListener(new Listener(update), I2PSession.PROTO_DATAGRAM, I2PSession.PORT_ANY)
|
||||
session.connect()
|
||||
log.info("Connected, going to sleep")
|
||||
Thread.sleep(Integer.MAX_VALUE)
|
||||
|
||||
}
|
||||
|
||||
static class Listener implements I2PSessionMuxedListener {
|
||||
|
||||
private final File json
|
||||
|
||||
Listener(File json) {
|
||||
this.json = json
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageAvailable(I2PSession session, int msgId, long size) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromport, int toport) {
|
||||
if (proto != I2PSession.PROTO_DATAGRAM) {
|
||||
log.warning("received uknown protocol $proto")
|
||||
return
|
||||
}
|
||||
|
||||
def payload = session.receiveMessage(msgId)
|
||||
def dissector = new I2PDatagramDissector()
|
||||
try {
|
||||
dissector.loadI2PDatagram(payload)
|
||||
def sender = dissector.getSender()
|
||||
log.info("Got an update ping from "+sender.toBase32())
|
||||
// I don't think we care about the payload at this point
|
||||
def maker = new I2PDatagramMaker(session)
|
||||
def response = maker.makeI2PDatagram(json.bytes)
|
||||
session.sendMessage(sender, response, I2PSession.PROTO_DATAGRAM, 0, 2)
|
||||
} catch (Exception e) {
|
||||
log.log(Level.WARNING, "exception responding to update request",e)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportAbuse(I2PSession session, int severity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(I2PSession session) {
|
||||
Log.severe("Disconnected from I2P router")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
log.log(Level.SEVERE, message, error)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user