diff --git a/core/src/main/groovy/com/muwire/core/files/FileManager.groovy b/core/src/main/groovy/com/muwire/core/files/FileManager.groovy index 887e511e..424a53f5 100644 --- a/core/src/main/groovy/com/muwire/core/files/FileManager.groovy +++ b/core/src/main/groovy/com/muwire/core/files/FileManager.groovy @@ -108,7 +108,7 @@ class FileManager { found = rootToFiles.get new InfoHash(e.searchHash) found = filter(found, e.oobInfohash) if (found != null && !found.isEmpty()) - re = new ResultsEvent(results: found.asList(), uuid: e.uuid) + re = new ResultsEvent(results: found.asList(), uuid: e.uuid, searchEvent: e) } else { def names = index.search e.searchTerms Set files = new HashSet<>() @@ -117,7 +117,7 @@ class FileManager { files.each { sharedFiles.add fileToSharedFile[it] } files = filter(sharedFiles, e.oobInfohash) if (!sharedFiles.isEmpty()) - re = new ResultsEvent(results: sharedFiles.asList(), uuid: e.uuid) + re = new ResultsEvent(results: sharedFiles.asList(), uuid: e.uuid, searchEvent: e) } diff --git a/core/src/main/groovy/com/muwire/core/search/ResultsEvent.groovy b/core/src/main/groovy/com/muwire/core/search/ResultsEvent.groovy index 059310eb..2b0542ce 100644 --- a/core/src/main/groovy/com/muwire/core/search/ResultsEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/search/ResultsEvent.groovy @@ -5,6 +5,7 @@ import com.muwire.core.SharedFile class ResultsEvent extends Event { + SearchEvent searchEvent SharedFile[] results UUID uuid } diff --git a/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy b/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy index 38e0cef4..91c4a986 100644 --- a/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy +++ b/core/src/main/groovy/com/muwire/core/search/ResultsParser.groovy @@ -12,8 +12,19 @@ class ResultsParser { public static UIResultEvent parse(Persona p, UUID uuid, def json) throws InvalidSearchResultException { if (json.type != "Result") throw new InvalidSearchResultException("not a result json") - if (json.version != 1) - throw new InvalidSearchResultException("unknown version $json.version") + switch(json.version) { + case 1: + return parseV1(p, uuid, json) + case 2: + return parseV2(p, uuid, json) + default: + throw new InvalidSearchResultException("unknown version $json.version") + + } + + } + + private static parseV1(Persona p, UUID uuid, def json) { if (json.name == null) throw new InvalidSearchResultException("name missing") if (json.size == null) @@ -52,4 +63,33 @@ class ResultsParser { throw new InvalidSearchResultException("parsing search result failed",e) } } + + private static UIResultEvent parseV2(Persona p, UUID uuid, def json) { + if (json.name == null) + throw new InvalidSearchResultException("name missing") + if (json.size == null) + throw new InvalidSearchResultException("length missing") + if (json.infohash == null) + throw new InvalidSearchResultException("infohash missing") + if (json.pieceSize == null) + throw new InvalidSearchResultException("pieceSize missing") + if (json.hashList != null) + throw new InvalidSearchResultException("V2 result with hashlist") + try { + String name = DataUtil.readi18nString(Base64.decode(json.name)) + long size = json.size + byte [] infoHash = Base64.decode(json.infohash) + if (infoHash.length != InfoHash.SIZE) + throw new InvalidSearchResultException("invalid infohash size $infoHash.length") + int pieceSize = json.pieceSize + return new UIResultEvent( sender : p, + name : name, + size : size, + infohash : new InfoHash(infoHash), + pieceSize : pieceSize, + uuid: uuid) + } catch (Exception e) { + throw new InvalidSearchResultException("parsing search result failed",e) + } + } } diff --git a/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy b/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy index 6799b7b5..342043ff 100644 --- a/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy +++ b/core/src/main/groovy/com/muwire/core/search/ResultsSender.groovy @@ -46,8 +46,8 @@ class ResultsSender { this.me = me } - void sendResults(UUID uuid, SharedFile[] results, Destination target) { - log.info("Sending $results.length results for uuid $uuid to ${target.toBase32()}") + void sendResults(UUID uuid, SharedFile[] results, Destination target, boolean oobInfohash) { + log.info("Sending $results.length results for uuid $uuid to ${target.toBase32()} oobInfohash : $oobInfohash") if (target.equals(me.destination)) { results.each { long length = it.getFile().length() @@ -64,7 +64,8 @@ class ResultsSender { eventBus.publish(uiResultEvent) } } else { - executor.execute(new ResultSendJob(uuid : uuid, results : results, target: target)) + executor.execute(new ResultSendJob(uuid : uuid, results : results, + target: target, oobInfohash : oobInfohash)) } } @@ -72,6 +73,7 @@ class ResultsSender { UUID uuid SharedFile [] results Destination target + boolean oobInfohash @Override public void run() { @@ -94,19 +96,20 @@ class ResultsSender { String encodedName = Base64.encode(baos.toByteArray()) def obj = [:] obj.type = "Result" - obj.version = 1 + obj.version = oobInfohash ? 2 : 1 obj.name = encodedName obj.infohash = Base64.encode(it.getInfoHash().getRoot()) obj.size = it.getFile().length() obj.pieceSize = it.getPieceSize() - byte [] hashList = it.getInfoHash().getHashList() - def hashListB64 = [] - for (int i = 0; i < hashList.length / InfoHash.SIZE; i++) { - System.arraycopy(hashList, InfoHash.SIZE * i, tmp, 0, InfoHash.SIZE) - hashListB64 << Base64.encode(tmp) + if (!oobInfohash) { + byte [] hashList = it.getInfoHash().getHashList() + def hashListB64 = [] + for (int i = 0; i < hashList.length / InfoHash.SIZE; i++) { + System.arraycopy(hashList, InfoHash.SIZE * i, tmp, 0, InfoHash.SIZE) + hashListB64 << Base64.encode(tmp) + } + obj.hashList = hashListB64 } - obj.hashList = hashListB64 - def json = jsonOutput.toJson(obj) os.writeShort((short)json.length()) os.write(json.getBytes(StandardCharsets.US_ASCII)) diff --git a/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy b/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy index 11806812..3ab7eef6 100644 --- a/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy +++ b/core/src/main/groovy/com/muwire/core/search/SearchManager.groovy @@ -44,7 +44,7 @@ public class SearchManager { log.info("No results for search uuid $event.uuid") return } - resultsSender.sendResults(event.uuid, event.results, target) + resultsSender.sendResults(event.uuid, event.results, target, event.searchEvent.oobInfohash) } boolean hasLocalSearch(UUID uuid) { diff --git a/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy b/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy index 177cd9e8..2b6472d6 100644 --- a/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy +++ b/core/src/main/groovy/com/muwire/core/upload/UploadManager.groovy @@ -115,6 +115,52 @@ public class UploadManager { } finally { eventBus.publish(new UploadFinishedEvent(uploader : uploader)) } + + // proceed with content + while(true) { + byte[] get = new byte[4] + dis.readFully(get) + if (get != "GET ".getBytes(StandardCharsets.US_ASCII)) { + log.warning("received a method other than GET on subsequent call") + e.close() + return + } + dis.readFully(infoHashStringBytes) + infoHashString = new String(infoHashStringBytes, StandardCharsets.US_ASCII) + log.info("Responding to upload request for root $infoHashString") + + infoHashRoot = Base64.decode(infoHashString) + sharedFiles = fileManager.getSharedFiles(infoHashRoot) + if (sharedFiles == null || sharedFiles.isEmpty()) { + log.info "file not found" + e.getOutputStream().write("404 File Not Found\r\n\r\n".getBytes(StandardCharsets.US_ASCII)) + e.getOutputStream().flush() + e.close() + return + } + + rn = new byte[2] + dis.readFully(rn) + if (rn != "\r\n".getBytes(StandardCharsets.US_ASCII)) { + log.warning("Malformed GET header") + e.close() + return + } + + request = Request.parseContentRequest(new InfoHash(infoHashRoot), e.getInputStream()) + if (request.downloader != null && request.downloader.destination != e.destination) { + log.info("Downloader persona doesn't match their destination") + e.close() + return + } + uploader = new ContentUploader(sharedFiles.iterator().next().file, request, e) + eventBus.publish(new UploadEvent(uploader : uploader)) + try { + uploader.respond() + } finally { + eventBus.publish(new UploadFinishedEvent(uploader : uploader)) + } + } } } diff --git a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy index 315d04ad..81bbb194 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/MainFrameController.groovy @@ -53,7 +53,7 @@ class MainFrameController { // this can be improved a lot def replaced = search.toLowerCase().trim().replaceAll(Constants.SPLIT_PATTERN, " ") def terms = replaced.split(" ") - searchEvent = new SearchEvent(searchTerms : terms, uuid : uuid, oobInfohash: false) + searchEvent = new SearchEvent(searchTerms : terms, uuid : uuid, oobInfohash: true) } core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true, replyTo: core.me.destination, receivedOn: core.me.destination, @@ -70,7 +70,8 @@ class MainFrameController { def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params) model.results[uuid.toString()] = group - def searchEvent = new SearchEvent(searchHash : Base64.decode(infoHash), uuid:uuid) + def searchEvent = new SearchEvent(searchHash : Base64.decode(infoHash), uuid:uuid, + oobInfohash: true) core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true, replyTo: core.me.destination, receivedOn: core.me.destination, originator : core.me))