diff --git a/apps/susimail/src/src/i2p/susi/util/Folder.java b/apps/susimail/src/src/i2p/susi/util/Folder.java index 75ae2088bd..62290605da 100644 --- a/apps/susimail/src/src/i2p/susi/util/Folder.java +++ b/apps/susimail/src/src/i2p/susi/util/Folder.java @@ -59,7 +59,7 @@ public class Folder { } private int pages, pageSize, currentPage; - private O[] unsortedElements, elements; + private O[] elements; private final Hashtable> sorter; private SortOrder sortingDirection; Comparator currentSorter; @@ -74,6 +74,7 @@ public class Folder { /** * Returns the current page. + * Starts at 1, even if empty. * * @return Returns the current page. */ @@ -83,6 +84,7 @@ public class Folder { /** * Sets the current page to the given parameter. + * Starts at 1. * * @param currentPage The current page to set. */ @@ -102,6 +104,7 @@ public class Folder { /** * Returns the number of pages in the folder. + * Minimum of 1 even if empty. * @return Returns the number of pages. */ public synchronized int getPages() { @@ -129,20 +132,6 @@ public class Folder { update(); } - /** - * Creates a copy of an array by copying its elements. - * - * @param source Array to copy. - * @return Copy of source. - */ - @SuppressWarnings("unchecked") - private O[] copyArray( O[] source ) - { - Object[] destination = new Object[source.length]; - for( int i = 0; i < source.length; i++ ) - destination[i] = source[i]; - return (O[])destination; - } /** * Recalculates variables. */ @@ -166,30 +155,25 @@ public class Folder { */ private void sort() { - if( currentSorter != null ) { - elements = copyArray( unsortedElements ); + if( currentSorter != null ) Arrays.sort( elements, currentSorter ); - } - else { - elements = unsortedElements; - } } /** * Set the array of objects the folder should manage. + * Does NOT copy the array. * * @param elements Array of Os. */ public synchronized void setElements( O[] elements ) { if (elements.length > 0) { - this.unsortedElements = elements; + this.elements = elements; if( currentSorter != null ) sort(); - else - this.elements = elements; - } else + } else { this.elements = null; + } update(); } @@ -198,7 +182,7 @@ public class Folder { * * @param element to remove */ - public synchronized void removeElement(O element) { + public void removeElement(O element) { removeElements(Collections.singleton(element)); } @@ -211,19 +195,49 @@ public class Folder { public synchronized void removeElements(Collection elems) { if (elements != null) { List list = new ArrayList(Arrays.asList(elements)); + boolean shouldUpdate = false; for (O e : elems) { - list.remove(e); + if (list.remove(e)) + shouldUpdate = true; + } + if (shouldUpdate) { + elements = (O[]) list.toArray(new Object[list.size()]); + update(); // will still be sorted } - elements = (O[]) list.toArray(new Object[list.size()]); } - if (unsortedElements != null) { - List list = new ArrayList(Arrays.asList(unsortedElements)); + } + + /** + * Add an element only if it does not already exist + * + * @param element to add + */ + public void addElement(O element) { + addElements(Collections.singleton(element)); + } + + /** + * Add elements only if it they do not already exist + * + * @param elems to adde + */ + @SuppressWarnings("unchecked") + public synchronized void addElements(Collection elems) { + if (elements != null) { + List list = new ArrayList(Arrays.asList(elements)); + boolean shouldUpdate = false; for (O e : elems) { - list.remove(e); + if (!list.contains(e)) { + list.add(e); + shouldUpdate = true; + } + } + if (shouldUpdate) { + elements = (O[]) list.toArray(new Object[list.size()]); + sort(); + update(); } - unsortedElements = (O[]) list.toArray(new Object[list.size()]); } - update(); } /** diff --git a/apps/susimail/src/src/i2p/susi/webmail/MailCache.java b/apps/susimail/src/src/i2p/susi/webmail/MailCache.java index 361db42ebe..1e06180412 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/MailCache.java +++ b/apps/susimail/src/src/i2p/susi/webmail/MailCache.java @@ -24,6 +24,7 @@ package i2p.susi.webmail; import i2p.susi.debug.Debug; +import i2p.susi.util.Config; import i2p.susi.util.ReadBuffer; import i2p.susi.webmail.pop3.POP3MailBox; import i2p.susi.webmail.pop3.POP3MailBox.FetchRequest; @@ -136,8 +137,10 @@ class MailCache { } } if (disk != null) { - if (disk.saveMail(mail) && mail.hasBody()) { - // TODO delete on server + if (disk.saveMail(mail) && mail.hasBody() && + false && // TO ENABLE + !Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) { + mailbox.queueForDeletion(mail.uidl); } } return mail; @@ -146,18 +149,23 @@ class MailCache { /** * Fetch any needed data from pop3 server. * Mail objects are inserted into the requests. + * After this, call getUIDLs() to get all known mail UIDLs. + * MUST already be connected, otherwise returns false. * + * @return true if any were fetched * @since 0.9.13 */ - public void getMail(Collection requests) { + public boolean getMail(boolean hOnly) { + Collection popKnown = mailbox.getUIDLs(); + if (popKnown == null) + return false; List fetches = new ArrayList(); // Fill in the answers from the cache and make a list of // requests.to send off - for (MailRequest mr : requests) { + for (String uidl : popKnown) { Mail mail = null, newMail = null; - String uidl = mr.getUIDL(); - boolean headerOnly = mr.getHeaderOnly(); + boolean headerOnly = hOnly; /* * synchronize update to hashtable @@ -175,7 +183,6 @@ class MailCache { } if (mail.markForDeletion) continue; - mr.setMail(mail); int sz = mail.getSize(); if (sz > 0 && sz <= FETCH_ALL_SIZE) headerOnly = false; @@ -187,7 +194,7 @@ class MailCache { continue; // found on disk, woo } } - POP3Request pr = new POP3Request(mr, mail, true); + POP3Request pr = new POP3Request(mail, true); fetches.add(pr); } } else { @@ -198,12 +205,13 @@ class MailCache { continue; // found on disk, woo } } - POP3Request pr = new POP3Request(mr, mail, false); + POP3Request pr = new POP3Request(mail, false); fetches.add(pr); } } } + boolean rv = false; if (!fetches.isEmpty()) { // Send off the fetches // gaah compiler @@ -220,14 +228,18 @@ class MailCache { } else { mail.setBody(rb); } + rv = true; if (disk != null) { - if (disk.saveMail(mail) && mail.hasBody()) { - // TODO delete on server + if (disk.saveMail(mail) && mail.hasBody() && + false && // TO ENABLE + !Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) { + mailbox.queueForDeletion(mail.uidl); } } } } } + return rv; } /** @@ -264,32 +276,21 @@ class MailCache { mailbox.queueForDeletion(toDelete); } - /** - * Incoming to us - */ - public interface MailRequest { - public String getUIDL(); - public boolean getHeaderOnly(); - public void setMail(Mail mail); - } - /** * Outgoing to POP3 */ private static class POP3Request implements FetchRequest { - public final MailRequest request; public final Mail mail; private final boolean headerOnly; public ReadBuffer buf; - public POP3Request(MailRequest req, Mail m, boolean hOnly) { - request = req; + public POP3Request(Mail m, boolean hOnly) { mail = m; headerOnly = hOnly; } public String getUIDL() { - return request.getUIDL(); + return mail.uidl; } public boolean getHeaderOnly() { diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java index 77f91d261d..0029292403 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java @@ -174,7 +174,7 @@ public class WebMail extends HttpServlet private static final String CONFIG_COMPOSER_ROWS = "composer.rows"; private static final String CONFIG_BCC_TO_SELF = "composer.bcc.to.self"; - private static final String CONFIG_LEAVE_ON_SERVER = "pop3.leave.on.server"; + static final String CONFIG_LEAVE_ON_SERVER = "pop3.leave.on.server"; private static final String CONFIG_DEBUG = "debug"; private static final String RC_PROP_THEME = "routerconsole.theme"; @@ -676,18 +676,14 @@ public class WebMail extends HttpServlet MailCache mc = new MailCache(mailbox, host, pop3PortNo, user, pass); sessionObject.mailCache = mc; sessionObject.folder = new Folder(); + if (!offline) { + // prime the cache, request all headers at once + // otherwise they are pulled one at a time by sortBy() below + mc.getMail(true); + } // get through cache so we have the disk-only ones too String[] uidls = mc.getUIDLs(); sessionObject.folder.setElements(uidls); - if (uidls.length > 0 && !offline) { - // prime the cache, request all headers at once - // otherwise they are pulled one at a time by sortBy() below - List reqs = new ArrayList(uidls.length); - for (int i = 0; i < uidls.length; i++) { - reqs.add(new CacheRequest(uidls[i])); - } - mc.getMail(reqs); - } //sessionObject.folder.addSorter( SORT_ID, new IDSorter( sessionObject.mailCache ) ); sessionObject.folder.addSorter( SORT_SENDER, new SenderSorter( sessionObject.mailCache ) ); @@ -714,30 +710,6 @@ public class WebMail extends HttpServlet } } - /** - * Outgoing to MailCache - * @since 0.9.13 - */ - private static class CacheRequest implements MailCache.MailRequest { - private final String uidl; - - public CacheRequest(String uidl) { - this.uidl = uidl; - } - - public String getUIDL() { - return uidl; - } - - public boolean getHeaderOnly() { - return true; - } - - public void setMail(Mail mail) { - // do nothing, this just pumps up the cache - } - } - /** * * @param sessionObject @@ -1036,6 +1008,7 @@ public class WebMail extends HttpServlet } if( buttonPressed( request, REFRESH ) ) { sessionObject.mailbox.refresh(); + sessionObject.mailCache.getMail(true); // get through cache so we have the disk-only ones too String[] uidls = sessionObject.mailCache.getUIDLs(); if (uidls != null) @@ -1461,8 +1434,10 @@ public class WebMail extends HttpServlet if( sessionObject.state != STATE_AUTH ) { // get through cache so we have the disk-only ones too String[] uidls = sessionObject.mailCache.getUIDLs(); - if (uidls != null) + if (uidls != null) { + // TODO why every time? sessionObject.folder.setElements(uidls); + } } if( ! sendAttachment( sessionObject, response ) ) { @@ -1852,7 +1827,7 @@ public class WebMail extends HttpServlet out.println( " \n" + "" + button( LOGIN, _("Login") ) + spacer + - button(OFFLINE, _("View Mail Offline") ) + spacer + + button(OFFLINE, _("Read Mail Offline") ) + spacer + " \n" + " \n" + "" + _("Learn about I2P mail") + "\n" + diff --git a/apps/susimail/src/src/i2p/susi/webmail/pop3/DelayedDeleter.java b/apps/susimail/src/src/i2p/susi/webmail/pop3/DelayedDeleter.java index 8ccdb91bc2..9ecc6696ac 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/pop3/DelayedDeleter.java +++ b/apps/susimail/src/src/i2p/susi/webmail/pop3/DelayedDeleter.java @@ -71,8 +71,10 @@ class DelayedDeleter { isDeleting = true; t.start(); } else { - Debug.debug(Debug.DEBUG, "Nothing to delete"); + Debug.debug(Debug.DEBUG, "Not deleting, only idle " + idle); } + } else { + Debug.debug(Debug.DEBUG, "Nothing to delete"); } schedule(CHECK_TIME); } diff --git a/apps/susimail/src/src/i2p/susi/webmail/pop3/POP3MailBox.java b/apps/susimail/src/src/i2p/susi/webmail/pop3/POP3MailBox.java index 5db78c7a80..7d24ce9861 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/pop3/POP3MailBox.java +++ b/apps/susimail/src/src/i2p/susi/webmail/pop3/POP3MailBox.java @@ -66,6 +66,7 @@ public class POP3MailBox { private Socket socket; private final AtomicLong lastActive; + private final AtomicLong lastChecked; private final Object synchronizer; private final DelayedDeleter delayedDeleter; @@ -92,6 +93,7 @@ public class POP3MailBox { // this appears in the UI so translate lastLine = _("No response from server"); lastActive = new AtomicLong(System.currentTimeMillis()); + lastChecked = new AtomicLong(); delayedDeleter = new DelayedDeleter(this); } @@ -270,10 +272,20 @@ public class POP3MailBox { */ public void queueForDeletion(Collection uidls) { for (String uidl : uidls) { - delayedDeleter.queueDelete(uidl); + queueForDeletion(uidl); } } + /** + * Queue for later deletion. Non-blocking. + * + * @since 0.9.13 + */ + public void queueForDeletion(String uidl) { + Debug.debug(Debug.DEBUG, "Queueing for deletion: " + uidl); + delayedDeleter.queueDelete(uidl); + } + /** * Delete all at once and close. Does not reconnect. * Do NOT call performDelete() after this. @@ -445,6 +457,15 @@ public class POP3MailBox { return lastActive.get(); } + /** + * Timestamp. When we last successfully got the UIDL list. + * + * @since 0.9.13 + */ + long getLastChecked() { + return lastChecked.get(); + } + /** * * @param response line starting with +OK @@ -489,6 +510,7 @@ public class POP3MailBox { } } } + lastChecked.set(System.currentTimeMillis()); } else { Debug.debug(Debug.DEBUG, "Error getting UIDL list from server."); } @@ -1085,12 +1107,12 @@ public class POP3MailBox { * * @return A new array of the available UIDLs. No particular order. */ - public String[] getUIDLs() + public Collection getUIDLs() { if (!isConnected()) return null; synchronized( synchronizer ) { - return uidlToID.keySet().toArray(new String[uidlToID.size()]); + return new ArrayList(uidlToID.keySet()); } } diff --git a/history.txt b/history.txt index d719d476ae..e27ba60179 100644 --- a/history.txt +++ b/history.txt @@ -2,6 +2,7 @@ * SusiMail: - Queue deletions for a later thread - Synch all folder access + - Fix fetching of new mail 2014-04-22 zzz * SusiMail: diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index f2522cd4ed..7ca1a3ec2c 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 = 9; + public final static long BUILD = 10; /** for example "-test" */ public final static String EXTRA = "";