[or-cvs] [metrics-web/master] Retire TemplateController and CustomGraphController.

karsten at torproject.org karsten at torproject.org
Mon Oct 11 11:51:54 UTC 2010


Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Mon, 11 Oct 2010 13:36:15 +0200
Subject: Retire TemplateController and CustomGraphController.
Commit: 8f8c64cbfdd046faba2c0a990e2b56135ec52bf5

The TemplateController class was used to assemble web pages consisting of
navigation bar, main body, and footer. We can simply use JSP's include
directive for this. Also move all JSPs to WEB-INF, so that they aren't
directly accessible for clients. Only the controller servlets are supposed
to access them when forwarding requests.

The CustomGraphController was used for the custom graphs page. Now that
all graphs can be customized and the custom graphs page has gone away, we
don't need the class anymore.
---
 build.xml                                          |    2 -
 etc/web.xml                                        |    4 +-
 .../ernie/web/CustomGraphController.java           |   79 -----
 .../torproject/ernie/web/GraphsNetworkServlet.java |    2 +-
 .../ernie/web/GraphsPackagesServlet.java           |    2 +-
 .../ernie/web/GraphsPerformanceServlet.java        |    4 +-
 src/org/torproject/ernie/web/GraphsServlet.java    |    3 +-
 .../torproject/ernie/web/GraphsUsersServlet.java   |    3 +-
 src/org/torproject/ernie/web/IndexServlet.java     |    5 +-
 .../torproject/ernie/web/ResearchDataServlet.java  |    3 +-
 .../ernie/web/ResearchPapersServlet.java           |    3 +-
 src/org/torproject/ernie/web/ResearchServlet.java  |    2 +-
 .../torproject/ernie/web/ResearchToolsServlet.java |    2 +-
 src/org/torproject/ernie/web/StatusServlet.java    |    2 +-
 .../torproject/ernie/web/TemplateController.java   |   68 ----
 templates/banner.tpl.jsp                           |   58 ----
 templates/footer.tpl.jsp                           |   13 -
 templates/graphs.tpl.jsp                           |   18 -
 templates/graphs_network.tpl.jsp                   |  274 ----------------
 templates/graphs_packages.tpl.jsp                  |   68 ----
 templates/graphs_performance.tpl.jsp               |   77 -----
 templates/graphs_users.tpl.jsp                     |  286 ----------------
 templates/index.tpl.jsp                            |   41 ---
 templates/main.tpl.jsp                             |   20 --
 templates/research.tpl.jsp                         |   11 -
 templates/research_data.tpl.jsp                    |  321 ------------------
 templates/research_papers.tpl.jsp                  |   65 ----
 templates/research_tools.tpl.jsp                   |  129 --------
 templates/status.tpl.jsp                           |   11 -
 web/WEB-INF/banner.jsp                             |   73 +++++
 web/WEB-INF/data.jsp                               |  340 ++++++++++++++++++++
 web/WEB-INF/footer.jsp                             |   13 +
 web/WEB-INF/graphs.jsp                             |   37 +++
 web/WEB-INF/index.jsp                              |   60 ++++
 web/WEB-INF/network.jsp                            |  292 +++++++++++++++++
 web/WEB-INF/packages.jsp                           |   87 +++++
 web/WEB-INF/papers.jsp                             |   95 ++++++
 web/WEB-INF/performance.jsp                        |   96 ++++++
 web/WEB-INF/research.jsp                           |   30 ++
 web/WEB-INF/status.jsp                             |   30 ++
 web/WEB-INF/tools.jsp                              |  148 +++++++++
 web/WEB-INF/users.jsp                              |  304 +++++++++++++++++
 web/data.jsp                                       |    4 -
 web/graphs.jsp                                     |    4 -
 web/index.jsp                                      |    4 -
 web/network.jsp                                    |    4 -
 web/packages.jsp                                   |    4 -
 web/papers.jsp                                     |    4 -
 web/performance.jsp                                |    4 -
 web/research.jsp                                   |    4 -
 web/status.jsp                                     |    4 -
 web/tools.jsp                                      |    4 -
 web/users.jsp                                      |    4 -
 53 files changed, 1624 insertions(+), 1601 deletions(-)
 delete mode 100644 src/org/torproject/ernie/web/CustomGraphController.java
 delete mode 100644 src/org/torproject/ernie/web/TemplateController.java
 delete mode 100644 templates/banner.tpl.jsp
 delete mode 100644 templates/footer.tpl.jsp
 delete mode 100644 templates/graphs.tpl.jsp
 delete mode 100644 templates/graphs_network.tpl.jsp
 delete mode 100644 templates/graphs_packages.tpl.jsp
 delete mode 100644 templates/graphs_performance.tpl.jsp
 delete mode 100644 templates/graphs_users.tpl.jsp
 delete mode 100644 templates/index.tpl.jsp
 delete mode 100644 templates/main.tpl.jsp
 delete mode 100644 templates/research.tpl.jsp
 delete mode 100644 templates/research_data.tpl.jsp
 delete mode 100644 templates/research_papers.tpl.jsp
 delete mode 100644 templates/research_tools.tpl.jsp
 delete mode 100644 templates/status.tpl.jsp
 create mode 100644 web/WEB-INF/banner.jsp
 create mode 100644 web/WEB-INF/data.jsp
 create mode 100644 web/WEB-INF/footer.jsp
 create mode 100644 web/WEB-INF/graphs.jsp
 create mode 100644 web/WEB-INF/index.jsp
 create mode 100644 web/WEB-INF/network.jsp
 create mode 100644 web/WEB-INF/packages.jsp
 create mode 100644 web/WEB-INF/papers.jsp
 create mode 100644 web/WEB-INF/performance.jsp
 create mode 100644 web/WEB-INF/research.jsp
 create mode 100644 web/WEB-INF/status.jsp
 create mode 100644 web/WEB-INF/tools.jsp
 create mode 100644 web/WEB-INF/users.jsp
 delete mode 100644 web/data.jsp
 delete mode 100644 web/graphs.jsp
 delete mode 100644 web/index.jsp
 delete mode 100644 web/network.jsp
 delete mode 100644 web/packages.jsp
 delete mode 100644 web/papers.jsp
 delete mode 100644 web/performance.jsp
 delete mode 100644 web/research.jsp
 delete mode 100644 web/status.jsp
 delete mode 100644 web/tools.jsp
 delete mode 100644 web/users.jsp

diff --git a/build.xml b/build.xml
index 64f471c..d444b91 100644
--- a/build.xml
+++ b/build.xml
@@ -37,8 +37,6 @@
       <zipfileset dir="${config}"
                   prefix="WEB-INF/classes"
                   includes="logging.properties"/>
-      <zipfileset dir="templates"
-                  prefix="WEB-INF/templates"/>
     </war>
   </target>
 </project>
diff --git a/etc/web.xml b/etc/web.xml
index 7432549..4401dc7 100644
--- a/etc/web.xml
+++ b/etc/web.xml
@@ -7,9 +7,7 @@
   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
 
   <welcome-file-list>
-    <welcome-file>
-      index.jsp
-    </welcome-file>
+    <welcome-file>WEB-INF/index.jsp</welcome-file>
   </welcome-file-list>
 
   <servlet>
diff --git a/src/org/torproject/ernie/web/CustomGraphController.java b/src/org/torproject/ernie/web/CustomGraphController.java
deleted file mode 100644
index 0fc741a..0000000
--- a/src/org/torproject/ernie/web/CustomGraphController.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.torproject.ernie.web;
-
-import java.util.*;
-import java.text.*;
-
-public class CustomGraphController  {
-
-  private Map<String, String[]> parameterMap;
-  private Set<String> error;
-  private String graphURI;
-  private static SimpleDateFormat simpledf;
-
-  static {
-    simpledf = new SimpleDateFormat("yyyy-MM-dd");
-    simpledf.setTimeZone(TimeZone.getTimeZone("UTC"));
-  }
-
-  /* Default constructor. */
-  public CustomGraphController() {
-    this.error = new HashSet<String>();
-  }
-
-  public void setParameterMap(Map<String, String[]> parameterMap) {
-    this.parameterMap = parameterMap;
-  }
-
-  /**
-   * Build a copy of the parameters passed to custom-graph.jsp to be
-   * passed to one of the graph image servlets.
-   * */
-  public String getGraphURL() {
-    try {
-      Map<String, String[]> mapCopy =
-          new HashMap<String, String[]>(parameterMap);
-      String graphURI = "/" + mapCopy.get("graph")[0] + ".png";
-
-      /*Make sure we don't pass this key/value to the servlet. */
-      mapCopy.remove("graph");
-
-      /* Make sure the user entered a valid date */
-      if (!mapCopy.containsKey("start") || !mapCopy.containsKey("end")) {
-        error.add("Invalid date format.");
-        return "";
-      }
-      simpledf.parse(mapCopy.get("start")[0]);
-      simpledf.parse(mapCopy.get("end")[0]);
-
-      int i = 0;
-      String uriChar;
-
-      for (Map.Entry<String, String[]> entry : mapCopy.entrySet()) {
-        uriChar = (i == 0) ? "?" : "&";
-        graphURI += (uriChar + entry.getKey() + "=" + entry.getValue()[0]);
-        i++;
-      }
-      return graphURI;
-      /* All of the forms are empty */
-    } catch (ParseException e)  {
-      error.add("Invalid date format.");
-      return "";
-    }
-  }
-
-  public String getGraphName()  {
-    return parameterMap.containsKey("graph") ?
-        parameterMap.get("graph")[0] : "error";
-  }
-  public String getGraphStart() {
-    return parameterMap.containsKey("start") ?
-        parameterMap.get("start")[0] : "error";
-  }
-  public String getGraphEnd() {
-    return parameterMap.containsKey("end") ?
-        parameterMap.get("end")[0] : "error";
-  }
-  public Set<String> getError() {
-    return this.error;
-  }
-}
diff --git a/src/org/torproject/ernie/web/GraphsNetworkServlet.java b/src/org/torproject/ernie/web/GraphsNetworkServlet.java
index 058e698..d2cb9ea 100644
--- a/src/org/torproject/ernie/web/GraphsNetworkServlet.java
+++ b/src/org/torproject/ernie/web/GraphsNetworkServlet.java
@@ -9,7 +9,7 @@ public class GraphsNetworkServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("network.jsp").forward(request,
+    request.getRequestDispatcher("WEB-INF/network.jsp").forward(request,
         response);
   }
 }
diff --git a/src/org/torproject/ernie/web/GraphsPackagesServlet.java b/src/org/torproject/ernie/web/GraphsPackagesServlet.java
index b3b8bed..0ad1039 100644
--- a/src/org/torproject/ernie/web/GraphsPackagesServlet.java
+++ b/src/org/torproject/ernie/web/GraphsPackagesServlet.java
@@ -9,7 +9,7 @@ public class GraphsPackagesServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("packages.jsp").forward(request,
+    request.getRequestDispatcher("WEB-INF/packages.jsp").forward(request,
         response);
   }
 }
diff --git a/src/org/torproject/ernie/web/GraphsPerformanceServlet.java b/src/org/torproject/ernie/web/GraphsPerformanceServlet.java
index df30f84..048146b 100644
--- a/src/org/torproject/ernie/web/GraphsPerformanceServlet.java
+++ b/src/org/torproject/ernie/web/GraphsPerformanceServlet.java
@@ -9,8 +9,8 @@ public class GraphsPerformanceServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("performance.jsp").forward(request,
-        response);
+    request.getRequestDispatcher("WEB-INF/performance.jsp").forward(
+        request, response);
   }
 }
 
diff --git a/src/org/torproject/ernie/web/GraphsServlet.java b/src/org/torproject/ernie/web/GraphsServlet.java
index 6dbaa7f..121881f 100644
--- a/src/org/torproject/ernie/web/GraphsServlet.java
+++ b/src/org/torproject/ernie/web/GraphsServlet.java
@@ -9,7 +9,8 @@ public class GraphsServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("graphs.jsp").forward(request, response);
+    request.getRequestDispatcher("WEB-INF/graphs.jsp").forward(request,
+        response);
   }
 }
 
diff --git a/src/org/torproject/ernie/web/GraphsUsersServlet.java b/src/org/torproject/ernie/web/GraphsUsersServlet.java
index 08c951a..f7bbbb9 100644
--- a/src/org/torproject/ernie/web/GraphsUsersServlet.java
+++ b/src/org/torproject/ernie/web/GraphsUsersServlet.java
@@ -9,7 +9,8 @@ public class GraphsUsersServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("users.jsp").forward(request, response);
+    request.getRequestDispatcher("WEB-INF/users.jsp").forward(request,
+        response);
   }
 }
 
diff --git a/src/org/torproject/ernie/web/IndexServlet.java b/src/org/torproject/ernie/web/IndexServlet.java
index a652fdd..cbf1e10 100644
--- a/src/org/torproject/ernie/web/IndexServlet.java
+++ b/src/org/torproject/ernie/web/IndexServlet.java
@@ -4,12 +4,13 @@ import javax.servlet.*;
 import javax.servlet.http.*;
 import java.io.*;
 
-public class IndexServlet {
+public class IndexServlet extends HttpServlet {
   public void doGet(HttpServletRequest request,
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("index.jsp").forward(request, response);
+    request.getRequestDispatcher("WEB-INF/index.jsp").forward(request,
+        response);
   }
 }
 
diff --git a/src/org/torproject/ernie/web/ResearchDataServlet.java b/src/org/torproject/ernie/web/ResearchDataServlet.java
index 086ad89..9ab5834 100644
--- a/src/org/torproject/ernie/web/ResearchDataServlet.java
+++ b/src/org/torproject/ernie/web/ResearchDataServlet.java
@@ -9,7 +9,8 @@ public class ResearchDataServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("data.jsp").forward(request, response);
+    request.getRequestDispatcher("WEB-INF/data.jsp").forward(request,
+        response);
   }
 }
 
diff --git a/src/org/torproject/ernie/web/ResearchPapersServlet.java b/src/org/torproject/ernie/web/ResearchPapersServlet.java
index 636fb47..c3e8484 100644
--- a/src/org/torproject/ernie/web/ResearchPapersServlet.java
+++ b/src/org/torproject/ernie/web/ResearchPapersServlet.java
@@ -9,7 +9,8 @@ public class ResearchPapersServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("papers.jsp").forward(request, response);
+    request.getRequestDispatcher("WEB-INF/papers.jsp").forward(request,
+        response);
   }
 }
 
diff --git a/src/org/torproject/ernie/web/ResearchServlet.java b/src/org/torproject/ernie/web/ResearchServlet.java
index 8c7b3fd..823e78c 100644
--- a/src/org/torproject/ernie/web/ResearchServlet.java
+++ b/src/org/torproject/ernie/web/ResearchServlet.java
@@ -9,7 +9,7 @@ public class ResearchServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("research.jsp").forward(request,
+    request.getRequestDispatcher("WEB-INF/research.jsp").forward(request,
         response);
   }
 }
