[or-cvs] [metrics-web/master 2/2] Reuse graph parameter checking for graph subpages.

karsten at torproject.org karsten at torproject.org
Sun Oct 17 15:00:11 UTC 2010


Author: Karsten Loesing <karsten.loesing at gmx.net>
Date: Sun, 17 Oct 2010 16:59:27 +0200
Subject: Reuse graph parameter checking for graph subpages.
Commit: eb4e47da383f0a4da2b3430d9b026e552682255c

---
 etc/web.xml                                        |   30 +--
 src/org/torproject/ernie/web/GraphController.java  |   78 ------
 src/org/torproject/ernie/web/GraphGenerator.java   |   78 ++++++
 .../torproject/ernie/web/GraphImageServlet.java    |    8 +-
 .../ernie/web/GraphParameterChecker.java           |    5 +-
 .../torproject/ernie/web/GraphsNetworkServlet.java |   16 --
 .../ernie/web/GraphsPackagesServlet.java           |   16 --
 .../ernie/web/GraphsPerformanceServlet.java        |   16 --
 .../ernie/web/GraphsSubpagesServlet.java           |   76 ++++++
 .../torproject/ernie/web/GraphsUsersServlet.java   |   16 --
 web/WEB-INF/network.jsp                            |  270 +++-----------------
 web/WEB-INF/packages.jsp                           |   45 +---
 web/WEB-INF/performance.jsp                        |   49 +---
 web/WEB-INF/users.jsp                              |  134 ++---------
 14 files changed, 232 insertions(+), 605 deletions(-)
 delete mode 100644 src/org/torproject/ernie/web/GraphController.java
 create mode 100644 src/org/torproject/ernie/web/GraphGenerator.java
 delete mode 100644 src/org/torproject/ernie/web/GraphsNetworkServlet.java
 delete mode 100644 src/org/torproject/ernie/web/GraphsPackagesServlet.java
 delete mode 100644 src/org/torproject/ernie/web/GraphsPerformanceServlet.java
 create mode 100644 src/org/torproject/ernie/web/GraphsSubpagesServlet.java
 delete mode 100644 src/org/torproject/ernie/web/GraphsUsersServlet.java

diff --git a/etc/web.xml b/etc/web.xml
index 0e73fb1..169160b 100644
--- a/etc/web.xml
+++ b/etc/web.xml
@@ -27,43 +27,25 @@
     <url-pattern>/graphs.html</url-pattern>
   </servlet-mapping>
   <servlet>
-    <servlet-name>GraphsNetwork</servlet-name>
+    <servlet-name>GraphsSubpages</servlet-name>
     <servlet-class>
-      org.torproject.ernie.web.GraphsNetworkServlet
+      org.torproject.ernie.web.GraphsSubpagesServlet
     </servlet-class>
   </servlet>
   <servlet-mapping>
-    <servlet-name>GraphsNetwork</servlet-name>
+    <servlet-name>GraphsSubpages</servlet-name>
     <url-pattern>/network.html</url-pattern>
   </servlet-mapping>
-  <servlet>
-    <servlet-name>GraphsUsers</servlet-name>
-    <servlet-class>
-      org.torproject.ernie.web.GraphsUsersServlet
-    </servlet-class>
-  </servlet>
   <servlet-mapping>
-    <servlet-name>GraphsUsers</servlet-name>
+    <servlet-name>GraphsSubpages</servlet-name>
     <url-pattern>/users.html</url-pattern>
   </servlet-mapping>
-  <servlet>
-    <servlet-name>GraphsPackages</servlet-name>
-    <servlet-class>
-      org.torproject.ernie.web.GraphsPackagesServlet
-    </servlet-class>
-  </servlet>
   <servlet-mapping>
-    <servlet-name>GraphsPackages</servlet-name>
+    <servlet-name>GraphsSubpages</servlet-name>
     <url-pattern>/packages.html</url-pattern>
   </servlet-mapping>
-  <servlet>
-    <servlet-name>GraphsPerformance</servlet-name>
-    <servlet-class>
-      org.torproject.ernie.web.GraphsPerformanceServlet
-    </servlet-class>
-  </servlet>
   <servlet-mapping>
-    <servlet-name>GraphsPerformance</servlet-name>
+    <servlet-name>GraphsSubpages</servlet-name>
     <url-pattern>/performance.html</url-pattern>
   </servlet-mapping>
   <servlet>
