diff --git a/apps/jetty/java/src/net/i2p/servlet/RequestWrapper.java b/apps/jetty/java/src/net/i2p/servlet/RequestWrapper.java index c0e06f9c1b8d92ad00f179ffd235c06af59b225e..ffc7e306a4eb76211df06c86d5fcadfeabf5aff0 100644 --- a/apps/jetty/java/src/net/i2p/servlet/RequestWrapper.java +++ b/apps/jetty/java/src/net/i2p/servlet/RequestWrapper.java @@ -101,6 +101,7 @@ public class RequestWrapper { /** * @return List of request parameter names + * @throws IllegalStateException if the request is too large */ public Enumeration<String> getParameterNames() { if (isMultiPartRequest) { @@ -117,6 +118,7 @@ public class RequestWrapper { log(se); } catch (IllegalStateException ise) { log(ise); + throw ise; } } return cachedParameterNames.keys(); @@ -139,6 +141,9 @@ public class RequestWrapper { return httpRequest.getContentType(); } + /** + * @throws IllegalStateException if the request is too large + */ public String getContentType( String partName ) { String result = null; @@ -153,6 +158,7 @@ public class RequestWrapper { log(se); } catch (IllegalStateException ise) { log(ise); + throw ise; } } return result; @@ -162,6 +168,9 @@ public class RequestWrapper { return httpRequest.getAttribute( string ); } + /** + * @throws IllegalStateException if the request is too large + */ public String getParameter( String name, String defaultValue ) { String result = defaultValue; @@ -192,6 +201,7 @@ public class RequestWrapper { log(se); } catch (IllegalStateException ise) { log(ise); + throw ise; } finally { if (in != null) try { in.close(); } catch (IOException ioe) {} } @@ -204,6 +214,9 @@ public class RequestWrapper { return result; } + /** + * @throws IllegalStateException if the request is too large + */ public String getFilename(String partName ) { String result = null; @@ -218,11 +231,15 @@ public class RequestWrapper { log(se); } catch (IllegalStateException ise) { log(ise); + throw ise; } } return result; } + /** + * @throws IllegalStateException if the request is too large + */ public InputStream getInputStream(String partName ) { InputStream result = null; @@ -237,6 +254,7 @@ public class RequestWrapper { log(se); } catch (IllegalStateException ise) { log(ise); + throw ise; } } return result; diff --git a/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java b/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java index b29892e70f6a7f85fb3f1b90b78d0704e00f948b..9fcce1aba043ac0ca3664990e252cb6e742c91de 100644 --- a/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java +++ b/apps/jetty/java/src/net/i2p/servlet/filters/XSSFilter.java @@ -9,6 +9,7 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; /** * @since 0.9.14 @@ -25,6 +26,14 @@ public class XSSFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response); + try { + chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response); + } catch (IllegalStateException ise) { + // Multipart form error, probably file too big + // We need to send the error quickly, if we just throw a ServletException, + // the data keeps coming and the connection gets reset. + // This way we at least get the error to the browser. + ((HttpServletResponse)response).sendError(413, ise.getMessage()); + } } } diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml index fc97aa97b82284c6748b3eb11821eb293da36e75..7b1c2008fa4519a1b9bfedd289c957a326fdba28 100644 --- a/apps/routerconsole/java/build.xml +++ b/apps/routerconsole/java/build.xml @@ -448,7 +448,7 @@ <!-- Add multipart config to servlets that need them --> <property name="__match1" value="<servlet-class>net.i2p.router.web.jsp." /> <property name="__match2" value="_jsp</servlet-class>" /> - <property name="__class1" value="${__match1}configclients${__match2}" /> + <property name="__class1" value="${__match1}configplugins${__match2}" /> <property name="__class2" value="${__match1}configfamily${__match2}" /> <property name="__class3" value="${__match1}configreseed${__match2}" /> <property name="__multipart" value=" diff --git a/apps/susimail/src/WEB-INF/web.xml b/apps/susimail/src/WEB-INF/web.xml index 24d073f0e835db1b1941e1a7b20417649155f2c4..83b96a1e2bb14b9ebf47c1abc675657ee7517247 100644 --- a/apps/susimail/src/WEB-INF/web.xml +++ b/apps/susimail/src/WEB-INF/web.xml @@ -17,9 +17,10 @@ <servlet-name>SusiMail</servlet-name> <servlet-class>i2p.susi.webmail.WebMail</servlet-class> <multipart-config> - <max-file-size>67108864</max-file-size> - <max-request-size>67108864</max-request-size> - <file-size-threshold>262144</file-size-threshold> + <!-- 23 MB. See SMTPClient for discussion --> + <max-file-size>24117248</max-file-size> + <max-request-size>24117248</max-request-size> + <file-size-threshold>131072</file-size-threshold> </multipart-config> </servlet> <servlet-mapping> diff --git a/apps/susimail/src/src/i2p/susi/webmail/Attachment.java b/apps/susimail/src/src/i2p/susi/webmail/Attachment.java index 3cccc447ccaa462a5916c2791fbc425391f4d1da..5fde45975910937ab7d067324b13fe08b8950e51 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/Attachment.java +++ b/apps/susimail/src/src/i2p/susi/webmail/Attachment.java @@ -73,7 +73,15 @@ public class Attachment { } /** - * Delete the data file + * The unencoded size + * @since 0.9.33 + */ + public long getSize() { + return data.length(); + } + + /** + * Delete the data file * @since 0.9.33 */ public void deleteData() { diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java index 044bb6f26898db8438862cfaf52e3c2570d71b1f..eec8f675be95b831efbcebd46bc7ad24773e3cd4 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java @@ -1648,16 +1648,22 @@ public class WebMail extends HttpServlet sessionObject.isMobile = isMobile; if (isPOST) { - String nonce = request.getParameter(SUSI_NONCE); - if (nonce == null || !sessionObject.isValidNonce(nonce)) { - // These two strings are already in the router console FormHandler, - // so translate with that bundle. - sessionObject.error = consoleGetString( - "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.", - ctx) - + '\n' + - consoleGetString("If the problem persists, verify that you have cookies enabled in your browser.", - ctx); + try { + String nonce = request.getParameter(SUSI_NONCE); + if (nonce == null || !sessionObject.isValidNonce(nonce)) { + // These two strings are already in the router console FormHandler, + // so translate with that bundle. + sessionObject.error = consoleGetString( + "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.", + ctx) + + '\n' + + consoleGetString("If the problem persists, verify that you have cookies enabled in your browser.", + ctx); + isPOST = false; + } + } catch (IllegalStateException ise) { + // too big, can't get any parameters + sessionObject.error += ise.getMessage() + '\n'; isPOST = false; } } @@ -2027,6 +2033,19 @@ public class WebMail extends HttpServlet sessionObject.error += "Internal error: Header line encoder not available."; } + long total = text.length(); + boolean multipart = sessionObject.attachments != null && !sessionObject.attachments.isEmpty(); + if (multipart) { + for(Attachment a : sessionObject.attachments) { + total += a.getSize(); + } + } + if (total > SMTPClient.BINARY_MAX_SIZE) { + ok = false; + sessionObject.error += _t("Email is too large, max is {0}", + DataHelper.formatSize2(SMTPClient.BINARY_MAX_SIZE, false) + 'B') + '\n'; + } + if( ok ) { StringBuilder body = new StringBuilder(1024); body.append( "From: " + from + "\r\n" ); @@ -2040,9 +2059,7 @@ public class WebMail extends HttpServlet sessionObject.error += e.getMessage(); } String boundary = "_=" + I2PAppContext.getGlobalContext().random().nextLong(); - boolean multipart = false; - if( sessionObject.attachments != null && !sessionObject.attachments.isEmpty() ) { - multipart = true; + if (multipart) { body.append( "\r\nMIME-Version: 1.0\r\nContent-type: multipart/mixed; boundary=\"" + boundary + "\"\r\n\r\n" ); } else { diff --git a/apps/susimail/src/src/i2p/susi/webmail/smtp/SMTPClient.java b/apps/susimail/src/src/i2p/susi/webmail/smtp/SMTPClient.java index 00f58f316b80877ba5bcaa3e92beb4271e4c0955..2383625a852486e3c7c4d2cf00ecc9ef113a2275 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/smtp/SMTPClient.java +++ b/apps/susimail/src/src/i2p/susi/webmail/smtp/SMTPClient.java @@ -47,6 +47,20 @@ import net.i2p.data.DataHelper; */ public class SMTPClient { + /** + * 31.84 MB + * smtp.postman.i2p as of 2017-12. + * @since 0.9.33 + */ + public static final long DEFAULT_MAX_SIZE = 33388608; + + /** + * About 23.25 MB. + * Base64 encodes 57 chars to 76 + \r\n on a line + * @since 0.9.33 + */ + public static final long BINARY_MAX_SIZE = (long) ((DEFAULT_MAX_SIZE * 57.0d / 78) - 32*1024); + private Socket socket; public String error; private String lastResponse;