diff --git a/src/org/torproject/ernie/web/ResearchToolsServlet.java b/src/org/torproject/ernie/web/ResearchToolsServlet.java
index 121425f..ab9d7df 100644
--- a/src/org/torproject/ernie/web/ResearchToolsServlet.java
+++ b/src/org/torproject/ernie/web/ResearchToolsServlet.java
@@ -9,7 +9,7 @@ public class ResearchToolsServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("tools.jsp").forward(request,
+    request.getRequestDispatcher("WEB-INF/tools.jsp").forward(request,
         response);
   }
 }
diff --git a/src/org/torproject/ernie/web/StatusServlet.java b/src/org/torproject/ernie/web/StatusServlet.java
index b9eeb71..f75ccba 100644
--- a/src/org/torproject/ernie/web/StatusServlet.java
+++ b/src/org/torproject/ernie/web/StatusServlet.java
@@ -9,7 +9,7 @@ public class StatusServlet extends HttpServlet {
       HttpServletResponse response) throws IOException, ServletException {
 
     /* Forward the request to the JSP that does all the hard work. */
-    request.getRequestDispatcher("status.jsp").forward(request,
+    request.getRequestDispatcher("WEB-INF/status.jsp").forward(request,
         response);
   }
 }
diff --git a/src/org/torproject/ernie/web/TemplateController.java b/src/org/torproject/ernie/web/TemplateController.java
deleted file mode 100644
index c505855..0000000
--- a/src/org/torproject/ernie/web/TemplateController.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package org.torproject.ernie.web;
-
-public class TemplateController {
-
-/*
- * TODO
- * Check if referenced template exists. If not,
- * set it to 404 page.
- */
-
-/**
- * The main parent template and layout.
- */
-    private String mainTemplate;
-
-/**
- * The current template, the default is index on initialization
- */
-    private String currentTemplate;
-
-/**
- * This is referencing name.
- */
-    private String templateName;
-
-/**
- * The page title to be displayed.
- */
-    private String title;
-
-    public TemplateController()  {
-        this.mainTemplate = "/WEB-INF/templates/main.tpl.jsp";
-        this.currentTemplate = "/WEB-INF/templates/index.tpl.jsp";
-        this.templateName = "index";
-        this.title = "Tor Metrics Portal";
-    }
-
-/*
-* Setters
-*/
-    public void setTemplate(String tpl) {
-        this.templateName = tpl;
-        this.currentTemplate = "/WEB-INF/templates/" + tpl + ".tpl.jsp";
-    }
-
-    public void setTitle(String title) {
-        this.title=title;
-    }
-
-/*
-* Getters
-*/
-    public String getTemplate() {
-        return currentTemplate;
-    }
-
-    public String getTemplateName() {
-        return templateName;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public String getMainTemplate() {
-        return mainTemplate;
-    }
-}
diff --git a/templates/banner.tpl.jsp b/templates/banner.tpl.jsp
deleted file mode 100644
index 4710ad9..0000000
--- a/templates/banner.tpl.jsp
+++ /dev/null
@@ -1,58 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController"
-    scope="request" />
-<table class="banner" border="0" cellpadding="0" cellspacing="0" summary="">
-<tr>
-  <td class="banner-left">
-    <a href="/index.html">
-      <img src="/images/top-left.png" alt="Click to go to home page"
-           width="193" height="79"></a></td>
-  <td class="banner-middle">
-    <a <% if (template.getTemplateName().equals("index")) {
-        %>class="current" <%} else {%>href="/index.html"<%}%>>Home</a>
-    <a <% if (template.getTemplateName().equals("graphs")) {
-        %>class="current" <%} else {%>href="/graphs.html"<%}%>>Graphs</a>
-    <a <% if (template.getTemplateName().equals("research")) {
-        %>class="current" <%} else {%>href="/research.html"<%}%>>Research</a>
-    <a <% if (template.getTemplateName().equals("status")) {
-        %>class="current" <%} else {%>href="/status.html"<%}%>>Status</a>
-    <%if (template.getTemplateName().startsWith("graphs")) {
-     %><br>
-      <font size="2">
-        <a <%if (template.getTemplateName().contains("network")){
-            %>class="current"<%} else {%>href="/network.html"<%}
-            %>>Network</a>
-        <a <%if (template.getTemplateName().contains("users")) {
-            %>class="current"<%} else {%>href="/users.html"<%}
-            %>>Users</a>
-        <a <%if (template.getTemplateName().contains("packages")) {
-            %>class="current"<%} else {%>href="/packages.html"<%}
-            %>>Packages</a>
-        <a <%if (template.getTemplateName().contains("performance")) {
-            %>class="current"<%} else {%>href="/performance.html"<%}
-            %>>Performance</a>
-      </font>
-    <%} else if (template.getTemplateName().startsWith("status")) {%>
-      <br>
-      <font size="2">
-        <a href="/exonerator.html">ExoneraTor</a>
-        <a href="/relay-search.html">Relay Search</a>
-        <a href="/consensus-health.html">Consensus Health</a>
-      </font>
-    <%} else if (template.getTemplateName().startsWith("research")) { %>
-      <br>
-      <font size="2">
-        <a <%if (template.getTemplateName().contains("papers")) {
-            %>class="current"<%} else {%> href="/papers.html"<%}
-            %>>Papers</a>
-        <a <%if (template.getTemplateName().contains("data")) {
-            %>class="current"<%} else {%> href="/data.html"<%}
-            %>>Data</a>
-        <a <%if (template.getTemplateName().contains("tools")) {
-            %>class="current"<%} else {%> href="/tools.html"<%}
-            %>>Tools</a>
-      </font>
-    <%}%>
-  </td>
-  <td class="banner-right"></td>
-</tr>
-</table>
diff --git a/templates/footer.tpl.jsp b/templates/footer.tpl.jsp
deleted file mode 100644
index abd7740..0000000
--- a/templates/footer.tpl.jsp
+++ /dev/null
@@ -1,13 +0,0 @@
-      <p>This material is supported in part by the National Science
-      Foundation under Grant No. CNS-0959138. Any opinions,
-      finding, and conclusions or recommendations expressed in this
-      material are those of the author(s) and do not necessarily reflect
-      the views of the National Science Foundation.</p>
-      <p>"Tor" and the "Onion Logo" are <a href="https://www.torproject.org/trademark-faq.html.en">registered trademarks</a> of The Tor Project, Inc.</p>
-      <p>Data on this site is freely available under a
-      <a href="http://creativecommons.org/publicdomain/zero/1.0/">CC0 no
-      copyright declaration</a>: To the extent possible under law, the Tor
-      Project has waived all copyright and related or neighboring rights
-      in the data. Graphs are licensed under a
-      <a href="http://creativecommons.org/licenses/by/3.0/us/">Creative
-      Commons Attribution 3.0 United States License</a>.</p>
diff --git a/templates/graphs.tpl.jsp b/templates/graphs.tpl.jsp
deleted file mode 100644
index 5884b23..0000000
--- a/templates/graphs.tpl.jsp
+++ /dev/null
@@ -1,18 +0,0 @@
-        <h2>Tor Metrics Portal: Graphs</h2>
-        <br>
-        <p>The graphs on this page visualize a small portion of the data
-        gathered in the Tor Metrics Project. The following graphs are
-        available:</p>
-        <ul>
-          <li>The <a href="network.html">Network page</a> has numerous
-          statistics on the network of relays and bridges.</li>
-          <li>The <a href="users.html">Users page</a> attempts to estimate
-          the number of users in the network.</li>
-          <li>There are numerous ways to download the Tor software. The
-          <a href="packages.html">Packages page</a> has statistics on the
-          number of packages requested from GetTor.</li>
-          <li>There are active and passive performance measurements of the
-          Tor network available on the
-          <a href="performance.html">Performance page</a>.</li>
-        </ul>
-        <br>
diff --git a/templates/graphs_network.tpl.jsp b/templates/graphs_network.tpl.jsp
deleted file mode 100644
index 965deed..0000000
--- a/templates/graphs_network.tpl.jsp
+++ /dev/null
@@ -1,274 +0,0 @@
-<%@page import="java.util.*" %>
-<h2>Tor Metrics Portal: Network</h2>
-<br>
-<h3>Relays and bridges in the network</h3>
-<br>
-<p>The following graph shows the average daily number of relays and
-bridges in the network.</p>
-<a name="networksize"></a>
-<%
-StringBuilder networksizeUrl = new StringBuilder("networksize.png");
-if ("networksize".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (parameters.size() > 0) {
-    networksizeUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        networksizeUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + networksizeUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Network size graph\">");
-%><form action="network.html#networksize">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="networksize">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("networksize".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("networksize".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<p><a href="csv/networksize.csv">CSV</a> file containing all data.</p>
-<br>
-
-<h3>Relays with Exit, Fast, Guard, and Stable flags</h3>
-<br>
-<p>The directory authorities assign certain flags to relays that clients
-use for their path selection decisions. The following graph shows the
-average number of relays with these flags assigned.</p>
-<a name="relayflags"></a>
-<%
-StringBuilder relayflagsUrl = new StringBuilder("relayflags.png");
-if ("relayflags".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  String[] flagParameters = request.getParameterValues("flag");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (flagParameters != null && flagParameters.length > 0) {
-    for (String flag : flagParameters) {
-      if (flag != null && flag.length() > 0) {
-        parameters.add("flag=" + flag);
-      }
-    }
-  }
-  if (parameters.size() > 0) {
-    relayflagsUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        relayflagsUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + relayflagsUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Relay flags graph\">");
-%><form action="network.html#relayflags">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="relayflags">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("relayflags".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("relayflags".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-      <label>Relay flags: </label>
-      <input type="checkbox" name="flag" value="Running"> Running
-      <input type="checkbox" name="flag" value="Exit"> Exit
-      <input type="checkbox" name="flag" value="Fast"> Fast
-      <input type="checkbox" name="flag" value="Guard"> Guard
-      <input type="checkbox" name="flag" value="Stable"> Stable
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<br>
-
-<h3>Relays by version</h3>
-<br>
-<p>Relays report the Tor version that they are running to the directory
-authorities. The following graph shows the number of relays by
-version.</p>
-<a name="versions"></a>
-<%
-StringBuilder versionsUrl = new StringBuilder("versions.png");
-if ("versions".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (parameters.size() > 0) {
-    versionsUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        versionsUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + versionsUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Relay versions graph\">");
-%><form action="network.html#versions">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="versions">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("versions".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("versions".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<br>
-
-<h3>Relays by platform</h3>
-<br>
-<p>Relays report the operating system they are running to the directory
-authorities. The following graph shows the number of relays by
-platform.</p>
-<a name="platforms"></a>
-<%
-StringBuilder platformsUrl = new StringBuilder("platforms.png");
-if ("platforms".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (parameters.size() > 0) {
-    platformsUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        platformsUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + platformsUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Relay platforms graph\">");
-%><form action="network.html#platforms">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="platforms">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("platforms".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("platforms".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<br>
-
-<h3>Total relay bandwidth in the network</h3>
-<br>
-<p>Relays report how much bandwidth they are willing to contribute and how
-many bytes they have read and written in the past 24 hours. The following
-graph shows total advertised bandwidth and bandwidth history of all relays
-in the network.</p>
-<a name="bandwidth"></a>
-<%
-StringBuilder bandwidthUrl = new StringBuilder("bandwidth.png");
-if ("bandwidth".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (parameters.size() > 0) {
-    bandwidthUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        bandwidthUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + bandwidthUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Relay bandwidth graph\">");
-%><form action="network.html#bandwidth">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="bandwidth">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("bandwidth".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("bandwidth".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<br>
-
diff --git a/templates/graphs_packages.tpl.jsp b/templates/graphs_packages.tpl.jsp
deleted file mode 100644
index cfa04f1..0000000
--- a/templates/graphs_packages.tpl.jsp
+++ /dev/null
@@ -1,68 +0,0 @@
-<%@page import="java.util.*" %>
-<h2>Tor Metrics Portal: Downloaded Packages</h2>
-<br>
-<h3>Packages requested from GetTor</h3>
-<br>
-<p>GetTor allows users to fetch the Tor software via email. The following
-graph shows the number of packages requested from GetTor per day.</p>
-<p>
-<a name="gettor"></a>
-<%
-if ("gettor".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end"),
-      bundleParameter = request.getParameter("bundle");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (bundleParameter != null && bundleParameter.length() > 0) {
-    parameters.add("bundle=" + bundleParameter);
-  }
-  StringBuilder url = new StringBuilder("gettor.png");
-  if (parameters.size() > 0) {
-    url.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        url.append("&" + parameters.get(i));
-      }
-    }
-  }
-  out.println("<img src=\"" + url.toString() + "\" width=\"576\" "
-      + "height=\"360\" alt=\"GetTor graph\">");
-} else {%>
-  <img src="gettor.png" width="576" height="360" alt="GetTor graph">
-<%
-}
-%><form action="packages.html#gettor">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="gettor">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("gettor".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("gettor".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-      Packages:
-      <input type="radio" name="bundle" value="all"> Total packages
-      <input type="radio" name="bundle" value="en"> TBB (en)
-      <input type="radio" name="bundle" value="zh_CN"> TBB (zh_CN)
-      <input type="radio" name="bundle" value="fa"> TBB (fa)
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-
-<p><a href="csv/gettor.csv">CSV</a> file containing all data.</p>
-<br>
diff --git a/templates/graphs_performance.tpl.jsp b/templates/graphs_performance.tpl.jsp
deleted file mode 100644
index 830e71f..0000000
--- a/templates/graphs_performance.tpl.jsp
+++ /dev/null
@@ -1,77 +0,0 @@
-<%@page import="java.util.*" %>
-<h2>Tor Metrics Portal: Performance</h2>
-<br>
-<h3>Time to download files over Tor</h3>
-<br>
-<p>The following graphs show the performance of the Tor network as
-experienced by its users. The graphs contain the average (median) time to
-request files of three different sizes over Tor as well as first and third
-quartile of request times.</p>
-<a name="torperf"></a>
-<%
-if ("torperf".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end"),
-      sourceParameter = request.getParameter("source"),
-      filesizeParameter = request.getParameter("filesize");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (sourceParameter != null && sourceParameter.length() > 0) {
-    parameters.add("source=" + sourceParameter);
-  }
-  if (filesizeParameter != null && filesizeParameter.length() > 0) {
-    parameters.add("filesize=" + filesizeParameter);
-  }
-  StringBuilder url = new StringBuilder("torperf.png");
-  if (parameters.size() > 0) {
-    url.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        url.append("&" + parameters.get(i));
-      }
-    }
-  }
-  out.println("<img src=\"" + url.toString() + "\" width=\"576\" "
-      + "height=\"360\" alt=\"Torperf graph\">");
-} else {%>
-  <img src="torperf.png" width="576" height="360" alt="Torperf graph">
-<%
-}
-%><form action="performance.html#torperf">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="torperf">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("torperf".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("torperf".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-      Source:
-      <input type="radio" name="source" value="torperf"> torperf
-      <input type="radio" name="source" value="moria"> moria
-      <input type="radio" name="source" value="siv"> siv
-    </p><p>
-      <label>File size: </label>
-      <input type="radio" name="filesize" value="50kb"> 50 KiB
-      <input type="radio" name="filesize" value="1mb"> 1 MiB
-      <input type="radio" name="filesize" value="5mb"> 5 MiB
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-
-<p><a href="csv/torperf.csv">CSV</a> file containing all data.</p>
-<br>
diff --git a/templates/graphs_users.tpl.jsp b/templates/graphs_users.tpl.jsp
deleted file mode 100644
index 25bef9d..0000000
--- a/templates/graphs_users.tpl.jsp
+++ /dev/null
@@ -1,286 +0,0 @@
-<%@page import="java.util.*" %>
-<h2>Tor Metrics Portal: Users</h2>
-<br>
-<h3>New or returning, directly connecting Tor users</h3>
-<br>
-<p>Users connecting to the Tor network for the first time request a list
-of running relays from one of currently seven directory authorities.
-Likewise, returning users whose network information is out of date connect
-to one of the directory authorities to download a fresh list of relays.
-The following graphs display an estimate of new or returning Tor users
-based on the requests as seen by gabelmoo, one of the directory
-authorities.</p>
-<a name="new-users"></a>
-<%
-StringBuilder newUsersUrl = new StringBuilder("new-users.png");
-if ("new-users".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  String[] countryParameters = request.getParameterValues("country");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (countryParameters != null && countryParameters.length > 0) {
-    for (String country : countryParameters) {
-      if (country != null && country.length() > 0) {
-        parameters.add("country=" + country);
-      }
-    }
-  }
-  if (parameters.size() > 0) {
-    newUsersUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        newUsersUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + newUsersUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"New users graph\">");
-%><form action="users.html#new-users">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="new-users">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("new-users".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("new-users".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-      Source:
-      <input type="radio" name="country" value="all">All users
-      <input type="radio" name="country" value="au">Australia
-      <input type="radio" name="country" value="bh">Bahrain
-      <input type="radio" name="country" value="br">Brazil
-      <input type="radio" name="country" value="ca">Canada
-      <input type="radio" name="country" value="cn">China
-      <input type="radio" name="country" value="cu">Cuba
-      <input type="radio" name="country" value="de">Germany
-      <input type="radio" name="country" value="et">Ethiopia
-      <input type="radio" name="country" value="fr">France
-      <input type="radio" name="country" value="gb">U.K.
-      <input type="radio" name="country" value="ir">Iran
-      <input type="radio" name="country" value="it">Italy
-      <input type="radio" name="country" value="jp">Japan
-      <input type="radio" name="country" value="kr">South Korea
-      <input type="radio" name="country" value="mm">Burma
-      <input type="radio" name="country" value="pl">Poland
-      <input type="radio" name="country" value="ru">Russia
-      <input type="radio" name="country" value="sa">Saudi Arabia
-      <input type="radio" name="country" value="se">Sweden
-      <input type="radio" name="country" value="sy">Syria
-      <input type="radio" name="country" value="tn">Tunisia
-      <input type="radio" name="country" value="tm">Turkmenistan
-      <input type="radio" name="country" value="us">U.S.A.
-      <input type="radio" name="country" value="uz">Uzbekistan
-      <input type="radio" name="country" value="vn">Vietnam
-      <input type="radio" name="country" value="ye">Yemen
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<p><a href="csv/new-users.csv">CSV</a> file containing all data.</p>
-<br>
-
-<h3>Recurring, directly connecting Tor users</h3>
-<br>
-<p>After being connected to the Tor network, users need to refresh their
-list of running relays on a regular basis. They send their requests to one
-out of a few hundred directory mirrors to save bandwidth of the directory
-authorities. The following graphs show an estimate of recurring Tor users
-based on the requests as seen by trusted, a particularly fast directory
-mirror.</p>
-<a name="direct-users"></a>
-<%
-StringBuilder directUsersUrl = new StringBuilder("direct-users.png");
-if ("direct-users".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  String[] countryParameters = request.getParameterValues("country");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (countryParameters != null && countryParameters.length > 0) {
-    for (String country : countryParameters) {
-      if (country != null && country.length() > 0) {
-        parameters.add("country=" + country);
-      }
-    }
-  }
-  if (parameters.size() > 0) {
-    directUsersUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        directUsersUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + directUsersUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Recurring users graph\">");
-%><form action="users.html#direct-users">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="direct-users">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("direct-users".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("direct-users".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-      Source:
-      <input type="radio" name="country" value="all">All users
-      <input type="radio" name="country" value="au">Australia
-      <input type="radio" name="country" value="bh">Bahrain
-      <input type="radio" name="country" value="br">Brazil
-      <input type="radio" name="country" value="ca">Canada
-      <input type="radio" name="country" value="cn">China
-      <input type="radio" name="country" value="cu">Cuba
-      <input type="radio" name="country" value="de">Germany
-      <input type="radio" name="country" value="et">Ethiopia
-      <input type="radio" name="country" value="fr">France
-      <input type="radio" name="country" value="gb">U.K.
-      <input type="radio" name="country" value="ir">Iran
-      <input type="radio" name="country" value="it">Italy
-      <input type="radio" name="country" value="jp">Japan
-      <input type="radio" name="country" value="kr">South Korea
-      <input type="radio" name="country" value="mm">Burma
-      <input type="radio" name="country" value="pl">Poland
-      <input type="radio" name="country" value="ru">Russia
-      <input type="radio" name="country" value="sa">Saudi Arabia
-      <input type="radio" name="country" value="se">Sweden
-      <input type="radio" name="country" value="sy">Syria
-      <input type="radio" name="country" value="tn">Tunisia
-      <input type="radio" name="country" value="tm">Turkmenistan
-      <input type="radio" name="country" value="us">U.S.A.
-      <input type="radio" name="country" value="uz">Uzbekistan
-      <input type="radio" name="country" value="vn">Vietnam
-      <input type="radio" name="country" value="ye">Yemen
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<p><a href="csv/direct-users.csv">CSV</a> file containing all data.</p>
-<p><a href="csv/monthly-users-peak.csv">CSV</a> file containing peak daily
-Tor users (recurring and bridge) per month by country.</p>
-<p><a href="csv/monthly-users-average.csv">CSV</a> file containing average
-daily Tor users (recurring and bridge) per month by country.</p>
-<br>
-
-<h3>Tor users via bridges</h3>
-<br>
-<p>Users who cannot connect directly to the Tor network instead connect
-via bridges, which are non-public relays. The following graphs display an
-estimate of Tor users via bridges based on the unique IP addresses as seen
-by a few hundred bridges.</p>
-<a name="bridge-users"></a>
-<%
-StringBuilder bridgeUsersUrl = new StringBuilder("bridge-users.png");
-if ("bridge-users".equals(request.getParameter("graph"))) {
-  List<String> parameters = new ArrayList<String>();
-// TODO check values here!
-  String startParameter = request.getParameter("start"),
-      endParameter = request.getParameter("end");
-  String[] countryParameters = request.getParameterValues("country");
-  if (startParameter != null && startParameter.length() > 0) {
-    parameters.add("start=" + startParameter);
-  }
-  if (endParameter != null && endParameter.length() > 0) {
-    parameters.add("end=" + endParameter);
-  }
-  if (countryParameters != null && countryParameters.length > 0) {
-    for (String country : countryParameters) {
-      if (country != null && country.length() > 0) {
-        parameters.add("country=" + country);
-      }
-    }
-  }
-  if (parameters.size() > 0) {
-    bridgeUsersUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        bridgeUsersUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + bridgeUsersUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Bridge users graph\">");
-%><form action="users.html#bridge-users">
-  <div class="formrow">
-    <input type="hidden" name="graph" value="bridge-users">
-    <p>
-    <label>Start date (yyyy-mm-dd):</label>
-      <input type="text" name="start" size="10"
-        value="<%=("bridge-users".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
-    <label>End date (yyyy-mm-dd):</label>
-      <input type="text" name="end" size="10"
-        value="<%=("bridge-users".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
-    </p><p>
-      Source:
-      <input type="radio" name="country" value="all">All users
-      <input type="radio" name="country" value="au">Australia
-      <input type="radio" name="country" value="bh">Bahrain
-      <input type="radio" name="country" value="br">Brazil
-      <input type="radio" name="country" value="ca">Canada
-      <input type="radio" name="country" value="cn">China
-      <input type="radio" name="country" value="cu">Cuba
-      <input type="radio" name="country" value="de">Germany
-      <input type="radio" name="country" value="et">Ethiopia
-      <input type="radio" name="country" value="fr">France
-      <input type="radio" name="country" value="gb">U.K.
-      <input type="radio" name="country" value="ir">Iran
-      <input type="radio" name="country" value="it">Italy
-      <input type="radio" name="country" value="jp">Japan
-      <input type="radio" name="country" value="kr">South Korea
-      <input type="radio" name="country" value="mm">Burma
-      <input type="radio" name="country" value="pl">Poland
-      <input type="radio" name="country" value="ru">Russia
-      <input type="radio" name="country" value="sa">Saudi Arabia
-      <input type="radio" name="country" value="se">Sweden
-      <input type="radio" name="country" value="sy">Syria
-      <input type="radio" name="country" value="tn">Tunisia
-      <input type="radio" name="country" value="tm">Turkmenistan
-      <input type="radio" name="country" value="us">U.S.A.
-      <input type="radio" name="country" value="uz">Uzbekistan
-      <input type="radio" name="country" value="vn">Vietnam
-      <input type="radio" name="country" value="ye">Yemen
-    </p><p>
-    <input class="submit" type="submit" value="Update graph">
-    </p>
-  </div>
-</form>
-<p><a href="csv/bridge-users.csv">CSV</a> file containing all data.</p>
-<p><a href="csv/monthly-users-peak.csv">CSV</a> file containing peak daily
-Tor users (recurring and bridge) per month by country.</p>
-<p><a href="csv/monthly-users-average.csv">CSV</a> file containing average
-daily Tor users (recurring and bridge) per month by country.</p>
-<br>
-
diff --git a/templates/index.tpl.jsp b/templates/index.tpl.jsp
deleted file mode 100644
index d7e74f4..0000000
--- a/templates/index.tpl.jsp
+++ /dev/null
@@ -1,41 +0,0 @@
-        <h2>Tor Metrics Portal</h2>
-        <br>
-        <p>The Tor Metrics Portal aggregates all kinds of interesting
-        data about the Tor network and visualizes them in graphs and
-        reports. This portal also provides easy access to the underlying
-        data and documentation for performing own analyses based on these
-        data. Find out more here:</p>
-        <ul>
-          <li>View daily updated <a href="graphs.html">graphs</a> on
-          estimated client numbers, on network performance, and other
-          statistics on the Tor network</li>
-          <li>Read <a href="papers.html">papers</a> and technical reports
-          on the measurement techniques and results of statistical
-          analysis of metrics data</li>
-          <li>Download the <a href="data.html">data</a> that is behind the
-          graphs and reports to make your own evaluations</li>
-          <li>Try out the <a href="tools.html">tools</a> to parse and
-          evaluate the metrics data</li>
-        </ul>
-        <br>
-        <h3>News</h3>
-        <ul>
-          <li>October 7, 2010: Custom graphs are now available for all
-          <a href="graphs.html">graphs</a>. Based on work by Kevin
-          Berry.</li>
-          <li>September 9, 2010: Custom
-          graphs on network size, relay platforms, versions, and
-          observed bandwidth available. Implemented by Kevin Berry.</li>
-          <li>September 2, 2010: New <a href="relay-search.html">relay
-          search</a> feature available.</li>
-          <li>August 31, 2010: Named relays can now be found more easily
-          on the consensus-health page by using anchors, e.g.,
-          <a href="consensus-health.html#gabelmoo">https://metrics.torproject.org/consensus-health.html#gabelmoo</a> .</li>
-          <li>August 16, 2010: There are now
-          <a href="users.html">graphs</a> for total new or returning and
-          recurring directly connecting users and total bridge users as
-          well as
-          for users coming from Australia, Brazil, Canada, France,
-          Germany, Italy, Japan, Poland, Russia, South Korea, Sweden,
-          U.K., and U.S.A.</li>
-        </ul>
diff --git a/templates/main.tpl.jsp b/templates/main.tpl.jsp
deleted file mode 100644
index 129022e..0000000
--- a/templates/main.tpl.jsp
+++ /dev/null
@@ -1,20 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
-<html>
-<head>
-  <title><jsp:getProperty name="template" property="title"/></title>
-  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
-  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
-  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
-</head>
-<body>
-  <div class="center">
-    <jsp:include page='/WEB-INF/templates/banner.tpl.jsp' />
-    <div class="main-column">
-      <jsp:include page='<%=template.getTemplate()%>' />
-    </div>
-  </div>
-  <div class="bottom" id="bottom">
-    <jsp:include page='/WEB-INF/templates/footer.tpl.jsp' />
-  </div>
-</body>
-</html>
diff --git a/templates/research.tpl.jsp b/templates/research.tpl.jsp
deleted file mode 100644
index d1e6cc0..0000000
--- a/templates/research.tpl.jsp
+++ /dev/null
@@ -1,11 +0,0 @@
-        <h2>Tor Metrics Portal: Research</h2>
-        <br>
-        <p>The Tor Metrics Project aims at supporting privacy enhancing
-        technologies research by making <a href="papers.html">papers</a>
-        and technical reports, gathered network
-        <a href="data.html">data</a>, and
-        <a href="tools.html">tools</a>
-        for processing these data available to the public. If you are
-        missing anything for your Tor-related research or want to share
-        your research results with others, please
-        <a href="mailto:tor-assistants at freehaven.net">let us know</a>!
diff --git a/templates/research_data.tpl.jsp b/templates/research_data.tpl.jsp
deleted file mode 100644
index 2084e10..0000000
--- a/templates/research_data.tpl.jsp
+++ /dev/null
@@ -1,321 +0,0 @@
-<%@page import="java.io.*" %>
-<%@page import="java.util.*" %>
-<%
-    /* Read /srv/metrics.torproject.org/ernie/remote-files-for-data-page,
-     * if that file exists, and add the contained URLs to this page.
-     * TODO make file location configurable. */
-    SortedMap<String, String> allFiles = new TreeMap<String, String>();
-    File remoteFiles = new File("/srv/metrics.torproject.org/ernie/"
-        + "remote-files-for-data-page");
-    if (remoteFiles.exists() && !remoteFiles.isDirectory()) {
-      BufferedReader br = new BufferedReader(new FileReader(remoteFiles));
-      String line = null;
-      while ((line = br.readLine()) != null) {
-        if (line.startsWith("#") || !line.contains("/")) {
-          continue;
-        }
-        allFiles.put(line.substring(line.lastIndexOf("/") + 1), line);
-      }
-    }
-    /* Add files in /srv/metrics.torproject.org/ernie/website/data/ to
-     * list of provided files on this page. TODO make dir location
-     * configurable. */
-    File localFiles =
-        new File("/srv/metrics.torproject.org/ernie/website/data/");
-    if (localFiles.exists() && localFiles.isDirectory()) {
-      for (File file : localFiles.listFiles()) {
-        allFiles.put(file.getName(), "/data/" + file.getName());
-      }
-    }
-%>
-        <h2>Tor Metrics Portal: Data</h2>
-        <br>
-        <p>One of the main goals of the Tor Metrics Project is to make all
-        gathered data available to the public. This approach enables
-        privacy researchers to perform their own analyses using real data
-        on the Tor network, and it acts as a safeguard to not gather data
-        that are too sensitive to publish. All signatures can be
-        <a href="https://www.torproject.org/verifying-signatures">verified</a>
-        using Karsten's PGP key (0xF7C11265). The following data are
-        available (see the <a href="tools.html">Tools</a> section for
-        details on processing the files):</p>
-        <ul>
-          <li><a href="#relaydesc">Relay descriptor archives</a></li>
-          <li><a href="#bridgedesc">Bridge descriptor archives</a></li>
-          <li><a href="#stats">Statistics produced by relays</a></li>
-          <li><a href="#performance">Performance data</a></li>
-          <li><a href="#exitlist">Exit lists</a></li>
-        </ul>
-        <br>
-        <a name="relaydesc"></a>
-        <h3>Relay descriptor archives</h3>
-        <br>
-        <p>The relay descriptor archives contain all documents that the
-        directory authorities make available about the network of relays.
-        These documents include network statuses, server (relay)
-        descriptors, and extra-info descriptors:</p>
-        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
-<%
-    String firstYearMonth = null, lastYearMonth = null;
-    for (Map.Entry<String, String> e : allFiles.entrySet()) {
-      String filename = e.getKey();
-      if (!filename.endsWith(".asc") &&
-          (filename.startsWith("tor-20") ||
-          filename.startsWith("statuses-20") ||
-          filename.startsWith("server-descriptors-20") ||
-          filename.startsWith("extra-infos-20") ||
-          filename.startsWith("votes-20") ||
-          filename.startsWith("consensuses-20"))) {
-        String yearMonth = filename.substring(filename.indexOf("20"));
-        yearMonth = yearMonth.substring(0, 7);
-        if (firstYearMonth == null ||
-            yearMonth.compareTo(firstYearMonth) < 0) {
-          firstYearMonth = yearMonth;
-        }
-        if (lastYearMonth == null ||
-            yearMonth.compareTo(lastYearMonth) > 0) {
-          lastYearMonth = yearMonth;
-        }
-      }
-    }
-    String currentYearMonth = firstYearMonth;
-    String[] monthNames = new String[] { "January", "February", "March",
-        "April", "May", "June", "July", "August", "September", "October",
-        "November", "December" };
-    String[] prefixes = new String[] { "tor-", "statuses-",
-        "server-descriptors-", "extra-infos-", "votes-", "consensuses-" };
-    String[] descriptions = new String[] { "v1 directories",
-        "v2 statuses", "server descriptors", "extra-infos", "v3 votes",
-        "v3 consensuses" };
-    Set<String> printedFiles = new HashSet<String>();
-    while (currentYearMonth.compareTo(lastYearMonth) <= 0) {
-      int currentYear = Integer.parseInt(currentYearMonth.substring(
-          0, 4));
-      int currentMonth = Integer.parseInt(currentYearMonth.substring(
-          5, 7));
-      out.write("          <tr>\n            <td>"
-          + monthNames[currentMonth - 1] + " " + currentYear + "</td>\n");
-      for (int i = 0; i < prefixes.length; i++) {
-        String prefix = prefixes[i];
-        String description = descriptions[i];
-        String file = prefix + currentYearMonth + ".tar.bz2";
-        String sig = file + ".asc";
-        if (allFiles.containsKey(file)) {
-          out.write("            <td><a href=\"" + allFiles.get(file)
-              + "\">" + description + "</a>");
-          printedFiles.add(file);
-          if (allFiles.containsKey(sig)) {
-            out.write("\n              (<a href=\"" + allFiles.get(sig)
-                + "\">sig</a>)</td>\n");
-            printedFiles.add(sig);
-          } else {
-            out.write("</td>\n");
-          }
-        } else {
-          out.write("            <td></td>\n");
-        }
-      }
-      out.write("          </tr>\n");
-      if (currentMonth < 12) {
-        currentMonth++;
-      } else {
-        currentYear++;
-        currentMonth = 1;
-      }
-      currentYearMonth = String.format("%d-%02d", currentYear,
-          currentMonth);
-    }
-%>
-        </table>
-        <br>
-        <a name="bridgedesc"></a>
-        <h3>Bridge descriptor archives</h3>
-        <br>
-        <p>The bridge descriptor archives contain similar documents as the
-        relay descriptor archives, but for the non-public bridges. The
-        descriptors have been sanitized before publication to remove all
-        information that could otherwise be used to locate bridges.
-        Beginning with May 2010, we stopped resolving IP addresses to
-        country codes and including those in the sanitized descriptors,
-        because it was tough to maintain; if your research requires this
-        or any other detail, contact us and we'll sort something out. The
-        files below contain all documents of a given month:</p>
-        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
-<%
-    firstYearMonth = lastYearMonth = null;
-    for (Map.Entry<String, String> e : allFiles.entrySet()) {
-      String filename = e.getKey();
-      if (!filename.endsWith(".asc") &&
-          filename.startsWith("bridge-descriptors-20")) {
-        String yearMonth = filename.substring(filename.indexOf("20"));
-        yearMonth = yearMonth.substring(0, 7);
-        if (firstYearMonth == null ||
-            yearMonth.compareTo(firstYearMonth) < 0) {
-          firstYearMonth = yearMonth;
-        }
-        if (lastYearMonth == null ||
-            yearMonth.compareTo(lastYearMonth) > 0) {
-          lastYearMonth = yearMonth;
-        }
-      }
-    }
-    currentYearMonth = firstYearMonth;
-    while (currentYearMonth.compareTo(lastYearMonth) <= 0) {
-      int currentYear = Integer.parseInt(currentYearMonth.substring(
-          0, 4));
-      int currentMonth = Integer.parseInt(currentYearMonth.substring(
-          5, 7));
-      String file = "bridge-descriptors-" + currentYearMonth + ".tar.bz2";
-      String sig = file + ".asc";
-      if (allFiles.containsKey(file)) {
-        out.write("          <tr><td><a href=\"" + allFiles.get(file)
-            + "\">" + monthNames[currentMonth - 1] + " " + currentYear
-            + "</a>");
-        printedFiles.add(file);
-        if (allFiles.containsKey(sig)) {
-          out.write("\n              (<a href=\"" + allFiles.get(sig)
-                + "\">sig</a>)</td></tr>\n");
-          printedFiles.add(sig);
-        } else {
-          out.write("</td></tr>\n");
-        }
-      }
-      if (currentMonth < 12) {
-        currentMonth++;
-      } else {
-        currentYear++;
-        currentMonth = 1;
-      }
-      currentYearMonth = String.format("%d-%02d", currentYear,
-          currentMonth);
-    }
-%>
-        </table>
-        <p></p>
-        <br>
-        <a name="stats"></a>
-        <h3>Statistics produced by relays</h3>
-        <br>
-        <p>Some of the relays are configured to gather statistics on the
-        number of requests or connecting clients, the number of processed
-        cells per queue, or the number of exiting bytes per port. Relays
-        running version 0.2.2.4-alpha can include these statistics in
-        extra-info descriptors, so that they are included in the relay
-        descriptor archives. The following files contain the statistics
-        produced by relays running earlier versions:</p>
-        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
-<%
-    SortedSet<String> statsSources = new TreeSet<String>();
-    for (Map.Entry<String, String> e : allFiles.entrySet()) {
-      String filename = e.getKey();
-      if (!filename.endsWith(".asc") &&
-          (filename.startsWith("buffer-") ||
-          filename.startsWith("dirreq-") ||
-          filename.startsWith("entry-") ||
-          (filename.startsWith("exit-") &&
-          !filename.startsWith("exit-list-")))) {
-        statsSources.add(filename.substring(filename.indexOf("-") + 1));
-      }
-    }
-    prefixes = new String[] { "buffer-", "dirreq-", "entry-", "exit-" };
-    for (String source : statsSources) {
-      String nickname = source.split("-")[0];
-      String fingerprint = source.split("-")[1];
-      fingerprint = fingerprint.substring(0, 8);
-      out.write("          <tr>\n            <td>" + nickname + " ("
-          + fingerprint + ")</td>\n");
-      for (int i = 0; i < prefixes.length; i++) {
-        String prefix = prefixes[i];
-        String file = prefix + source;
-        String sig = file + ".asc";
-        if (allFiles.containsKey(file)) {
-          out.write("            <td><a href=\"" + allFiles.get(file)
-              + "\">" + prefix + "stats</a>");
-          printedFiles.add(file);
-          if (allFiles.containsKey(sig)) {
-            out.write("\n              (<a href=\"" + allFiles.get(sig)
-                + "\">sig</a>)</td>\n");
-            printedFiles.add(sig);
-          } else {
-            out.write("</td>\n");
-          }
-        } else {
-          out.write("            <td></td>\n");
-        }
-      }
-      out.write("          </tr>\n");
-    }
-%>
-        </table>
-        <br>
-        <a name="performance"></a>
-        <h3>Performance data</h3>
-        <br>
-        <p>We are measuring the performance of the Tor network by
-        periodically requesting files of different sizes and recording the
-        time needed to do so. The files below contain the output of the
-        torperf application and are updated every hour:</p>
-        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
-<%
-    SortedSet<String> torperfSources = new TreeSet<String>();
-    for (Map.Entry<String, String> e : allFiles.entrySet()) {
-      String filename = e.getKey();
-      if (filename.endsWith("b.data")) {
-        torperfSources.add(filename.substring(0, filename.indexOf("-")));
-      }
-    }
-    for (String source : torperfSources) {
-      out.write("          <tr>\n            <td>" + source + "</td>\n");
-      String file = source + "-50kb.data";
-      if (allFiles.containsKey(file)) {
-        out.write("            <td><a href=\"" + allFiles.get(file)
-            + "\">50 KiB requests</a></td>\n");
-        printedFiles.add(file);
-      } else {
-        out.write("            <td></td>\n");
-      }
-      file = source + "-1mb.data";
-      if (allFiles.containsKey(file)) {
-        out.write("            <td><a href=\"" + allFiles.get(file)
-            + "\">1 MiB requests</a></td>\n");
-        printedFiles.add(file);
-      } else {
-        out.write("            <td></td>\n");
-      }
-      file = source + "-5mb.data";
-      if (allFiles.containsKey(file)) {
-        out.write("            <td><a href=\"" + allFiles.get(file)
-            + "\">5 MiB requests</a></td>\n");
-        printedFiles.add(file);
-      } else {
-        out.write("            <td></td>\n");
-      }
-      out.write("          </tr>\n");
-    }
-%>
-        </table>
-        <br>
-        <a name="exitlist"></a>
-        <h3>Exit lists</h3>
-        <br>
-        <p>We are archiving the bulk exit lists used by
-        <a href="https://check.torproject.org/">Tor Check</a> containing
-        the IP addresses that exit relays exit from:</p>
-        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
-<%
-    for (Map.Entry<String, String> e : allFiles.entrySet()) {
-      String file = e.getKey();
-      String url = e.getValue();
-      if (file.startsWith("exit-list")) {
-        String yearMonth = file.substring(file.indexOf("exit-list-")
-            + "exit-list-".length());
-        String year = yearMonth.substring(0, 4);
-        String monthName = monthNames[Integer.parseInt(
-            yearMonth.substring(5, 7)) - 1];
-        out.write("          <tr><td><a href=\"" + url + "\">"
-            + monthName + " " + year + "</a></td></tr>\n");
-        printedFiles.add(file);
-      }
-    }
-%>
-        </table>
diff --git a/templates/research_papers.tpl.jsp b/templates/research_papers.tpl.jsp
deleted file mode 100644
index 7856494..0000000
--- a/templates/research_papers.tpl.jsp
+++ /dev/null
@@ -1,65 +0,0 @@
-        <h2>Tor Metrics Portal: Papers</h2>
-        <br>
-        <p>The <a href="#papers">papers</a> and
-        <a href="#techreports">technical reports</a> listed on this page
-        originate from, or are based on, work performed in the Tor Metrics
-        Project.</p>
-        <br>
-        <a name="papers"></a>
-        <h3>Papers</h3>
-        <br>
-        These papers summarize some of the results of of the Tor Metrics
-        Project and have been accepted for publication at academic
-        conferences or workshops.
-        <ul>
-          <li>Karsten Loesing. Measuring the Tor Network from Public
-          Directory Information. 2nd Hot Topics in Privacy Enhancing
-          Technologies (HotPETs 2009), Seattle, WA, USA, August 2009.
-          (<a href="papers/hotpets09.pdf">PDF</a>, 582K)</li>
-          <li>Karsten Loesing, Steven J. Murdoch, Roger Dingledine. A Case
-          Study on Measuring Statistical Data in the Tor Anonymity
-          Network. Accepted for publication at Workshop on Ethics in
-          Computer Security Research (WECSR 2010), Tenerife, Spain,
-          January 2010. (<a href="papers/wecsr10.pdf">PDF</a>, 160K)</li>
-        </ul>
-        <br>
-        <a name="techreports"></a>
-        <h3>Technical reports</h3>
-        <br>
-        The technical reports listed here have been the first place to
-        publish novel kinds of statistics on the Tor network. Some, but
-        not all, of the results contained in these technical reports have
-        been included in the <a href="#papers">papers</a> above or in the
-        daily updated <a href="graphs.html">graphs</a>.
-        <ul>
-          <li>Evaluation of Relays from Public Directory Data
-          (<a href="papers/dirarch-2009-06-22.pdf">PDF</a>,
-          558K, last updated on June 22, 2009).</li>
-          <li>Analysis of Bridge Usage in Tor
-          (<a href="papers/bridges-2009-06-22.pdf">PDF</a>,
-          76K, last updated on June 22, 2009).</li>
-          <li>Evaluation of Client Requests to the Directories to
-          determine total numbers and countries of users
-          (<a href="papers/directory-requests-2009-06-25.pdf">PDF</a>,
-          207K, last updated on June 25, 2009).</li>
-          <li>Possible problems of directory authorities assigning Stable
-          and Guard flags
-          (<a href="papers/relayflags-2009-04-01.pdf">PDF</a>,
-          2M from April 1, 2009).</li>
-          <li>Simulation of the number of Fast, Stable, and Guard flags
-          for changed requirements
-          (<a href="papers/flagrequirements-2009-04-11.pdf">PDF</a>,
-          229K from April 11, 2009).</li>
-          <li>Reducing the Circuit Window Size in Tor
-          (<a href="papers/circwindow-2009-09-20.pdf">PDF</a>,
-          137K from September 20, 2009).</li>
-          <li>Performance of Requests over the Tor Network
-          (<a href="papers/torperf-2009-09-22.pdf">PDF</a>,
-          2.8M from September 22, 2009).</li>
-          <li>Analysis of Circuit Queues in Tor
-          (<a href="papers/bufferstats-2009-08-25.pdf">PDF</a>,
-          196K from August 25, 2009).</li>
-          <li>Comparison of GeoIP Databases for Tor
-          (<a href="papers/geoipdbcomp-2009-10-23.pdf">PDF</a>,
-          546K from October 23, 2009).</li>
-        </ul>
diff --git a/templates/research_tools.tpl.jsp b/templates/research_tools.tpl.jsp
deleted file mode 100644
index e04f043..0000000
--- a/templates/research_tools.tpl.jsp
+++ /dev/null
@@ -1,129 +0,0 @@
-        <h2>Tor Metrics Portal: Tools</h2>
-        <br>
-        <p>This page contains a collection of tools that can be used to
-        gather statistics as provided on the <a href="data.html">Data</a>
-        page and to process the resulting files to generate
-        <a href="graphs.html">graphs</a>.</p>
-        <ul>
-          <li><a href="#metrics-db">Metrics database</a></li>
-          <li><a href="#metrics-web">Metrics website</a></li>
-          <li><a href="#dirarch">Directory-archive script</a></li>
-          <li><a href="#bridgesan">Bridge descriptor sanitizer</a></li>
-          <li><a href="#torperf">Torperf</a></li>
-          <li><a href="#exonerator">ExoneraTor</a></li>
-          <li><a href="#visitor">VisiTor</a></li>
-        </ul>
-        <br>
-        <a name="metrics-db"></a>
-        <h3>Metrics database</h3>
-        <br>
-        <p>The metrics database software is a Java application that parses
-        Tor's directory data and imports it into a PostgreSQL database.
-        This database can then be used to look up relays or perform
-        statistical analysis on the Tor network. The metrics database is
-        also used by this website.</p>
-        <ul>
-          <li>Browse the
-          <a href="https://gitweb.torproject.org/metrics-db.git/tree">Git
-          repository</a></li>
-          <li><tt>git clone git://git.torproject.org/metrics-db</tt></li>
-        </ul>
-        <br>
-        <a name="metrics-web"></a>
-        <h3>Metrics website</h3>
-        <br>
-        <p>The metrics website software is a Tomcat application that
-        accesses the metrics database to generate this website. The
-        metrics website uses R to generate custom graphs on demand.</p>
-        <ul>
-          <li>Browse the
-          <a href="https://gitweb.torproject.org/metrics-web.git/tree">Git
-          repository</a></li>
-          <li><tt>git clone git://git.torproject.org/metrics-web</tt></li>
-        </ul>
-        <br>
-        <a name="dirarch"></a>
-        <h3>Directory-archive script</h3>
-        <br>
-        <p>The directory-archive script consists of a bunch of shell
-        scripts that periodically download relay descriptors, sort them
-        into a directory structure, and compile monthly tarballs. The
-        tarballs are quite similar to the ones provided on the
-        <a href="data.html#relaydesc">Data</a> page, with a few
-        exceptions: the provided tarballs are the result of combining two
-        directory-archive script outputs, splitting v3 votes and v3
-        consensuses into separate tarballs and replacing all colons in
-        filenames with dashes. The <a href="#metrics-db">metrics database
-        software</a> contains similar functionality to the
-        directory-archive script.</p>
-        <ul>
-          <li>Browse the <a href="https://gitweb.torproject.org/tor.git/tree/HEAD:/contrib/directory-archive">contrib/directory-archive/</a>
-          subdirectory in the Tor sources</li>
-          <li><tt>git clone git://git.torproject.org/tor</tt></li>
-        </ul>
-        <br>
-        <a name="bridgesan"></a>
-        <h3>Bridge descriptor sanitizer</h3>
-        <br>
-        <p>The bridge authority Tonga maintains a list of bridges in order
-        to serve bridge addresses and descriptors to its clients. Every
-        half hour, Tonga takes a snapshot of the known bridge descriptors
-        and copies them to byblos for later statistical analysis. As a
-        guiding principle, the Tor project makes all data that it uses for
-        statistical analysis available to the interested public, in order
-        to maximize transparency towards the community. However, the
-        bridge descriptors contain the IP addresses and other contact
-        information of bridges that must not be made public, or the
-        purpose of bridges as non-public entry points into the Tor network
-        would be obsolete. This script takes the half-hourly snapshots as
-        input, removes all possibly sensitive information from the
-        descriptors, and puts out the sanitized bridge descriptors that
-        are safe to be published. The <a href="#metrics-db">metrics
-        database software</a> contains similar functionality to the bridge
-        descriptor sanitizer.</p>
-        <ul>
-          <li>Browse the <a href="https://gitweb.torproject.org/metrics-utils.git/tree/HEAD:/bridge-desc-sanitizer">Git repository</a></li>
-          <li><tt>git clone git://git.torproject.org/metrics-utils</tt></li>
-        </ul>
-        <br>
-        <a name="torperf"></a>
-        <h3>Torperf</h3>
-        <br>
-        <p>Torperf is a little tool that measures Tor's performance as
-        users experience it. Torperf uses a trivial SOCKS client to
-        download files of various sizes over the Tor network and notes how
-        long substeps take.</p>
-        <ul>
-          <li>Browse the <a href="https://gitweb.torproject.org/torperf.git">Git repository</a></li>
-          <li><tt>git clone git://git.torproject.org/torperf</tt></li>
-        </ul>
-        <br>
-        <a name="exonerator"></a>
-        <h3>ExoneraTor</h3>
-        <br>
-        <p>ExoneraTor parses the relay descriptor archives to answer the
-        question whether some IP address was a Tor relay. This script is
-        available as a Python and a Java version with equivalent
-        functionality. There is also a web version of
-        <a href="/exonerator.html">ExoneraTor</a> available.</p>
-        <ul>
-          <li>Download
-          <a href="dist/exonerator-0.0.1.tar">ExoneraTor 0.0.1</a>
-          (<a href="dist/exonerator-0.0.1.tar.asc">sig</a>)
-          <li>Browse the <a href="https://gitweb.torproject.org/metrics-utils.git/tree/HEAD:/exonerator">Git repository</a>
-          <li><tt>git clone git://git.torproject.org/metrics-utils</tt></li>
-        </ul>
-        <br>
-        <a name="visitor"></a>
-        <h3>VisiTor</h3>
-        <br>
-        <p>VisiTor is a script that parses a web server log and the exit
-        list archives to tell how many of the requests come from Tor
-        users.</p>
-        <ul>
-          <li>Download
-          <a href="dist/visitor-0.0.3.tar">VisiTor 0.0.3</a>
-          (<a href="dist/visitor-0.0.3.tar.asc">sig</a>)
-          <li>Browse the <a href="https://gitweb.torproject.org/metrics-utils.git/tree/HEAD:/visitor">Git repository</a></li>
-          <li><tt>git clone git://git.torproject.org/metrics-utils</tt></li>
-        </ul>
diff --git a/templates/status.tpl.jsp b/templates/status.tpl.jsp
deleted file mode 100644
index 3be8fcc..0000000
--- a/templates/status.tpl.jsp
+++ /dev/null
@@ -1,11 +0,0 @@
-        <h2>Tor Metrics Portal: Status</h2>
-        <br>
-        <p>The network data collected by the Tor Metrics Project can be
-        used to analyze the Tor network status from a few years ago until
-        an hour ago. There are currently two applications for this data:
-        The <a href="exonerator.html">ExoneraTor</a> tells you whether
-        some IP address was a Tor relay at a given time, the
-        <a href="relay-search.html">Relay Search</a> lets you search the
-        descriptor archive for a relay, and the
-        <a href="consensus-health.html">Consensus Health</a> summarizes
-        information about the latest network consensus voting process.
diff --git a/web/WEB-INF/banner.jsp b/web/WEB-INF/banner.jsp
new file mode 100644
index 0000000..335f44c
--- /dev/null
+++ b/web/WEB-INF/banner.jsp
@@ -0,0 +1,73 @@
+<table class="banner" border="0" cellpadding="0" cellspacing="0" summary="">
+<tr>
+  <td class="banner-left">
+    <a href="/index.html">
+      <img src="/images/top-left.png" alt="Click to go to home page"
+           width="193" height="79"></a></td>
+  <td class="banner-middle">
+    <% String currentPage = request.getRequestURI(); %>
+    <a <% if (currentPage.endsWith("index.jsp")) {
+        %>class="current"<%} else {%>href="/index.html"<%}%>>Home</a>
+    <a <% if (currentPage.endsWith("graphs.jsp")) {
+        %>class="current"<%} else {%>href="/graphs.html"<%}%>>Graphs</a>
+    <a <% if (currentPage.endsWith("research.jsp")) {
+        %>class="current"<%} else {%>href="/research.html"<%}%>>Research</a>
+    <a <% if (currentPage.endsWith("status.jsp")) {
+        %>class="current"<%} else {%>href="/status.html"<%}%>>Status</a>
+    <%if (currentPage.endsWith("graphs.jsp") ||
+         currentPage.endsWith("network.jsp") ||
+         currentPage.endsWith("users.jsp") ||
+         currentPage.endsWith("packages.jsp") ||
+         currentPage.endsWith("performance.jsp")) {
+     %><br>
+      <font size="2">
+        <a <%if (currentPage.endsWith("network.jsp")){
+            %>class="current"<%} else {%>href="/network.html"<%}
+            %>>Network</a>
+        <a <%if (currentPage.endsWith("users.jsp")) {
+            %>class="current"<%} else {%>href="/users.html"<%}
+            %>>Users</a>
+        <a <%if (currentPage.endsWith("packages.jsp")) {
+            %>class="current"<%} else {%>href="/packages.html"<%}
+            %>>Packages</a>
+        <a <%if (currentPage.endsWith("performance.jsp")) {
+            %>class="current"<%} else {%>href="/performance.html"<%}
+            %>>Performance</a>
+      </font>
+    <%} else if (currentPage.endsWith("status.jsp") ||
+                 currentPage.endsWith("exonerator.jsp") ||
+                 currentPage.endsWith("relay-search.jsp") ||
+                 currentPage.endsWith("consensus-health.jsp")) {
+     %><br>
+      <font size="2">
+        <a <%if (currentPage.endsWith("exonerator.jsp")){
+            %>class="current"<%} else {%>href="/exonerator.html"<%}
+            %>>ExoneraTor</a>
+        <a <%if (currentPage.endsWith("relay-search.jsp")){
+            %>class="current"<%} else {%>href="/relay-search.html"<%}
+            %>>Relay Search</a>
+        <a <%if (currentPage.endsWith("consensus-health.jsp")){
+            %>class="current"<%} else {%>href="/consensus-health.html"<%}
+            %>>Consensus Health</a>
+      </font>
+    <%} else if (currentPage.endsWith("research.jsp") ||
+                 currentPage.endsWith("papers.jsp") ||
+                 currentPage.endsWith("data.jsp") ||
+                 currentPage.endsWith("tools.jsp")) {
+     %><br>
+      <font size="2">
+        <a <%if (currentPage.endsWith("papers.jsp")) {
+            %>class="current"<%} else {%> href="/papers.html"<%}
+            %>>Papers</a>
+        <a <%if (currentPage.endsWith("data.jsp")) {
+            %>class="current"<%} else {%> href="/data.html"<%}
+            %>>Data</a>
+        <a <%if (currentPage.endsWith("tools.jsp")) {
+            %>class="current"<%} else {%> href="/tools.html"<%}
+            %>>Tools</a>
+      </font>
+    <%}%>
+  </td>
+  <td class="banner-right"></td>
+</tr>
+</table>
diff --git a/web/WEB-INF/data.jsp b/web/WEB-INF/data.jsp
new file mode 100644
index 0000000..e773680
--- /dev/null
+++ b/web/WEB-INF/data.jsp
@@ -0,0 +1,340 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+<%@page import="java.io.*" %>
+<%@page import="java.util.*" %>
+<%
+    /* Read /srv/metrics.torproject.org/ernie/remote-files-for-data-page,
+     * if that file exists, and add the contained URLs to this page.
+     * TODO make file location configurable. */
+    SortedMap<String, String> allFiles = new TreeMap<String, String>();
+    File remoteFiles = new File("/srv/metrics.torproject.org/ernie/"
+        + "remote-files-for-data-page");
+    if (remoteFiles.exists() && !remoteFiles.isDirectory()) {
+      BufferedReader br = new BufferedReader(new FileReader(remoteFiles));
+      String line = null;
+      while ((line = br.readLine()) != null) {
+        if (line.startsWith("#") || !line.contains("/")) {
+          continue;
+        }
+        allFiles.put(line.substring(line.lastIndexOf("/") + 1), line);
+      }
+    }
+    /* Add files in /srv/metrics.torproject.org/ernie/website/data/ to
+     * list of provided files on this page. TODO make dir location
+     * configurable. */
+    File localFiles =
+        new File("/srv/metrics.torproject.org/ernie/website/data/");
+    if (localFiles.exists() && localFiles.isDirectory()) {
+      for (File file : localFiles.listFiles()) {
+        allFiles.put(file.getName(), "/data/" + file.getName());
+      }
+    }
+%>
+        <h2>Tor Metrics Portal: Data</h2>
+        <br>
+        <p>One of the main goals of the Tor Metrics Project is to make all
+        gathered data available to the public. This approach enables
+        privacy researchers to perform their own analyses using real data
+        on the Tor network, and it acts as a safeguard to not gather data
+        that are too sensitive to publish. All signatures can be
+        <a href="https://www.torproject.org/verifying-signatures">verified</a>
+        using Karsten's PGP key (0xF7C11265). The following data are
+        available (see the <a href="tools.html">Tools</a> section for
+        details on processing the files):</p>
+        <ul>
+          <li><a href="#relaydesc">Relay descriptor archives</a></li>
+          <li><a href="#bridgedesc">Bridge descriptor archives</a></li>
+          <li><a href="#stats">Statistics produced by relays</a></li>
+          <li><a href="#performance">Performance data</a></li>
+          <li><a href="#exitlist">Exit lists</a></li>
+        </ul>
+        <br>
+        <a name="relaydesc"></a>
+        <h3>Relay descriptor archives</h3>
+        <br>
+        <p>The relay descriptor archives contain all documents that the
+        directory authorities make available about the network of relays.
+        These documents include network statuses, server (relay)
+        descriptors, and extra-info descriptors:</p>
+        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
+<%
+    String firstYearMonth = null, lastYearMonth = null;
+    for (Map.Entry<String, String> e : allFiles.entrySet()) {
+      String filename = e.getKey();
+      if (!filename.endsWith(".asc") &&
+          (filename.startsWith("tor-20") ||
+          filename.startsWith("statuses-20") ||
+          filename.startsWith("server-descriptors-20") ||
+          filename.startsWith("extra-infos-20") ||
+          filename.startsWith("votes-20") ||
+          filename.startsWith("consensuses-20"))) {
+        String yearMonth = filename.substring(filename.indexOf("20"));
+        yearMonth = yearMonth.substring(0, 7);
+        if (firstYearMonth == null ||
+            yearMonth.compareTo(firstYearMonth) < 0) {
+          firstYearMonth = yearMonth;
+        }
+        if (lastYearMonth == null ||
+            yearMonth.compareTo(lastYearMonth) > 0) {
+          lastYearMonth = yearMonth;
+        }
+      }
+    }
+    String currentYearMonth = firstYearMonth;
+    String[] monthNames = new String[] { "January", "February", "March",
+        "April", "May", "June", "July", "August", "September", "October",
+        "November", "December" };
+    String[] prefixes = new String[] { "tor-", "statuses-",
+        "server-descriptors-", "extra-infos-", "votes-", "consensuses-" };
+    String[] descriptions = new String[] { "v1 directories",
+        "v2 statuses", "server descriptors", "extra-infos", "v3 votes",
+        "v3 consensuses" };
+    Set<String> printedFiles = new HashSet<String>();
+    while (currentYearMonth.compareTo(lastYearMonth) <= 0) {
+      int currentYear = Integer.parseInt(currentYearMonth.substring(
+          0, 4));
+      int currentMonth = Integer.parseInt(currentYearMonth.substring(
+          5, 7));
+      out.write("          <tr>\n            <td>"
+          + monthNames[currentMonth - 1] + " " + currentYear + "</td>\n");
+      for (int i = 0; i < prefixes.length; i++) {
+        String prefix = prefixes[i];
+        String description = descriptions[i];
+        String file = prefix + currentYearMonth + ".tar.bz2";
+        String sig = file + ".asc";
+        if (allFiles.containsKey(file)) {
+          out.write("            <td><a href=\"" + allFiles.get(file)
+              + "\">" + description + "</a>");
+          printedFiles.add(file);
+          if (allFiles.containsKey(sig)) {
+            out.write("\n              (<a href=\"" + allFiles.get(sig)
+                + "\">sig</a>)</td>\n");
+            printedFiles.add(sig);
+          } else {
+            out.write("</td>\n");
+          }
+        } else {
+          out.write("            <td></td>\n");
+        }
+      }
+      out.write("          </tr>\n");
+      if (currentMonth < 12) {
+        currentMonth++;
+      } else {
+        currentYear++;
+        currentMonth = 1;
+      }
+      currentYearMonth = String.format("%d-%02d", currentYear,
+          currentMonth);
+    }
+%>
+        </table>
+        <br>
+        <a name="bridgedesc"></a>
+        <h3>Bridge descriptor archives</h3>
+        <br>
+        <p>The bridge descriptor archives contain similar documents as the
+        relay descriptor archives, but for the non-public bridges. The
+        descriptors have been sanitized before publication to remove all
+        information that could otherwise be used to locate bridges.
+        Beginning with May 2010, we stopped resolving IP addresses to
+        country codes and including those in the sanitized descriptors,
+        because it was tough to maintain; if your research requires this
+        or any other detail, contact us and we'll sort something out. The
+        files below contain all documents of a given month:</p>
+        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
+<%
+    firstYearMonth = lastYearMonth = null;
+    for (Map.Entry<String, String> e : allFiles.entrySet()) {
+      String filename = e.getKey();
+      if (!filename.endsWith(".asc") &&
+          filename.startsWith("bridge-descriptors-20")) {
+        String yearMonth = filename.substring(filename.indexOf("20"));
+        yearMonth = yearMonth.substring(0, 7);
+        if (firstYearMonth == null ||
+            yearMonth.compareTo(firstYearMonth) < 0) {
+          firstYearMonth = yearMonth;
+        }
+        if (lastYearMonth == null ||
+            yearMonth.compareTo(lastYearMonth) > 0) {
+          lastYearMonth = yearMonth;
+        }
+      }
+    }
+    currentYearMonth = firstYearMonth;
+    while (currentYearMonth.compareTo(lastYearMonth) <= 0) {
+      int currentYear = Integer.parseInt(currentYearMonth.substring(
+          0, 4));
+      int currentMonth = Integer.parseInt(currentYearMonth.substring(
+          5, 7));
+      String file = "bridge-descriptors-" + currentYearMonth + ".tar.bz2";
+      String sig = file + ".asc";
+      if (allFiles.containsKey(file)) {
+        out.write("          <tr><td><a href=\"" + allFiles.get(file)
+            + "\">" + monthNames[currentMonth - 1] + " " + currentYear
+            + "</a>");
+        printedFiles.add(file);
+        if (allFiles.containsKey(sig)) {
+          out.write("\n              (<a href=\"" + allFiles.get(sig)
+                + "\">sig</a>)</td></tr>\n");
+          printedFiles.add(sig);
+        } else {
+          out.write("</td></tr>\n");
+        }
+      }
+      if (currentMonth < 12) {
+        currentMonth++;
+      } else {
+        currentYear++;
+        currentMonth = 1;
+      }
+      currentYearMonth = String.format("%d-%02d", currentYear,
+          currentMonth);
+    }
+%>
+        </table>
+        <p></p>
+        <br>
+        <a name="stats"></a>
+        <h3>Statistics produced by relays</h3>
+        <br>
+        <p>Some of the relays are configured to gather statistics on the
+        number of requests or connecting clients, the number of processed
+        cells per queue, or the number of exiting bytes per port. Relays
+        running version 0.2.2.4-alpha can include these statistics in
+        extra-info descriptors, so that they are included in the relay
+        descriptor archives. The following files contain the statistics
+        produced by relays running earlier versions:</p>
+        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
+<%
+    SortedSet<String> statsSources = new TreeSet<String>();
+    for (Map.Entry<String, String> e : allFiles.entrySet()) {
+      String filename = e.getKey();
+      if (!filename.endsWith(".asc") &&
+          (filename.startsWith("buffer-") ||
+          filename.startsWith("dirreq-") ||
+          filename.startsWith("entry-") ||
+          (filename.startsWith("exit-") &&
+          !filename.startsWith("exit-list-")))) {
+        statsSources.add(filename.substring(filename.indexOf("-") + 1));
+      }
+    }
+    prefixes = new String[] { "buffer-", "dirreq-", "entry-", "exit-" };
+    for (String source : statsSources) {
+      String nickname = source.split("-")[0];
+      String fingerprint = source.split("-")[1];
+      fingerprint = fingerprint.substring(0, 8);
+      out.write("          <tr>\n            <td>" + nickname + " ("
+          + fingerprint + ")</td>\n");
+      for (int i = 0; i < prefixes.length; i++) {
+        String prefix = prefixes[i];
+        String file = prefix + source;
+        String sig = file + ".asc";
+        if (allFiles.containsKey(file)) {
+          out.write("            <td><a href=\"" + allFiles.get(file)
+              + "\">" + prefix + "stats</a>");
+          printedFiles.add(file);
+          if (allFiles.containsKey(sig)) {
+            out.write("\n              (<a href=\"" + allFiles.get(sig)
+                + "\">sig</a>)</td>\n");
+            printedFiles.add(sig);
+          } else {
+            out.write("</td>\n");
+          }
+        } else {
+          out.write("            <td></td>\n");
+        }
+      }
+      out.write("          </tr>\n");
+    }
+%>
+        </table>
+        <br>
+        <a name="performance"></a>
+        <h3>Performance data</h3>
+        <br>
+        <p>We are measuring the performance of the Tor network by
+        periodically requesting files of different sizes and recording the
+        time needed to do so. The files below contain the output of the
+        torperf application and are updated every hour:</p>
+        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
+<%
+    SortedSet<String> torperfSources = new TreeSet<String>();
+    for (Map.Entry<String, String> e : allFiles.entrySet()) {
+      String filename = e.getKey();
+      if (filename.endsWith("b.data")) {
+        torperfSources.add(filename.substring(0, filename.indexOf("-")));
+      }
+    }
+    for (String source : torperfSources) {
+      out.write("          <tr>\n            <td>" + source + "</td>\n");
+      String file = source + "-50kb.data";
+      if (allFiles.containsKey(file)) {
+        out.write("            <td><a href=\"" + allFiles.get(file)
+            + "\">50 KiB requests</a></td>\n");
+        printedFiles.add(file);
+      } else {
+        out.write("            <td></td>\n");
+      }
+      file = source + "-1mb.data";
+      if (allFiles.containsKey(file)) {
+        out.write("            <td><a href=\"" + allFiles.get(file)
+            + "\">1 MiB requests</a></td>\n");
+        printedFiles.add(file);
+      } else {
+        out.write("            <td></td>\n");
+      }
+      file = source + "-5mb.data";
+      if (allFiles.containsKey(file)) {
+        out.write("            <td><a href=\"" + allFiles.get(file)
+            + "\">5 MiB requests</a></td>\n");
+        printedFiles.add(file);
+      } else {
+        out.write("            <td></td>\n");
+      }
+      out.write("          </tr>\n");
+    }
+%>
+        </table>
+        <br>
+        <a name="exitlist"></a>
+        <h3>Exit lists</h3>
+        <br>
+        <p>We are archiving the bulk exit lists used by
+        <a href="https://check.torproject.org/">Tor Check</a> containing
+        the IP addresses that exit relays exit from:</p>
+        <table width="100%" border="0" cellpadding="5" cellspacing="0" summary="">
+<%
+    for (Map.Entry<String, String> e : allFiles.entrySet()) {
+      String file = e.getKey();
+      String url = e.getValue();
+      if (file.startsWith("exit-list")) {
+        String yearMonth = file.substring(file.indexOf("exit-list-")
+            + "exit-list-".length());
+        String year = yearMonth.substring(0, 4);
+        String monthName = monthNames[Integer.parseInt(
+            yearMonth.substring(5, 7)) - 1];
+        out.write("          <tr><td><a href=\"" + url + "\">"
+            + monthName + " " + year + "</a></td></tr>\n");
+        printedFiles.add(file);
+      }
+    }
+%>
+        </table>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/footer.jsp b/web/WEB-INF/footer.jsp
new file mode 100644
index 0000000..abd7740
--- /dev/null
+++ b/web/WEB-INF/footer.jsp
@@ -0,0 +1,13 @@
+      <p>This material is supported in part by the National Science
+      Foundation under Grant No. CNS-0959138. Any opinions,
+      finding, and conclusions or recommendations expressed in this
+      material are those of the author(s) and do not necessarily reflect
+      the views of the National Science Foundation.</p>
+      <p>"Tor" and the "Onion Logo" are <a href="https://www.torproject.org/trademark-faq.html.en">registered trademarks</a> of The Tor Project, Inc.</p>
+      <p>Data on this site is freely available under a
+      <a href="http://creativecommons.org/publicdomain/zero/1.0/">CC0 no
+      copyright declaration</a>: To the extent possible under law, the Tor
+      Project has waived all copyright and related or neighboring rights
+      in the data. Graphs are licensed under a
+      <a href="http://creativecommons.org/licenses/by/3.0/us/">Creative
+      Commons Attribution 3.0 United States License</a>.</p>
diff --git a/web/WEB-INF/graphs.jsp b/web/WEB-INF/graphs.jsp
new file mode 100644
index 0000000..4b72d05
--- /dev/null
+++ b/web/WEB-INF/graphs.jsp
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+        <h2>Tor Metrics Portal: Graphs</h2>
+        <br>
+        <p>The graphs on this page visualize a small portion of the data
+        gathered in the Tor Metrics Project. The following graphs are
+        available:</p>
+        <ul>
+          <li>The <a href="network.html">Network page</a> has numerous
+          statistics on the network of relays and bridges.</li>
+          <li>The <a href="users.html">Users page</a> attempts to estimate
+          the number of users in the network.</li>
+          <li>There are numerous ways to download the Tor software. The
+          <a href="packages.html">Packages page</a> has statistics on the
+          number of packages requested from GetTor.</li>
+          <li>There are active and passive performance measurements of the
+          Tor network available on the
+          <a href="performance.html">Performance page</a>.</li>
+        </ul>
+        <br>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/index.jsp b/web/WEB-INF/index.jsp
new file mode 100644
index 0000000..c63d2e5
--- /dev/null
+++ b/web/WEB-INF/index.jsp
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+        <h2>Tor Metrics Portal</h2>
+        <br>
+        <p>The Tor Metrics Portal aggregates all kinds of interesting
+        data about the Tor network and visualizes them in graphs and
+        reports. This portal also provides easy access to the underlying
+        data and documentation for performing own analyses based on these
+        data. Find out more here:</p>
+        <ul>
+          <li>View daily updated <a href="graphs.html">graphs</a> on
+          estimated client numbers, on network performance, and other
+          statistics on the Tor network</li>
+          <li>Read <a href="papers.html">papers</a> and technical reports
+          on the measurement techniques and results of statistical
+          analysis of metrics data</li>
+          <li>Download the <a href="data.html">data</a> that is behind the
+          graphs and reports to make your own evaluations</li>
+          <li>Try out the <a href="tools.html">tools</a> to parse and
+          evaluate the metrics data</li>
+        </ul>
+        <br>
+        <h3>News</h3>
+        <ul>
+          <li>October 7, 2010: Custom graphs are now available for all
+          <a href="graphs.html">graphs</a>. Based on work by Kevin
+          Berry.</li>
+          <li>September 9, 2010: Custom
+          graphs on network size, relay platforms, versions, and
+          observed bandwidth available. Implemented by Kevin Berry.</li>
+          <li>September 2, 2010: New <a href="relay-search.html">relay
+          search</a> feature available.</li>
+          <li>August 31, 2010: Named relays can now be found more easily
+          on the consensus-health page by using anchors, e.g.,
+          <a href="consensus-health.html#gabelmoo">https://metrics.torproject.org/consensus-health.html#gabelmoo</a> .</li>
+          <li>August 16, 2010: There are now
+          <a href="users.html">graphs</a> for total new or returning and
+          recurring directly connecting users and total bridge users as
+          well as
+          for users coming from Australia, Brazil, Canada, France,
+          Germany, Italy, Japan, Poland, Russia, South Korea, Sweden,
+          U.K., and U.S.A.</li>
+        </ul>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/network.jsp b/web/WEB-INF/network.jsp
new file mode 100644
index 0000000..a07c5a3
--- /dev/null
+++ b/web/WEB-INF/network.jsp
@@ -0,0 +1,292 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+<%@page import="java.util.*" %>
+<h2>Tor Metrics Portal: Network</h2>
+<br>
+<h3>Relays and bridges in the network</h3>
+<br>
+<p>The following graph shows the average daily number of relays and
+bridges in the network.</p>
+<a name="networksize"></a>
+<%
+StringBuilder networksizeUrl = new StringBuilder("networksize.png");
+if ("networksize".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (parameters.size() > 0) {
+    networksizeUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        networksizeUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + networksizeUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"Network size graph\">");
+%><form action="network.html#networksize">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="networksize">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("networksize".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("networksize".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<p><a href="csv/networksize.csv">CSV</a> file containing all data.</p>
+<br>
+
+<h3>Relays with Exit, Fast, Guard, and Stable flags</h3>
+<br>
+<p>The directory authorities assign certain flags to relays that clients
+use for their path selection decisions. The following graph shows the
+average number of relays with these flags assigned.</p>
+<a name="relayflags"></a>
+<%
+StringBuilder relayflagsUrl = new StringBuilder("relayflags.png");
+if ("relayflags".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  String[] flagParameters = request.getParameterValues("flag");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (flagParameters != null && flagParameters.length > 0) {
+    for (String flag : flagParameters) {
+      if (flag != null && flag.length() > 0) {
+        parameters.add("flag=" + flag);
+      }
+    }
+  }
+  if (parameters.size() > 0) {
+    relayflagsUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        relayflagsUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + relayflagsUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"Relay flags graph\">");
+%><form action="network.html#relayflags">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="relayflags">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("relayflags".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("relayflags".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+      <label>Relay flags: </label>
+      <input type="checkbox" name="flag" value="Running"> Running
+      <input type="checkbox" name="flag" value="Exit"> Exit
+      <input type="checkbox" name="flag" value="Fast"> Fast
+      <input type="checkbox" name="flag" value="Guard"> Guard
+      <input type="checkbox" name="flag" value="Stable"> Stable
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<br>
+
+<h3>Relays by version</h3>
+<br>
+<p>Relays report the Tor version that they are running to the directory
+authorities. The following graph shows the number of relays by
+version.</p>
+<a name="versions"></a>
+<%
+StringBuilder versionsUrl = new StringBuilder("versions.png");
+if ("versions".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (parameters.size() > 0) {
+    versionsUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        versionsUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + versionsUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"Relay versions graph\">");
+%><form action="network.html#versions">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="versions">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("versions".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("versions".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<br>
+
+<h3>Relays by platform</h3>
+<br>
+<p>Relays report the operating system they are running to the directory
+authorities. The following graph shows the number of relays by
+platform.</p>
+<a name="platforms"></a>
+<%
+StringBuilder platformsUrl = new StringBuilder("platforms.png");
+if ("platforms".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (parameters.size() > 0) {
+    platformsUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        platformsUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + platformsUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"Relay platforms graph\">");
+%><form action="network.html#platforms">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="platforms">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("platforms".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("platforms".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<br>
+
+<h3>Total relay bandwidth in the network</h3>
+<br>
+<p>Relays report how much bandwidth they are willing to contribute and how
+many bytes they have read and written in the past 24 hours. The following
+graph shows total advertised bandwidth and bandwidth history of all relays
+in the network.</p>
+<a name="bandwidth"></a>
+<%
+StringBuilder bandwidthUrl = new StringBuilder("bandwidth.png");
+if ("bandwidth".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (parameters.size() > 0) {
+    bandwidthUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        bandwidthUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + bandwidthUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"Relay bandwidth graph\">");
+%><form action="network.html#bandwidth">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="bandwidth">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("bandwidth".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("bandwidth".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<br>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/packages.jsp b/web/WEB-INF/packages.jsp
new file mode 100644
index 0000000..4d08c0d
--- /dev/null
+++ b/web/WEB-INF/packages.jsp
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+<%@page import="java.util.*" %>
+<h2>Tor Metrics Portal: Downloaded Packages</h2>
+<br>
+<h3>Packages requested from GetTor</h3>
+<br>
+<p>GetTor allows users to fetch the Tor software via email. The following
+graph shows the number of packages requested from GetTor per day.</p>
+<p>
+<a name="gettor"></a>
+<%
+if ("gettor".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end"),
+      bundleParameter = request.getParameter("bundle");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (bundleParameter != null && bundleParameter.length() > 0) {
+    parameters.add("bundle=" + bundleParameter);
+  }
+  StringBuilder url = new StringBuilder("gettor.png");
+  if (parameters.size() > 0) {
+    url.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        url.append("&" + parameters.get(i));
+      }
+    }
+  }
+  out.println("<img src=\"" + url.toString() + "\" width=\"576\" "
+      + "height=\"360\" alt=\"GetTor graph\">");
+} else {%>
+  <img src="gettor.png" width="576" height="360" alt="GetTor graph">
+<%
+}
+%><form action="packages.html#gettor">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="gettor">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("gettor".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("gettor".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+      Packages:
+      <input type="radio" name="bundle" value="all"> Total packages
+      <input type="radio" name="bundle" value="en"> TBB (en)
+      <input type="radio" name="bundle" value="zh_CN"> TBB (zh_CN)
+      <input type="radio" name="bundle" value="fa"> TBB (fa)
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+
+<p><a href="csv/gettor.csv">CSV</a> file containing all data.</p>
+<br>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/papers.jsp b/web/WEB-INF/papers.jsp
new file mode 100644
index 0000000..21634c4
--- /dev/null
+++ b/web/WEB-INF/papers.jsp
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+        <h2>Tor Metrics Portal: Papers</h2>
+        <br>
+        <p>The <a href="#papers">papers</a> and
+        <a href="#techreports">technical reports</a> listed on this page
+        originate from, or are based on, work performed in the Tor Metrics
+        Project.</p>
+        <br>
+        <a name="papers"></a>
+        <h3>Papers</h3>
+        <br>
+        These papers summarize some of the results of of the Tor Metrics
+        Project and have been accepted for publication at academic
+        conferences or workshops.
+        <ul>
+          <li>Karsten Loesing. Measuring the Tor Network from Public
+          Directory Information. 2nd Hot Topics in Privacy Enhancing
+          Technologies (HotPETs 2009), Seattle, WA, USA, August 2009.
+          (<a href="papers/hotpets09.pdf">PDF</a>, 582K)</li>
+          <li>Karsten Loesing, Steven J. Murdoch, Roger Dingledine. A Case
+          Study on Measuring Statistical Data in the Tor Anonymity
+          Network. Accepted for publication at Workshop on Ethics in
+          Computer Security Research (WECSR 2010), Tenerife, Spain,
+          January 2010. (<a href="papers/wecsr10.pdf">PDF</a>, 160K)</li>
+        </ul>
+        <br>
+        <a name="techreports"></a>
+        <h3>Technical reports</h3>
+        <br>
+        The technical reports listed here have been the first place to
+        publish novel kinds of statistics on the Tor network. Some, but
+        not all, of the results contained in these technical reports have
+        been included in the <a href="#papers">papers</a> above or in the
+        daily updated <a href="graphs.html">graphs</a>.
+        <ul>
+          <li>Evaluation of Relays from Public Directory Data
+          (<a href="papers/dirarch-2009-06-22.pdf">PDF</a>,
+          558K, last updated on June 22, 2009).</li>
+          <li>Analysis of Bridge Usage in Tor
+          (<a href="papers/bridges-2009-06-22.pdf">PDF</a>,
+          76K, last updated on June 22, 2009).</li>
+          <li>Evaluation of Client Requests to the Directories to
+          determine total numbers and countries of users
+          (<a href="papers/directory-requests-2009-06-25.pdf">PDF</a>,
+          207K, last updated on June 25, 2009).</li>
+          <li>Possible problems of directory authorities assigning Stable
+          and Guard flags
+          (<a href="papers/relayflags-2009-04-01.pdf">PDF</a>,
+          2M from April 1, 2009).</li>
+          <li>Simulation of the number of Fast, Stable, and Guard flags
+          for changed requirements
+          (<a href="papers/flagrequirements-2009-04-11.pdf">PDF</a>,
+          229K from April 11, 2009).</li>
+          <li>Reducing the Circuit Window Size in Tor
+          (<a href="papers/circwindow-2009-09-20.pdf">PDF</a>,
+          137K from September 20, 2009).</li>
+          <li>Performance of Requests over the Tor Network
+          (<a href="papers/torperf-2009-09-22.pdf">PDF</a>,
+          2.8M from September 22, 2009).</li>
+          <li>Analysis of Circuit Queues in Tor
+          (<a href="papers/bufferstats-2009-08-25.pdf">PDF</a>,
+          196K from August 25, 2009).</li>
+          <li>Comparison of GeoIP Databases for Tor
+          (<a href="papers/geoipdbcomp-2009-10-23.pdf">PDF</a>,
+          546K from October 23, 2009).</li>
+        </ul>
+        <h2>Tor Metrics Portal: Status</h2>
+        <br>
+        <p>The network data collected by the Tor Metrics Project can be
+        used to analyze the Tor network status from a few years ago until
+        an hour ago. There are currently two applications for this data:
+        The <a href="exonerator.html">ExoneraTor</a> tells you whether
+        some IP address was a Tor relay at a given time, the
+        <a href="relay-search.html">Relay Search</a> lets you search the
+        descriptor archive for a relay, and the
+        <a href="consensus-health.html">Consensus Health</a> summarizes
+        information about the latest network consensus voting process.
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/performance.jsp b/web/WEB-INF/performance.jsp
new file mode 100644
index 0000000..5dcc97f
--- /dev/null
+++ b/web/WEB-INF/performance.jsp
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+<%@page import="java.util.*" %>
+<h2>Tor Metrics Portal: Performance</h2>
+<br>
+<h3>Time to download files over Tor</h3>
+<br>
+<p>The following graphs show the performance of the Tor network as
+experienced by its users. The graphs contain the average (median) time to
+request files of three different sizes over Tor as well as first and third
+quartile of request times.</p>
+<a name="torperf"></a>
+<%
+if ("torperf".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end"),
+      sourceParameter = request.getParameter("source"),
+      filesizeParameter = request.getParameter("filesize");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (sourceParameter != null && sourceParameter.length() > 0) {
+    parameters.add("source=" + sourceParameter);
+  }
+  if (filesizeParameter != null && filesizeParameter.length() > 0) {
+    parameters.add("filesize=" + filesizeParameter);
+  }
+  StringBuilder url = new StringBuilder("torperf.png");
+  if (parameters.size() > 0) {
+    url.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        url.append("&" + parameters.get(i));
+      }
+    }
+  }
+  out.println("<img src=\"" + url.toString() + "\" width=\"576\" "
+      + "height=\"360\" alt=\"Torperf graph\">");
+} else {%>
+  <img src="torperf.png" width="576" height="360" alt="Torperf graph">
+<%
+}
+%><form action="performance.html#torperf">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="torperf">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("torperf".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("torperf".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+      Source:
+      <input type="radio" name="source" value="torperf"> torperf
+      <input type="radio" name="source" value="moria"> moria
+      <input type="radio" name="source" value="siv"> siv
+    </p><p>
+      <label>File size: </label>
+      <input type="radio" name="filesize" value="50kb"> 50 KiB
+      <input type="radio" name="filesize" value="1mb"> 1 MiB
+      <input type="radio" name="filesize" value="5mb"> 5 MiB
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+
+<p><a href="csv/torperf.csv">CSV</a> file containing all data.</p>
+<br>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/research.jsp b/web/WEB-INF/research.jsp
new file mode 100644
index 0000000..6f473bd
--- /dev/null
+++ b/web/WEB-INF/research.jsp
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+        <h2>Tor Metrics Portal: Research</h2>
+        <br>
+        <p>The Tor Metrics Project aims at supporting privacy enhancing
+        technologies research by making <a href="papers.html">papers</a>
+        and technical reports, gathered network
+        <a href="data.html">data</a>, and
+        <a href="tools.html">tools</a>
+        for processing these data available to the public. If you are
+        missing anything for your Tor-related research or want to share
+        your research results with others, please
+        <a href="mailto:tor-assistants at freehaven.net">let us know</a>!
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/status.jsp b/web/WEB-INF/status.jsp
new file mode 100644
index 0000000..ee493ea
--- /dev/null
+++ b/web/WEB-INF/status.jsp
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+        <h2>Tor Metrics Portal: Status</h2>
+        <br>
+        <p>The network data collected by the Tor Metrics Project can be
+        used to analyze the Tor network status from a few years ago until
+        an hour ago. There are currently two applications for this data:
+        The <a href="exonerator.html">ExoneraTor</a> tells you whether
+        some IP address was a Tor relay at a given time, the
+        <a href="relay-search.html">Relay Search</a> lets you search the
+        descriptor archive for a relay, and the
+        <a href="consensus-health.html">Consensus Health</a> summarizes
+        information about the latest network consensus voting process.
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/tools.jsp b/web/WEB-INF/tools.jsp
new file mode 100644
index 0000000..d96a855
--- /dev/null
+++ b/web/WEB-INF/tools.jsp
@@ -0,0 +1,148 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+        <h2>Tor Metrics Portal: Tools</h2>
+        <br>
+        <p>This page contains a collection of tools that can be used to
+        gather statistics as provided on the <a href="data.html">Data</a>
+        page and to process the resulting files to generate
+        <a href="graphs.html">graphs</a>.</p>
+        <ul>
+          <li><a href="#metrics-db">Metrics database</a></li>
+          <li><a href="#metrics-web">Metrics website</a></li>
+          <li><a href="#dirarch">Directory-archive script</a></li>
+          <li><a href="#bridgesan">Bridge descriptor sanitizer</a></li>
+          <li><a href="#torperf">Torperf</a></li>
+          <li><a href="#exonerator">ExoneraTor</a></li>
+          <li><a href="#visitor">VisiTor</a></li>
+        </ul>
+        <br>
+        <a name="metrics-db"></a>
+        <h3>Metrics database</h3>
+        <br>
+        <p>The metrics database software is a Java application that parses
+        Tor's directory data and imports it into a PostgreSQL database.
+        This database can then be used to look up relays or perform
+        statistical analysis on the Tor network. The metrics database is
+        also used by this website.</p>
+        <ul>
+          <li>Browse the
+          <a href="https://gitweb.torproject.org/metrics-db.git/tree">Git
+          repository</a></li>
+          <li><tt>git clone git://git.torproject.org/metrics-db</tt></li>
+        </ul>
+        <br>
+        <a name="metrics-web"></a>
+        <h3>Metrics website</h3>
+        <br>
+        <p>The metrics website software is a Tomcat application that
+        accesses the metrics database to generate this website. The
+        metrics website uses R to generate custom graphs on demand.</p>
+        <ul>
+          <li>Browse the
+          <a href="https://gitweb.torproject.org/metrics-web.git/tree">Git
+          repository</a></li>
+          <li><tt>git clone git://git.torproject.org/metrics-web</tt></li>
+        </ul>
+        <br>
+        <a name="dirarch"></a>
+        <h3>Directory-archive script</h3>
+        <br>
+        <p>The directory-archive script consists of a bunch of shell
+        scripts that periodically download relay descriptors, sort them
+        into a directory structure, and compile monthly tarballs. The
+        tarballs are quite similar to the ones provided on the
+        <a href="data.html#relaydesc">Data</a> page, with a few
+        exceptions: the provided tarballs are the result of combining two
+        directory-archive script outputs, splitting v3 votes and v3
+        consensuses into separate tarballs and replacing all colons in
+        filenames with dashes. The <a href="#metrics-db">metrics database
+        software</a> contains similar functionality to the
+        directory-archive script.</p>
+        <ul>
+          <li>Browse the <a href="https://gitweb.torproject.org/tor.git/tree/HEAD:/contrib/directory-archive">contrib/directory-archive/</a>
+          subdirectory in the Tor sources</li>
+          <li><tt>git clone git://git.torproject.org/tor</tt></li>
+        </ul>
+        <br>
+        <a name="bridgesan"></a>
+        <h3>Bridge descriptor sanitizer</h3>
+        <br>
+        <p>The bridge authority Tonga maintains a list of bridges in order
+        to serve bridge addresses and descriptors to its clients. Every
+        half hour, Tonga takes a snapshot of the known bridge descriptors
+        and copies them to byblos for later statistical analysis. As a
+        guiding principle, the Tor project makes all data that it uses for
+        statistical analysis available to the interested public, in order
+        to maximize transparency towards the community. However, the
+        bridge descriptors contain the IP addresses and other contact
+        information of bridges that must not be made public, or the
+        purpose of bridges as non-public entry points into the Tor network
+        would be obsolete. This script takes the half-hourly snapshots as
+        input, removes all possibly sensitive information from the
+        descriptors, and puts out the sanitized bridge descriptors that
+        are safe to be published. The <a href="#metrics-db">metrics
+        database software</a> contains similar functionality to the bridge
+        descriptor sanitizer.</p>
+        <ul>
+          <li>Browse the <a href="https://gitweb.torproject.org/metrics-utils.git/tree/HEAD:/bridge-desc-sanitizer">Git repository</a></li>
+          <li><tt>git clone git://git.torproject.org/metrics-utils</tt></li>
+        </ul>
+        <br>
+        <a name="torperf"></a>
+        <h3>Torperf</h3>
+        <br>
+        <p>Torperf is a little tool that measures Tor's performance as
+        users experience it. Torperf uses a trivial SOCKS client to
+        download files of various sizes over the Tor network and notes how
+        long substeps take.</p>
+        <ul>
+          <li>Browse the <a href="https://gitweb.torproject.org/torperf.git">Git repository</a></li>
+          <li><tt>git clone git://git.torproject.org/torperf</tt></li>
+        </ul>
+        <br>
+        <a name="exonerator"></a>
+        <h3>ExoneraTor</h3>
+        <br>
+        <p>ExoneraTor parses the relay descriptor archives to answer the
+        question whether some IP address was a Tor relay. This script is
+        available as a Python and a Java version with equivalent
+        functionality. There is also a web version of
+        <a href="/exonerator.html">ExoneraTor</a> available.</p>
+        <ul>
+          <li>Download
+          <a href="dist/exonerator-0.0.1.tar">ExoneraTor 0.0.1</a>
+          (<a href="dist/exonerator-0.0.1.tar.asc">sig</a>)
+          <li>Browse the <a href="https://gitweb.torproject.org/metrics-utils.git/tree/HEAD:/exonerator">Git repository</a>
+          <li><tt>git clone git://git.torproject.org/metrics-utils</tt></li>
+        </ul>
+        <br>
+        <a name="visitor"></a>
+        <h3>VisiTor</h3>
+        <br>
+        <p>VisiTor is a script that parses a web server log and the exit
+        list archives to tell how many of the requests come from Tor
+        users.</p>
+        <ul>
+          <li>Download
+          <a href="dist/visitor-0.0.3.tar">VisiTor 0.0.3</a>
+          (<a href="dist/visitor-0.0.3.tar.asc">sig</a>)
+          <li>Browse the <a href="https://gitweb.torproject.org/metrics-utils.git/tree/HEAD:/visitor">Git repository</a></li>
+          <li><tt>git clone git://git.torproject.org/metrics-utils</tt></li>
+        </ul>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/WEB-INF/users.jsp b/web/WEB-INF/users.jsp
new file mode 100644
index 0000000..c5b592a
--- /dev/null
+++ b/web/WEB-INF/users.jsp
@@ -0,0 +1,304 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+  <title>Tor Metrics Portal: Tools</title>
+  <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
+  <link href="/css/stylesheet-ltr.css" type="text/css" rel="stylesheet">
+  <link href="/images/favicon.ico" type="image/x-icon" rel="shortcut icon">
+</head>
+<body>
+  <div class="center">
+    <%@ include file="banner.jsp"%>
+    <div class="main-column">
+<%@page import="java.util.*" %>
+<h2>Tor Metrics Portal: Users</h2>
+<br>
+<h3>New or returning, directly connecting Tor users</h3>
+<br>
+<p>Users connecting to the Tor network for the first time request a list
+of running relays from one of currently seven directory authorities.
+Likewise, returning users whose network information is out of date connect
+to one of the directory authorities to download a fresh list of relays.
+The following graphs display an estimate of new or returning Tor users
+based on the requests as seen by gabelmoo, one of the directory
+authorities.</p>
+<a name="new-users"></a>
+<%
+StringBuilder newUsersUrl = new StringBuilder("new-users.png");
+if ("new-users".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  String[] countryParameters = request.getParameterValues("country");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (countryParameters != null && countryParameters.length > 0) {
+    for (String country : countryParameters) {
+      if (country != null && country.length() > 0) {
+        parameters.add("country=" + country);
+      }
+    }
+  }
+  if (parameters.size() > 0) {
+    newUsersUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        newUsersUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + newUsersUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"New users graph\">");
+%><form action="users.html#new-users">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="new-users">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("new-users".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("new-users".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+      Source:
+      <input type="radio" name="country" value="all">All users
+      <input type="radio" name="country" value="au">Australia
+      <input type="radio" name="country" value="bh">Bahrain
+      <input type="radio" name="country" value="br">Brazil
+      <input type="radio" name="country" value="ca">Canada
+      <input type="radio" name="country" value="cn">China
+      <input type="radio" name="country" value="cu">Cuba
+      <input type="radio" name="country" value="de">Germany
+      <input type="radio" name="country" value="et">Ethiopia
+      <input type="radio" name="country" value="fr">France
+      <input type="radio" name="country" value="gb">U.K.
+      <input type="radio" name="country" value="ir">Iran
+      <input type="radio" name="country" value="it">Italy
+      <input type="radio" name="country" value="jp">Japan
+      <input type="radio" name="country" value="kr">South Korea
+      <input type="radio" name="country" value="mm">Burma
+      <input type="radio" name="country" value="pl">Poland
+      <input type="radio" name="country" value="ru">Russia
+      <input type="radio" name="country" value="sa">Saudi Arabia
+      <input type="radio" name="country" value="se">Sweden
+      <input type="radio" name="country" value="sy">Syria
+      <input type="radio" name="country" value="tn">Tunisia
+      <input type="radio" name="country" value="tm">Turkmenistan
+      <input type="radio" name="country" value="us">U.S.A.
+      <input type="radio" name="country" value="uz">Uzbekistan
+      <input type="radio" name="country" value="vn">Vietnam
+      <input type="radio" name="country" value="ye">Yemen
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<p><a href="csv/new-users.csv">CSV</a> file containing all data.</p>
+<br>
+
+<h3>Recurring, directly connecting Tor users</h3>
+<br>
+<p>After being connected to the Tor network, users need to refresh their
+list of running relays on a regular basis. They send their requests to one
+out of a few hundred directory mirrors to save bandwidth of the directory
+authorities. The following graphs show an estimate of recurring Tor users
+based on the requests as seen by trusted, a particularly fast directory
+mirror.</p>
+<a name="direct-users"></a>
+<%
+StringBuilder directUsersUrl = new StringBuilder("direct-users.png");
+if ("direct-users".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  String[] countryParameters = request.getParameterValues("country");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (countryParameters != null && countryParameters.length > 0) {
+    for (String country : countryParameters) {
+      if (country != null && country.length() > 0) {
+        parameters.add("country=" + country);
+      }
+    }
+  }
+  if (parameters.size() > 0) {
+    directUsersUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        directUsersUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + directUsersUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"Recurring users graph\">");
+%><form action="users.html#direct-users">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="direct-users">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("direct-users".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("direct-users".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+      Source:
+      <input type="radio" name="country" value="all">All users
+      <input type="radio" name="country" value="au">Australia
+      <input type="radio" name="country" value="bh">Bahrain
+      <input type="radio" name="country" value="br">Brazil
+      <input type="radio" name="country" value="ca">Canada
+      <input type="radio" name="country" value="cn">China
+      <input type="radio" name="country" value="cu">Cuba
+      <input type="radio" name="country" value="de">Germany
+      <input type="radio" name="country" value="et">Ethiopia
+      <input type="radio" name="country" value="fr">France
+      <input type="radio" name="country" value="gb">U.K.
+      <input type="radio" name="country" value="ir">Iran
+      <input type="radio" name="country" value="it">Italy
+      <input type="radio" name="country" value="jp">Japan
+      <input type="radio" name="country" value="kr">South Korea
+      <input type="radio" name="country" value="mm">Burma
+      <input type="radio" name="country" value="pl">Poland
+      <input type="radio" name="country" value="ru">Russia
+      <input type="radio" name="country" value="sa">Saudi Arabia
+      <input type="radio" name="country" value="se">Sweden
+      <input type="radio" name="country" value="sy">Syria
+      <input type="radio" name="country" value="tn">Tunisia
+      <input type="radio" name="country" value="tm">Turkmenistan
+      <input type="radio" name="country" value="us">U.S.A.
+      <input type="radio" name="country" value="uz">Uzbekistan
+      <input type="radio" name="country" value="vn">Vietnam
+      <input type="radio" name="country" value="ye">Yemen
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<p><a href="csv/direct-users.csv">CSV</a> file containing all data.</p>
+<p><a href="csv/monthly-users-peak.csv">CSV</a> file containing peak daily
+Tor users (recurring and bridge) per month by country.</p>
+<p><a href="csv/monthly-users-average.csv">CSV</a> file containing average
+daily Tor users (recurring and bridge) per month by country.</p>
+<br>
+
+<h3>Tor users via bridges</h3>
+<br>
+<p>Users who cannot connect directly to the Tor network instead connect
+via bridges, which are non-public relays. The following graphs display an
+estimate of Tor users via bridges based on the unique IP addresses as seen
+by a few hundred bridges.</p>
+<a name="bridge-users"></a>
+<%
+StringBuilder bridgeUsersUrl = new StringBuilder("bridge-users.png");
+if ("bridge-users".equals(request.getParameter("graph"))) {
+  List<String> parameters = new ArrayList<String>();
+// TODO check values here!
+  String startParameter = request.getParameter("start"),
+      endParameter = request.getParameter("end");
+  String[] countryParameters = request.getParameterValues("country");
+  if (startParameter != null && startParameter.length() > 0) {
+    parameters.add("start=" + startParameter);
+  }
+  if (endParameter != null && endParameter.length() > 0) {
+    parameters.add("end=" + endParameter);
+  }
+  if (countryParameters != null && countryParameters.length > 0) {
+    for (String country : countryParameters) {
+      if (country != null && country.length() > 0) {
+        parameters.add("country=" + country);
+      }
+    }
+  }
+  if (parameters.size() > 0) {
+    bridgeUsersUrl.append("?" + parameters.get(0));
+    if (parameters.size() > 1) {
+      for (int i = 1; i < parameters.size(); i++) {
+        bridgeUsersUrl.append("&" + parameters.get(i));
+      }
+    }
+  }
+}
+out.println("<img src=\"" + bridgeUsersUrl.toString() + "\" width=\"576\" "
+    + "height=\"360\" alt=\"Bridge users graph\">");
+%><form action="users.html#bridge-users">
+  <div class="formrow">
+    <input type="hidden" name="graph" value="bridge-users">
+    <p>
+    <label>Start date (yyyy-mm-dd):</label>
+      <input type="text" name="start" size="10"
+        value="<%=("bridge-users".equals(request.getParameter("graph")) &&
+                   request.getParameter("start") != null) ?
+                      request.getParameter("start") : ""%>">
+    <label>End date (yyyy-mm-dd):</label>
+      <input type="text" name="end" size="10"
+        value="<%=("bridge-users".equals(request.getParameter("graph")) &&
+                   request.getParameter("end") != null) ?
+                      request.getParameter("end") : ""%>">
+    </p><p>
+      Source:
+      <input type="radio" name="country" value="all">All users
+      <input type="radio" name="country" value="au">Australia
+      <input type="radio" name="country" value="bh">Bahrain
+      <input type="radio" name="country" value="br">Brazil
+      <input type="radio" name="country" value="ca">Canada
+      <input type="radio" name="country" value="cn">China
+      <input type="radio" name="country" value="cu">Cuba
+      <input type="radio" name="country" value="de">Germany
+      <input type="radio" name="country" value="et">Ethiopia
+      <input type="radio" name="country" value="fr">France
+      <input type="radio" name="country" value="gb">U.K.
+      <input type="radio" name="country" value="ir">Iran
+      <input type="radio" name="country" value="it">Italy
+      <input type="radio" name="country" value="jp">Japan
+      <input type="radio" name="country" value="kr">South Korea
+      <input type="radio" name="country" value="mm">Burma
+      <input type="radio" name="country" value="pl">Poland
+      <input type="radio" name="country" value="ru">Russia
+      <input type="radio" name="country" value="sa">Saudi Arabia
+      <input type="radio" name="country" value="se">Sweden
+      <input type="radio" name="country" value="sy">Syria
+      <input type="radio" name="country" value="tn">Tunisia
+      <input type="radio" name="country" value="tm">Turkmenistan
+      <input type="radio" name="country" value="us">U.S.A.
+      <input type="radio" name="country" value="uz">Uzbekistan
+      <input type="radio" name="country" value="vn">Vietnam
+      <input type="radio" name="country" value="ye">Yemen
+    </p><p>
+    <input class="submit" type="submit" value="Update graph">
+    </p>
+  </div>
+</form>
+<p><a href="csv/bridge-users.csv">CSV</a> file containing all data.</p>
+<p><a href="csv/monthly-users-peak.csv">CSV</a> file containing peak daily
+Tor users (recurring and bridge) per month by country.</p>
+<p><a href="csv/monthly-users-average.csv">CSV</a> file containing average
+daily Tor users (recurring and bridge) per month by country.</p>
+<br>
+    </div>
+  </div>
+  <div class="bottom" id="bottom">
+    <%@ include file="footer.jsp"%>
+  </div>
+</body>
+</html>
diff --git a/web/data.jsp b/web/data.jsp
deleted file mode 100644
index 4650eb7..0000000
--- a/web/data.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="research_data"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Data"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/graphs.jsp b/web/graphs.jsp
deleted file mode 100644
index e1c823b..0000000
--- a/web/graphs.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="graphs"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Graphs"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/index.jsp b/web/index.jsp
deleted file mode 100644
index 09cdeb5..0000000
--- a/web/index.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="index"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/network.jsp b/web/network.jsp
deleted file mode 100644
index e144505..0000000
--- a/web/network.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="graphs_network"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Network"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/packages.jsp b/web/packages.jsp
deleted file mode 100644
index 904b248..0000000
--- a/web/packages.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="graphs_packages"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Packages"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/papers.jsp b/web/papers.jsp
deleted file mode 100644
index 2bd6d10..0000000
--- a/web/papers.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="research_papers"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Papers"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/performance.jsp b/web/performance.jsp
deleted file mode 100644
index 56204b4..0000000
--- a/web/performance.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="graphs_performance"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Performance"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/research.jsp b/web/research.jsp
deleted file mode 100644
index 89a0887..0000000
--- a/web/research.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="research"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Research"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/status.jsp b/web/status.jsp
deleted file mode 100644
index 449c53f..0000000
--- a/web/status.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="status"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Status"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/tools.jsp b/web/tools.jsp
deleted file mode 100644
index 36aa75a..0000000
--- a/web/tools.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="research_tools"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Tools"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
diff --git a/web/users.jsp b/web/users.jsp
deleted file mode 100644
index bb0c143..0000000
--- a/web/users.jsp
+++ /dev/null
@@ -1,4 +0,0 @@
-<jsp:useBean id="template" class="org.torproject.ernie.web.TemplateController" scope="request" />
-<jsp:setProperty name="template" property="template" value="graphs_users"/>
-<jsp:setProperty name="template" property="title" value="Tor Metrics Portal: Users"/>
-<%@ include file="/WEB-INF/templates/main.tpl.jsp" %>
-- 
1.7.1



More information about the tor-commits mailing list