forked from I2P_Developers/i2p.i2p
SusiMail:
- Put UIDL in attachment links, remove session object UIDL (ticket #1373) - Store UIDL in MailPart - Fix download of attachments without a Content-Transfer-Encoding - Fix escaping inside debug html comments - Fix error return for attachment not found
This commit is contained in:
@ -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) {
|
||||
|
@ -49,14 +49,22 @@ class MailPart {
|
||||
public final List<MailPart> 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 {
|
||||
|
@ -416,7 +416,7 @@ public class WebMail extends HttpServlet
|
||||
Folder<String> folder;
|
||||
String user, pass, host, error, info;
|
||||
String replyTo, replyCC;
|
||||
String subject, body, showUIDL;
|
||||
String subject, body;
|
||||
public StringBuilder sentMail;
|
||||
public ArrayList<Attachment> attachments;
|
||||
public boolean reallyDelete;
|
||||
@ -584,6 +584,7 @@ public class WebMail extends HttpServlet
|
||||
|
||||
if( html ) {
|
||||
out.println( "<!-- " );
|
||||
out.println( "Debug: Showing Mail Part with hash code " + mailPart.hashCode());
|
||||
out.println( "Debug: Mail Part headers follow");
|
||||
for( int i = 0; i < mailPart.headerLines.length; i++ ) {
|
||||
// fix Content-Type: multipart/alternative; boundary="----------8CDE39ECAF2633"
|
||||
@ -687,8 +688,9 @@ public class WebMail extends HttpServlet
|
||||
String type = mailPart.type;
|
||||
if (type != null && type.startsWith("image/")) {
|
||||
// we at least show images safely...
|
||||
out.println("<img src=\"" + myself + "?" + RAW_ATTACHMENT + "=" +
|
||||
mailPart.hashCode() + "\">");
|
||||
out.println("<img src=\"" + myself + '?' + RAW_ATTACHMENT + '=' +
|
||||
mailPart.hashCode() +
|
||||
"&" + B64UIDL + '=' + Base64.encode(mailPart.uidl) + "\">");
|
||||
} else if (type != null && (
|
||||
// type list from snark
|
||||
type.startsWith("audio/") || type.equals("application/ogg") ||
|
||||
@ -700,11 +702,15 @@ public class WebMail extends HttpServlet
|
||||
type.equals("application/x-tar") || type.equals("application/x-bzip2") ||
|
||||
type.equals("application/pdf") || type.equals("application/x-bittorrent") ||
|
||||
type.equals("application/pgp-signature"))) {
|
||||
out.println( "<a href=\"" + myself + "?" + RAW_ATTACHMENT + "=" +
|
||||
mailPart.hashCode() + "\">" + _t("Download attachment {0}", ident) + "</a>");
|
||||
out.println( "<a href=\"" + myself + '?' + RAW_ATTACHMENT + '=' +
|
||||
mailPart.hashCode() +
|
||||
"&" + B64UIDL + '=' + Base64.encode(mailPart.uidl) + "\">" +
|
||||
_t("Download attachment {0}", ident) + "</a>");
|
||||
} else {
|
||||
out.println( "<a target=\"_blank\" href=\"" + myself + "?" + DOWNLOAD + "=" +
|
||||
mailPart.hashCode() + "\">" + _t("Download attachment {0}", ident) + "</a>" +
|
||||
out.println( "<a target=\"_blank\" href=\"" + myself + '?' + DOWNLOAD + '=' +
|
||||
mailPart.hashCode() +
|
||||
"&" + B64UIDL + '=' + Base64.encode(mailPart.uidl) + "\">" +
|
||||
_t("Download attachment {0}", ident) + "</a>" +
|
||||
" (" + _t("File is packed into a zipfile for security reasons.") + ')');
|
||||
}
|
||||
out.println( "</div>" );
|
||||
@ -716,6 +722,11 @@ public class WebMail extends HttpServlet
|
||||
if( html )
|
||||
out.println( "</td></tr>" );
|
||||
}
|
||||
if( html ) {
|
||||
out.println( "<!-- " );
|
||||
out.println( "Debug: End of Mail Part with hash code " + mailPart.hashCode());
|
||||
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
|
||||
"<div class=\"page\"><div class=\"header\"><img class=\"header\" src=\"" + sessionObject.imgPath + "susimail.png\" alt=\"Susimail\"></div>\n" +
|
||||
"<form method=\"POST\" enctype=\"multipart/form-data\" action=\"" + myself + "\" accept-charset=\"UTF-8\">\n" +
|
||||
"<input type=\"hidden\" name=\"" + SUSI_NONCE + "\" value=\"" + nonce + "\">");
|
||||
if( sessionObject.state == STATE_SHOW && sessionObject.showUIDL != null) {
|
||||
String b64UIDL = Base64.encode(sessionObject.showUIDL);
|
||||
out.println("<input type=\"hidden\" name=\"" + B64UIDL + "\" value=\"" + b64UIDL + "\">");
|
||||
if( sessionObject.state == STATE_SHOW) {
|
||||
if (showUIDL != null) {
|
||||
// reencode, as showUIDL may have changed, and also for XSS
|
||||
b64UIDL = Base64.encode(showUIDL);
|
||||
out.println("<input type=\"hidden\" name=\"" + B64UIDL + "\" value=\"" + b64UIDL + "\">");
|
||||
}
|
||||
}
|
||||
if( sessionObject.error != null && sessionObject.error.length() > 0 ) {
|
||||
out.println( "<div class=\"notifications\" onclick=\"this.remove()\"><p class=\"error\">" + quoteHTML(sessionObject.error).replace("\n", "<br>") + "</p></div>" );
|
||||
@ -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( "<p class=\"error\">" + _t("Really delete this message?") + " " + button( REALLYDELETE, _t("Yes, really delete it!") ) + "</p>" );
|
||||
}
|
||||
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( "Debug: Mail header and body follow");
|
||||
// FIXME encoding, escaping --, etc... but disabled.
|
||||
ReadBuffer body = mail.getBody();
|
||||
out.println( quoteHTML( new String(body.content, body.offset, body.length ) ) );
|
||||
out.println(quoteHTML(new String(body.content, body.offset, body.length)).replace("--", "—"));
|
||||
out.println( "-->" );
|
||||
}
|
||||
out.println("<div class=\"topbuttons\">");
|
||||
@ -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("<div id=\"messagenav\">" +
|
||||
( 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("</div></div>");
|
||||
//if (Config.hasConfigFile())
|
||||
// out.println(button( RELOAD, _t("Reload Config") ) + spacer);
|
||||
|
20
history.txt
20
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)
|
||||
|
@ -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 = "";
|
||||
|
Reference in New Issue
Block a user