From 31719d30cfd3c704c9e380349f74f909e9459a24 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Fri, 9 Feb 2018 22:53:25 +0000
Subject: [PATCH] SusiMail: Improve speed of subject sorter Use ID instead of
 hashcode for mailparts, so attachments are bookmarkable Recognize fw:

---
 .../src/src/i2p/susi/webmail/Mail.java        |  3 +-
 .../src/src/i2p/susi/webmail/MailPart.java    | 18 +++++-
 .../src/src/i2p/susi/webmail/WebMail.java     | 63 ++++++++++---------
 3 files changed, 50 insertions(+), 34 deletions(-)

diff --git a/apps/susimail/src/src/i2p/susi/webmail/Mail.java b/apps/susimail/src/src/i2p/susi/webmail/Mail.java
index 26506a2912..e4f1bcb73b 100644
--- a/apps/susimail/src/src/i2p/susi/webmail/Mail.java
+++ b/apps/susimail/src/src/i2p/susi/webmail/Mail.java
@@ -45,6 +45,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Pattern;
 
 import net.i2p.I2PAppContext;
@@ -179,7 +180,7 @@ class Mail {
 			// TODO just fail?
 			if (headerLines == null)
 				headerLines = new String[0];
-			part = new MailPart(uidl, rb, in, in, headerLines);
+			part = new MailPart(uidl, new AtomicInteger(), rb, in, in, headerLines);
 			rb.readComplete(true);
 			// may only be available after reading and calling readComplete()
 			size = rb.getLength();
diff --git a/apps/susimail/src/src/i2p/susi/webmail/MailPart.java b/apps/susimail/src/src/i2p/susi/webmail/MailPart.java
index a57a6dda5d..543926b7e6 100644
--- a/apps/susimail/src/src/i2p/susi/webmail/MailPart.java
+++ b/apps/susimail/src/src/i2p/susi/webmail/MailPart.java
@@ -44,6 +44,7 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import net.i2p.data.DataHelper;
 
@@ -79,6 +80,7 @@ class MailPart {
 	 *  @since 0.9.33
 	 */
 	public final String uidl;
+	private final int intID;
 	
 	/**
 	 *  @param readBuffer has zero offset for top-level MailPart.
@@ -88,9 +90,10 @@ class MailPart {
 	 *  @param hdrlines non-null for top-level MailPart, where they
 	 *         were already parsed in Mail. Null otherwise
 	 */
-	public MailPart(String uidl, Buffer readBuffer, InputStream in, ReadCounter counter, String[] hdrlines) throws IOException
+	public MailPart(String uidl, AtomicInteger id, Buffer readBuffer, InputStream in, ReadCounter counter, String[] hdrlines) throws IOException
 	{
 		this.uidl = uidl;
+		intID = id.getAndIncrement();
 		buffer = readBuffer;
 		
 		parts = new ArrayList<MailPart>(4);
@@ -219,7 +222,7 @@ class MailPart {
 					match = DataHelper.getASCII("\r\n--" + boundary);
 					eofin = new EOFOnMatchInputStream(in, counter, match);
 				}
-				MailPart newPart = new MailPart(uidl, buffer, eofin, eofin, null);
+				MailPart newPart = new MailPart(uidl, id, buffer, eofin, eofin, null);
 				parts.add( newPart );
 				tmpEnd = (int) eofin.getRead();
 				if (!eofin.wasFound()) {
@@ -233,7 +236,7 @@ class MailPart {
 			}
 		}
 		else if( message ) {
-			MailPart newPart = new MailPart(uidl, buffer, in, counter, null);
+			MailPart newPart = new MailPart(uidl, id, buffer, in, counter, null);
 			// TODO newPart doesn't save message headers we might like to display,
 			// like From, To, and Subject
 			parts.add( newPart );			
@@ -251,6 +254,15 @@ class MailPart {
 		//	Debug.debug(Debug.DEBUG, "New " + this);
 	}
 
+	/**
+	 *  A value unique across all the parts of this Mail,
+	 *  and constant across restarts, so it may be part of a bookmark.
+	 *
+	 *  @since 0.9.34
+	 */
+	public int getID() { return intID; }
+
+
 	/**
 	 *  Swallow "\r\n" or "--\r\n".
 	 *  We don't have any pushback if this goes wrong.
diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java
index d91484548a..64038723cf 100644
--- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java
+++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java
@@ -132,9 +132,9 @@ public class WebMail extends HttpServlet
 	 * hidden params
 	 */
 	private static final String SUSI_NONCE = "susiNonce";
-	private static final String B64UIDL = "b64uidl";
-	private static final String PREV_B64UIDL = "prevb64uidl";
-	private static final String NEXT_B64UIDL = "nextb64uidl";
+	private static final String B64UIDL = "msg";
+	private static final String PREV_B64UIDL = "prevmsg";
+	private static final String NEXT_B64UIDL = "nextmsg";
 	private static final String PREV_PAGE_NUM = "prevpagenum";
 	private static final String NEXT_PAGE_NUM = "nextpagenum";
 	private static final String CURRENT_SORT = "currentsort";
@@ -349,6 +349,8 @@ public class WebMail extends HttpServlet
 	 * @author susi
 	 */
 	private static class SubjectSorter extends SorterBase {
+		private static final String xre = _t("Re:").toLowerCase(Locale.US);
+		private static final String xfwd = _t("Fwd:").toLowerCase(Locale.US);
 		private final Comparator<Object> collator = Collator.getInstance();
 
 		public SubjectSorter( MailCache mailCache )
@@ -359,31 +361,29 @@ public class WebMail extends HttpServlet
 		protected int compare(Mail a, Mail b) {
 			String as = a.subject;
 			String bs = b.subject;
-			if (as.toLowerCase().startsWith("re:")) {
+			String aslc = as.toLowerCase(Locale.US);
+			String bslc = bs.toLowerCase(Locale.US);
+			if (aslc.startsWith("re:") || aslc.startsWith("fw:")) {
 				as = as.substring(3).trim();
-			} else if (as.toLowerCase().startsWith("fwd:")) {
+			} else if (aslc.startsWith("fwd:")) {
 				as = as.substring(4).trim();
 			} else {
-				String xre = _t("Re:").toLowerCase();
-				if (as.toLowerCase().startsWith(xre)) {
+				if (aslc.startsWith(xre)) {
 					as = as.substring(xre.length()).trim();
 				} else {
-					String xfwd = _t("Fwd:").toLowerCase();
-					if (as.toLowerCase().startsWith(xfwd))
+					if (aslc.startsWith(xfwd))
 						as = as.substring(xfwd.length()).trim();
 				}
 			}
-			if (bs.toLowerCase().startsWith("re:")) {
+			if (bslc.startsWith("re:") || bslc.startsWith("fw:")) {
 				bs = bs.substring(3).trim();
-			} else if (bs.toLowerCase().startsWith("fwd:")) {
+			} else if (bslc.startsWith("fwd:")) {
 				bs = bs.substring(4).trim();
 			} else {
-				String xre = _t("Re:").toLowerCase();
-				if (bs.toLowerCase().startsWith(xre)) {
+				if (bslc.startsWith(xre)) {
 					bs = bs.substring(xre.length()).trim();
 				} else {
-					String xfwd = _t("Fwd:").toLowerCase();
-					if (bs.toLowerCase().startsWith(xfwd))
+					if (bslc.startsWith(xfwd))
 						bs = bs.substring(xfwd.length()).trim();
 				}
 			}
@@ -618,7 +618,7 @@ public class WebMail extends HttpServlet
 
 		if( html ) {
 			out.println( "<!-- " );
-			out.println( "Debug: Showing Mail Part at level " + level + " with hash code " + mailPart.hashCode());
+			out.println( "Debug: Showing Mail Part at level " + level + " with ID " + mailPart.getID());
 			out.println( "Debug: Mail Part headers follow");
 			for( int i = 0; i < mailPart.headerLines.length; i++ ) {
 				// fix Content-Type: multipart/alternative; boundary="----------8CDE39ECAF2633"
@@ -642,7 +642,7 @@ public class WebMail extends HttpServlet
 							if (chosen.equals(subPart))
 								continue;
 							out.println( "<!-- " );
-							out.println( "Debug: Not showing alternative Mail Part at level " + (level + 1) + " with hash code " + subPart.hashCode());
+							out.println( "Debug: Not showing alternative Mail Part at level " + (level + 1) + " with ID " + subPart.getID());
 							out.println( "Debug: Mail Part headers follow");
 							for( int i = 0; i < subPart.headerLines.length; i++ ) {
 								out.println( subPart.headerLines[i].replace("--", "&#45;&#45;") );
@@ -751,7 +751,7 @@ public class WebMail extends HttpServlet
 						}
 						name = quoteHTML(name);
 						out.println("<img src=\"" + myself + '?' + RAW_ATTACHMENT + '=' +
-							 mailPart.hashCode() +
+							 mailPart.getID() +
 							 "&amp;" + B64UIDL + '=' + Base64.encode(mailPart.uidl) +
 							 "\" alt=\"" + name + "\">");
 					} else if (type != null && (
@@ -765,14 +765,15 @@ public class WebMail extends HttpServlet
 						type.equals("application/x-7z-compressed") || type.equals("application/x-rar-compressed") ||
 						type.equals("application/x-tar") || type.equals("application/x-bzip2") ||
 						type.equals("application/pdf") || type.equals("application/x-bittorrent") ||
+						type.equals("application/pgp-encrypted") ||
 						type.equals("application/pgp-signature"))) {
 						out.println( "<a href=\"" + myself + '?' + RAW_ATTACHMENT + '=' +
-							 mailPart.hashCode() +
+							 mailPart.getID() +
 							 "&amp;" + B64UIDL + '=' + Base64.encode(mailPart.uidl) + "\">" +
 							 _t("Download attachment {0}", ident) + "</a>");
 					} else {
 						out.println( "<a target=\"_blank\" href=\"" + myself + '?' + DOWNLOAD + '=' +
-							 mailPart.hashCode() +
+							 mailPart.getID() +
 							 "&amp;" + B64UIDL + '=' + Base64.encode(mailPart.uidl) + "\">" +
 							 _t("Download attachment {0}", ident) + "</a>" +
 							 " (" + _t("File is packed into a zipfile for security reasons.") + ')');
@@ -788,7 +789,7 @@ public class WebMail extends HttpServlet
 		}
 		if( html ) {
 			out.println( "<!-- " );
-			out.println( "Debug: End of Mail Part at level " + level + " with hash code " + mailPart.hashCode());
+			out.println( "Debug: End of Mail Part at level " + level + " with ID " + mailPart.getID());
 			out.println( "-->" );
 		}
 	}
@@ -1167,6 +1168,9 @@ public class WebMail extends HttpServlet
 							if (!(sessionObject.subject.startsWith("Fwd:") ||
 							      sessionObject.subject.startsWith("fwd:") ||
 							      sessionObject.subject.startsWith("FWD:") ||
+							      sessionObject.subject.startsWith("Fw:") ||
+							      sessionObject.subject.startsWith("fw:") ||
+							      sessionObject.subject.startsWith("FW:") ||
 							      sessionObject.subject.startsWith(_t("Fwd:")))) {
 								sessionObject.subject = _t("Fwd:") + ' ' + sessionObject.subject;
 							}
@@ -1437,9 +1441,9 @@ public class WebMail extends HttpServlet
 		}       	
 		if( str != null ) {
 			try {
-				int hashCode = Integer.parseInt( str );
+				int id = Integer.parseInt( str );
 				Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL);
-				MailPart part = mail != null ? getMailPartFromHashCode( mail.getPart(), hashCode ) : null;
+				MailPart part = mail != null ? getMailPartFromID(mail.getPart(), id) : null;
 				if( part != null ) {
 					if (sendAttachment(sessionObject, part, response, isRaw))
 						return true;
@@ -1484,21 +1488,20 @@ public class WebMail extends HttpServlet
 
 	/**
 	 * Recursive.
-	 * FIXME can't be bookmarked.
-	 * @param hashCode
+	 * @param id as retrieved from getID()
 	 * @return the part or null
 	 */
-	private static MailPart getMailPartFromHashCode( MailPart part, int hashCode )
+	private static MailPart getMailPartFromID(MailPart part, int id)
 	{
 		if( part == null )
 			return null;
 		
-		if( part.hashCode() == hashCode )
+		if (part.getID() == id)
 			return part;
 		
 		if( part.multipart || part.message ) {
 			for( MailPart p : part.parts ) {
-				MailPart subPart = getMailPartFromHashCode( p, hashCode );
+				MailPart subPart = getMailPartFromID(p, id);
 				if( subPart != null )
 					return subPart;
 			}
@@ -2132,7 +2135,7 @@ public class WebMail extends HttpServlet
 				if (name == null) {
 					name = part.description;
 					if (name == null)
-						name = "part" + part.hashCode();
+						name = "part" + part.getID();
 				}
 			}
 			String name2 = FilenameUtil.sanitizeFilename(name);
@@ -2679,7 +2682,7 @@ public class WebMail extends HttpServlet
 			             "</p>");
 		}
 		Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL);
-		if(!RELEASE && mail != null && mail.hasBody() && mail.getBody().getLength() < 4096) {
+		if(!RELEASE && mail != null && mail.hasBody() && mail.getBody().getLength() < 16384) {
 			out.println( "<!--" );
 			out.println( "Debug: Mail header and body follow");
 			Buffer body = mail.getBody();
-- 
GitLab