* SusiMail:

- Fix fetching of new mail
   - More Folder cleanup
   - Prep for not leaving on server
   - Prep for background checker
This commit is contained in:
zzz
2014-04-23 22:46:57 +00:00
parent b365817c99
commit 1e4b43314c
7 changed files with 113 additions and 98 deletions

View File

@ -59,7 +59,7 @@ public class Folder<O extends Object> {
} }
private int pages, pageSize, currentPage; private int pages, pageSize, currentPage;
private O[] unsortedElements, elements; private O[] elements;
private final Hashtable<String, Comparator<O>> sorter; private final Hashtable<String, Comparator<O>> sorter;
private SortOrder sortingDirection; private SortOrder sortingDirection;
Comparator<O> currentSorter; Comparator<O> currentSorter;
@ -74,6 +74,7 @@ public class Folder<O extends Object> {
/** /**
* Returns the current page. * Returns the current page.
* Starts at 1, even if empty.
* *
* @return Returns the current page. * @return Returns the current page.
*/ */
@ -83,6 +84,7 @@ public class Folder<O extends Object> {
/** /**
* Sets the current page to the given parameter. * Sets the current page to the given parameter.
* Starts at 1.
* *
* @param currentPage The current page to set. * @param currentPage The current page to set.
*/ */
@ -102,6 +104,7 @@ public class Folder<O extends Object> {
/** /**
* Returns the number of pages in the folder. * Returns the number of pages in the folder.
* Minimum of 1 even if empty.
* @return Returns the number of pages. * @return Returns the number of pages.
*/ */
public synchronized int getPages() { public synchronized int getPages() {
@ -129,20 +132,6 @@ public class Folder<O extends Object> {
update(); 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. * Recalculates variables.
*/ */
@ -166,30 +155,25 @@ public class Folder<O extends Object> {
*/ */
private void sort() private void sort()
{ {
if( currentSorter != null ) { if( currentSorter != null )
elements = copyArray( unsortedElements );
Arrays.sort( elements, currentSorter ); Arrays.sort( elements, currentSorter );
}
else {
elements = unsortedElements;
}
} }
/** /**
* Set the array of objects the folder should manage. * Set the array of objects the folder should manage.
* Does NOT copy the array.
* *
* @param elements Array of Os. * @param elements Array of Os.
*/ */
public synchronized void setElements( O[] elements ) public synchronized void setElements( O[] elements )
{ {
if (elements.length > 0) { if (elements.length > 0) {
this.unsortedElements = elements; this.elements = elements;
if( currentSorter != null ) if( currentSorter != null )
sort(); sort();
else } else {
this.elements = elements;
} else
this.elements = null; this.elements = null;
}
update(); update();
} }
@ -198,7 +182,7 @@ public class Folder<O extends Object> {
* *
* @param element to remove * @param element to remove
*/ */
public synchronized void removeElement(O element) { public void removeElement(O element) {
removeElements(Collections.singleton(element)); removeElements(Collections.singleton(element));
} }
@ -211,19 +195,49 @@ public class Folder<O extends Object> {
public synchronized void removeElements(Collection<O> elems) { public synchronized void removeElements(Collection<O> elems) {
if (elements != null) { if (elements != null) {
List<O> list = new ArrayList<O>(Arrays.asList(elements)); List<O> list = new ArrayList<O>(Arrays.asList(elements));
boolean shouldUpdate = false;
for (O e : elems) { 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<O> list = new ArrayList<O>(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<O> elems) {
if (elements != null) {
List<O> list = new ArrayList<O>(Arrays.asList(elements));
boolean shouldUpdate = false;
for (O e : elems) { 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();
} }
/** /**

View File

@ -24,6 +24,7 @@
package i2p.susi.webmail; package i2p.susi.webmail;
import i2p.susi.debug.Debug; import i2p.susi.debug.Debug;
import i2p.susi.util.Config;
import i2p.susi.util.ReadBuffer; import i2p.susi.util.ReadBuffer;
import i2p.susi.webmail.pop3.POP3MailBox; import i2p.susi.webmail.pop3.POP3MailBox;
import i2p.susi.webmail.pop3.POP3MailBox.FetchRequest; import i2p.susi.webmail.pop3.POP3MailBox.FetchRequest;
@ -136,8 +137,10 @@ class MailCache {
} }
} }
if (disk != null) { if (disk != null) {
if (disk.saveMail(mail) && mail.hasBody()) { if (disk.saveMail(mail) && mail.hasBody() &&
// TODO delete on server false && // TO ENABLE
!Boolean.parseBoolean(Config.getProperty(WebMail.CONFIG_LEAVE_ON_SERVER))) {
mailbox.queueForDeletion(mail.uidl);
} }
} }
return mail; return mail;
@ -146,18 +149,23 @@ class MailCache {
/** /**
* Fetch any needed data from pop3 server. * Fetch any needed data from pop3 server.
* Mail objects are inserted into the requests. * 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 * @since 0.9.13
*/ */
public void getMail(Collection<MailRequest> requests) { public boolean getMail(boolean hOnly) {
Collection<String> popKnown = mailbox.getUIDLs();
if (popKnown == null)
return false;
List<POP3Request> fetches = new ArrayList<POP3Request>(); List<POP3Request> fetches = new ArrayList<POP3Request>();
// Fill in the answers from the cache and make a list of // Fill in the answers from the cache and make a list of
// requests.to send off // requests.to send off
for (MailRequest mr : requests) { for (String uidl : popKnown) {
Mail mail = null, newMail = null; Mail mail = null, newMail = null;
String uidl = mr.getUIDL(); boolean headerOnly = hOnly;
boolean headerOnly = mr.getHeaderOnly();
/* /*
* synchronize update to hashtable * synchronize update to hashtable
@ -175,7 +183,6 @@ class MailCache {
} }
if (mail.markForDeletion) if (mail.markForDeletion)
continue; continue;
mr.setMail(mail);
int sz = mail.getSize(); int sz = mail.getSize();
if (sz > 0 && sz <= FETCH_ALL_SIZE) if (sz > 0 && sz <= FETCH_ALL_SIZE)
headerOnly = false; headerOnly = false;
@ -187,7 +194,7 @@ class MailCache {
continue; // found on disk, woo continue; // found on disk, woo
} }
} }
POP3Request pr = new POP3Request(mr, mail, true); POP3Request pr = new POP3Request(mail, true);
fetches.add(pr); fetches.add(pr);
} }
} else { } else {
@ -198,12 +205,13 @@ class MailCache {
continue; // found on disk, woo continue; // found on disk, woo
} }
} }
POP3Request pr = new POP3Request(mr, mail, false); POP3Request pr = new POP3Request(mail, false);
fetches.add(pr); fetches.add(pr);
} }
} }
} }
boolean rv = false;
if (!fetches.isEmpty()) { if (!fetches.isEmpty()) {
// Send off the fetches // Send off the fetches
// gaah compiler // gaah compiler
@ -220,14 +228,18 @@ class MailCache {
} else { } else {
mail.setBody(rb); mail.setBody(rb);
} }
rv = true;
if (disk != null) { if (disk != null) {
if (disk.saveMail(mail) && mail.hasBody()) { if (disk.saveMail(mail) && mail.hasBody() &&
// TODO delete on server 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); mailbox.queueForDeletion(toDelete);
} }
/**
* Incoming to us
*/
public interface MailRequest {
public String getUIDL();
public boolean getHeaderOnly();
public void setMail(Mail mail);
}
/** /**
* Outgoing to POP3 * Outgoing to POP3
*/ */
private static class POP3Request implements FetchRequest { private static class POP3Request implements FetchRequest {
public final MailRequest request;
public final Mail mail; public final Mail mail;
private final boolean headerOnly; private final boolean headerOnly;
public ReadBuffer buf; public ReadBuffer buf;
public POP3Request(MailRequest req, Mail m, boolean hOnly) { public POP3Request(Mail m, boolean hOnly) {
request = req;
mail = m; mail = m;
headerOnly = hOnly; headerOnly = hOnly;
} }
public String getUIDL() { public String getUIDL() {
return request.getUIDL(); return mail.uidl;
} }
public boolean getHeaderOnly() { public boolean getHeaderOnly() {

View File

@ -174,7 +174,7 @@ public class WebMail extends HttpServlet
private static final String CONFIG_COMPOSER_ROWS = "composer.rows"; 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_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 CONFIG_DEBUG = "debug";
private static final String RC_PROP_THEME = "routerconsole.theme"; 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); MailCache mc = new MailCache(mailbox, host, pop3PortNo, user, pass);
sessionObject.mailCache = mc; sessionObject.mailCache = mc;
sessionObject.folder = new Folder<String>(); sessionObject.folder = new Folder<String>();
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 // get through cache so we have the disk-only ones too
String[] uidls = mc.getUIDLs(); String[] uidls = mc.getUIDLs();
sessionObject.folder.setElements(uidls); 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<MailCache.MailRequest> reqs = new ArrayList<MailCache.MailRequest>(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_ID, new IDSorter( sessionObject.mailCache ) );
sessionObject.folder.addSorter( SORT_SENDER, new SenderSorter( 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 * @param sessionObject
@ -1036,6 +1008,7 @@ public class WebMail extends HttpServlet
} }
if( buttonPressed( request, REFRESH ) ) { if( buttonPressed( request, REFRESH ) ) {
sessionObject.mailbox.refresh(); sessionObject.mailbox.refresh();
sessionObject.mailCache.getMail(true);
// get through cache so we have the disk-only ones too // get through cache so we have the disk-only ones too
String[] uidls = sessionObject.mailCache.getUIDLs(); String[] uidls = sessionObject.mailCache.getUIDLs();
if (uidls != null) if (uidls != null)
@ -1461,8 +1434,10 @@ public class WebMail extends HttpServlet
if( sessionObject.state != STATE_AUTH ) { if( sessionObject.state != STATE_AUTH ) {
// get through cache so we have the disk-only ones too // get through cache so we have the disk-only ones too
String[] uidls = sessionObject.mailCache.getUIDLs(); String[] uidls = sessionObject.mailCache.getUIDLs();
if (uidls != null) if (uidls != null) {
// TODO why every time?
sessionObject.folder.setElements(uidls); sessionObject.folder.setElements(uidls);
}
} }
if( ! sendAttachment( sessionObject, response ) ) { if( ! sendAttachment( sessionObject, response ) ) {
@ -1852,7 +1827,7 @@ public class WebMail extends HttpServlet
out.println( out.println(
"<tr><td colspan=\"2\">&nbsp;</td></tr>\n" + "<tr><td colspan=\"2\">&nbsp;</td></tr>\n" +
"<tr><td></td><td align=\"left\">" + button( LOGIN, _("Login") ) + spacer + "<tr><td></td><td align=\"left\">" + button( LOGIN, _("Login") ) + spacer +
button(OFFLINE, _("View Mail Offline") ) + spacer + button(OFFLINE, _("Read Mail Offline") ) + spacer +
" <input class=\"cancel\" type=\"reset\" value=\"" + _("Reset") + "\"></td></tr>\n" + " <input class=\"cancel\" type=\"reset\" value=\"" + _("Reset") + "\"></td></tr>\n" +
"<tr><td colspan=\"2\">&nbsp;</td></tr>\n" + "<tr><td colspan=\"2\">&nbsp;</td></tr>\n" +
"<tr><td></td><td align=\"left\"><a href=\"http://hq.postman.i2p/?page_id=14\">" + _("Learn about I2P mail") + "</a></td></tr>\n" + "<tr><td></td><td align=\"left\"><a href=\"http://hq.postman.i2p/?page_id=14\">" + _("Learn about I2P mail") + "</a></td></tr>\n" +

View File

@ -71,8 +71,10 @@ class DelayedDeleter {
isDeleting = true; isDeleting = true;
t.start(); t.start();
} else { } 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); schedule(CHECK_TIME);
} }

View File

@ -66,6 +66,7 @@ public class POP3MailBox {
private Socket socket; private Socket socket;
private final AtomicLong lastActive; private final AtomicLong lastActive;
private final AtomicLong lastChecked;
private final Object synchronizer; private final Object synchronizer;
private final DelayedDeleter delayedDeleter; private final DelayedDeleter delayedDeleter;
@ -92,6 +93,7 @@ public class POP3MailBox {
// this appears in the UI so translate // this appears in the UI so translate
lastLine = _("No response from server"); lastLine = _("No response from server");
lastActive = new AtomicLong(System.currentTimeMillis()); lastActive = new AtomicLong(System.currentTimeMillis());
lastChecked = new AtomicLong();
delayedDeleter = new DelayedDeleter(this); delayedDeleter = new DelayedDeleter(this);
} }
@ -270,10 +272,20 @@ public class POP3MailBox {
*/ */
public void queueForDeletion(Collection<String> uidls) { public void queueForDeletion(Collection<String> uidls) {
for (String uidl : 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. * Delete all at once and close. Does not reconnect.
* Do NOT call performDelete() after this. * Do NOT call performDelete() after this.
@ -445,6 +457,15 @@ public class POP3MailBox {
return lastActive.get(); 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 * @param response line starting with +OK
@ -489,6 +510,7 @@ public class POP3MailBox {
} }
} }
} }
lastChecked.set(System.currentTimeMillis());
} else { } else {
Debug.debug(Debug.DEBUG, "Error getting UIDL list from server."); 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. * @return A new array of the available UIDLs. No particular order.
*/ */
public String[] getUIDLs() public Collection<String> getUIDLs()
{ {
if (!isConnected()) if (!isConnected())
return null; return null;
synchronized( synchronizer ) { synchronized( synchronizer ) {
return uidlToID.keySet().toArray(new String[uidlToID.size()]); return new ArrayList(uidlToID.keySet());
} }
} }

View File

@ -2,6 +2,7 @@
* SusiMail: * SusiMail:
- Queue deletions for a later thread - Queue deletions for a later thread
- Synch all folder access - Synch all folder access
- Fix fetching of new mail
2014-04-22 zzz 2014-04-22 zzz
* SusiMail: * SusiMail:

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */ /** deprecated */
public final static String ID = "Monotone"; public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION; public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 9; public final static long BUILD = 10;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";