diff --git a/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy b/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy index 6c3c5d7d..628d0830 100644 --- a/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy +++ b/core/src/main/groovy/com/muwire/core/connection/ConnectionAcceptor.groovy @@ -379,10 +379,10 @@ class ConnectionAcceptor { dis.readFully(RUST) if (RUST != "RUST\r\n".getBytes(StandardCharsets.US_ASCII)) throw new IOException("Invalid TRUST connection") - String header - while ((header = DataUtil.readTillRN(dis)) != ""); // ignore headers for now - - OutputStream os = e.getOutputStream() + + Map headers = DataUtil.readAllHeaders(dis) + + OutputStream os = e.getOutputStream() if (!settings.allowTrustLists) { os.write("403 Not Allowed\r\n\r\n".getBytes(StandardCharsets.US_ASCII)) os.flush() @@ -390,22 +390,53 @@ class ConnectionAcceptor { return } - os.write("200 OK\r\n\r\n".getBytes(StandardCharsets.US_ASCII)) + os.write("200 OK\r\n".getBytes(StandardCharsets.US_ASCII)) + + boolean json = headers.containsKey('Json') && Boolean.parseBoolean(headers['Json']) + List good = new ArrayList<>(trustService.good.values()) - int size = Math.min(Short.MAX_VALUE * 2, good.size()) - good = good.subList(0, size) - DataOutputStream dos = new DataOutputStream(os) - dos.writeShort(size) - good.each { - it.persona.write(dos) - } - List bad = new ArrayList<>(trustService.bad.values()) - size = Math.min(Short.MAX_VALUE * 2, bad.size()) - bad = bad.subList(0, size) - dos.writeShort(size) - bad.each { - it.persona.write(dos) + DataOutputStream dos = new DataOutputStream(os) + + if (!json) { + os.write("\r\n") + int size = Math.min(Short.MAX_VALUE * 2, good.size()) + good = good.subList(0, size) + dos.writeShort(size) + good.each { + it.persona.write(dos) + } + + size = Math.min(Short.MAX_VALUE * 2, bad.size()) + bad = bad.subList(0, size) + dos.writeShort(size) + bad.each { + it.persona.write(dos) + } + } else { + dos.write("Json: true\r\n") + dos.write("Good:${good.size()}\r\n") + dos.write("Bad:${bad.size()}\r\n") + dos.write("\r\n") + + good.each { + def obj = [:] + obj.persona = it.persona.toBase64() + obj.reason = it.reason + String toJson = JsonOutput.toJson(obj) + byte [] payload = toJson.getBytes(StandardCharsets.US_ASCII) + dos.writeShort(payload.length) + dos.write(payload) + } + bad.each { + def obj = [:] + obj.persona = it.persona.toBase64() + obj.reason = it.reason + String toJson = JsonOutput.toJson(obj) + byte [] payload = toJson.getBytes(StandardCharsets.US_ASCII) + dos.writeShort(payload.length) + dos.write(payload) + } } dos.flush() diff --git a/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy b/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy index a09de744..e01f9f0c 100644 --- a/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy +++ b/core/src/main/groovy/com/muwire/core/trust/RemoteTrustList.groovy @@ -3,6 +3,7 @@ package com.muwire.core.trust import java.util.concurrent.ConcurrentHashMap import com.muwire.core.Persona +import com.muwire.core.trust.TrustService.TrustEntry import net.i2p.util.ConcurrentHashSet @@ -10,7 +11,7 @@ class RemoteTrustList { public enum Status { NEW, UPDATING, UPDATED, UPDATE_FAILED } private final Persona persona - private final Set good, bad + private final Set good, bad volatile long timestamp volatile boolean forceUpdate Status status = Status.NEW diff --git a/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy b/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy index 2f894451..5694c5d4 100644 --- a/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy +++ b/core/src/main/groovy/com/muwire/core/trust/TrustService.groovy @@ -136,5 +136,16 @@ class TrustService extends Service { this.persona = persona this.reason = reason } + + public int hashCode() { + persona.hashCode() + } + + public boolean equals(Object o) { + if (!(o instanceof TrustEntry)) + return false + persona == o.persona + } } + } diff --git a/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy b/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy index 966ceb2c..c9e8f864 100644 --- a/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy +++ b/core/src/main/groovy/com/muwire/core/trust/TrustSubscriber.groovy @@ -12,9 +12,12 @@ import com.muwire.core.Persona import com.muwire.core.UILoadedEvent import com.muwire.core.connection.Endpoint import com.muwire.core.connection.I2PConnector +import com.muwire.core.trust.TrustService.TrustEntry import com.muwire.core.util.DataUtil +import groovy.json.JsonSlurper import groovy.util.logging.Log +import net.i2p.data.Base64 import net.i2p.data.Destination @Log @@ -109,7 +112,9 @@ class TrustSubscriber { endpoint = i2pConnector.connect(trustList.persona.destination) OutputStream os = endpoint.getOutputStream() InputStream is = endpoint.getInputStream() - os.write("TRUST\r\n\r\n".getBytes(StandardCharsets.US_ASCII)) + os.write("TRUST\r\n".getBytes(StandardCharsets.US_ASCII)) + os.write("Json:true\r\n") + os.write("\r\n") os.flush() String codeString = DataUtil.readTillRN(is) @@ -123,24 +128,47 @@ class TrustSubscriber { return false } - // swallow any headers - String header - while (( header = DataUtil.readTillRN(is)) != ""); - + Map headers = DataUtil.readAllHeaders(is) DataInputStream dis = new DataInputStream(is) + Set good = new HashSet<>() + Set bad = new HashSet<>() + + if (headers.containsKey('Json') && Boolean.parseBoolean(headers['Json'])) { + int countGood = Integer.parseInt(headers['Good']) + int countBad = Integer.parseInt(headers['Bad']) + + JsonSlurper slurper = new JsonSlurper() + + for (int i = 0; i < countGood; i++) { + int length = dis.readUnsignedShort() + byte []payload = new byte[length] + dis.readFully(payload) + def json = slurper.parse(payload) + Persona persona = new Persona(new ByteArrayInputStream(Base64.decode(json.persona))) + good.add(new TrustEntry(persona, json.reason)) + } + + for (int i = 0; i < countBad; i++) { + int length = dis.readUnsignedShort() + byte []payload = new byte[length] + dis.readFully(payload) + def json = slurper.parse(payload) + Persona persona = new Persona(new ByteArrayInputStream(Base64.decode(json.persona))) + bad.add(new TrustEntry(persona, json.reason)) + } + + } else { + int nGood = dis.readUnsignedShort() + for (int i = 0; i < nGood; i++) { + Persona p = new Persona(dis) + good.add(p) + } - Set good = new HashSet<>() - int nGood = dis.readUnsignedShort() - for (int i = 0; i < nGood; i++) { - Persona p = new Persona(dis) - good.add(p) - } - - Set bad = new HashSet<>() - int nBad = dis.readUnsignedShort() - for (int i = 0; i < nBad; i++) { - Persona p = new Persona(dis) - bad.add(p) + int nBad = dis.readUnsignedShort() + for (int i = 0; i < nBad; i++) { + Persona p = new Persona(dis) + bad.add(p) + } } trustList.timestamp = now diff --git a/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy b/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy index c4ffac70..89c4c37c 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/TrustListController.groovy @@ -27,7 +27,7 @@ class TrustListController { if (selectedRow < 0) return String reason = JOptionPane.showInputDialog("Enter reason (optional)") - Persona p = model.trusted[selectedRow] + Persona p = model.trusted[selectedRow].persona eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED, reason : reason)) view.fireUpdate("trusted-table") } @@ -38,7 +38,7 @@ class TrustListController { if (selectedRow < 0) return String reason = JOptionPane.showInputDialog("Enter reason (optional)") - Persona p = model.distrusted[selectedRow] + Persona p = model.distrusted[selectedRow].persona eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.TRUSTED, reason : reason)) view.fireUpdate("distrusted-table") } @@ -49,7 +49,7 @@ class TrustListController { if (selectedRow < 0) return String reason = JOptionPane.showInputDialog("Enter reason (optional)") - Persona p = model.trusted[selectedRow] + Persona p = model.trusted[selectedRow].persona eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED, reason : reason)) view.fireUpdate("trusted-table") } @@ -60,7 +60,7 @@ class TrustListController { if (selectedRow < 0) return String reason = JOptionPane.showInputDialog("Enter reason (optional)") - Persona p = model.distrusted[selectedRow] + Persona p = model.distrusted[selectedRow].persona eventBus.publish(new TrustEvent(persona : p, level : TrustLevel.DISTRUSTED, reason : reason)) view.fireUpdate("distrusted-table") } diff --git a/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy b/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy index b6ea40a5..23860015 100644 --- a/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/TrustListView.groovy @@ -49,8 +49,9 @@ class TrustListView { scrollPane (constraints : BorderLayout.CENTER){ table(id : "trusted-table", autoCreateRowSorter : true, rowHeight : rowHeight) { tableModel(list : model.trusted) { - closureColumn(header: "Trusted Users", type : String, read : {it.getHumanReadableName()}) - closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.destination).toString()}) + closureColumn(header: "Trusted Users", type : String, read : {it.persona.getHumanReadableName()}) + closureColumn(header: "Reason", type : String, read : {it.reason}) + closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.persona.destination).toString()}) } } } @@ -65,8 +66,9 @@ class TrustListView { scrollPane (constraints : BorderLayout.CENTER ){ table(id : "distrusted-table", autoCreateRowSorter : true, rowHeight : rowHeight) { tableModel(list : model.distrusted) { - closureColumn(header: "Distrusted Users", type : String, read : {it.getHumanReadableName()}) - closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.destination).toString()}) + closureColumn(header: "Distrusted Users", type : String, read : {it.persona.getHumanReadableName()}) + closureColumn(header: "Reason", type:String, read : {it.reason}) + closureColumn(header: "Your Trust", type : String, read : {model.trustService.getLevel(it.persona.destination).toString()}) } } }