From 4d2dc1c8e89f585f7dea244fd518129efe322c01 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 22 Apr 2014 18:45:09 +0000 Subject: [PATCH] * SusiMail: - Show sender name in folder view - Add support for configured sender name - Add HTML escaping of '&' - Fix Folder sorting so UP is up and DOWN is down - Use replace() instead of replaceAll() where appropriate - Fix capture by show page after back button --- .../src/src/i2p/susi/util/Folder.java | 53 +++++++----------- .../src/src/i2p/susi/webmail/Mail.java | 55 ++++++++++++++---- .../src/src/i2p/susi/webmail/WebMail.java | 56 +++++++++++++++---- .../src/i2p/susi/webmail/encoding/HTML.java | 5 +- .../src/i2p/susi/webmail/smtp/SMTPClient.java | 2 +- history.txt | 5 ++ 6 files changed, 119 insertions(+), 57 deletions(-) diff --git a/apps/susimail/src/src/i2p/susi/util/Folder.java b/apps/susimail/src/src/i2p/susi/util/Folder.java index 2dd4012c6..afd04c820 100644 --- a/apps/susimail/src/src/i2p/susi/util/Folder.java +++ b/apps/susimail/src/src/i2p/susi/util/Folder.java @@ -51,13 +51,17 @@ public class Folder { public static final String PAGESIZE = "pager.pagesize"; public static final int DEFAULT_PAGESIZE = 10; - public static final boolean DOWN = false; - public static final boolean UP = true; + public enum SortOrder { + /** lowest to highest */ + DOWN, + /** reverse sort, highest to lowest */ + UP; + } private int pages, pageSize, currentPage; private O[] unsortedElements, elements; private final Hashtable> sorter; - private boolean sortingDirection; + private SortOrder sortingDirection; Comparator currentSorter; public Folder() @@ -65,7 +69,7 @@ public class Folder { pages = 1; currentPage = 1; sorter = new Hashtable>(); - sortingDirection = UP; + sortingDirection = SortOrder.DOWN; } /** @@ -232,14 +236,9 @@ public class Folder { if( elements != null ) { int pageSize = getPageSize(); int offset = ( currentPage - 1 ) * pageSize; - int step = 1; - if( sortingDirection == DOWN ) { - offset = elements.length - offset - 1; - step = -1; - } for( int i = 0; i < pageSize && offset >= 0 && offset < elements.length; i++ ) { list.add( elements[offset] ); - offset += step; + offset++; } } return list.iterator(); @@ -303,6 +302,8 @@ public class Folder { public void sortBy( String id ) { currentSorter = sorter.get( id ); + if (sortingDirection == SortOrder.UP) + currentSorter = Collections.reverseOrder(currentSorter); sort(); } @@ -318,12 +319,7 @@ public class Folder { if( elements != null ) { int pageSize = getPageSize(); int offset = ( currentPage - 1 ) * pageSize; - int step = 1; - if( sortingDirection == DOWN ) { - offset = elements.length - offset - 1; - step = -1; - } - offset += x * step; + offset += x; if( offset >= 0 && offset < elements.length ) result = elements[offset]; } @@ -332,10 +328,11 @@ public class Folder { /** * Sets the sorting direction of the folder. + * Does not re-sort. Caller must call sortBy() * * @param direction @link UP or @link DOWN */ - public void setSortingDirection( boolean direction ) + public void setSortingDirection(SortOrder direction) { sortingDirection = direction; } @@ -347,9 +344,6 @@ public class Folder { */ public O getFirstElement() { - /* - * sorting direction is taken into account from getElement - */ return elements == null ? null : getElement( 0 ); } @@ -360,9 +354,6 @@ public class Folder { */ public O getLastElement() { - /* - * sorting direction is taken into account from getElement - */ return elements == null ? null : getElement( elements.length - 1 ); } @@ -384,7 +375,6 @@ public class Folder { /** * Retrieves the next element in the sorted array. - * Sorting direction is taken into account. * * @param element * @return The next element @@ -396,7 +386,7 @@ public class Folder { int i = getIndexOf( element ); if( i != -1 && elements != null ) { - i += sortingDirection == UP ? 1 : -1; + i++; if( i >= 0 && i < elements.length ) result = elements[i]; } @@ -405,7 +395,6 @@ public class Folder { /** * Retrieves the previous element in the sorted array. - * Sorting direction is taken into account. * * @param element * @return The previous element @@ -417,14 +406,14 @@ public class Folder { int i = getIndexOf( element ); if( i != -1 && elements != null ) { - i += sortingDirection == DOWN ? 1 : -1; + i--; if( i >= 0 && i < elements.length ) result = elements[i]; } return result; } /** - * Retrieves element at index i. Depends on sorting direction. + * Retrieves element at index i. * * @param i * @return Element at index i @@ -434,8 +423,6 @@ public class Folder { O result = null; if( elements != null ) { - if( sortingDirection == DOWN ) - i = elements.length - i - 1; result = elements[i]; } return result; @@ -459,7 +446,6 @@ public class Folder { /** * Returns true, if elements.equals( lastElementOfTheSortedArray ). - * The sorting direction influences which element is taken for comparison. * * @param element */ @@ -467,12 +453,11 @@ public class Folder { { if( elements == null ) return false; - return elements[ sortingDirection == DOWN ? 0 : elements.length - 1 ].equals( element ); + return elements[elements.length - 1].equals( element ); } /** * Returns true, if elements.equals( firstElementOfTheSortedArray ). - * The sorting direction influences which element is taken for comparison. * * @param element */ @@ -480,6 +465,6 @@ public class Folder { { if( elements == null ) return false; - return elements[ sortingDirection == UP ? 0 : elements.length - 1 ].equals( element ); + return elements[0].equals( element ); } } diff --git a/apps/susimail/src/src/i2p/susi/webmail/Mail.java b/apps/susimail/src/src/i2p/susi/webmail/Mail.java index 84983e873..9903589fb 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/Mail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/Mail.java @@ -55,17 +55,20 @@ class Mail { private static final String unknown = "unknown"; private int size; - public String sender, reply, subject, dateString, - formattedSender, formattedSubject, + public String sender, // as received, trimmed only, not HTML escaped + reply, subject, dateString, + formattedSender, // address only, enclosed with <>, not HTML escaped + formattedSubject, formattedDate, // US Locale, UTC localFormattedDate, // Current Locale, local time zone - shortSender, shortSubject, + shortSender, // Either name or address but not both, HTML escaped, double-quotes removed, truncated with with hellip + shortSubject, // HTML escaped, truncated with hellip quotedDate; // Current Locale, local time zone, longer format public final String uidl; public Date date; private ReadBuffer header, body; private MailPart part; - String[] to, cc; + String[] to, cc; // addresses only, enclosed by <> public String error; @@ -165,7 +168,9 @@ class Mail { } return addresses == 1; } + /** + * Returns the first email address portion, enclosed by <> * @param address */ public static String getAddress(String address ) @@ -181,6 +186,16 @@ class Mail { return null; } + + /** + * A little misnamed. Adds all addresses from the comma-separated + * line in text to the recipients list. + * + * @param text comma-separated + * @param recipients out param + * @param ok will be returned + * @return true if ALL e-mail addresses are valid AND the in parameter was true + */ public static boolean getRecipientsFromList( ArrayList recipients, String text, boolean ok ) { if( text != null && text.length() > 0 ) { @@ -203,6 +218,15 @@ class Mail { } return ok; } + + /** + * Adds all items from the list + * to the builder, separated by tabs. + * + * @param text comma-separated + * @param buf out param + * @param prefix prepended to the addresses + */ public static void appendRecipients( StringBuilder buf, ArrayList recipients, String prefix ) { for( String recipient : recipients ) { @@ -212,6 +236,7 @@ class Mail { buf.append( "\r\n" ); } } + public void parseHeaders() { DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm"); @@ -258,11 +283,18 @@ class Mail { if( line.startsWith( "From:" ) ) { sender = line.substring( 5 ).trim(); formattedSender = getAddress( sender ); - shortSender = formattedSender.trim(); - if( shortSender.length() > 40 ) { - shortSender = shortSender.substring( 0, 37 ).trim() + "…"; - } + shortSender = sender.replace("\"", "").trim(); + int lt = shortSender.indexOf('<'); + if (lt > 0) + shortSender = shortSender.substring(0, lt).trim(); + else if (lt < 0 && shortSender.contains("@")) + shortSender = '<' + shortSender + '>'; // add missing <> (but thunderbird doesn't...) + boolean trim = shortSender.length() > 40; + if (trim) + shortSender = shortSender.substring( 0, 37 ).trim(); shortSender = html.encode( shortSender ); + if (trim) + shortSender += "…"; // must be after html encode } else if( line.startsWith( "Date:" ) ) { dateString = line.substring( 5 ).trim(); @@ -282,9 +314,12 @@ class Mail { subject = line.substring( 8 ).trim(); formattedSubject = subject; shortSubject = formattedSubject; - if( formattedSubject.length() > 60 ) - shortSubject = formattedSubject.substring( 0, 57 ).trim() + "…"; + boolean trim = formattedSubject.length() > 60; + if (trim) + shortSubject = formattedSubject.substring( 0, 57 ).trim(); shortSubject = html.encode( shortSubject ); + if (trim) + shortSubject += "…"; // must be after html encode } else if( line.toLowerCase(Locale.US).startsWith( "reply-to:" ) ) { reply = getAddress( line.substring( 9 ).trim() ); diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java index 79b5c759f..d3bffc05d 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java @@ -167,6 +167,7 @@ public class WebMail extends HttpServlet private static final String CONFIG_SENDER_FIXED = "sender.fixed"; private static final String CONFIG_SENDER_DOMAIN = "sender.domain"; + private static final String CONFIG_SENDER_NAME = "sender.name"; private static final String CONFIG_COMPOSER_COLS = "composer.cols"; private static final String CONFIG_COMPOSER_ROWS = "composer.rows"; @@ -244,7 +245,9 @@ public class WebMail extends HttpServlet return (b == null) ? 0 : 1; if (b == null) return -1; - return collator.compare(a.formattedSender, b.formattedSender); + String as = a.sender.replace("\"", "").replace("<", "").replace(">", ""); + String bs = b.sender.replace("\"", "").replace("<", "").replace(">", ""); + return collator.compare(as, bs); } } @@ -578,7 +581,7 @@ public class WebMail extends HttpServlet static String quoteHTML( String line ) { if( line != null ) - line = line.replaceAll( "<", "<" ).replaceAll( ">", ">" ); + line = line.replace("&", "&").replace( "<", "<" ).replace( ">", ">" ); else line = ""; return line; @@ -689,7 +692,8 @@ public class WebMail extends HttpServlet sessionObject.folder.addSorter( SORT_SUBJECT, new SubjectSorter( sessionObject.mailCache ) ); sessionObject.folder.addSorter( SORT_DATE, new DateSorter( sessionObject.mailCache ) ); sessionObject.folder.addSorter( SORT_SIZE, new SizeSorter( sessionObject.mailCache ) ); - sessionObject.folder.setSortingDirection( Folder.DOWN ); + // reverse sort, latest mail first + sessionObject.folder.setSortingDirection(Folder.SortOrder.UP); sessionObject.folder.sortBy(SORT_DATE); sessionObject.reallyDelete = false; Debug.debug(Debug.DEBUG, "CONNECTED, YAY"); @@ -816,6 +820,22 @@ public class WebMail extends HttpServlet if( sessionObject.state == STATE_SHOW ) { if( buttonPressed( request, LIST ) ) { sessionObject.state = STATE_LIST; + } else if (buttonPressed( request, CANCEL ) || + buttonPressed( request, PREVPAGE ) || // All these buttons are not shown but we could be lost + buttonPressed( request, NEXTPAGE ) || + buttonPressed( request, FIRSTPAGE ) || + buttonPressed( request, LASTPAGE ) || + buttonPressed( request, SETPAGESIZE ) || + buttonPressed( request, MARKALL ) || + buttonPressed( request, CLEAR ) || + buttonPressed( request, INVERT ) || + buttonPressed( request, SORT_ID ) || + buttonPressed( request, SORT_SENDER ) || + buttonPressed( request, SORT_SUBJECT ) || + buttonPressed( request, SORT_DATE ) || + buttonPressed( request, SORT_SIZE ) || + buttonPressed( request, REFRESH )) { + sessionObject.state = STATE_LIST; } } /* @@ -865,9 +885,9 @@ public class WebMail extends HttpServlet if (part != null) { if( reply || replyAll ) { if( mail.reply != null && Mail.validateAddress( mail.reply ) ) - sessionObject.replyTo = Mail.getAddress( mail.reply ); + sessionObject.replyTo = mail.reply; else if( mail.sender != null && Mail.validateAddress( mail.sender ) ) - sessionObject.replyTo = Mail.getAddress( mail.sender ); + sessionObject.replyTo = mail.sender; sessionObject.subject = "Re: " + mail.formattedSubject; StringWriter text = new StringWriter(); PrintWriter pw = new PrintWriter( text ); @@ -1274,11 +1294,10 @@ public class WebMail extends HttpServlet String str = request.getParameter( sort_id ); if( str != null ) { if( str.equalsIgnoreCase("up")) { - sessionObject.folder.setSortingDirection( Folder.UP ); + sessionObject.folder.setSortingDirection(Folder.SortOrder.UP); sessionObject.folder.sortBy( sort_id ); - } - if( str.equalsIgnoreCase("down")) { - sessionObject.folder.setSortingDirection( Folder.DOWN ); + } else if( str.equalsIgnoreCase("down")) { + sessionObject.folder.setSortingDirection(Folder.SortOrder.DOWN); sessionObject.folder.sortBy( sort_id ); } } @@ -1550,7 +1569,7 @@ public class WebMail extends HttpServlet name = part.name; else name = "part" + part.hashCode(); - String name2 = name.replaceAll( "\\.", "_" ); + String name2 = name.replace( "\\.", "_" ); response.setContentType( "application/zip; name=\"" + name2 + ".zip\"" ); response.addHeader( "Content-Disposition:", "attachment; filename=\"" + name2 + ".zip\"" ); ZipEntry entry = new ZipEntry( name ); @@ -1740,8 +1759,23 @@ public class WebMail extends HttpServlet String fixed = Config.getProperty( CONFIG_SENDER_FIXED, "true" ); if( from == null || !fixed.equalsIgnoreCase("false")) { + String user = sessionObject.user; + String name = Config.getProperty(CONFIG_SENDER_NAME); + if (name != null) { + name = name.trim(); + if (name.contains(" ")) + from = '"' + name + "\" "; + else + from = name + ' '; + } else { + from = ""; + } + if (user.contains("@")) { String domain = Config.getProperty( CONFIG_SENDER_DOMAIN, "mail.i2p" ); - from = "<" + sessionObject.user + "@" + domain + ">"; + from += '<' + user + '@' + domain + '>'; + } else { + from += '<' + user + '>'; + } } String to = request.getParameter( NEW_TO, sessionObject.replyTo != null ? sessionObject.replyTo : "" ); diff --git a/apps/susimail/src/src/i2p/susi/webmail/encoding/HTML.java b/apps/susimail/src/src/i2p/susi/webmail/encoding/HTML.java index 0c0195b91..3db8cf605 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/encoding/HTML.java +++ b/apps/susimail/src/src/i2p/susi/webmail/encoding/HTML.java @@ -50,7 +50,10 @@ public class HTML implements Encoding { */ public String encode(String str) throws EncodingException { - return str.replaceAll( "<", "<" ).replaceAll( ">", ">" ).replaceAll( "\r{0,1}\n", "
\r\n" ); + return str.replace("&", "&") // must be first + .replace( "<", "<" ) + .replace( ">", ">" ) + .replaceAll( "\r{0,1}\n", "
\r\n" ); } /* (non-Javadoc) 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 d35da95a1..644b19ee8 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/smtp/SMTPClient.java +++ b/apps/susimail/src/src/i2p/susi/webmail/smtp/SMTPClient.java @@ -263,7 +263,7 @@ public class SMTPClient { } if (ok) { if( body.indexOf( "\r\n.\r\n" ) != -1 ) - body = body.replaceAll( "\r\n.\r\n", "\r\n..\r\n" ); + body = body.replace( "\r\n.\r\n", "\r\n..\r\n" ); socket.getOutputStream().write(DataHelper.getUTF8(body)); socket.getOutputStream().write(DataHelper.getASCII("\r\n.\r\n")); int result = sendCmd(null); diff --git a/history.txt b/history.txt index fa952c382..c703ae3dd 100644 --- a/history.txt +++ b/history.txt @@ -2,6 +2,11 @@ * SusiMail: - Add persistent cache - Fix encoding in sent mails on non-UTF8 platforms + - Show sender name in folder view + - Add support for configured sender name + - Add HTML escaping of '&' + - Fix Folder sorting so UP is up and DOWN is down + - Fix capture by show page after back button 2014-04-21 zzz * SusiMail: