diff --git a/apps/susimail/src/src/i2p/susi/webmail/Mail.java b/apps/susimail/src/src/i2p/susi/webmail/Mail.java index 3b43564d8f..c188e3d36a 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/Mail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/Mail.java @@ -124,7 +124,7 @@ class Mail { body = rb; size = rb.length; try { - part = new MailPart(rb); + part = new MailPart(uidl, rb); } catch (DecodingException de) { Debug.debug(Debug.ERROR, "Decode error: " + de); } catch (RuntimeException e) { diff --git a/apps/susimail/src/src/i2p/susi/webmail/MailPart.java b/apps/susimail/src/src/i2p/susi/webmail/MailPart.java index 22eeae2b4b..5ede374dd3 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/MailPart.java +++ b/apps/susimail/src/src/i2p/susi/webmail/MailPart.java @@ -49,14 +49,22 @@ class MailPart { public final List parts; public final boolean multipart, message; public final ReadBuffer buffer; + /** + * the UIDL of the mail, same for all parts + * @since 0.9.33 + */ + public final String uidl; - public MailPart( ReadBuffer readBuffer ) throws DecodingException +/// todo add UIDL to constructors, use in WebMail.showpart() + + public MailPart(String uidl, ReadBuffer readBuffer) throws DecodingException { - this(readBuffer, readBuffer.offset, readBuffer.length); + this(uidl, readBuffer, readBuffer.offset, readBuffer.length); } - public MailPart( ReadBuffer readBuffer, int offset, int length ) throws DecodingException + public MailPart(String uidl, ReadBuffer readBuffer, int offset, int length) throws DecodingException { + this.uidl = uidl; begin = offset; end = offset + length; buffer = readBuffer; @@ -176,7 +184,7 @@ class MailPart { if( beginLastPart != -1 ) { int endLastPart = Math.min(i + 2, end); - MailPart newPart = new MailPart( buffer, beginLastPart, endLastPart - beginLastPart ); + MailPart newPart = new MailPart(uidl, buffer, beginLastPart, endLastPart - beginLastPart); parts.add( newPart ); } beginLastPart = k; @@ -187,13 +195,13 @@ class MailPart { } } else if( message ) { - MailPart newPart = new MailPart(buffer, beginBody, end - beginBody); + MailPart newPart = new MailPart(uidl, buffer, beginBody, end - beginBody); parts.add( newPart ); } } /** - * @param offset 2 for sendAttachment, 0 otherwise, don't know why + * @param offset 2 for sendAttachment, 0 otherwise, probably for \r\n * @since 0.9.13 */ public ReadBuffer decode(int offset) throws DecodingException { diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java index d4335d605f..3389c4071d 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java @@ -416,7 +416,7 @@ public class WebMail extends HttpServlet Folder folder; String user, pass, host, error, info; String replyTo, replyCC; - String subject, body, showUIDL; + String subject, body; public StringBuilder sentMail; public ArrayList attachments; public boolean reallyDelete; @@ -584,6 +584,7 @@ public class WebMail extends HttpServlet if( html ) { out.println( "" ); + } } /** * prepare line for presentation between html tags @@ -1019,9 +1030,8 @@ public class WebMail extends HttpServlet // This is the I2P Base64, not the encoder uidl = Base64.decodeToString(b64UIDL); } - } - else { - uidl = sessionObject.showUIDL; + } else { + uidl = Base64.decodeToString(request.getParameter(B64UIDL)); } if( uidl != null ) { @@ -1135,7 +1145,6 @@ public class WebMail extends HttpServlet String uidl = Base64.decodeToString(show); if(uidl != null) { sessionObject.state = STATE_SHOW; - sessionObject.showUIDL = uidl; } else { sessionObject.error += _t("Message id not valid.") + '\n'; } @@ -1277,42 +1286,31 @@ public class WebMail extends HttpServlet * process buttons of message view * @param sessionObject * @param request + * @return the next UIDL to see (if PREV/NEXT pushed), or null (if REALLYDELETE pushed), or showUIDL */ - private static void processMessageButtons(SessionObject sessionObject, RequestWrapper request) + private static String processMessageButtons(SessionObject sessionObject, String showUIDL, RequestWrapper request) { if( buttonPressed( request, PREV ) ) { - String uidl = sessionObject.folder.getPreviousElement( sessionObject.showUIDL ); - if( uidl != null ) - sessionObject.showUIDL = uidl; + String uidl = sessionObject.folder.getPreviousElement(showUIDL); + if(uidl == null) + sessionObject.state = STATE_LIST; + return uidl; } if( buttonPressed( request, NEXT ) ) { - String uidl = sessionObject.folder.getNextElement( sessionObject.showUIDL ); - if( uidl != null ) - sessionObject.showUIDL = uidl; + String uidl = sessionObject.folder.getNextElement(showUIDL); + if(uidl == null) + sessionObject.state = STATE_LIST; + return uidl; } sessionObject.reallyDelete = buttonPressed( request, DELETE ); - if( buttonPressed( request, REALLYDELETE ) ) { - /* - * first find the next message - */ - String nextUIDL = sessionObject.folder.getNextElement( sessionObject.showUIDL ); - if( nextUIDL == null ) { - /* - * nothing found? then look for the previous one - */ - nextUIDL = sessionObject.folder.getPreviousElement( sessionObject.showUIDL ); - if( nextUIDL == null ) - /* - * still nothing found? then this was the last message, so go back to the folder - */ - sessionObject.state = STATE_LIST; - } - sessionObject.mailCache.delete( sessionObject.showUIDL ); - sessionObject.folder.removeElement(sessionObject.showUIDL); - sessionObject.showUIDL = nextUIDL; + sessionObject.state = STATE_LIST; + sessionObject.mailCache.delete(showUIDL); + sessionObject.folder.removeElement(showUIDL); + return null; } + return showUIDL; } /** @@ -1321,18 +1319,19 @@ public class WebMail extends HttpServlet * @param request * @return If true, we sent an attachment or 404, do not send any other response */ - private static boolean processDownloadLink(SessionObject sessionObject, RequestWrapper request, HttpServletResponse response) + private static boolean processDownloadLink(SessionObject sessionObject, String showUIDL, + RequestWrapper request, HttpServletResponse response) { String str = request.getParameter(DOWNLOAD); boolean isRaw = false; if (str == null) { str = request.getParameter(RAW_ATTACHMENT); - isRaw = str != null; + isRaw = true; } if( str != null ) { try { int hashCode = Integer.parseInt( str ); - Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FetchMode.ALL ); + Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL); MailPart part = mail != null ? getMailPartFromHashCode( mail.getPart(), hashCode ) : null; if( part != null ) { if (sendAttachment(sessionObject, part, response, isRaw)) @@ -1340,14 +1339,12 @@ public class WebMail extends HttpServlet } } catch( NumberFormatException nfe ) {} // error if we get here - sessionObject.error += _t("Attachment not found."); - if (isRaw) { - try { - response.sendError(404, _t("Attachment not found.")); - } catch (IOException ioe) {} - } + try { + response.sendError(404, _t("Attachment not found.")); + } catch (IOException ioe) {} + return true; } - return isRaw; + return false; } @@ -1359,12 +1356,13 @@ public class WebMail extends HttpServlet * @return If true, we sent the file or 404, do not send any other response * @since 0.9.18 */ - private static boolean processSaveAsLink(SessionObject sessionObject, RequestWrapper request, HttpServletResponse response) + private static boolean processSaveAsLink(SessionObject sessionObject, String showUIDL, + RequestWrapper request, HttpServletResponse response) { String str = request.getParameter(SAVE_AS); if( str == null ) return false; - Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FetchMode.ALL ); + Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL); if( mail != null ) { if (sendMailSaveAs(sessionObject, mail, response)) return true; @@ -1378,6 +1376,8 @@ public class WebMail extends HttpServlet } /** + * Recursive. + * FIXME can't be bookmarked. * @param hashCode * @return the part or null */ @@ -1716,28 +1716,37 @@ public class WebMail extends HttpServlet } } + // ?show= links - this forces STATE_SHOW + String b64UIDL = request.getParameter(SHOW); + // attachment links, images, next/prev/delete on show form + if (b64UIDL == null) + b64UIDL = request.getParameter(B64UIDL); + String showUIDL = Base64.decodeToString(b64UIDL); if( sessionObject.state == STATE_SHOW ) { if (isPOST) - processMessageButtons( sessionObject, request ); + showUIDL = processMessageButtons(sessionObject, showUIDL, request); // ?download=nnn link should be valid in any state // but depends on current UIDL - if (processDownloadLink(sessionObject, request, response)) { + if (processDownloadLink(sessionObject, showUIDL, request, response)) { // download or raw view sent, or 404 return; } - if (isPOST && processSaveAsLink(sessionObject, request, response)) { + if (isPOST && processSaveAsLink(sessionObject, showUIDL, request, response)) { // download or sent, or 404 return; } // If the last message has just been deleted then // sessionObject.state = STATE_LIST and // sessionObject.showUIDL = null - if ( sessionObject.showUIDL != null ) { - Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FetchMode.ALL ); + if (showUIDL != null) { + Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL); if( mail != null && mail.error.length() > 0 ) { sessionObject.error += mail.error; mail.error = ""; } + } else { + // can't SHOW without a UIDL + sessionObject.state = STATE_LIST; } } @@ -1766,7 +1775,7 @@ public class WebMail extends HttpServlet //subtitle = ngettext("1 Message", "{0} Messages", sessionObject.mailbox.getNumMails()); subtitle = ngettext("1 Message", "{0} Messages", sessionObject.folder.getSize()); } else if( sessionObject.state == STATE_SHOW ) { - Mail mail = sessionObject.mailCache.getMail(sessionObject.showUIDL, MailCache.FetchMode.HEADER); + Mail mail = showUIDL != null ? sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.HEADER) : null; if (mail != null && mail.shortSubject != null) subtitle = mail.shortSubject; // already HTML encoded else @@ -1814,9 +1823,12 @@ public class WebMail extends HttpServlet "
\"Susimail\"
\n" + "
\n" + ""); - if( sessionObject.state == STATE_SHOW && sessionObject.showUIDL != null) { - String b64UIDL = Base64.encode(sessionObject.showUIDL); - out.println(""); + if( sessionObject.state == STATE_SHOW) { + if (showUIDL != null) { + // reencode, as showUIDL may have changed, and also for XSS + b64UIDL = Base64.encode(showUIDL); + out.println(""); + } } if( sessionObject.error != null && sessionObject.error.length() > 0 ) { out.println( "

" + quoteHTML(sessionObject.error).replace("\n", "
") + "

" ); @@ -1834,7 +1846,7 @@ public class WebMail extends HttpServlet showFolder( out, sessionObject, request ); else if( sessionObject.state == STATE_SHOW ) - showMessage( out, sessionObject ); + showMessage(out, sessionObject, showUIDL); else if( sessionObject.state == STATE_NEW ) showCompose( out, sessionObject, request ); @@ -1869,15 +1881,13 @@ public class WebMail extends HttpServlet if(part != null) { ReadBuffer content = part.buffer; - if( part.encoding != null ) { - try { - // why +2 ?? - content = part.decode(2); - } - catch (DecodingException e) { - sessionObject.error += _t("Error decoding content: {0}", e.getMessage()) + '\n'; - content = null; - } + // we always decode, even if part.encoding is null, will default to 7bit + try { + // +2 probably for \r\n + content = part.decode(2); + } catch (DecodingException e) { + sessionObject.error += _t("Error decoding content: {0}", e.getMessage()) + '\n'; + content = null; } if(content == null) return false; @@ -2400,18 +2410,18 @@ public class WebMail extends HttpServlet * @param out * @param sessionObject */ - private static void showMessage( PrintWriter out, SessionObject sessionObject ) + private static void showMessage(PrintWriter out, SessionObject sessionObject, String showUIDL) { if( sessionObject.reallyDelete ) { out.println( "

" + _t("Really delete this message?") + " " + button( REALLYDELETE, _t("Yes, really delete it!") ) + "

" ); } - Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FetchMode.ALL ); + Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL); if(!RELEASE && mail != null && mail.hasBody()) { out.println( "" ); } out.println("
"); @@ -2425,10 +2435,11 @@ public class WebMail extends HttpServlet else out.println(button(DELETE, _t("Delete"))); out.println(button(LOGOUT, _t("Logout") )); + // TODO make these GETs not POSTs so we have a consistent URL out.println("
" + - ( sessionObject.folder.isFirstElement( sessionObject.showUIDL ) ? button2( PREV, _t("Previous") ) : button( PREV, _t("Previous") ) ) + spacer + + ( sessionObject.folder.isFirstElement(showUIDL) ? button2( PREV, _t("Previous") ) : button( PREV, _t("Previous") ) ) + spacer + button( LIST, _t("Back to Folder") ) + spacer + - ( sessionObject.folder.isLastElement( sessionObject.showUIDL ) ? button2( NEXT, _t("Next") ) : button( NEXT, _t("Next") ) )); + ( sessionObject.folder.isLastElement(showUIDL) ? button2( NEXT, _t("Next") ) : button( NEXT, _t("Next") ) )); out.println("
"); //if (Config.hasConfigFile()) // out.println(button( RELOAD, _t("Reload Config") ) + spacer); diff --git a/history.txt b/history.txt index 4eb973ac4a..8762427c05 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,21 @@ +2017-12-08 zzz + * SusiMail: + - Reference mails by UIDL, not index on page, put UIDL in + attachment links, remove session object UIDL (ticket #1373) + - Fix download of attachments without a Content-Transfer-Encoding + - Fix error return for attachment not found + - Set encoding for text attachments + - Add more safe mime types for downloading + +2017-12-07 zzz + * Console: Don't list aliased tunnels separately on + /tunnels and /configtunnels + * i2psnark: Better locking on BitField byte array + * SusiMail: Recheck max size if SMTP server reports less than default + * Util: Deprecate BigPipedInputStream + 2017-12-05 zzz + * Console: Fix multipart config for /configplugins * SusiMail: - Don't store attachments of composed email in-memory (ticket #1668) - Fix bug corrupting sent text and text attachments @@ -8,6 +25,9 @@ - Fix handling of unimplemented encoders - Add test code for encoders - Error message improvements + - Adjust multipart size limits + - Better handling of errors when multipart limits are exceeded + - Test for total size limit 2017-12-04 zzz * Servlet: Refactor RequestWrapper to use Servlet 3.0 API (ticket #2109) diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 7f4edb9752..31c35f8c3e 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 = 13; + public final static long BUILD = 14; /** for example "-test" */ public final static String EXTRA = "";