Compare commits
10 Commits
muwire-0.5
...
muwire-0.6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fbf9add82a | ||
![]() |
7379263fef | ||
![]() |
7d50843754 | ||
![]() |
f4a2864942 | ||
![]() |
afaadf65a4 | ||
![]() |
7bd422d6b4 | ||
![]() |
3f47274f61 | ||
![]() |
419e9a0ce6 | ||
![]() |
ac1068a681 | ||
![]() |
549457e36f |
@@ -32,7 +32,7 @@ import com.muwire.core.UILoadedEvent
|
||||
import com.muwire.core.files.AllFilesLoadedEvent
|
||||
|
||||
class CliLanterna {
|
||||
private static final String MW_VERSION = "0.5.10"
|
||||
private static final String MW_VERSION = "0.6.0"
|
||||
|
||||
private static volatile Core core
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import com.muwire.core.search.QueryEvent
|
||||
import com.muwire.core.search.SearchEvent
|
||||
import com.muwire.core.search.UIResultBatchEvent
|
||||
import com.muwire.core.search.UIResultEvent
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import net.i2p.crypto.DSAEngine
|
||||
import net.i2p.data.Base64
|
||||
@@ -45,13 +46,16 @@ class SearchModel {
|
||||
|
||||
def searchEvent
|
||||
byte [] payload
|
||||
UUID uuid = UUID.randomUUID()
|
||||
long timestamp = System.currentTimeMillis()
|
||||
byte [] sig2 = DataUtil.signUUID(uuid, timestamp, core.spk)
|
||||
if (hashSearch) {
|
||||
searchEvent = new SearchEvent(searchHash : root, uuid : UUID.randomUUID(), oobInfohash : true, compressedResults : true)
|
||||
searchEvent = new SearchEvent(searchHash : root, uuid : uuid, oobInfohash : true, compressedResults : true)
|
||||
payload = root
|
||||
} else {
|
||||
def nonEmpty = SplitPattern.termify(query)
|
||||
payload = String.join(" ", nonEmpty).getBytes(StandardCharsets.UTF_8)
|
||||
searchEvent = new SearchEvent(searchTerms : nonEmpty, uuid : UUID.randomUUID(), oobInfohash: true,
|
||||
searchEvent = new SearchEvent(searchTerms : nonEmpty, uuid : uuid, oobInfohash: true,
|
||||
searchComments : core.muOptions.searchComments, compressedResults : true)
|
||||
}
|
||||
|
||||
@@ -61,7 +65,7 @@ class SearchModel {
|
||||
|
||||
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : firstHop,
|
||||
replyTo: core.me.destination, receivedOn: core.me.destination,
|
||||
originator : core.me, sig: sig.data))
|
||||
originator : core.me, sig: sig.data, queryTime : timestamp, sig2 : sig2))
|
||||
}
|
||||
|
||||
void unregister() {
|
||||
|
@@ -406,7 +406,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.5.10")
|
||||
Core core = new Core(props, home, "0.6.0")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -153,6 +153,10 @@ abstract class Connection implements Closeable {
|
||||
query.originator = e.originator.toBase64()
|
||||
if (e.sig != null)
|
||||
query.sig = Base64.encode(e.sig)
|
||||
if (e.queryTime > 0)
|
||||
query.queryTime = e.queryTime
|
||||
if (e.sig2 != null)
|
||||
query.sig2 = Base64.encode(e.sig2)
|
||||
messages.put(query)
|
||||
}
|
||||
|
||||
@@ -232,7 +236,6 @@ abstract class Connection implements Closeable {
|
||||
if (search.compressedResults != null)
|
||||
compressedResults = search.compressedResults
|
||||
byte[] sig = null
|
||||
// TODO: make this mandatory at some point
|
||||
if (search.sig != null) {
|
||||
sig = Base64.decode(search.sig)
|
||||
byte [] payload
|
||||
@@ -247,8 +250,36 @@ abstract class Connection implements Closeable {
|
||||
return
|
||||
} else
|
||||
log.info("query signature verified")
|
||||
} else
|
||||
} else {
|
||||
log.info("no signature in query")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: make this mandatory at some point
|
||||
byte[] sig2 = null
|
||||
long queryTime = 0
|
||||
if (search.sig2 != null) {
|
||||
if (search.queryTime == null) {
|
||||
log.info("extended signature but no timestamp")
|
||||
return
|
||||
}
|
||||
sig2 = Base64.decode(search.sig2)
|
||||
queryTime = search.queryTime
|
||||
byte [] payload = (search.uuid + String.valueOf(queryTime)).getBytes(StandardCharsets.US_ASCII)
|
||||
def spk = originator.destination.getSigningPublicKey()
|
||||
def signature = new Signature(Constants.SIG_TYPE, sig2)
|
||||
if (!DSAEngine.getInstance().verifySignature(signature, payload, spk)) {
|
||||
log.info("extended signature didn't match uuid and timestamp")
|
||||
return
|
||||
} else {
|
||||
log.info("extended query signature verified")
|
||||
if (queryTime < System.currentTimeMillis() - Constants.MAX_QUERY_AGE) {
|
||||
log.info("query too old")
|
||||
return
|
||||
}
|
||||
}
|
||||
} else
|
||||
log.info("no extended signature in query")
|
||||
|
||||
SearchEvent searchEvent = new SearchEvent(searchTerms : search.keywords,
|
||||
searchHash : infohash,
|
||||
@@ -262,7 +293,9 @@ abstract class Connection implements Closeable {
|
||||
originator : originator,
|
||||
receivedOn : endpoint.destination,
|
||||
firstHop : search.firstHop,
|
||||
sig : sig )
|
||||
sig : sig,
|
||||
queryTime : queryTime,
|
||||
sig2 : sig2 )
|
||||
eventBus.publish(event)
|
||||
|
||||
}
|
||||
|
@@ -159,7 +159,9 @@ class ConnectionAcceptor {
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.log(Level.WARNING, "incoming connection failed",ex)
|
||||
e.getOutputStream().close()
|
||||
try {
|
||||
e.getOutputStream().close()
|
||||
} catch (Exception ignore) {}
|
||||
e.close()
|
||||
eventBus.publish new ConnectionEvent(endpoint: e, incoming: true, leaf: null, status: ConnectionAttemptStatus.FAILED)
|
||||
}
|
||||
@@ -208,7 +210,9 @@ class ConnectionAcceptor {
|
||||
os.writeShort(json.bytes.length)
|
||||
os.write(json.bytes)
|
||||
}
|
||||
e.outputStream.close()
|
||||
try {
|
||||
e.outputStream.close()
|
||||
} catch (Exception ignored) {}
|
||||
e.close()
|
||||
eventBus.publish(new ConnectionEvent(endpoint: e, incoming: true, leaf: leaf, status: ConnectionAttemptStatus.REJECTED))
|
||||
}
|
||||
|
@@ -13,6 +13,8 @@ class QueryEvent extends Event {
|
||||
Persona originator
|
||||
Destination receivedOn
|
||||
byte[] sig
|
||||
long queryTime
|
||||
byte[] sig2
|
||||
|
||||
String toString() {
|
||||
"searchEvent: $searchEvent firstHop:$firstHop, replyTo:${replyTo.toBase32()}" +
|
||||
|
@@ -13,6 +13,7 @@ import com.muwire.core.files.FileSharedEvent
|
||||
import com.muwire.core.search.QueryEvent
|
||||
import com.muwire.core.search.SearchEvent
|
||||
import com.muwire.core.search.UIResultBatchEvent
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
@@ -176,9 +177,12 @@ class UpdateClient {
|
||||
signer = payload.signer
|
||||
log.info("starting search for new version hash $payload.infoHash")
|
||||
Signature sig = DSAEngine.getInstance().sign(updateInfoHash.getRoot(), spk)
|
||||
def searchEvent = new SearchEvent(searchHash : updateInfoHash.getRoot(), uuid : UUID.randomUUID(), oobInfohash : true, persona : me)
|
||||
UUID uuid = UUID.randomUUID()
|
||||
long timestamp = System.currentTimeMillis()
|
||||
byte [] sig2 = DataUtil.signUUID(uuid, timestamp, spk)
|
||||
def searchEvent = new SearchEvent(searchHash : updateInfoHash.getRoot(), uuid : uuid, oobInfohash : true, persona : me)
|
||||
def queryEvent = new QueryEvent(searchEvent : searchEvent, firstHop : true, replyTo : me.destination,
|
||||
receivedOn : me.destination, originator : me, sig : sig.data)
|
||||
receivedOn : me.destination, originator : me, sig : sig.data, queryTime : timestamp, sig2 : sig2)
|
||||
eventBus.publish(queryEvent)
|
||||
}
|
||||
}
|
||||
|
@@ -13,4 +13,6 @@ public class Constants {
|
||||
public static final int MAX_RESULTS = 0x1 << 16;
|
||||
|
||||
public static final int MAX_COMMENT_LENGTH = 0x1 << 15;
|
||||
|
||||
public static final long MAX_QUERY_AGE = 5 * 60 * 1000L;
|
||||
}
|
||||
|
@@ -15,11 +15,15 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.muwire.core.Constants;
|
||||
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
|
||||
public class DataUtil {
|
||||
@@ -203,4 +207,10 @@ public class DataUtil {
|
||||
.collect(Collectors.joining(","));
|
||||
props.setProperty(property, encoded);
|
||||
}
|
||||
|
||||
public static byte[] signUUID(UUID uuid, long timestamp, SigningPrivateKey spk) {
|
||||
byte [] payload = (uuid.toString() + String.valueOf(timestamp)).getBytes(StandardCharsets.US_ASCII);
|
||||
Signature sig = DSAEngine.getInstance().sign(payload, spk);
|
||||
return sig.getData();
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
group = com.muwire
|
||||
version = 0.5.10
|
||||
version = 0.6.0
|
||||
i2pVersion = 0.9.43
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
|
@@ -7,10 +7,13 @@ import griffon.core.mvc.MVCGroup
|
||||
import griffon.core.mvc.MVCGroupConfiguration
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import groovy.json.StringEscapeUtils
|
||||
import net.i2p.crypto.DSAEngine
|
||||
import net.i2p.data.Base64
|
||||
import net.i2p.data.Signature
|
||||
import net.i2p.data.SigningPrivateKey
|
||||
|
||||
import java.awt.Desktop
|
||||
import java.awt.event.ActionEvent
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
@@ -42,6 +45,7 @@ import com.muwire.core.trust.TrustLevel
|
||||
import com.muwire.core.trust.TrustSubscriptionEvent
|
||||
import com.muwire.core.upload.HashListUploader
|
||||
import com.muwire.core.upload.Uploader
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
@ArtifactProviderFor(GriffonController)
|
||||
class MainFrameController {
|
||||
@@ -120,9 +124,10 @@ class MainFrameController {
|
||||
|
||||
Signature sig = DSAEngine.getInstance().sign(payload, core.spk)
|
||||
|
||||
long timestamp = System.currentTimeMillis()
|
||||
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : firstHop,
|
||||
replyTo: core.me.destination, receivedOn: core.me.destination,
|
||||
originator : core.me, sig : sig.data))
|
||||
originator : core.me, sig : sig.data, queryTime : timestamp, sig2 : DataUtil.signUUID(uuid, timestamp, core.spk)))
|
||||
|
||||
}
|
||||
|
||||
@@ -139,14 +144,16 @@ class MainFrameController {
|
||||
|
||||
byte [] infoHashBytes = Base64.decode(infoHash)
|
||||
Signature sig = DSAEngine.getInstance().sign(infoHashBytes, core.spk)
|
||||
long timestamp = System.currentTimeMillis()
|
||||
byte [] sig2 = DataUtil.signUUID(uuid, timestamp, core.spk)
|
||||
|
||||
def searchEvent = new SearchEvent(searchHash : Base64.decode(infoHash), uuid:uuid,
|
||||
oobInfohash: true, persona : core.me)
|
||||
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
|
||||
replyTo: core.me.destination, receivedOn: core.me.destination,
|
||||
originator : core.me, sig : sig.data))
|
||||
originator : core.me, sig : sig.data, queryTime : timestamp, sig2 : sig2))
|
||||
}
|
||||
|
||||
|
||||
private int selectedDownload() {
|
||||
def downloadsTable = builder.getVariable("downloads-table")
|
||||
def selected = downloadsTable.getSelectedRow()
|
||||
@@ -383,7 +390,7 @@ class MainFrameController {
|
||||
@ControllerAction
|
||||
void showFileDetails() {
|
||||
def selected = view.selectedSharedFiles()
|
||||
if (selected.size() != 1) {
|
||||
if (selected == null || selected.size() != 1) {
|
||||
JOptionPane.showMessageDialog(null, "Please select only one file to view it's details")
|
||||
return
|
||||
}
|
||||
@@ -392,6 +399,19 @@ class MainFrameController {
|
||||
params['core'] = core
|
||||
mvcGroup.createMVCGroup("shared-file", params)
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void openContainingFolder() {
|
||||
def selected = view.selectedSharedFiles()
|
||||
if (selected == null || selected.size() != 1) {
|
||||
JOptionPane.showMessageDialog(null, "Please select only one file to open it's containing folder")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
Desktop.getDesktop().open(selected[0].file.getParentFile())
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
void saveMuWireSettings() {
|
||||
core.saveMuSettings()
|
||||
|
@@ -608,6 +608,9 @@ class MainFrameView {
|
||||
JMenuItem certifySelectedFiles = new JMenuItem("Certify selected files")
|
||||
certifySelectedFiles.addActionListener({mvcGroup.controller.issueCertificate()})
|
||||
sharedFilesMenu.add(certifySelectedFiles)
|
||||
JMenuItem openContainingFolder = new JMenuItem("Open containing folder")
|
||||
openContainingFolder.addActionListener({mvcGroup.controller.openContainingFolder()})
|
||||
sharedFilesMenu.add(openContainingFolder)
|
||||
JMenuItem showFileDetails = new JMenuItem("Show file details")
|
||||
showFileDetails.addActionListener({mvcGroup.controller.showFileDetails()})
|
||||
sharedFilesMenu.add(showFileDetails)
|
||||
|
@@ -346,7 +346,8 @@ class SearchTabView {
|
||||
model.senders2.addAll(results)
|
||||
int selectedRow = sendersTable2.getSelectedRow()
|
||||
sendersTable2.model.fireTableDataChanged()
|
||||
sendersTable2.selectionModel.setSelectionInterval(selectedRow,selectedRow)
|
||||
if (selectedRow < results.size())
|
||||
sendersTable2.selectionModel.setSelectionInterval(selectedRow,selectedRow)
|
||||
})
|
||||
|
||||
resultsTable2.addMouseListener(new MouseAdapter() {
|
||||
@@ -367,7 +368,7 @@ class SearchTabView {
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
selectionModel.addListSelectionListener({
|
||||
int row = selectedSenderRow()
|
||||
if (row < 0) {
|
||||
if (row < 0 || model.senders2[row] == null) {
|
||||
model.browseActionEnabled = false
|
||||
model.viewCertificatesActionEnabled = false
|
||||
model.trustButtonsEnabled = false
|
||||
@@ -438,6 +439,17 @@ class SearchTabView {
|
||||
if (lastResults2SortEvent != null)
|
||||
selectedRow = resultsTable2.rowSorter.convertRowIndexToModel(selectedRow)
|
||||
InfoHash infohash = model.results2[selectedRow]
|
||||
|
||||
Persona sender = selectedSender()
|
||||
if (sender == null) // really shouldn't happen
|
||||
return model.hashBucket[infohash].first()
|
||||
|
||||
for (UIResultEvent candidate : model.hashBucket[infohash]) {
|
||||
if (candidate.sender == sender)
|
||||
return candidate
|
||||
}
|
||||
|
||||
// also shouldn't happen
|
||||
return model.hashBucket[infohash].first()
|
||||
} else {
|
||||
int[] selectedRows = resultsTable.getSelectedRows()
|
||||
@@ -492,7 +504,7 @@ class SearchTabView {
|
||||
if (row < 0)
|
||||
return null
|
||||
if (model.groupedByFile)
|
||||
return model.senders2[row].sender
|
||||
return model.senders2[row]?.sender
|
||||
else
|
||||
return model.senders[row]
|
||||
}
|
||||
|
Reference in New Issue
Block a user