[tor-commits] [exonerator/master] Separate response-writing parts from other parts.
karsten at torproject.org
karsten at torproject.org
Mon Jul 13 21:09:05 UTC 2015
commit 9d6a0a6b73c237ccb980c67091fbf28cb4f8fd45
Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Mon Jul 13 23:04:05 2015 +0200
Separate response-writing parts from other parts.
---
.../torproject/exonerator/ExoneraTorServlet.java | 464 +++++++++++---------
1 file changed, 264 insertions(+), 200 deletions(-)
diff --git a/src/org/torproject/exonerator/ExoneraTorServlet.java b/src/org/torproject/exonerator/ExoneraTorServlet.java
index 04e87f2..fd7fe8e 100644
--- a/src/org/torproject/exonerator/ExoneraTorServlet.java
+++ b/src/org/torproject/exonerator/ExoneraTorServlet.java
@@ -1,4 +1,4 @@
-/* Copyright 2011, 2012 The Tor Project
+/* Copyright 2011--2015 The Tor Project
* See LICENSE for licensing information */
package org.torproject.exonerator;
@@ -56,78 +56,13 @@ public class ExoneraTorServlet extends HttpServlet {
}
}
- private void writeHeader(PrintWriter out) throws IOException {
- out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 "
- + "Transitional//EN\">\n"
- + "<html>\n"
- + " <head>\n"
- + " <title>ExoneraTor</title>\n"
- + " <meta http-equiv=\"content-type\" content=\"text/html; "
- + "charset=ISO-8859-1\">\n"
- + " <link href=\"/css/stylesheet-ltr.css\" type=\"text/css\" "
- + "rel=\"stylesheet\">\n"
- + " <link href=\"/images/favicon.ico\" "
- + "type=\"image/x-icon\" rel=\"shortcut icon\">\n"
- + " </head>\n"
- + " <body>\n"
- + " <div class=\"center\">\n"
- + " <div class=\"main-column\" style=\"margin:5; "
- + "Padding:0;\">\n"
- + " <h2>ExoneraTor</h2>\n"
- + " <h3>or: a website that tells you whether a given IP "
- + "address was a Tor relay</h3>\n"
- + " <br>\n"
- + " <p>Just because you see an Internet connection from a "
- + "particular IP address does not mean you know <i>who</i> "
- + "originated the traffic. Tor anonymizes Internet traffic by "
- + "\"<a href=\"https://www.torproject.org/about/overview"
- + "#thesolution\">onion routing</a>,\" sending packets "
- + "through a series of encrypted hops before they reach their "
- + "destination. Therefore, if you see traffic from a Tor node, "
- + "you may be seeing traffic that originated from someone "
- + "using Tor, rather than from the node operator itself. The "
- + "Tor Project and Tor node operators have no records of the "
- + "traffic that passes over the network, but we do maintain "
- + "current and historical records of which IP addresses are "
- + "part of the Tor network.</p>\n"
- + " <br>\n"
- + " <p>ExoneraTor tells you whether there was a Tor relay "
- + "running on a given IP address at a given time. ExoneraTor "
- + "learns these facts by parsing the public relay lists that "
- + "are collected from the Tor directory authorities and the "
- + "exit lists collected by TorDNSEL. By inputting an IP "
- + "address and time, you can determine whether that IP was "
- + "then a part of the Tor network.</p>\n"
- + " <br>\n"
- + " <p><font color=\"red\"><b>Notice:</b> Note that the "
- + "information you are providing below may be visible to "
- + "anyone who can read the network traffic between you and "
- + "this web server or who has access to this web "
- + "server.</font></p>\n"
- + " <br>\n");
- }
-
- private void writeFooter(PrintWriter out) throws IOException {
- out.println(" <br>\n"
- + " </div>\n"
- + " </div>\n"
- + " <div class=\"bottom\" id=\"bottom\">\n"
- + " <p>\"Tor\" and the \"Onion Logo\" are <a "
- + "href=\"https://www.torproject.org/docs/trademark-faq.html.en"
- + "\">registered trademarks</a> of The Tor Project, Inc.</p>\n"
- + " </div>\n"
- + " </body>\n"
- + "</html>");
- out.close();
- }
-
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException,
ServletException {
/* Start writing response. */
PrintWriter out = response.getWriter();
- writeHeader(out);
+ this.writeHeader(out);
/* Open a database connection that we'll use to handle the whole
* request. */
@@ -136,11 +71,8 @@ public class ExoneraTorServlet extends HttpServlet {
try {
conn = this.ds.getConnection();
} catch (SQLException e) {
- out.println("<p><font color=\"red\"><b>Warning: </b></font>Unable "
- + "to connect to the database. If this problem persists, "
- + "please <a href=\"mailto:tor-assistants at torproject.org\">let "
- + "us know</a>!</p>\n");
- writeFooter(out);
+ this.writeUnableToConnectToDatabaseWarning(out);
+ this.writeFooter(out);
return;
}
@@ -163,12 +95,8 @@ public class ExoneraTorServlet extends HttpServlet {
/* Looks like we don't have any consensuses. */
}
if (firstValidAfter < 0L || lastValidAfter < 0L) {
- out.println("<p><font color=\"red\"><b>Warning: </b></font>This "
- + "server doesn't have any relay lists available. If this "
- + "problem persists, please "
- + "<a href=\"mailto:tor-assistants at lists.torproject.org\">let "
- + "us know</a>!</p>\n");
- writeFooter(out);
+ this.writeNoDataWarning(out);
+ this.writeFooter(out);
try {
conn.close();
this.logger.info("Returned a database connection to the pool "
@@ -179,9 +107,6 @@ public class ExoneraTorServlet extends HttpServlet {
return;
}
- out.println("<a name=\"relay\"></a><h3>Was there a Tor relay running "
- + "on this IP address?</h3>");
-
/* Parse IP parameter. */
Pattern ipv4AddressPattern = Pattern.compile(
"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
@@ -283,47 +208,11 @@ public class ExoneraTorServlet extends HttpServlet {
}
/* Write form with IP address and timestamp. */
- out.println(" <form action=\"#relay\">\n"
- + " <table>\n"
- + " <tr>\n"
- + " <td align=\"right\">IP address in question:"
- + "</td>\n"
- + " <td><input type=\"text\" name=\"ip\" size=\"30\""
- + (relayIP.length() > 0 ? " value=\"" + relayIP + "\""
- : "")
- + ">"
- + (ipWarning.length() > 0 ? "<br><font color=\"red\">"
- + ipWarning + "</font>" : "")
- + "</td>\n"
- + " <td><i>(Ex.: 86.59.21.38 or "
- + "2001:858:2:2:aabb:0:563b:1526)</i></td>\n"
- + " </tr>\n"
- + " <tr>\n"
- + " <td align=\"right\">Date:</td>\n"
- + " <td><input type=\"text\" name=\"timestamp\""
- + " size=\"30\""
- + (timestampStr.length() > 0 ? " value=\"" + timestampStr + "\""
- : "")
- + ">"
- + (timestampWarning.length() > 0 ? "<br><font color=\"red\">"
- + timestampWarning + "</font>" : "")
- + "</td>\n"
- + " <td><i>(Ex.: 2010-01-01)"
- + "</i></td>\n"
- + " </tr>\n"
- + " <tr>\n"
- + " <td></td>\n"
- + " <td>\n"
- + " <input type=\"submit\">\n"
- + " <input type=\"reset\">\n"
- + " </td>\n"
- + " <td></td>\n"
- + " </tr>\n"
- + " </table>\n"
- + " </form>\n");
+ this.writeForm(out, relayIP, ipWarning, timestampStr,
+ timestampWarning);
if (relayIP.length() < 1 || timestamp < 1) {
- writeFooter(out);
+ this.writeFooter(out);
try {
conn.close();
this.logger.info("Returned a database connection to the pool "
@@ -334,19 +223,12 @@ public class ExoneraTorServlet extends HttpServlet {
return;
}
- out.printf("<p>Looking up IP address %s in the relay lists "
- + "published ", relayIP);
long timestampFrom, timestampTo;
/* Consider all consensuses published on or within a day of the given
* date. */
timestampFrom = timestamp - 24L * 60L * 60L * 1000L;
timestampTo = timestamp + 2 * 24L * 60L * 60L * 1000L - 1L;
- out.printf("on or within a day of %s", timestampStr);
- out.print(" as well as in the relevant exit lists. Clients could "
- + "have selected any of these relays to build circuits. "
- + "You may follow the links to relay lists and relay descriptors "
- + "to grep for the lines printed below and confirm that results "
- + "are correct.<br>\n");
+ this.writeSearchInfos(out, relayIP, timestampStr);
SimpleDateFormat validAfterTimeFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
validAfterTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
@@ -370,17 +252,8 @@ public class ExoneraTorServlet extends HttpServlet {
* interval. */
}
if (relevantConsensuses.isEmpty()) {
- out.println(" <p>No relay lists found!</p>\n"
- + " <p>Result is INDECISIVE!</p>\n"
- + " <p>We cannot make any statement whether there was "
- + "a Tor relay running on IP address " + relayIP + " on "
- + timestampStr + "! We "
- + "did not find any relevant relay lists on or within a day of "
- + "the given date. If "
- + "you think this is an error on our side, please "
- + "<a href=\"mailto:tor-assistants at torproject.org\">contact "
- + "us</a>!</p>\n");
- writeFooter(out);
+ this.writeNoDataForThisInterval(out, relayIP, timestampStr);
+ this.writeFooter(out);
try {
conn.close();
this.logger.info("Returned a database connection to the pool "
@@ -394,7 +267,7 @@ public class ExoneraTorServlet extends HttpServlet {
/* Search for status entries with the given IP address as onion
* routing address, plus status entries of relays having an exit list
* entry with the given IP address as exit address. */
- SortedSet<String> tableRows = new TreeSet<String>();
+ List<String[]> tableRows = new ArrayList<String[]>();
try {
CallableStatement cs = conn.prepareCall(
"{call search_statusentries_by_address_date(?, ?)}");
@@ -408,7 +281,7 @@ public class ExoneraTorServlet extends HttpServlet {
SortedSet<String> addresses = new TreeSet<String>();
long validafter = rs.getTimestamp(3, utcCalendar).getTime();
String validAfterString = validAfterTimeFormat.format(validafter);
- String fingerprint = rs.getString(4);
+ String fingerprint = rs.getString(4).toUpperCase();
String nickname = "(Unknown)";
String exit = "Unknown";
for (String line : new String(rawstatusentry).split("\n")) {
@@ -428,15 +301,14 @@ public class ExoneraTorServlet extends HttpServlet {
if (exitaddress != null && exitaddress.length() > 0) {
addresses.add(exitaddress);
}
- StringBuilder html = new StringBuilder();
- html.append("<tr><td>" + validAfterString + "</td><td>");
+ StringBuilder sb = new StringBuilder();
int writtenAddresses = 0;
for (String address : addresses) {
- html.append((writtenAddresses++ > 0 ? ", " : "") + address);
+ sb.append((writtenAddresses++ > 0 ? ", " : "") + address);
}
- html.append("</td><td>" + fingerprint.toUpperCase() + "</td><td>"
- + nickname + "</td><td>" + exit + "</td></tr>\n");
- tableRows.add(html.toString());
+ String[] tableRow = new String[] { validAfterString,
+ sb.toString(), fingerprint, nickname, exit };
+ tableRows.add(tableRow);
}
rs.close();
cs.close();
@@ -446,26 +318,8 @@ public class ExoneraTorServlet extends HttpServlet {
/* Print out what we found. */
if (!tableRows.isEmpty()) {
- out.print("<br>\n");
- out.print("<table>\n");
- out.print("<thead>\n");
- out.print("<tr><th>Timestamp (UTC)</th><th>IP address(es)</th>"
- + "<th>Identity fingerprint</th><th>Nickname</th><th>Exit</th>"
- + "</tr>\n");
- out.print("</thead>\n");
- out.print("<tbody>\n");
- for (String tableRow : tableRows) {
- out.print(tableRow);
- }
- out.print("</tbody>\n");
- out.print("</table>\n");
+ this.writeResultsTable(out, tableRows);
} else {
- out.printf(" <p>None found!</p>\n"
- + " <p>Result is NEGATIVE with high certainty!</p>\n"
- + " <p>We did not find IP "
- + "address " + relayIP + " in any of the relay or exit lists "
- + "that were published on or within a day of %s.</p>\n",
- timestampStr);
/* Run another query to find out if there are relays running on
* other IP addresses in the same /24 or /48 network and tell the
* user about it. */
@@ -542,27 +396,13 @@ public class ExoneraTorServlet extends HttpServlet {
}
}
}
- if (!addressesInSameNetwork.isEmpty()) {
- if (!relayIP.contains(":")) {
- out.print(" <p>The following other IP addresses of Tor "
- + "relays in the same /24 network were found in relay "
- + "and/or exit lists on or within a day of " + timestampStr
- + " that could be related "
- + "to IP address " + relayIP + ":</p>\n");
- } else {
- out.print(" <p>The following other IP addresses of Tor "
- + "relays in the same /48 network were found in relay "
- + "lists on or within a day of " + timestampStr
- + " that could be related to IP "
- + "address " + relayIP + ":</p>\n");
- }
- out.print(" <ul>\n");
- for (String s : addressesInSameNetwork) {
- out.print(" <li>" + s + "</li>\n");
- }
- out.print(" </ul>\n");
+ if (addressesInSameNetwork.isEmpty()) {
+ this.writeNoneFound(out, relayIP, timestampStr);
+ } else {
+ this.writeAddressesInSameNetwork(out, relayIP, timestampStr,
+ addressesInSameNetwork);
}
- writeFooter(out);
+ this.writeFooter(out);
try {
conn.close();
this.logger.info("Returned a database connection to the pool "
@@ -575,20 +415,9 @@ public class ExoneraTorServlet extends HttpServlet {
/* Print out result. */
if (!tableRows.isEmpty()) {
- out.print(" <p>Result is POSITIVE with high certainty!"
- + "</p>\n"
- + " <p>We found one or more relays on IP address "
- + relayIP + " in a ");
- out.print("relay list published on or within a day of "
- + timestampStr);
- out.print(" that clients were likely to know.</p>\n");
+ this.writeSummaryPositive(out, relayIP, timestampStr);
} else {
- out.println(" <p>Result is NEGATIVE "
- + "with high certainty!</p>\n");
- out.println(" <p>We did not find any relay on IP address "
- + relayIP
- + " in the relay lists on or within a day of " + timestampStr
- + ".</p>\n");
+ this.writeSummaryNegative(out, relayIP, timestampStr);
}
try {
@@ -598,7 +427,242 @@ public class ExoneraTorServlet extends HttpServlet {
- requestedConnection) + " millis.");
} catch (SQLException e) {
}
- writeFooter(out);
+ this.writeFooter(out);
+ }
+
+ /* Helper methods for writing the response. */
+
+ private void writeHeader(PrintWriter out) throws IOException {
+ out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 "
+ + "Transitional//EN\">\n"
+ + "<html>\n"
+ + " <head>\n"
+ + " <title>ExoneraTor</title>\n"
+ + " <meta http-equiv=\"content-type\" content=\"text/html; "
+ + "charset=ISO-8859-1\">\n"
+ + " <link href=\"/css/stylesheet-ltr.css\" type=\"text/css\" "
+ + "rel=\"stylesheet\">\n"
+ + " <link href=\"/images/favicon.ico\" "
+ + "type=\"image/x-icon\" rel=\"shortcut icon\">\n"
+ + " </head>\n"
+ + " <body>\n"
+ + " <div class=\"center\">\n"
+ + " <div class=\"main-column\" style=\"margin:5; "
+ + "Padding:0;\">\n"
+ + " <h2>ExoneraTor</h2>\n"
+ + " <h3>or: a website that tells you whether a given IP "
+ + "address was a Tor relay</h3>\n"
+ + " <br>\n"
+ + " <p>Just because you see an Internet connection from a "
+ + "particular IP address does not mean you know <i>who</i> "
+ + "originated the traffic. Tor anonymizes Internet traffic by "
+ + "\"<a href=\"https://www.torproject.org/about/overview"
+ + "#thesolution\">onion routing</a>,\" sending packets "
+ + "through a series of encrypted hops before they reach their "
+ + "destination. Therefore, if you see traffic from a Tor node, "
+ + "you may be seeing traffic that originated from someone "
+ + "using Tor, rather than from the node operator itself. The "
+ + "Tor Project and Tor node operators have no records of the "
+ + "traffic that passes over the network, but we do maintain "
+ + "current and historical records of which IP addresses are "
+ + "part of the Tor network.</p>\n"
+ + " <br>\n"
+ + " <p>ExoneraTor tells you whether there was a Tor relay "
+ + "running on a given IP address at a given time. ExoneraTor "
+ + "learns these facts by parsing the public relay lists that "
+ + "are collected from the Tor directory authorities and the "
+ + "exit lists collected by TorDNSEL. By inputting an IP "
+ + "address and time, you can determine whether that IP was "
+ + "then a part of the Tor network.</p>\n"
+ + " <br>\n"
+ + " <p><font color=\"red\"><b>Notice:</b> Note that the "
+ + "information you are providing below may be visible to "
+ + "anyone who can read the network traffic between you and "
+ + "this web server or who has access to this web "
+ + "server.</font></p>\n"
+ + " <br>\n");
+ }
+
+ private void writeUnableToConnectToDatabaseWarning(PrintWriter out)
+ throws IOException {
+ out.println("<p><font color=\"red\"><b>Warning: </b></font>Unable "
+ + "to connect to the database. If this problem persists, "
+ + "please <a href=\"mailto:tor-assistants at torproject.org\">let "
+ + "us know</a>!</p>\n");
+ }
+
+ private void writeNoDataWarning(PrintWriter out) throws IOException {
+ out.println("<p><font color=\"red\"><b>Warning: </b></font>This "
+ + "server doesn't have any relay lists available. If this "
+ + "problem persists, please "
+ + "<a href=\"mailto:tor-assistants at lists.torproject.org\">let "
+ + "us know</a>!</p>\n");
+ }
+
+ private void writeForm(PrintWriter out, String relayIP,
+ String ipWarning, String timestampStr, String timestampWarning)
+ throws IOException {
+ out.println("<a name=\"relay\"></a><h3>Was there a Tor relay running "
+ + "on this IP address?</h3>");
+ out.println(" <form action=\"#relay\">\n"
+ + " <table>\n"
+ + " <tr>\n"
+ + " <td align=\"right\">IP address in question:"
+ + "</td>\n"
+ + " <td><input type=\"text\" name=\"ip\" size=\"30\""
+ + (relayIP.length() > 0 ? " value=\"" + relayIP + "\""
+ : "")
+ + ">"
+ + (ipWarning.length() > 0 ? "<br><font color=\"red\">"
+ + ipWarning + "</font>" : "")
+ + "</td>\n"
+ + " <td><i>(Ex.: 86.59.21.38 or "
+ + "2001:858:2:2:aabb:0:563b:1526)</i></td>\n"
+ + " </tr>\n"
+ + " <tr>\n"
+ + " <td align=\"right\">Date:</td>\n"
+ + " <td><input type=\"text\" name=\"timestamp\""
+ + " size=\"30\""
+ + (timestampStr.length() > 0 ? " value=\"" + timestampStr + "\""
+ : "")
+ + ">"
+ + (timestampWarning.length() > 0 ? "<br><font color=\"red\">"
+ + timestampWarning + "</font>" : "")
+ + "</td>\n"
+ + " <td><i>(Ex.: 2010-01-01)"
+ + "</i></td>\n"
+ + " </tr>\n"
+ + " <tr>\n"
+ + " <td></td>\n"
+ + " <td>\n"
+ + " <input type=\"submit\">\n"
+ + " <input type=\"reset\">\n"
+ + " </td>\n"
+ + " <td></td>\n"
+ + " </tr>\n"
+ + " </table>\n"
+ + " </form>\n");
+ }
+
+ private void writeSearchInfos(PrintWriter out, String relayIP,
+ String timestampStr) throws IOException {
+ out.printf("<p>Looking up IP address %s in the relay lists "
+ + "published ", relayIP);
+ out.printf("on or within a day of %s", timestampStr);
+ out.print(" as well as in the relevant exit lists. Clients could "
+ + "have selected any of these relays to build circuits. "
+ + "You may follow the links to relay lists and relay descriptors "
+ + "to grep for the lines printed below and confirm that results "
+ + "are correct.<br>\n");
+ }
+
+ private void writeNoDataForThisInterval(PrintWriter out, String relayIP,
+ String timestampStr) throws IOException {
+ out.println(" <p>No relay lists found!</p>\n"
+ + " <p>Result is INDECISIVE!</p>\n"
+ + " <p>We cannot make any statement whether there was "
+ + "a Tor relay running on IP address " + relayIP + " on "
+ + timestampStr + "! We "
+ + "did not find any relevant relay lists on or within a day of "
+ + "the given date. If "
+ + "you think this is an error on our side, please "
+ + "<a href=\"mailto:tor-assistants at torproject.org\">contact "
+ + "us</a>!</p>\n");
+ }
+
+ private void writeResultsTable(PrintWriter out,
+ List<String[]> tableRows) throws IOException {
+ out.print("<br>\n");
+ out.print("<table>\n");
+ out.print("<thead>\n");
+ out.print("<tr><th>Timestamp (UTC)</th><th>IP address(es)</th>"
+ + "<th>Identity fingerprint</th><th>Nickname</th><th>Exit</th>"
+ + "</tr>\n");
+ out.print("</thead>\n");
+ out.print("<tbody>\n");
+ for (String[] tableRow : tableRows) {
+ out.print("<tr>");
+ for (String tableColumn : tableRow) {
+ out.print("<td>" + tableColumn + "</td>");
+ }
+ out.print("</tr>\n");
+ }
+ out.print("</tbody>\n");
+ out.print("</table>\n");
+ }
+
+ private void writeNoneFound(PrintWriter out, String relayIP,
+ String timestampStr) throws IOException {
+ out.printf(" <p>None found!</p>\n"
+ + " <p>Result is NEGATIVE with high certainty!</p>\n"
+ + " <p>We did not find IP "
+ + "address " + relayIP + " in any of the relay or exit lists "
+ + "that were published on or within a day of %s.</p>\n",
+ timestampStr);
+ }
+
+ private void writeAddressesInSameNetwork(PrintWriter out,
+ String relayIP, String timestampStr,
+ List<String> addressesInSameNetwork) throws IOException {
+ out.printf(" <p>None found!</p>\n"
+ + " <p>Result is NEGATIVE with high certainty!</p>\n"
+ + " <p>We did not find IP "
+ + "address " + relayIP + " in any of the relay or exit lists "
+ + "that were published on or within a day of %s.</p>\n",
+ timestampStr);
+ if (!relayIP.contains(":")) {
+ out.print(" <p>The following other IP addresses of Tor "
+ + "relays in the same /24 network were found in relay "
+ + "and/or exit lists on or within a day of " + timestampStr
+ + " that could be related "
+ + "to IP address " + relayIP + ":</p>\n");
+ } else {
+ out.print(" <p>The following other IP addresses of Tor "
+ + "relays in the same /48 network were found in relay "
+ + "lists on or within a day of " + timestampStr
+ + " that could be related to IP "
+ + "address " + relayIP + ":</p>\n");
+ }
+ out.print(" <ul>\n");
+ for (String s : addressesInSameNetwork) {
+ out.print(" <li>" + s + "</li>\n");
+ }
+ out.print(" </ul>\n");
+ }
+
+ private void writeSummaryPositive(PrintWriter out, String relayIP,
+ String timestampStr) throws IOException {
+ out.print(" <p>Result is POSITIVE with high certainty!"
+ + "</p>\n"
+ + " <p>We found one or more relays on IP address "
+ + relayIP + " in a ");
+ out.print("relay list published on or within a day of "
+ + timestampStr);
+ out.print(" that clients were likely to know.</p>\n");
+ }
+
+ private void writeSummaryNegative(PrintWriter out, String relayIP,
+ String timestampStr) throws IOException {
+ out.println(" <p>Result is NEGATIVE "
+ + "with high certainty!</p>\n");
+ out.println(" <p>We did not find any relay on IP address "
+ + relayIP
+ + " in the relay lists on or within a day of " + timestampStr
+ + ".</p>\n");
+ }
+
+ private void writeFooter(PrintWriter out) throws IOException {
+ out.println(" <br>\n"
+ + " </div>\n"
+ + " </div>\n"
+ + " <div class=\"bottom\" id=\"bottom\">\n"
+ + " <p>\"Tor\" and the \"Onion Logo\" are <a "
+ + "href=\"https://www.torproject.org/docs/trademark-faq.html.en"
+ + "\">registered trademarks</a> of The Tor Project, Inc.</p>\n"
+ + " </div>\n"
+ + " </body>\n"
+ + "</html>");
+ out.close();
}
}
More information about the tor-commits
mailing list