diff --git a/src/org/torproject/ernie/web/GraphController.java b/src/org/torproject/ernie/web/GraphController.java
deleted file mode 100644
index 01d59de..0000000
--- a/src/org/torproject/ernie/web/GraphController.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.torproject.ernie.web;
-
-import java.io.*;
-import java.util.*;
-
-import org.rosuda.REngine.Rserve.*;
-import org.rosuda.REngine.*;
-
-public class GraphController {
-
-  /* Host and port where Rserve is listening. */
-  private String rserveHost;
-  private int rservePort;
-
-  /* Some parameters for our cache of graph images. */
-  private String cachedGraphsDirectory;
-  private long maxCacheAge;
-
-  public GraphController(String rserveHost, String rservePort,
-      String maxCacheAge, String cachedGraphsDir) {
-
-    this.rserveHost = rserveHost;
-    this.rservePort = Integer.parseInt(rservePort);
-    this.cachedGraphsDirectory = cachedGraphsDir;
-    this.maxCacheAge = Long.parseLong(maxCacheAge);
-  }
-
-  /* Generate a graph using the given R query that has a placeholder for
-   * the absolute path to the image to be created. */
-  public byte[] generateGraph(String rQuery, String imageFilename) {
-
-    /* See if we need to generate this graph. */
-    File imageFile = new File(this.cachedGraphsDirectory + "/"
-        + imageFilename);
-    long now = System.currentTimeMillis();
-    if (!imageFile.exists() || imageFile.lastModified() < now
-        - this.maxCacheAge * 1000L) {
-
-      /* We do. Update the R query to contain the absolute path to the file
-       * to be generated, create a connection to Rserve, run the R query,
-       * and close the connection. The generated graph will be on disk. */
-      rQuery = String.format(rQuery, imageFile.getAbsolutePath());
-      try {
-        RConnection rc = new RConnection(rserveHost, rservePort);
-        rc.eval(rQuery);
-        rc.close();
-      } catch (RserveException e) {
-        return null;
-      }
-
-      /* Check that we really just generated the file */
-      if (!imageFile.exists() || imageFile.lastModified() < now
-          - this.maxCacheAge * 1000L) {
-        return null;
-      }
-    }
-
-    /* Read the image from disk and write it to a byte array. */
-    byte[] result = null;
-    try {
-      BufferedInputStream bis = new BufferedInputStream(
-          new FileInputStream(imageFile), 1024);
-      ByteArrayOutputStream baos = new ByteArrayOutputStream();
-      byte[] buffer = new byte[1024];
-      int length;
-      while ((length = bis.read(buffer)) > 0) {
-        baos.write(buffer, 0, length);
-      }
-      result = baos.toByteArray();
-    } catch (IOException e) {
-      return null;
-    }
-
-    /* Return the graph bytes. */
-    return result;
-  }
-}
-
diff --git a/src/org/torproject/ernie/web/GraphGenerator.java b/src/org/torproject/ernie/web/GraphGenerator.java
new file mode 100644
index 0000000..2b59e2e
--- /dev/null
+++ b/src/org/torproject/ernie/web/GraphGenerator.java
@@ -0,0 +1,78 @@
+package org.torproject.ernie.web;
+
+import java.io.*;
+import java.util.*;
+
+import org.rosuda.REngine.Rserve.*;
+import org.rosuda.REngine.*;
+
+public class GraphGenerator {
+
+  /* Host and port where Rserve is listening. */
+  private String rserveHost;
+  private int rservePort;
+
+  /* Some parameters for our cache of graph images. */
+  private String cachedGraphsDirectory;
+  private long maxCacheAge;
+
+  public GraphGenerator(String rserveHost, String rservePort,
+      String maxCacheAge, String cachedGraphsDir) {
+
+    this.rserveHost = rserveHost;
+    this.rservePort = Integer.parseInt(rservePort);
+    this.cachedGraphsDirectory = cachedGraphsDir;
+    this.maxCacheAge = Long.parseLong(maxCacheAge);
+  }
+
+  /* Generate a graph using the given R query that has a placeholder for
+   * the absolute path to the image to be created. */
+  public byte[] generateGraph(String rQuery, String imageFilename) {
+
+    /* See if we need to generate this graph. */
+    File imageFile = new File(this.cachedGraphsDirectory + "/"
+        + imageFilename);
+    long now = System.currentTimeMillis();
+    if (!imageFile.exists() || imageFile.lastModified() < now
+        - this.maxCacheAge * 1000L) {
+
+      /* We do. Update the R query to contain the absolute path to the file
+       * to be generated, create a connection to Rserve, run the R query,
+       * and close the connection. The generated graph will be on disk. */
+      rQuery = String.format(rQuery, imageFile.getAbsolutePath());
+      try {
+        RConnection rc = new RConnection(rserveHost, rservePort);
+        rc.eval(rQuery);
+        rc.close();
+      } catch (RserveException e) {
+        return null;
+      }
+
+      /* Check that we really just generated the file */
+      if (!imageFile.exists() || imageFile.lastModified() < now
+          - this.maxCacheAge * 1000L) {
+        return null;
+      }
+    }
+
+    /* Read the image from disk and write it to a byte array. */
+    byte[] result = null;
+    try {
+      BufferedInputStream bis = new BufferedInputStream(
+          new FileInputStream(imageFile), 1024);
+      ByteArrayOutputStream baos = new ByteArrayOutputStream();
+      byte[] buffer = new byte[1024];
+      int length;
+      while ((length = bis.read(buffer)) > 0) {
+        baos.write(buffer, 0, length);
+      }
+      result = baos.toByteArray();
+    } catch (IOException e) {
+      return null;
+    }
+
+    /* Return the graph bytes. */
+    return result;
+  }
+}
+
diff --git a/src/org/torproject/ernie/web/GraphImageServlet.java b/src/org/torproject/ernie/web/GraphImageServlet.java
index add55c7..ae0c82d 100644
--- a/src/org/torproject/ernie/web/GraphImageServlet.java
+++ b/src/org/torproject/ernie/web/GraphImageServlet.java
@@ -8,12 +8,12 @@ import javax.servlet.http.*;
 
 /**
  * Servlet that reads an HTTP request for a graph image, asks the
- * GraphController to generate this graph if it's not in the cache, and
+ * GraphGenerator to generate this graph if it's not in the cache, and
  * returns the image bytes to the client.
  */
 public class GraphImageServlet extends HttpServlet {
 
-  private GraphController graphController;
+  private GraphGenerator graphGenerator;
 
   public void init() {
     ServletConfig servletConfig = getServletConfig();
@@ -22,7 +22,7 @@ public class GraphImageServlet extends HttpServlet {
     String maxCacheAge = servletConfig.getInitParameter("maxCacheAge");
     String cachedGraphsDir = servletConfig.getInitParameter(
         "cachedGraphsDir");
-    this.graphController = new GraphController(rserveHost, rservePort,
+    this.graphGenerator = new GraphGenerator(rserveHost, rservePort,
         maxCacheAge, cachedGraphsDir);
   }
 
@@ -85,7 +85,7 @@ public class GraphImageServlet extends HttpServlet {
 
     /* Request graph from graph controller, which either returns it from
      * its cache or asks Rserve to generate it. */
-    byte[] graphBytes = graphController.generateGraph(rQuery,
+    byte[] graphBytes = graphGenerator.generateGraph(rQuery,
         imageFilename);
 
     /* Make sure that we have a graph to return. */
diff --git a/src/org/torproject/ernie/web/GraphParameterChecker.java b/src/org/torproject/ernie/web/GraphParameterChecker.java
index 2bbbaf9..d4e085f 100644
--- a/src/org/torproject/ernie/web/GraphParameterChecker.java
+++ b/src/org/torproject/ernie/web/GraphParameterChecker.java
@@ -93,7 +93,10 @@ public class GraphParameterChecker {
         supportedGraphParameters.contains("end")) {
       String[] startParameter = (String[]) requestParameters.get("start");
       String[] endParameter = (String[]) requestParameters.get("end");
-      if (startParameter == null && endParameter == null) {
+      if ((startParameter == null || startParameter.length < 1 ||
+          startParameter[0].length() < 1) &&
+          (endParameter == null || endParameter.length < 1 ||
+          endParameter[0].length() < 1)) {
         /* If no start and end parameters are given, set default date
          * range to the past 90 days. */
         long now = System.currentTimeMillis();
diff --git a/src/org/torproject/ernie/web/GraphsNetworkServlet.java b/src/org/torproject/ernie/web/GraphsNetworkServlet.java
deleted file mode 100644
index d2cb9ea..0000000
--- a/src/org/torproject/ernie/web/GraphsNetworkServlet.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.torproject.ernie.web;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-import java.io.*;
-
-public class GraphsNetworkServlet 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("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
deleted file mode 100644
index 0ad1039..0000000
--- a/src/org/torproject/ernie/web/GraphsPackagesServlet.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.torproject.ernie.web;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-import java.io.*;
-
-public class GraphsPackagesServlet 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("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
deleted file mode 100644
index 048146b..0000000
--- a/src/org/torproject/ernie/web/GraphsPerformanceServlet.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.torproject.ernie.web;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-import java.io.*;
-
-public class GraphsPerformanceServlet 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("WEB-INF/performance.jsp").forward(
-        request, response);
-  }
-}
-
diff --git a/src/org/torproject/ernie/web/GraphsSubpagesServlet.java b/src/org/torproject/ernie/web/GraphsSubpagesServlet.java
new file mode 100644
index 0000000..1ae39d1
--- /dev/null
+++ b/src/org/torproject/ernie/web/GraphsSubpagesServlet.java
@@ -0,0 +1,76 @@
+package org.torproject.ernie.web;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+import java.io.*;
+import java.util.*;
+
+public class GraphsSubpagesServlet extends HttpServlet {
+
+  /* Available graphs subpages with corresponding JSP to which requests
+   * are forwarded. */
+  private Map<String, String> availableGraphsSubpages;
+
+  public GraphsSubpagesServlet() {
+    this.availableGraphsSubpages = new HashMap<String, String>();
+    this.availableGraphsSubpages.put("network.html",
+        "WEB-INF/network.jsp");
+    this.availableGraphsSubpages.put("users.html", "WEB-INF/users.jsp");
+    this.availableGraphsSubpages.put("packages.html",
+        "WEB-INF/packages.jsp");
+    this.availableGraphsSubpages.put("performance.html",
+        "WEB-INF/performance.jsp");
+  }
+
+  public void doGet(HttpServletRequest request,
+      HttpServletResponse response) throws IOException, ServletException {
+
+    /* Find out which graph subpage was requested and look up which JSP
+     * handles this subpage. */
+    String requestedPage = request.getRequestURI();
+    if (requestedPage == null) {
+      response.sendError(HttpServletResponse.SC_BAD_REQUEST);
+      return;
+    }
+    if (requestedPage.contains("/")) {
+      requestedPage = requestedPage.substring(requestedPage.
+          lastIndexOf("/") + 1);
+    }
+    if (!availableGraphsSubpages.containsKey(requestedPage)) {
+      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+      return;
+    }
+    String jsp = availableGraphsSubpages.get(requestedPage);
+
+    /* Find out which graph type was requested, if any. */
+    String requestedGraph = request.getParameter("graph");
+    if (requestedGraph != null) {
+
+      /* Check if the passed parameters are valid. */
+      Map<String, String[]> checkedParameters = GraphParameterChecker.
+          getInstance().checkParameters(requestedGraph,
+          request.getParameterMap());
+      if (checkedParameters != null) {
+
+        /* Set the graph's attributes to the appropriate values, so that
+         * we can display the correct graph and prepopulate the form. */
+        StringBuilder urlBuilder = new StringBuilder();
+        for (Map.Entry<String, String[]> param :
+            checkedParameters.entrySet()) {
+          request.setAttribute(requestedGraph.replaceAll("-", "_") + "_"
+              + param.getKey(), param.getValue());
+          for (String paramValue : param.getValue()) {
+            urlBuilder.append("&" + param.getKey() + "=" + paramValue);
+          }
+        }
+        String url = "?" + urlBuilder.toString().substring(1);
+        request.setAttribute(requestedGraph.replaceAll("-", "_") + "_url",
+            url);
+      }
+    }
+
+    /* Forward the request to the JSP that does all the hard work. */
+    request.getRequestDispatcher(jsp).forward(request, response);
+  }
+}
+
diff --git a/src/org/torproject/ernie/web/GraphsUsersServlet.java b/src/org/torproject/ernie/web/GraphsUsersServlet.java
deleted file mode 100644
index f7bbbb9..0000000
--- a/src/org/torproject/ernie/web/GraphsUsersServlet.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.torproject.ernie.web;
-
-import javax.servlet.*;
-import javax.servlet.http.*;
-import java.io.*;
-
-public class GraphsUsersServlet 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("WEB-INF/users.jsp").forward(request,
-        response);
-  }
-}
-
diff --git a/web/WEB-INF/network.jsp b/web/WEB-INF/network.jsp
index ff4a357..9e2a08b 100644
--- a/web/WEB-INF/network.jsp
+++ b/web/WEB-INF/network.jsp
@@ -1,3 +1,4 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 <html>
 <head>
@@ -10,7 +11,6 @@
   <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>
@@ -18,44 +18,18 @@
 <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">
+<img src="networksize.png${networksize_url}"
+     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") : ""%>">
+             value="${networksize_start[0]}">
     <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") : ""%>">
+             value="${networksize_end[0]}">
     </p><p>
     <input class="submit" type="submit" value="Update graph">
     </p>
@@ -70,52 +44,18 @@ out.println("<img src=\"" + networksizeUrl.toString() + "\" width=\"576\" "
 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">
+<img src="relayflags.png${relayflags_url}"
+     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") : ""%>">
+             value="${relayflags_start[0]}">
     <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") : ""%>">
+             value="${relayflags_end[0]}">
     </p><p>
       <label>Relay flags: </label>
       <input type="checkbox" name="flag" value="Running"> Running
@@ -135,52 +75,18 @@ out.println("<img src=\"" + relayflagsUrl.toString() + "\" width=\"576\" "
 <p>The same graph on the average number of relays with flags assigned is
 available on 1-hour detail.</p>
 <a name="relayflags-hour"></a>
-<%
-StringBuilder relayflagsHourUrl = new StringBuilder("relayflags-hour.png");
-if ("relayflags-hour".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) {
-    relayflagsHourUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        relayflagsHourUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + relayflagsHourUrl.toString()
-    + "\" width=\"576\" height=\"360\" alt=\"Relay flags graph\">");
-%><form action="network.html#relayflags-hour">
+<img src="relayflags-hour.png${relayflags_hour_url}"
+     width="576" height="360" alt="Relay flags graph">
+<form action="network.html#relayflags-hour">
   <div class="formrow">
     <input type="hidden" name="graph" value="relayflags-hour">
     <p>
     <label>Start date (yyyy-mm-dd):</label>
       <input type="text" name="start" size="10"
-        value="<%=("relayflags-hour".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
+             value="${relayflags_hour_start[0]}">
     <label>End date (yyyy-mm-dd):</label>
       <input type="text" name="end" size="10"
-        value="<%=("relayflags-hour".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
+             value="${relayflags_hour_end[0]}">
     </p><p>
       <label>Relay flags: </label>
       <input type="checkbox" name="flag" value="Running"> Running
@@ -201,44 +107,18 @@ out.println("<img src=\"" + relayflagsHourUrl.toString()
 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">
+<img src="versions.png${versions_url}"
+     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") : ""%>">
+             value="${versions_start[0]}">
     <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") : ""%>">
+             value="${versions_end[0]}">
     </p><p>
     <input class="submit" type="submit" value="Update graph">
     </p>
@@ -252,44 +132,18 @@ out.println("<img src=\"" + versionsUrl.toString() + "\" width=\"576\" "
 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">
+<img src="platforms.png${platforms_url}"
+     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") : ""%>">
+             value="${platforms_start[0]}">
     <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") : ""%>">
+             value="${platforms_end[0]}">
     </p><p>
     <input class="submit" type="submit" value="Update graph">
     </p>
@@ -304,44 +158,18 @@ 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">
+<img src="bandwidth.png${bandwidth_url}"
+     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") : ""%>">
+             value="${bandwidth_start[0]}">
     <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") : ""%>">
+             value="${bandwidth_end[0]}">
     </p><p>
     <input class="submit" type="submit" value="Update graph">
     </p>
@@ -357,44 +185,18 @@ total written and read bytes as well as written and read dir bytes. The
 dir bytes are extrapolated from those relays who report them to reflect
 the number of written and read dir bytes by all relays.</p>
 <a name="dirbytes"></a>
-<%
-StringBuilder dirbytesUrl = new StringBuilder("dirbytes.png");
-if ("dirbytes".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) {
-    dirbytesUrl.append("?" + parameters.get(0));
-    if (parameters.size() > 1) {
-      for (int i = 1; i < parameters.size(); i++) {
-        dirbytesUrl.append("&" + parameters.get(i));
-      }
-    }
-  }
-}
-out.println("<img src=\"" + dirbytesUrl.toString() + "\" width=\"576\" "
-    + "height=\"360\" alt=\"Dir bytes graph\">");
-%><form action="network.html#dirbytes">
+<img src="dirbytes.png${dirbytes_url}"
+     width="576" height="360" alt="Dir bytes graph">
+<form action="network.html#dirbytes">
   <div class="formrow">
     <input type="hidden" name="graph" value="dirbytes">
     <p>
     <label>Start date (yyyy-mm-dd):</label>
       <input type="text" name="start" size="10"
-        value="<%=("dirbytes".equals(request.getParameter("graph")) &&
-                   request.getParameter("start") != null) ?
-                      request.getParameter("start") : ""%>">
+             value="${dirbytes_start[0]}">
     <label>End date (yyyy-mm-dd):</label>
       <input type="text" name="end" size="10"
-        value="<%=("dirbytes".equals(request.getParameter("graph")) &&
-                   request.getParameter("end") != null) ?
-                      request.getParameter("end") : ""%>">
+             value="${dirbytes_end[0]}">
     </p><p>
     <input class="submit" type="submit" value="Update graph">
     </p>
diff --git a/web/WEB-INF/packages.jsp b/web/WEB-INF/packages.jsp
index 5decbef..8869155 100644
--- a/web/WEB-INF/packages.jsp
+++ b/web/WEB-INF/packages.jsp
@@ -1,3 +1,4 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 <html>
 <head>
@@ -10,7 +11,6 @@
   <div class="center">
     <%@ include file="banner.jsp"%>
     <div class="main-column">
-<%@page import="java.util.*" %>
 <h2>Tor Metrics Portal: Packages</h2>
 <br>
 <h3>Packages requested from GetTor</h3>
@@ -19,51 +19,18 @@
 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">
+<img src="gettor.png${gettor_url}"
+     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") : ""%>">
+             value="${gettor_start[0]}">
     <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") : ""%>">
+             value="${gettor_end[0]}">
     </p><p>
       Packages:
       <input type="radio" name="bundle" value="all"> Total packages
diff --git a/web/WEB-INF/performance.jsp b/web/WEB-INF/performance.jsp
index 6365196..adcf126 100644
--- a/web/WEB-INF/performance.jsp
+++ b/web/WEB-INF/performance.jsp
@@ -1,3 +1,4 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 <html>
 <head>
@@ -10,7 +11,6 @@
   <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>
@@ -20,55 +20,18 @@ 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">
+<img src="torperf.png${torperf_url}"
+     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") : ""%>">
+             value="${torperf_start[0]}">
     <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") : ""%>">
+             value="${torperf_end[0]}">
     </p><p>
       Source:
       <input type="radio" name="source" value="torperf"> torperf
diff --git a/web/WEB-INF/users.jsp b/web/WEB-INF/users.jsp
index b54fb8b..835aede 100644
--- a/web/WEB-INF/users.jsp
+++ b/web/WEB-INF/users.jsp
@@ -1,3 +1,4 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 <html>
 <head>
@@ -10,7 +11,6 @@
   <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>
@@ -23,52 +23,18 @@ 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">
+<img src="new-users.png${new_users_url}"
+     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") : ""%>">
+             value="${new_users_start[0]}">
     <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") : ""%>">
+             value="${new_users_end[0]}">
     </p><p>
       Source:
       <input type="radio" name="country" value="all">All users
@@ -115,52 +81,18 @@ 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">
+<img src="direct-users.png${direct_users_url}"
+     width="576" height="360" alt="Direct 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") : ""%>">
+             value="${direct_users_start[0]}">
     <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") : ""%>">
+             value="${direct_users_end[0]}">
     </p><p>
       Source:
       <input type="radio" name="country" value="all">All users
@@ -209,52 +141,18 @@ 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">
+<img src="bridge-users.png${bridge_users_url}"
+     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") : ""%>">
+             value="${bridge_users_start[0]}">
     <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") : ""%>">
+             value="${bridge_users_end[0]}">
     </p><p>
       Source:
       <input type="radio" name="country" value="all">All users
-- 
1.7.1



More information about the tor-commits mailing list