diff --git a/build.gradle b/build.gradle index 20ee360e81f..6e1c4f6e196 100644 --- a/build.gradle +++ b/build.gradle @@ -267,6 +267,7 @@ dependencies { testImplementation "org.mockito:mockito-core:1.9.5" testImplementation "org.hamcrest:hamcrest:3.0" testImplementation "org.objenesis:objenesis:1.0" + testImplementation "org.jsoup:jsoup:1.21.2" } task tar(type: Tar) { diff --git a/src/freenet/clients/http/N2NTMToadlet.java b/src/freenet/clients/http/N2NTMToadlet.java index 6ebb5c27566..6e09ccc49b8 100644 --- a/src/freenet/clients/http/N2NTMToadlet.java +++ b/src/freenet/clients/http/N2NTMToadlet.java @@ -16,7 +16,9 @@ import freenet.node.NodeClientCore; import freenet.node.NodeStarter; import freenet.node.PeerManager; +import freenet.support.Base64; import freenet.support.HTMLNode; +import freenet.support.IllegalBase64Exception; import freenet.support.Logger; import freenet.support.MultiValueTable; import freenet.support.SizeUtil; @@ -45,44 +47,50 @@ public void handleMethodGET(URI uri, HTTPRequest request, ToadletContext ctx) return; if (request.isParameterSet("peernode_hashcode")) { - PageNode page = ctx.getPageMaker().getPageNode(l10n("sendMessage"), ctx); - HTMLNode contentNode = page.getContentNode(); - - String peernode_name = null; String input_hashcode_string = request.getParam("peernode_hashcode"); - int input_hashcode = -1; - try { - input_hashcode = Integer.parseInt(input_hashcode_string); - } catch (NumberFormatException e) { - // ignore here, handle below - } - if (input_hashcode != -1) { - DarknetPeerNode[] peerNodes = node.getDarknetConnections(); - for (DarknetPeerNode pn: peerNodes) { - int peer_hashcode = pn.hashCode(); - if (peer_hashcode == input_hashcode) { - peernode_name = pn.getName(); - break; - } - } - } - if (peernode_name == null) { - contentNode.addChild(createPeerInfobox("infobox-error", - l10n("peerNotFoundTitle"), l10n("peerNotFoundWithHash", - "hash", input_hashcode_string))); - this.writeHTMLReply(ctx, 200, "OK", page.generate()); - return; - } - HashMap peers = new HashMap(); - peers.put(input_hashcode_string, peernode_name); - createN2NTMSendForm(ctx.isAdvancedModeEnabled(), contentNode, ctx, peers); - this.writeHTMLReply(ctx, 200, "OK", page.generate()); + createWriteN2NTMForm(ctx, input_hashcode_string, ""); return; } MultiValueTable headers = MultiValueTable.from("Location", "/friends/"); ctx.sendReplyHeaders(302, "Found", headers, null, 0); } + private void createWriteN2NTMForm(ToadletContext ctx, String input_hashcode_string, String initialContent) + throws ToadletContextClosedException, IOException { + PageNode page = ctx.getPageMaker().getPageNode(l10n("sendMessage"), ctx); + HTMLNode pageNode = page.outer; + HTMLNode contentNode = page.getContentNode(); + + String peernode_name = null; + int input_hashcode = -1; + try { + input_hashcode = Integer.parseInt(input_hashcode_string); + } catch (NumberFormatException e) { + // ignore here, handle below + } + if (input_hashcode != -1) { + DarknetPeerNode[] peerNodes = node.getDarknetConnections(); + for (DarknetPeerNode pn: peerNodes) { + int peer_hashcode = pn.hashCode(); + if (peer_hashcode == input_hashcode) { + peernode_name = pn.getName(); + break; + } + } + } + if (peernode_name == null) { + contentNode.addChild(createPeerInfobox("infobox-error", + l10n("peerNotFoundTitle"), l10n("peerNotFoundWithHash", + "hash", input_hashcode_string))); + this.writeHTMLReply(ctx, 200, "OK", pageNode.generate()); + return; + } + HashMap peers = new HashMap(); + peers.put(input_hashcode_string, peernode_name); + createN2NTMSendForm(pageNode, ctx.isAdvancedModeEnabled(), contentNode, ctx, peers, initialContent); + this.writeHTMLReply(ctx, 200, "OK", pageNode.generate()); + } + private String l10n(String key, String pattern[], String value[]) { return NodeL10n.getBase().getString("N2NTMToadlet." + key, pattern, value); } @@ -138,6 +146,25 @@ public void handleMethodPOST(URI uri, HTTPRequest request, ToadletContext ctx) return; } + if (request.isPartSet("replyTo")) { + String message = request.getPartAsStringFailsafe("replyTo", 1024 * 1024); + message = message.trim(); + try { + message = Base64.decodeUTF8(message); + } catch (IllegalBase64Exception e) { + Logger.error(this, "could not decode replyTo text", e); + message = ""; + } + if (!message.isEmpty()) { + message = "\n> " + String.join("\n> ", message.split("\n")) + "\n\n"; + message = message.replaceAll("\n> >", "\n>>") + .substring(1); // strip the initial linebreak again + } + String input_hashcode_string = request.getPartAsStringFailsafe("peernode_hashcode", 1024); + createWriteN2NTMForm(ctx, input_hashcode_string, message); + return; + } + if (request.isPartSet("n2nm-upload") || request.isPartSet(LocalFileBrowserToadlet.selectFile) || request.isPartSet("send")) { File filename = null; String message = request.getPartAsStringFailsafe("message", 1024 * 1024); @@ -269,6 +296,11 @@ public static void addUnsentMessageTextInfo(HTMLNode node, String message) { public static void createN2NTMSendForm(boolean advancedMode, HTMLNode contentNode, ToadletContext ctx, HashMap peers) throws ToadletContextClosedException, IOException { + createN2NTMSendForm(null, advancedMode, contentNode, ctx, peers, ""); + } + public static void createN2NTMSendForm(HTMLNode pageNode, boolean advancedMode, + HTMLNode contentNode, ToadletContext ctx, HashMap peers, String initalContent) + throws ToadletContextClosedException, IOException { HTMLNode infobox = contentNode.addChild("div", new String[] { "class", "id" }, new String[] { "infobox", "n2nbox" }); infobox.addChild("div", "class", "infobox-header", l10n("sendMessage")); @@ -288,8 +320,10 @@ public static void createN2NTMSendForm(boolean advancedMode, "value" }, new String[] { "hidden", "node_" + peerNodeHash, "1" }); } - messageForm.addChild("textarea", new String[] { "id", "name", "rows", - "cols" }, new String[] { "n2ntmtext", "message", "8", "74" }); + messageForm.addChild("textarea", + new String[] { "id", "name", "rows", "cols" }, + new String[] { "n2ntmtext", "message", "8", "74" }, + initalContent); if(advancedMode){ messageForm.addChild("br"); messageForm.addChild("#", NodeL10n.getBase().getString("N2NTMToadlet.mayAttachFile")); diff --git a/src/freenet/l10n/freenet.l10n.de.properties b/src/freenet/l10n/freenet.l10n.de.properties index 67f90f6c98a..8745f6f4067 100644 --- a/src/freenet/l10n/freenet.l10n.de.properties +++ b/src/freenet/l10n/freenet.l10n.de.properties @@ -1034,7 +1034,7 @@ N2NTMToadlet.uploadFailed=Das Hochladen der Datei ist fehlgeschlagen. N2NTMUserAlert.delete=Löschen N2NTMUserAlert.header=Von: ${from} (${composed}) N2NTMUserAlert.headerShort=Nachricht von ${from} -N2NTMUserAlert.reply=Antworten +UserAlertManager.reply=Antworten N2NTMUserAlert.title=Knoten-zu-Knoten-Text-Nachricht ${number} von ${peername} (${peer}) Node.acceptSeedConnectionsShort=Ein Seednode sein? Node.acceptSeedConnections=Wenn ja, wird Ihr Knoten Verbindungen von neuen Knoten, die sich im unsicheren Modus (OpenNet) befinden, akzeptieren und diesen helfen, sich aus eigener Kraft ins Netzwerk zu integrieren. Dies erlaubt es jedem Knoten mit Ihrer Knoten-Referenz sich zu verbinden, aber für Zwecke der Bekanntgabe: Er kann nur Anfragen an Knoten senden, die er durch die Bekanntgabe bekommt (möglicherweise einschließlich dieses Knotens, wenn wir nicht viele Fremde haben). diff --git a/src/freenet/l10n/freenet.l10n.el.properties b/src/freenet/l10n/freenet.l10n.el.properties index ce526e67d74..8b1f5a28e12 100644 --- a/src/freenet/l10n/freenet.l10n.el.properties +++ b/src/freenet/l10n/freenet.l10n.el.properties @@ -392,7 +392,7 @@ N2NTMToadlet.sentTitle=Απεστάλη N2NTMToadlet.uploadFailed=Αποτυχία ανεβάσματος αρχείου. N2NTMUserAlert.delete=Διαγραφή N2NTMUserAlert.headerShort=Μήνυμα εστάλη από ${from} -N2NTMUserAlert.reply=Απάντηση +UserAlertManager.reply=Απάντηση NodeClientCore.minDiskFreeLongTerm=Minimum free disk space NodeClientCore.minDiskFreeShortTerm=Minimum free disk space during decode NodeIPDetector.maybeSymmetricTitle=Προβλήματα σύνδεσης diff --git a/src/freenet/l10n/freenet.l10n.en.properties b/src/freenet/l10n/freenet.l10n.en.properties index d00092d7c69..b03c544202d 100644 --- a/src/freenet/l10n/freenet.l10n.en.properties +++ b/src/freenet/l10n/freenet.l10n.en.properties @@ -1108,7 +1108,7 @@ N2NTMToadlet.uploadFailed=Failed to upload file. N2NTMUserAlert.delete=Delete N2NTMUserAlert.header=From: ${from} (${composed}) N2NTMUserAlert.headerShort=Message from ${from} -N2NTMUserAlert.reply=Reply +UserAlertManager.reply=Reply N2NTMUserAlert.title=Node to Node Text Message ${number} from ${peername} (${peer}) Node.acceptSeedConnectionsShort=Be a seednode? Node.acceptSeedConnections=If true, the node will accept connections from new insecure-mode nodes and help to bootstrap them onto the network. This allows any node with your noderef to connect, but only for purposes of announcement: it can only do requests to nodes that it gets through the announcement (possibly including this node if we don't have many Strangers). diff --git a/src/freenet/l10n/freenet.l10n.es.properties b/src/freenet/l10n/freenet.l10n.es.properties index de1b40e097d..ed036a22d6c 100644 --- a/src/freenet/l10n/freenet.l10n.es.properties +++ b/src/freenet/l10n/freenet.l10n.es.properties @@ -1095,7 +1095,7 @@ N2NTMToadlet.uploadFailed=Fallo al subir fichero. N2NTMUserAlert.delete=Borrar N2NTMUserAlert.header=De: ${from} (${composed}) N2NTMUserAlert.headerShort=Mensaje de ${from} -N2NTMUserAlert.reply=Responder +UserAlertManager.reply=Responder N2NTMUserAlert.title=Mensaje de texto entre nodos ${number} de ${peername} (${peer}) Node.acceptSeedConnectionsShort=¿Actuar de nodo semilla? Node.acceptSeedConnections=Si se activa, su nodo aceptará conexiones de nodos con el nuevo modo-inseguro activado, y ayudará a integrarlos en la red. Esto permite que cualquier nodo que tenga su noderef (referencia del nodo de usted) se conecte, pero únicamente con el propósito de anunciarse en la red: sólo puede hacer peticiones a nodos a los que llegue a través de anunciar (posiblemente incluyendo este mismo nodo si no tenemos muchos desconocidos). diff --git a/src/freenet/l10n/freenet.l10n.fa.properties b/src/freenet/l10n/freenet.l10n.fa.properties index 3a769ef1cd4..20128126ea2 100644 --- a/src/freenet/l10n/freenet.l10n.fa.properties +++ b/src/freenet/l10n/freenet.l10n.fa.properties @@ -536,7 +536,7 @@ N2NTMToadlet.tooLongTitle=بیش از حد طولانی N2NTMToadlet.uploadFailed=آپلود فایل ناموفق بود. N2NTMUserAlert.delete=حذف N2NTMUserAlert.headerShort=پیام از ${from} -N2NTMUserAlert.reply=پاسخ +UserAlertManager.reply=پاسخ Node.bwlimitMustBePositive=محدودیت پهنای باند آپلود باید مثبت باشد. Node.clientCacheType=نوع حافظه نهان کلاینت؟ Node.clientCacheSize=اندازه حافظه نهان کلاینت (bytes, MB, GB, TB, غیره) diff --git a/src/freenet/l10n/freenet.l10n.fi.properties b/src/freenet/l10n/freenet.l10n.fi.properties index 1a1af9db352..88c58f852cd 100644 --- a/src/freenet/l10n/freenet.l10n.fi.properties +++ b/src/freenet/l10n/freenet.l10n.fi.properties @@ -716,7 +716,7 @@ N2NTMToadlet.tooLongTitle=Liian pitkä N2NTMToadlet.uploadFailed=Tiedoston lähettäminen epäonnistui. N2NTMUserAlert.delete=Poista N2NTMUserAlert.headerShort=Viesti lähettäjältä ${from} -N2NTMUserAlert.reply=Vastaa +UserAlertManager.reply=Vastaa Node.acceptSeedConnectionsShort=Ole lähdesolmu Node.assumeNATed=Oleta että porttia ei ole uudelleenohjattu. Node.bindToLong=IP-osoite, johon kiinnittyä diff --git a/src/freenet/l10n/freenet.l10n.fr.properties b/src/freenet/l10n/freenet.l10n.fr.properties index 8c0123ff843..426194bcc13 100644 --- a/src/freenet/l10n/freenet.l10n.fr.properties +++ b/src/freenet/l10n/freenet.l10n.fr.properties @@ -1014,7 +1014,7 @@ N2NTMToadlet.uploadFailed=Échec de téléversement du fichier. N2NTMUserAlert.delete=Supprimer N2NTMUserAlert.header=De : ${from} (${composed}) N2NTMUserAlert.headerShort=Message de ${from} -N2NTMUserAlert.reply=Répondre +UserAlertManager.reply=Répondre N2NTMUserAlert.title=Message texte de nœud à nœud ${number} de ${peername} (${peer}) Node.acceptSeedConnectionsShort=Être un nœud de propagation ? Node.acceptSeedConnections=Si cette option est vraie, le nœud acceptera les connexions de nouveaux nœuds en mode non sécurisé et les aidera à démarrer sur le réseau. Cela permet à n’importe quel nœud possédant votre réfnœud de se connecter, mais seulement dans le but de s’annoncer : ce dernier ne pourra faire de requêtes qu’aux nœuds qu’il obtiendra grâce à l’annonce (incluant possiblement ce nœud si nous n’avons pas beaucoup d’inconnus). diff --git a/src/freenet/l10n/freenet.l10n.it.properties b/src/freenet/l10n/freenet.l10n.it.properties index e5265280877..ccfc91918ac 100644 --- a/src/freenet/l10n/freenet.l10n.it.properties +++ b/src/freenet/l10n/freenet.l10n.it.properties @@ -1014,7 +1014,7 @@ N2NTMToadlet.uploadFailed=Upload del file fallito. N2NTMUserAlert.delete=Elimina N2NTMUserAlert.header=Da: ${from} (${composed}) N2NTMUserAlert.headerShort=Messaggio da ${from} -N2NTMUserAlert.reply=Rispondi +UserAlertManager.reply=Rispondi N2NTMUserAlert.title=Messaggio di testo da nodo a nodo (N2NTM) ${number} da: ${peername} (${peer}) Node.acceptSeedConnectionsShort=Abilita seednode? Node.acceptSeedConnections=Selezionando "vero" il nodo accetterà connessioni da nodi che intendono usare la modalità insicura (opennet) e li aiuterà a connettersi alla rete Freenet. Ciò rende possibile la connessione a qualsiasi nodo in possesso della referenza ma limitata dall'annuncio: le richieste di connessione sono indirizzate solo ai nodi aggiunti tramite annuncio. Il nodo locale potrebbe essere incluso a meno che non abbia già molti peer opennet (visibili alla pagina "Sconosciuti"). diff --git a/src/freenet/l10n/freenet.l10n.ja.properties b/src/freenet/l10n/freenet.l10n.ja.properties index f6b43b58498..892d437339e 100644 --- a/src/freenet/l10n/freenet.l10n.ja.properties +++ b/src/freenet/l10n/freenet.l10n.ja.properties @@ -972,7 +972,7 @@ N2NTMToadlet.returnToFriends=フレンドリストに戻る N2NTMToadlet.tooLongTitle=長すぎる N2NTMUserAlert.delete=削除 N2NTMUserAlert.headerShort=${from} からのメッセージ -N2NTMUserAlert.reply=返信 +UserAlertManager.reply=返信 N2NTMUserAlert.title=${number} から ${peername} (${peer})へのノード間メッセージ Node.alwaysAllowLocalAddresses=常にローカルアドレス経由での接続を許容しますか? Node.alwaysAllowLocalAddressesLong=Trueに設定すると、Freenetはローカル(localhost, LAn)アドレス経由のノードとパブリックIPと同様に接続しようとします。これがセットされていなくても、特定のダークネットピア(オープンネットでない)には有効にできます。 もしあなたが同じLANのノードと接続したく、ニセのレファレンスであなたのノードがUDPパケットをLANのマシンに送信する危険を無視する場合セットしてください。 diff --git a/src/freenet/l10n/freenet.l10n.nb-no.properties b/src/freenet/l10n/freenet.l10n.nb-no.properties index ce6a8d22ea5..52f6df506cc 100644 --- a/src/freenet/l10n/freenet.l10n.nb-no.properties +++ b/src/freenet/l10n/freenet.l10n.nb-no.properties @@ -905,7 +905,7 @@ N2NTMToadlet.uploadFailed=Klarte ikke å laste opp fil. N2NTMUserAlert.delete=Slett N2NTMUserAlert.header=Fra: ${from} (${composed}) N2NTMUserAlert.headerShort=Melding fra ${from} -N2NTMUserAlert.reply=Svar +UserAlertManager.reply=Svar N2NTMUserAlert.title=Node til Node tekstmelding ${number} fra ${peername} (${peer}) Node.acceptSeedConnectionsShort=Bli en seednode? Node.alwaysAllowLocalAddresses=Alltid tillat tilkobling til noder via lokale adresser? diff --git a/src/freenet/l10n/freenet.l10n.nl.properties b/src/freenet/l10n/freenet.l10n.nl.properties index 766351f34ba..08109983fbf 100644 --- a/src/freenet/l10n/freenet.l10n.nl.properties +++ b/src/freenet/l10n/freenet.l10n.nl.properties @@ -1014,7 +1014,7 @@ N2NTMToadlet.uploadFailed=Het uploaden van het bestand is mislukt. N2NTMUserAlert.delete=Verwijderen N2NTMUserAlert.header=Van: ${from} (${composed}) N2NTMUserAlert.headerShort=Bericht van ${from} -N2NTMUserAlert.reply=Beantwoord +UserAlertManager.reply=Beantwoord N2NTMUserAlert.title=Node naar Node tekstbericht ${number} van ${peername} (${peer}) Node.acceptSeedConnectionsShort=Seednode functionaliteit inschakelen? Node.acceptSeedConnections=Indien inschakeld zal deze Freenet node verbindingen accepteren van nieuwe Vreemden en hen helpen de weg te vinden op het Freenet-netwerk. Dit zorgt ervoor dat iedere node verbinding met deze Freenet-node kan maken, maar alleen gedurende het verkenningsproces. Deze node verwerkt alleen verzoeken van nodes die reageren op een aankondigingsbericht. diff --git a/src/freenet/l10n/freenet.l10n.pt-br.properties b/src/freenet/l10n/freenet.l10n.pt-br.properties index c580acc1786..41cee566632 100644 --- a/src/freenet/l10n/freenet.l10n.pt-br.properties +++ b/src/freenet/l10n/freenet.l10n.pt-br.properties @@ -1095,7 +1095,7 @@ N2NTMToadlet.uploadFailed=Falha ao inserir arquivo. N2NTMUserAlert.delete=Apagar N2NTMUserAlert.header=De: ${from} (${composed}) N2NTMUserAlert.headerShort=Mensagem de ${from} -N2NTMUserAlert.reply=Responder +UserAlertManager.reply=Responder N2NTMUserAlert.title=Mensagem de Texto de No a Nó ${number} de ${peername} (${peer}) Node.acceptSeedConnectionsShort=Ser um nó propagador? Node.acceptSeedConnections=Se verdadeiro, o nó aceitará conexões de novos nós de modo inseguro e ajudará a inicializá-los na rede. Isso permite que qualquer nó com o seu noderef se conecte, mas apenas para fins de anúncio: ele só pode fazer solicitações aos nós que ele recebe através do anúncio (possivelmente incluindo este nó se não tivermos muitos Estranhos). diff --git a/src/freenet/l10n/freenet.l10n.pt_PT.properties b/src/freenet/l10n/freenet.l10n.pt_PT.properties index 4b888d60f90..abacdc9f561 100644 --- a/src/freenet/l10n/freenet.l10n.pt_PT.properties +++ b/src/freenet/l10n/freenet.l10n.pt_PT.properties @@ -412,7 +412,7 @@ N2NTMToadlet.sendMessageShort=Enviar mensagem N2NTMToadlet.sentTitle=Enviar N2NTMUserAlert.delete=Eliminar N2NTMUserAlert.headerShort=Mensagem de ${from} -N2NTMUserAlert.reply=Responder +UserAlertManager.reply=Responder Node.bandwidthLimitMustBePositiveOrMinusOne=O limite de largura de banda de transferência deve ser positivo ou -1. Node.bindToLong=Endereço de IP a associar Node.clientCacheType=Tipo de cache de cliente? diff --git a/src/freenet/l10n/freenet.l10n.ru.properties b/src/freenet/l10n/freenet.l10n.ru.properties index 724083e7e2a..1c9b5e2b1d7 100644 --- a/src/freenet/l10n/freenet.l10n.ru.properties +++ b/src/freenet/l10n/freenet.l10n.ru.properties @@ -1092,7 +1092,7 @@ N2NTMToadlet.uploadFailed=Сбой при выгрузке файла. N2NTMUserAlert.delete=Удалить N2NTMUserAlert.header=От: ${from} (${composed}) N2NTMUserAlert.headerShort=Сообщение от ${from} -N2NTMUserAlert.reply=Ответить +UserAlertManager.reply=Ответить N2NTMUserAlert.title=Текстовое сообщение ${number} от ${peername} (${peer}) Node.acceptSeedConnectionsShort=Быть узлом-источником? Node.acceptSeedConnections=Узел-источник принимает запросы от новых узлов Опеннета и помогает им подключиться к сети. Любой узел, который знает о вашем узле, может к вам подключиться для пересылки анонсов: такой узел сможет отсылать запросы только тем узлам, информацию о которых он получил в ответ на свой анонс (возможно, запросы будут адресованы и вам, если к вашему узлу подключено недостаточно незнакомцев). diff --git a/src/freenet/l10n/freenet.l10n.sv.properties b/src/freenet/l10n/freenet.l10n.sv.properties index a9c34f4ec32..219004c6885 100644 --- a/src/freenet/l10n/freenet.l10n.sv.properties +++ b/src/freenet/l10n/freenet.l10n.sv.properties @@ -462,7 +462,7 @@ N2NTMToadlet.sentTitle=Skickat N2NTMToadlet.tooLongTitle=För långt N2NTMUserAlert.delete=Radera N2NTMUserAlert.headerShort=Meddelande från ${from} -N2NTMUserAlert.reply=Svara +UserAlertManager.reply=Svara N2NTMUserAlert.title=Nod till Nod Text meddelande nummer ${number} från ${peername} (${peer}) Node.bindTo=IP adress att knyta till Node.bindToLong=IP adress att knyta till diff --git a/src/freenet/l10n/freenet.l10n.zh-cn.properties b/src/freenet/l10n/freenet.l10n.zh-cn.properties index aebf143b3af..b6a9c6a36b0 100644 --- a/src/freenet/l10n/freenet.l10n.zh-cn.properties +++ b/src/freenet/l10n/freenet.l10n.zh-cn.properties @@ -1095,7 +1095,7 @@ N2NTMToadlet.uploadFailed=上传文件失败。 N2NTMUserAlert.delete=删除 N2NTMUserAlert.header=来自: ${from} (${composed}) N2NTMUserAlert.headerShort=来自 ${from} 的信息 -N2NTMUserAlert.reply=回复 +UserAlertManager.reply=回复 N2NTMUserAlert.title=${number} 条来自 ${peername} (${peer}) 的点对点文本信息 Node.acceptSeedConnectionsShort=作为种子节点? Node.acceptSeedConnections=如果设置为“是”,节点将会接受新的非安全模式节点的连接并帮助它们引导到网络。这会允许任何拥有您节点信息的节点连接,但是仅用于宣告目的: 这些新节点仅能向通过宣告获得的其他节点 (如果没有足够的陌生节点,那么可能包括本节点) 请求数据。 diff --git a/src/freenet/l10n/freenet.l10n.zh-tw.properties b/src/freenet/l10n/freenet.l10n.zh-tw.properties index f1e383d19c8..36e5a4a1e4d 100644 --- a/src/freenet/l10n/freenet.l10n.zh-tw.properties +++ b/src/freenet/l10n/freenet.l10n.zh-tw.properties @@ -1005,7 +1005,7 @@ N2NTMToadlet.uploadFailed=上傳檔案失敗。 N2NTMUserAlert.delete=刪除 N2NTMUserAlert.header=來自: ${from} (${composed}) N2NTMUserAlert.headerShort=來自 ${from} 的訊息 -N2NTMUserAlert.reply=回覆 +UserAlertManager.reply=回覆 N2NTMUserAlert.title=從${peername}(${peer})來的點對點文字訊息${number} Node.acceptSeedConnectionsShort=作為種子節點? Node.acceptSeedConnections=若為是, 節點會接受以不安全模式運作的新節點的連線, 並且幫助它們自載於網路上. 設為是會讓任何擁有你的參照的節點都能和你連線, 但只限於通報的用途: 該節點只能夠向經由通報取得的友點發出請求(也可能包含本節點, 如果我們沒有很多"陌生人"連線的話). diff --git a/src/freenet/node/useralerts/DownloadFeedUserAlert.java b/src/freenet/node/useralerts/DownloadFeedUserAlert.java index 567a808d9c2..3895557e6cd 100644 --- a/src/freenet/node/useralerts/DownloadFeedUserAlert.java +++ b/src/freenet/node/useralerts/DownloadFeedUserAlert.java @@ -10,7 +10,7 @@ import freenet.node.PeerNode; import freenet.support.HTMLNode; -public class DownloadFeedUserAlert extends AbstractUserAlert implements NodeToNodeMessageUserAlert { +public class DownloadFeedUserAlert extends AbstractUserAlert implements UserAlertFromPeer { private final WeakReference peerRef; private final FreenetURI uri; private final int fileNumber; @@ -47,6 +47,11 @@ public String getText() { return sb.toString(); } + @Override + public String getMessageText() { + return description == null ? "" : description; + } + @Override public String getShortText() { return getTitle(); @@ -71,6 +76,14 @@ public HTMLNode getHTMLText() { return alertNode; } + @Override + public PeerNode getSourceNode() { + if (peerRef == null) { + return null; + } + return peerRef.get(); + } + @Override public String dismissButtonText() { return l10n("delete"); diff --git a/src/freenet/node/useralerts/N2NTMUserAlert.java b/src/freenet/node/useralerts/N2NTMUserAlert.java index b2d60da3aa9..7836e563069 100644 --- a/src/freenet/node/useralerts/N2NTMUserAlert.java +++ b/src/freenet/node/useralerts/N2NTMUserAlert.java @@ -14,8 +14,8 @@ import freenet.node.PeerNode; import freenet.support.HTMLNode; -// Node To Node Text Message User Alert -public class N2NTMUserAlert extends AbstractUserAlert implements NodeToNodeMessageUserAlert { +// Node To Node Text Message User Alert: messaging between users +public class N2NTMUserAlert extends AbstractUserAlert implements UserAlertFromPeer { private final WeakReference peerRef; private final String messageText; private final int fileNumber; @@ -79,14 +79,17 @@ public HTMLNode getHTMLText() { if (i != lines.length - 1) alertNode.addChild("br"); } - - DarknetPeerNode pn = (DarknetPeerNode) peerRef.get(); - if (pn != null) - alertNode.addChild("p").addChild("a", "href", "/send_n2ntm/?peernode_hashcode=" + pn.hashCode(), - l10n("reply")); return alertNode; } + @Override + public PeerNode getSourceNode() { + if (peerRef == null) { + return null; + } + return peerRef.get(); + } + @Override public String dismissButtonText() { return l10n("delete"); @@ -122,6 +125,7 @@ public long getUpdatedTime() { return receivedTime; } + @Override public String getMessageText() { return messageText; } diff --git a/src/freenet/node/useralerts/UserAlert.java b/src/freenet/node/useralerts/UserAlert.java index 991012bd782..cb8831a4e65 100644 --- a/src/freenet/node/useralerts/UserAlert.java +++ b/src/freenet/node/useralerts/UserAlert.java @@ -4,6 +4,7 @@ package freenet.node.useralerts; import freenet.clients.fcp.FCPMessage; +import freenet.node.PeerNode; import freenet.support.HTMLNode; public interface UserAlert { @@ -24,6 +25,13 @@ public interface UserAlert { */ public String getText(); + /** + * Content of the body of the alert (no metadata). + */ + default public String getMessageText() { + return getText(); + } + /** * Content of alert (HTML). */ @@ -49,7 +57,7 @@ public interface UserAlert { public void isValid(boolean validity); public String dismissButtonText(); - + public boolean shouldUnregisterOnDismiss(); /** diff --git a/src/freenet/node/useralerts/UserAlertFromPeer.java b/src/freenet/node/useralerts/UserAlertFromPeer.java new file mode 100644 index 00000000000..df49bd8c7ca --- /dev/null +++ b/src/freenet/node/useralerts/UserAlertFromPeer.java @@ -0,0 +1,13 @@ +/* This code is part of Freenet. It is distributed under the GNU General + * Public License, version 2 (or at your option any later version). See + * http://www.gnu.org/ for further details of the GPL. */ +package freenet.node.useralerts; + +import freenet.node.PeerNode; + +public interface UserAlertFromPeer { + + public PeerNode getSourceNode(); + public String getMessageText(); + +} diff --git a/src/freenet/node/useralerts/UserAlertManager.java b/src/freenet/node/useralerts/UserAlertManager.java index c685c20dddb..24f968b2f62 100644 --- a/src/freenet/node/useralerts/UserAlertManager.java +++ b/src/freenet/node/useralerts/UserAlertManager.java @@ -37,6 +37,7 @@ import freenet.clients.fcp.FCPConnectionHandler; import freenet.l10n.NodeL10n; import freenet.node.NodeClientCore; +import freenet.node.PeerNode; import freenet.support.Base64; import freenet.support.HTMLNode; import freenet.support.Logger; @@ -243,11 +244,37 @@ public HTMLNode renderAlert(UserAlert userAlert) { userAlertNode.addChild("div", "class", "infobox-header", userAlert.getTitle()); HTMLNode alertContentNode = userAlertNode.addChild("div", "class", "infobox-content"); alertContentNode.addChild(userAlert.getHTMLText()); + if (userAlert instanceof UserAlertFromPeer) { + UserAlertFromPeer n2nUserAlert = (UserAlertFromPeer) userAlert; + alertContentNode.addChild(renderReplyButton(n2nUserAlert, n2nUserAlert.getSourceNode())); + } alertContentNode.addChild(renderDismissButton(userAlert, null)); return userAlertNode; } + private HTMLNode renderReplyButton(UserAlertFromPeer userAlert, PeerNode peerNode) { + HTMLNode form = new HTMLNode("form", + new String[]{"method", "action"}, + new String[]{"post", "/send_n2ntm/"}); + form.addChild("input", + new String[]{"hidden", "name", "value"}, + new String[]{"true", "replyTo", Base64.encodeUTF8(userAlert.getMessageText())}); + form.addChild("input", + new String[]{"hidden", "name", "value"}, + new String[]{"true", "peernode_hashcode", peerNode == null + ? "" + : Integer.valueOf(peerNode.hashCode()).toString()}); + form.addChild("input", + new String[] { "type", "name", "value" }, + new String[] { "hidden", "formPassword", core.getFormPassword()}); + form.addChild("button", + new String[]{"type"}, + new String[]{"submit"}, + l10n("reply")); + return form; + } + public HTMLNode renderDismissButton(UserAlert userAlert, String redirectToAfterDisable) { HTMLNode result = new HTMLNode("div"); if (userAlert.userCanDismiss()) { diff --git a/test/freenet/node/useralerts/UserAlertManagerTest.java b/test/freenet/node/useralerts/UserAlertManagerTest.java index 10e7d72ea15..00b735d7f45 100644 --- a/test/freenet/node/useralerts/UserAlertManagerTest.java +++ b/test/freenet/node/useralerts/UserAlertManagerTest.java @@ -1,5 +1,7 @@ package freenet.node.useralerts; +import static freenet.test.Matchers.isHtml; +import static freenet.test.Matchers.withElement; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; @@ -10,7 +12,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import freenet.node.DarknetPeerNode; +import freenet.node.PeerNode; +import freenet.support.HTMLNode; import java.io.StringReader; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -193,6 +199,16 @@ private Document createAtomXml() throws Exception { return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(atom))); } + @Test + public void renderingAnAlertWithAN2NTMRendersAReplyButton() { + DarknetPeerNode darknetPeerNode = mock(DarknetPeerNode.class, RETURNS_DEEP_STUBS); + when(darknetPeerNode.getWeakRef()).thenReturn(new WeakReference<>(mock(PeerNode.class))); + UserAlert userAlert = new N2NTMUserAlert(darknetPeerNode, "", 0, 0, 0, 0); + when(nodeClientCore.getFormPassword()).thenReturn("form-password"); + HTMLNode htmlNode = userAlertManager.renderAlert(userAlert); + assertThat(htmlNode.generate(), isHtml(withElement("form[method='post'][action='/send_n2ntm/'] button[type='submit']"))); + } + private final NodeClientCore nodeClientCore = mock(NodeClientCore.class, RETURNS_DEEP_STUBS); { diff --git a/test/freenet/test/Matchers.java b/test/freenet/test/Matchers.java new file mode 100644 index 00000000000..2574a7b7133 --- /dev/null +++ b/test/freenet/test/Matchers.java @@ -0,0 +1,74 @@ +package freenet.test; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +public class Matchers { + + /** + * Parses a {@link String} as HTML and verifies its content against the + * given {@link Element} matcher. + *

Usage

+ *

+ * Asserts that a string contains an <html> element with a nested + * <body> element: + *

+ *
+	 * String someHtml = generateHtml();
+	 * assertThat(someHtml, isHtml(withElement("html body")));
+	 * 
+ * + * @param elementMatcher The matcher for the HTML element + * @return A matcher for a {@link String} containing HTML + * @see #withElement(String) for matching elements + */ + public static Matcher isHtml(Matcher elementMatcher) { + return new TypeSafeDiagnosingMatcher() { + @Override + protected boolean matchesSafely(String html, Description mismatchDescription) { + org.jsoup.nodes.Document document = Jsoup.parse(html); + if (!elementMatcher.matches(document)) { + elementMatcher.describeMismatch(document, mismatchDescription); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("html matching ").appendDescriptionOf(elementMatcher); + } + }; + } + + /** + * Matches an element, if the given selector returns at least one match. + * + * @param selector The selector to match + * @return A matcher for the given selector + * @see Selector syntax + */ + public static Matcher withElement(String selector) { + return new TypeSafeDiagnosingMatcher() { + @Override + protected boolean matchesSafely(Element element, Description mismatchDescription) { + Elements elements = element.select(selector); + if (elements.isEmpty()) { + mismatchDescription.appendText("no element matching ").appendValue(selector); + return false; + } + return true; + } + + @Override + public void describeTo(Description description) { + description.appendText("element matching ").appendValue(selector); + } + }; + } + +}