diff --git a/apps/i2psnark/web.xml b/apps/i2psnark/web.xml index d98f63733..a189fc42b 100644 --- a/apps/i2psnark/web.xml +++ b/apps/i2psnark/web.xml @@ -18,6 +18,15 @@ org.klomp.snark.web.I2PSnarkServlet 1 + + + net.i2p.servlet.ErrorServlet + net.i2p.servlet.ErrorServlet + + name + I2PSnark + + @@ -25,6 +34,11 @@ org.klomp.snark.web.I2PSnarkServlet / + + + net.i2p.servlet.ErrorServlet + /.error + @@ -36,4 +50,16 @@ + + 403 + /.error + + + 404 + /.error + + + 500 + /.error + diff --git a/apps/i2ptunnel/jsp/web.xml b/apps/i2ptunnel/jsp/web.xml index 238732868..312b64b91 100644 --- a/apps/i2ptunnel/jsp/web.xml +++ b/apps/i2ptunnel/jsp/web.xml @@ -13,13 +13,32 @@ /* + + net.i2p.servlet.ErrorServlet + net.i2p.servlet.ErrorServlet + + name + Hidden Services Manager + + + net.i2p.i2ptunnel.jsp.index_jsp - - / + + + + + + net.i2p.i2ptunnel.jsp.index_jsp + /index + + + + net.i2p.i2ptunnel.jsp.index_jsp + /index.html @@ -37,6 +56,11 @@ /register + + net.i2p.servlet.ErrorServlet + /error + + @@ -50,4 +74,17 @@ index.html index.jsp + + + 403 + /error + + + 404 + /error + + + 500 + /error + diff --git a/apps/imagegen/imagegen/webapp/src/main/webapp/WEB-INF/web.xml b/apps/imagegen/imagegen/webapp/src/main/webapp/WEB-INF/web.xml index 7e6ff2edf..df4bb0cd4 100644 --- a/apps/imagegen/imagegen/webapp/src/main/webapp/WEB-INF/web.xml +++ b/apps/imagegen/imagegen/webapp/src/main/webapp/WEB-INF/web.xml @@ -21,7 +21,16 @@ net.i2p.imagegen.RandomArtServlet 1 - + + + net.i2p.servlet.ErrorServlet + net.i2p.servlet.ErrorServlet + + name + imagegen + + + @@ -38,6 +47,12 @@ net.i2p.imagegen.RandomArtServlet /ra + + + net.i2p.servlet.ErrorServlet + /error + + @@ -49,4 +64,17 @@ + + 403 + /error + + + 404 + /error + + + 500 + /error + + diff --git a/apps/jetty/java/src/net/i2p/servlet/ErrorServlet.java b/apps/jetty/java/src/net/i2p/servlet/ErrorServlet.java new file mode 100644 index 000000000..beebf22e5 --- /dev/null +++ b/apps/jetty/java/src/net/i2p/servlet/ErrorServlet.java @@ -0,0 +1,238 @@ +package net.i2p.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.charset.Charset; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.ServletException; + +import net.i2p.CoreVersion; +import net.i2p.I2PAppContext; +import net.i2p.data.DataHelper; +import net.i2p.util.NativeBigInteger; +import net.i2p.util.Translate; + +import org.eclipse.jetty.server.Server; + +/** + * Common servlet for errors + * This is intended for webapps and local plugins. + * It is not appropriate for eepsites or remotely-accessible plugins, + * as it uses local console resources. + * + * See http://www.eclipse.org/jetty/documentation/current/custom-error-pages.html + * for how to add to web.xml or see examples in bundled webapps. + * + * Init parameters: + * + * + * Most strings are translated with the console bundle, as we're using the same strings + * as in error.jsp and error500.jsp. + * + * Supported error codes: 403, 404, and 500+. + * Others must be added here. + * + * @since 0.9.34 adapted from routerconsole error.jsp, error500.jsp, and CSSHelper + */ +public class ErrorServlet extends HttpServlet { + + private static final long serialVersionUID = 99356750L; + private final I2PAppContext _context; + private static final String CONSOLE_BUNDLE_NAME = "net.i2p.router.web.messages"; + private static final String PROP_THEME_NAME = "routerconsole.theme"; + private static final String PROP_THEME_PFX = PROP_THEME_NAME + '.'; + private static final String DEFAULT_THEME = "light"; + private static final String BASE_THEME_PATH = "/themes/console/"; + private static final String DEFAULT_ICO = "images/favicon.ico"; + private static final String DEFAULT_CSS = "console.css"; + /** to be added to head */ + private final String _icoPath = BASE_THEME_PATH + '/' + DEFAULT_ICO; + private String _cssPath; + /** for webapp translation */ + private String _webappName; + private String _bundleName; + private String _defaultBundle; + + public ErrorServlet() { + super(); + _context = I2PAppContext.getGlobalContext(); + } + + @Override + public void init() throws ServletException { + super.init(); + _cssPath = getInitParameter("CSSPath"); + if (_cssPath == null) { + String dir = BASE_THEME_PATH + + _context.getProperty(PROP_THEME_NAME, DEFAULT_THEME); + _cssPath = dir + '/' + DEFAULT_CSS; + } + _webappName = getInitParameter("name"); + if (_webappName == null) + _webappName = "unknown"; + _bundleName = getInitParameter("bundle"); + _defaultBundle = _bundleName != null ? _bundleName : CONSOLE_BUNDLE_NAME; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setCharacterEncoding("UTF-8"); + resp.setHeader("X-Content-Type-Options", "nosniff"); + resp.setHeader("Accept-Ranges", "none"); + resp.setDateHeader("Expires", 0); + resp.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate"); + resp.setHeader("Pragma", "no-cache"); + resp.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'none'"); + Integer ERROR_CODE = (Integer) req.getAttribute("javax.servlet.error.status_code"); + String ERROR_URI = (String) req.getAttribute("javax.servlet.error.request_uri"); + String ERROR_MESSAGE = (String) req.getAttribute("javax.servlet.error.message"); + Throwable ERROR_THROWABLE = (Throwable) req.getAttribute("javax.servlet.error.exception"); + int errorCode = ERROR_CODE != null ? ERROR_CODE.intValue() : 0; + if (ERROR_CODE != null) { + resp.setStatus(errorCode); + } + if (ERROR_URI == null) + ERROR_URI = ""; + else + ERROR_URI = DataHelper.escapeHTML(ERROR_URI); + if (ERROR_MESSAGE == null) + ERROR_MESSAGE = ""; + else + ERROR_MESSAGE = DataHelper.escapeHTML(ERROR_MESSAGE); + if (errorCode == 404 && + (ERROR_URI.endsWith(".png") || + ERROR_URI.endsWith(".jpg") || + ERROR_URI.endsWith(".gif") || + ERROR_URI.endsWith(".ico") || + ERROR_URI.endsWith(".svg") || + ERROR_URI.endsWith(".txt") || + ERROR_URI.endsWith(".css"))) { + // keep it simple + resp.setContentType("text/plain"); + PrintWriter out = resp.getWriter(); + out.println(_t("Error {0}", 404) + ": " + ERROR_URI + ' ' + _t("not found")); + out.close(); + return; + } + resp.setContentType("text/html"); + PrintWriter out = resp.getWriter(); + out.println(""); + out.print(""); + if (errorCode == 404) + out.print(_t("Page Not Found")); + else + out.print(_t("Internal Error")); + out.println(""); + out.println(""); + if (_icoPath != null) + out.println(""); + out.println(""); + out.println(""); + out.println("
"); + out.println("\""
"); + out.println("" + _t("Configuration") + " " + _t("Help") + ""); + out.println("
"); + out.println("
"); + out.println("

" + _t("Error {0}", errorCode) + ": " + ERROR_MESSAGE + "

"); + out.println("

" + _t("WebApp") + ": " + _w(_webappName) + "

"); + outputMessage(out, errorCode, ERROR_MESSAGE, ERROR_URI, ERROR_THROWABLE); + out.println("
"); + out.close(); + } + + /** + * Override for specific cases. + * This supports 403, 404, and 500+. + * Output HTML that goes inside the div. + * + * @param errorCode e.g. 404 + * @param errorMsg non-null, may be empty, already HTML-escaped + * @param errorURI non-null, may be empty, already HTML-escaped + * @param errorCause may be null + */ + protected void outputMessage(PrintWriter out, int errorCode, String errorMsg, String errorURI, Throwable errorCause) { + if (errorCode == 404) { + out.println(_t("Sorry! You appear to be requesting a non-existent Router Console page or resource.")); + out.println("
"); + out.println(_t("Error {0}", 404) + ": " + errorURI + " " + _t("not found")); + } else if (errorCode == 403 || errorCode >= 500 || errorCause != null) { + out.println(_t("Sorry! There has been an internal error.")); + out.println("
"); + out.println("

"); + out.println(_t("Please report bugs on {0} or {1}.", + "trac.i2p2.i2p", + "trac.i2p2.de")); + out.println("

"); + out.println(_t("Please include this information in bug reports") + ':'); + out.print("

"); + out.print(_t("Error Details")); + out.println("

"); + out.println(_t("Error {0}", errorCode) + ": " + errorURI + " " + errorMsg); + out.println("

"); + if (errorCause != null) { + StringWriter sw = new StringWriter(2048); + PrintWriter pw = new PrintWriter(sw); + errorCause.printStackTrace(pw); + pw.flush(); + String trace = sw.toString(); + trace = trace.replace("&", "&").replace("<", "<").replace(">", ">"); + trace = trace.replace("\n", "
    \n"); + out.print(trace); + } + out.print("

"); + out.println(_t("I2P Version and Running Environment")); + out.println("

"); + // router puts its version here + String version = System.getProperty("router.version", CoreVersion.VERSION); + out.println("I2P version: " + version + "
"); + out.println("Java version: " + System.getProperty("java.vendor") + ' ' + System.getProperty("java.version") + + " (" + System.getProperty("java.runtime.name") + ' ' + System.getProperty("java.runtime.version") + ")
"); + out.println("Wrapper version: " + System.getProperty("wrapper.version", "none") + "
"); + try { + // wrap in case not running on Jetty + out.println("Server version: " + Server.getVersion() + "
"); + } catch (Throwable t) {} + out.println("Platform: " + System.getProperty("os.name") + ' ' + System.getProperty("os.arch") + + ' ' + System.getProperty("os.version") + "
"); + out.println("Processor: " + NativeBigInteger.cpuModel() + " (" + NativeBigInteger.cpuType() + ")
"); + out.println("Jbigi: " + NativeBigInteger.loadStatus() + "
"); + out.println("Encoding: " + System.getProperty("file.encoding") + "
"); + out.println("Charset: " + Charset.defaultCharset().name()); + out.println("

"); + out.println(_t("Note that system information, log timestamps, and log messages may provide clues to your location; please review everything you include in a bug report.")); + out.println("

"); + } else { + out.println("

Unsupported error " + errorCode + "

"); + } + } + + /** translate a string, with webapp bundle */ + protected String _w(String s) { + return Translate.getString(s, _context, _defaultBundle); + } + + /** translate a string, console bundle */ + protected String _t(String s) { + return Translate.getString(s, _context, CONSOLE_BUNDLE_NAME); + } + + /** translate a string, console bundle */ + protected String _t(String s, Object o) { + return Translate.getString(s, o, _context, CONSOLE_BUNDLE_NAME); + } + + /** translate a string, console bundle */ + protected String _t(String s, Object o, Object o2) { + return Translate.getString(s, o, o2, _context, CONSOLE_BUNDLE_NAME); + } +} diff --git a/apps/susidns/src/WEB-INF/web-template.xml b/apps/susidns/src/WEB-INF/web-template.xml index 7dd017e52..9e1c63ef5 100644 --- a/apps/susidns/src/WEB-INF/web-template.xml +++ b/apps/susidns/src/WEB-INF/web-template.xml @@ -29,6 +29,15 @@ i2p.susi.dns.TranslateSVGServlet + + net.i2p.servlet.ErrorServlet + net.i2p.servlet.ErrorServlet + + name + Addressbook + + + addressbook-runner /addressbook-runner @@ -63,6 +72,17 @@ /index + + i2p.susi.dns.jsp.index_jsp + /index.html + + + + i2p.susi.dns.jsp.index_jsp + + + + i2p.susi.dns.jsp.export_jsp /export @@ -73,6 +93,11 @@ /images/* + + net.i2p.servlet.ErrorServlet + /error + + 30 @@ -83,4 +108,17 @@ index.jsp + + 403 + /error + + + 404 + /error + + + 500 + /error + + diff --git a/apps/susidns/src/build.xml b/apps/susidns/src/build.xml index 1f7ee71d5..4492fd59e 100644 --- a/apps/susidns/src/build.xml +++ b/apps/susidns/src/build.xml @@ -106,7 +106,6 @@ - @@ -123,7 +122,7 @@ - + diff --git a/apps/susidns/src/index.html b/apps/susidns/src/index.html deleted file mode 100644 index d87d78345..000000000 --- a/apps/susidns/src/index.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - -susidns - - -Enter - - - diff --git a/apps/susimail/src/WEB-INF/web.xml b/apps/susimail/src/WEB-INF/web.xml index 83b96a1e2..b322c097f 100644 --- a/apps/susimail/src/WEB-INF/web.xml +++ b/apps/susimail/src/WEB-INF/web.xml @@ -23,10 +23,26 @@ 131072 + + + net.i2p.servlet.ErrorServlet + net.i2p.servlet.ErrorServlet + + name + SusiMail + + + SusiMail /susimail + + + net.i2p.servlet.ErrorServlet + /error + + 1440 @@ -48,25 +64,17 @@ SUSIMAILJSESSIONID - + + 403 + /error + + + 404 + /error + + + 500 + /error + diff --git a/history.txt b/history.txt index 33af07fc0..6f3f79578 100644 --- a/history.txt +++ b/history.txt @@ -2,6 +2,7 @@ * Console: - Hide links to webapps and eepsite if not running (ticket #2161) - Hide link to /configplugins if disabled + - Add error handler to webapps (ticket #2155) * i2ptunnel: Hide links to webapps that are not runnning (ticket #2161) * SusDNS: Translate svg image text (ticket #1749) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index e1f87e077..8ab24107c 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 16; + public final static long BUILD = 17; /** for example "-test" */ public final static String EXTRA = "";