* SusiMail:

- Queue deletions for a delayed background thread
   - Synch all folder access
   - NPE fixes
   - Javadoc fixes
This commit is contained in:
zzz
2014-04-23 19:40:57 +00:00
parent bbb04774d1
commit b365817c99
8 changed files with 260 additions and 47 deletions

View File

@@ -42,7 +42,7 @@ import java.util.List;
* and then fetch the content of the current page with * and then fetch the content of the current page with
* currentPageIterator(). * currentPageIterator().
* *
* Warning - unsynchronized - not thread safe * All public methods are synchronized.
* *
* @author susi * @author susi
*/ */
@@ -77,7 +77,7 @@ public class Folder<O extends Object> {
* *
* @return Returns the current page. * @return Returns the current page.
*/ */
public int getCurrentPage() { public synchronized int getCurrentPage() {
return currentPage; return currentPage;
} }
@@ -86,7 +86,7 @@ public class Folder<O extends Object> {
* *
* @param currentPage The current page to set. * @param currentPage The current page to set.
*/ */
public void setCurrentPage(int currentPage) { public synchronized void setCurrentPage(int currentPage) {
if( currentPage >= 1 && currentPage <= pages ) if( currentPage >= 1 && currentPage <= pages )
this.currentPage = currentPage; this.currentPage = currentPage;
} }
@@ -96,7 +96,7 @@ public class Folder<O extends Object> {
* *
* @return Returns the size of the folder. * @return Returns the size of the folder.
*/ */
public int getSize() { public synchronized int getSize() {
return elements != null ? elements.length : 0; return elements != null ? elements.length : 0;
} }
@@ -104,7 +104,7 @@ public class Folder<O extends Object> {
* Returns the number of pages in the folder. * Returns the number of pages in the folder.
* @return Returns the number of pages. * @return Returns the number of pages.
*/ */
public int getPages() { public synchronized int getPages() {
return pages; return pages;
} }
@@ -114,7 +114,7 @@ public class Folder<O extends Object> {
* *
* @return Returns the pageSize. * @return Returns the pageSize.
*/ */
public int getPageSize() { public synchronized int getPageSize() {
return pageSize > 0 ? pageSize : Config.getProperty( PAGESIZE, DEFAULT_PAGESIZE ); return pageSize > 0 ? pageSize : Config.getProperty( PAGESIZE, DEFAULT_PAGESIZE );
} }
@@ -123,7 +123,7 @@ public class Folder<O extends Object> {
* *
* @param pageSize The page size to set. * @param pageSize The page size to set.
*/ */
public void setPageSize(int pageSize) { public synchronized void setPageSize(int pageSize) {
if( pageSize > 0 ) if( pageSize > 0 )
this.pageSize = pageSize; this.pageSize = pageSize;
update(); update();
@@ -180,7 +180,7 @@ public class Folder<O extends Object> {
* *
* @param elements Array of Os. * @param elements Array of Os.
*/ */
public void setElements( O[] elements ) public synchronized void setElements( O[] elements )
{ {
if (elements.length > 0) { if (elements.length > 0) {
this.unsortedElements = elements; this.unsortedElements = elements;
@@ -198,17 +198,17 @@ public class Folder<O extends Object> {
* *
* @param element to remove * @param element to remove
*/ */
public void removeElement(O element) { public synchronized void removeElement(O element) {
removeElements(Collections.singleton(element)); removeElements(Collections.singleton(element));
} }
/** /**
* Remove elements * Remove elements
* *
* @param elements to remove * @param elems to remove
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public 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));
for (O e : elems) { for (O e : elems) {
@@ -228,9 +228,14 @@ public class Folder<O extends Object> {
/** /**
* Returns an iterator containing the elements on the current page. * Returns an iterator containing the elements on the current page.
* This iterator is over a copy of the current page, and so
* is thread safe w.r.t. other operations on this folder,
* but will not reflect subsequent changes, and iter.remove()
* will not change the folder.
*
* @return Iterator containing the elements on the current page. * @return Iterator containing the elements on the current page.
*/ */
public Iterator<O> currentPageIterator() public synchronized Iterator<O> currentPageIterator()
{ {
ArrayList<O> list = new ArrayList<O>(); ArrayList<O> list = new ArrayList<O>();
if( elements != null ) { if( elements != null ) {
@@ -247,7 +252,7 @@ public class Folder<O extends Object> {
/** /**
* Turns folder to next page. * Turns folder to next page.
*/ */
public void nextPage() public synchronized void nextPage()
{ {
currentPage++; currentPage++;
if( currentPage > pages ) if( currentPage > pages )
@@ -257,7 +262,7 @@ public class Folder<O extends Object> {
/** /**
* Turns folder to previous page. * Turns folder to previous page.
*/ */
public void previousPage() public synchronized void previousPage()
{ {
currentPage--; currentPage--;
if( currentPage < 1 ) if( currentPage < 1 )
@@ -267,7 +272,7 @@ public class Folder<O extends Object> {
/** /**
* Sets folder to display first page. * Sets folder to display first page.
*/ */
public void firstPage() public synchronized void firstPage()
{ {
currentPage = 1; currentPage = 1;
} }
@@ -275,7 +280,7 @@ public class Folder<O extends Object> {
/** /**
* Sets folder to display last page. * Sets folder to display last page.
*/ */
public void lastPage() public synchronized void lastPage()
{ {
currentPage = pages; currentPage = pages;
} }
@@ -287,7 +292,7 @@ public class Folder<O extends Object> {
* @param id ID to identify the Comparator with @link sortBy() * @param id ID to identify the Comparator with @link sortBy()
* @param sorter a Comparator to sort the Array given by @link setElements() * @param sorter a Comparator to sort the Array given by @link setElements()
*/ */
public void addSorter( String id, Comparator<O> sorter ) public synchronized void addSorter( String id, Comparator<O> sorter )
{ {
this.sorter.put( id, sorter ); this.sorter.put( id, sorter );
} }
@@ -299,7 +304,7 @@ public class Folder<O extends Object> {
* *
* @param id ID to identify the Comparator stored with @link addSorter() * @param id ID to identify the Comparator stored with @link addSorter()
*/ */
public void sortBy( String id ) public synchronized void sortBy( String id )
{ {
currentSorter = sorter.get( id ); currentSorter = sorter.get( id );
if (sortingDirection == SortOrder.UP) if (sortingDirection == SortOrder.UP)
@@ -313,7 +318,7 @@ public class Folder<O extends Object> {
* @param x Position of the element on the current page. * @param x Position of the element on the current page.
* @return Element on the current page on the given position. * @return Element on the current page on the given position.
*/ */
public O getElementAtPosXonCurrentPage( int x ) public synchronized O getElementAtPosXonCurrentPage( int x )
{ {
O result = null; O result = null;
if( elements != null ) { if( elements != null ) {
@@ -332,7 +337,7 @@ public class Folder<O extends Object> {
* *
* @param direction @link UP or @link DOWN * @param direction @link UP or @link DOWN
*/ */
public void setSortingDirection(SortOrder direction) public synchronized void setSortingDirection(SortOrder direction)
{ {
sortingDirection = direction; sortingDirection = direction;
} }
@@ -342,7 +347,7 @@ public class Folder<O extends Object> {
* *
* @return First element. * @return First element.
*/ */
public O getFirstElement() public synchronized O getFirstElement()
{ {
return elements == null ? null : getElement( 0 ); return elements == null ? null : getElement( 0 );
} }
@@ -352,7 +357,7 @@ public class Folder<O extends Object> {
* *
* @return Last element. * @return Last element.
*/ */
public O getLastElement() public synchronized O getLastElement()
{ {
return elements == null ? null : getElement( elements.length - 1 ); return elements == null ? null : getElement( elements.length - 1 );
} }
@@ -379,7 +384,7 @@ public class Folder<O extends Object> {
* @param element * @param element
* @return The next element * @return The next element
*/ */
public O getNextElement( O element ) public synchronized O getNextElement( O element )
{ {
O result = null; O result = null;
@@ -399,7 +404,7 @@ public class Folder<O extends Object> {
* @param element * @param element
* @return The previous element * @return The previous element
*/ */
public O getPreviousElement( O element ) public synchronized O getPreviousElement( O element )
{ {
O result = null; O result = null;
@@ -431,7 +436,7 @@ public class Folder<O extends Object> {
/** /**
* Returns true, if folder shows points to the last page. * Returns true, if folder shows points to the last page.
*/ */
public boolean isLastPage() public synchronized boolean isLastPage()
{ {
return currentPage == pages; return currentPage == pages;
} }
@@ -439,7 +444,7 @@ public class Folder<O extends Object> {
/** /**
* Returns true, if folder shows points to the first page. * Returns true, if folder shows points to the first page.
*/ */
public boolean isFirstPage() public synchronized boolean isFirstPage()
{ {
return currentPage == 1; return currentPage == 1;
} }
@@ -449,7 +454,7 @@ public class Folder<O extends Object> {
* *
* @param element * @param element
*/ */
public boolean isLastElement( O element ) public synchronized boolean isLastElement( O element )
{ {
if( elements == null ) if( elements == null )
return false; return false;
@@ -461,7 +466,7 @@ public class Folder<O extends Object> {
* *
* @param element * @param element
*/ */
public boolean isFirstElement( O element ) public synchronized boolean isFirstElement( O element )
{ {
if( elements == null ) if( elements == null )
return false; return false;

View File

@@ -223,7 +223,6 @@ class Mail {
* Adds all items from the list * Adds all items from the list
* to the builder, separated by tabs. * to the builder, separated by tabs.
* *
* @param text comma-separated
* @param buf out param * @param buf out param
* @param prefix prepended to the addresses * @param prefix prepended to the addresses
*/ */

View File

@@ -261,7 +261,7 @@ class MailCache {
} }
if (toDelete.isEmpty()) if (toDelete.isEmpty())
return; return;
mailbox.delete(toDelete); mailbox.queueForDeletion(toDelete);
} }
/** /**

View File

@@ -886,7 +886,7 @@ public class WebMail extends HttpServlet
/* /*
* extract original sender from Reply-To: or From: * extract original sender from Reply-To: or From:
*/ */
MailPart part = mail.getPart(); MailPart part = mail != null ? mail.getPart() : null;
if (part != null) { if (part != null) {
if( reply || replyAll ) { if( reply || replyAll ) {
if( mail.reply != null && Mail.validateAddress( mail.reply ) ) if( mail.reply != null && Mail.validateAddress( mail.reply ) )
@@ -1173,7 +1173,7 @@ public class WebMail extends HttpServlet
try { try {
int hashCode = Integer.parseInt( str ); int hashCode = Integer.parseInt( str );
Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FETCH_ALL ); Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FETCH_ALL );
MailPart part = getMailPartFromHashCode( mail.getPart(), hashCode ); MailPart part = mail != null ? getMailPartFromHashCode( mail.getPart(), hashCode ) : null;
if( part != null ) if( part != null )
sessionObject.showAttachment = part; sessionObject.showAttachment = part;
} }
@@ -1910,6 +1910,8 @@ public class WebMail extends HttpServlet
for( Iterator<String> it = sessionObject.folder.currentPageIterator(); it != null && it.hasNext(); ) { for( Iterator<String> it = sessionObject.folder.currentPageIterator(); it != null && it.hasNext(); ) {
String uidl = it.next(); String uidl = it.next();
Mail mail = sessionObject.mailCache.getMail( uidl, MailCache.FETCH_HEADER ); Mail mail = sessionObject.mailCache.getMail( uidl, MailCache.FETCH_HEADER );
if (mail == null)
continue;
String link = "<a href=\"" + myself + "?" + SHOW + "=" + i + "\">"; String link = "<a href=\"" + myself + "?" + SHOW + "=" + i + "\">";
boolean idChecked = false; boolean idChecked = false;

View File

@@ -0,0 +1,100 @@
package i2p.susi.webmail.pop3;
import i2p.susi.debug.Debug;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.util.I2PAppThread;
import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.SimpleTimer2;
/**
* Queue UIDLs for later deletion
*
* @since 0.9.13
*/
class DelayedDeleter {
private final POP3MailBox mailbox;
private final Set<String> toDelete;
private final SimpleTimer2.TimedEvent timer;
private volatile boolean isDeleting;
private volatile boolean isDead;
private final long CHECK_TIME = 5*60*1000;
private final long MIN_IDLE = 5*60*1000;
public DelayedDeleter(POP3MailBox mailbox) {
this.mailbox = mailbox;
toDelete = new ConcurrentHashSet<String>();
timer = new Checker();
}
public void queueDelete(String uidl) {
toDelete.add(uidl);
}
public void removeQueued(String uidl) {
toDelete.remove(uidl);
}
public Collection<String> getQueued() {
List<String> rv = new ArrayList<String>(toDelete);
return rv;
}
public void cancel() {
isDead = true;
timer.cancel();
}
private class Checker extends SimpleTimer2.TimedEvent {
public Checker() {
super(I2PAppContext.getGlobalContext().simpleTimer2(), CHECK_TIME + 5*1000);
}
public void timeReached() {
if (isDead)
return;
if (!toDelete.isEmpty() && !isDeleting) {
long idle = System.currentTimeMillis() - mailbox.getLastActivity();
if (idle >= MIN_IDLE) {
Debug.debug(Debug.DEBUG, "Threading delayed delete for " + toDelete.size() +
" mails after " + idle + " ms idle");
Thread t = new Deleter();
isDeleting = true;
t.start();
} else {
Debug.debug(Debug.DEBUG, "Nothing to delete");
}
}
schedule(CHECK_TIME);
}
}
private class Deleter extends I2PAppThread {
public Deleter() {
super("Susimail-Delete");
}
public void run() {
try {
List<String> uidls = new ArrayList<String>(toDelete);
Collection<String> deleted = mailbox.delete(uidls);
Debug.debug(Debug.DEBUG, "Deleted " + deleted.size() + " of " + toDelete.size() + " mails");
toDelete.removeAll(deleted);
} finally {
isDeleting = false;
if (!isDead)
timer.schedule(CHECK_TIME);
}
}
}
}

View File

@@ -36,6 +36,8 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
@@ -63,8 +65,10 @@ public class POP3MailBox {
private final HashMap<String, Integer> uidlToID; private final HashMap<String, Integer> uidlToID;
private Socket socket; private Socket socket;
private final AtomicLong lastActive;
private final Object synchronizer; private final Object synchronizer;
private final DelayedDeleter delayedDeleter;
/** /**
* Does not connect. Caller must call connectToServer() if desired. * Does not connect. Caller must call connectToServer() if desired.
@@ -87,6 +91,8 @@ public class POP3MailBox {
synchronizer = new Object(); synchronizer = new Object();
// 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());
delayedDeleter = new DelayedDeleter(this);
} }
/** /**
@@ -232,10 +238,12 @@ public class POP3MailBox {
/** /**
* Call performDelete() after this or they will come back * Call performDelete() after this or they will come back
* UNUSED
* *
* @param uidl * @param uidl
* @return Success of delete operation: true if successful. * @return Success of delete operation: true if successful.
*/ */
/****
public boolean delete( String uidl ) public boolean delete( String uidl )
{ {
Debug.debug(Debug.DEBUG, "delete(" + uidl + ")"); Debug.debug(Debug.DEBUG, "delete(" + uidl + ")");
@@ -253,15 +261,28 @@ public class POP3MailBox {
return delete(id); return delete(id);
} }
} }
****/
/** /**
* Delete all at once, close and reconnect * Queue for later deletion. Non-blocking.
* Do NOT call performDelete() after this
* Does not provide any success/failure result
* *
* @since 0.9.13 * @since 0.9.13
*/ */
public void delete(Collection<String> uidls) { public void queueForDeletion(Collection<String> uidls) {
for (String uidl : uidls) {
delayedDeleter.queueDelete(uidl);
}
}
/**
* Delete all at once and close. Does not reconnect.
* Do NOT call performDelete() after this.
* Returns all UIDLs successfully deleted OR were not known by the server.
*
* @since 0.9.13
*/
Collection<String> delete(Collection<String> uidls) {
List<String> rv = new ArrayList<String>(uidls.size());
List<SendRecv> srs = new ArrayList<SendRecv>(uidls.size() + 1); List<SendRecv> srs = new ArrayList<SendRecv>(uidls.size() + 1);
synchronized( synchronizer ) { synchronized( synchronizer ) {
try { try {
@@ -269,26 +290,41 @@ public class POP3MailBox {
checkConnection(); checkConnection();
} catch (IOException ioe) { } catch (IOException ioe) {
Debug.debug( Debug.DEBUG, "Error deleting: " + ioe); Debug.debug( Debug.DEBUG, "Error deleting: " + ioe);
return; return rv;
} }
for (String uidl : uidls) { for (String uidl : uidls) {
int id = getIDfromUIDL(uidl); int id = getIDfromUIDL(uidl);
if (id < 0) if (id < 0) {
// presumed already deleted
rv.add(uidl);
continue; continue;
}
SendRecv sr = new SendRecv("DELE " + id, Mode.A1); SendRecv sr = new SendRecv("DELE " + id, Mode.A1);
sr.savedObject = uidl;
srs.add(sr); srs.add(sr);
} }
if (srs.isEmpty()) if (srs.isEmpty())
return; return rv;
// TODO don't quit now, just set timer to quit later // TODO don't quit now, just set timer to quit later
SendRecv sr = new SendRecv("QUIT", Mode.A1); SendRecv quit = new SendRecv("QUIT", Mode.A1);
srs.add(sr); srs.add(quit);
try { try {
sendCmds(srs); sendCmds(srs);
// do NOT call close() here, we included QUIT above
try { try {
socket.close(); socket.close();
} catch (IOException e) {} } catch (IOException e) {}
clear(); clear();
// result of QUIT
boolean success = srs.get(srs.size() - 1).result;
if (success) {
for (int i = 0; i < srs.size() - 1; i++) {
SendRecv sr = srs.get(i);
// ignore sr.result, if it failed it's because
// it's already deleted
rv.add((String) sr.savedObject);
}
}
// why reconnect? // why reconnect?
//connect(); //connect();
} catch (IOException ioe) { } catch (IOException ioe) {
@@ -296,14 +332,17 @@ public class POP3MailBox {
// todo maybe // todo maybe
} }
} }
return rv;
} }
/** /**
* delete message on pop3 server * delete message on pop3 server
* UNUSED
* *
* @param id message id * @param id message id
* @return Success of delete operation: true if successful. * @return Success of delete operation: true if successful.
*/ */
/****
private boolean delete(int id) private boolean delete(int id)
{ {
Debug.debug(Debug.DEBUG, "delete(" + id + ")"); Debug.debug(Debug.DEBUG, "delete(" + id + ")");
@@ -320,6 +359,7 @@ public class POP3MailBox {
} }
return result; return result;
} }
****/
/** /**
* Get cached size of a message (via previous LIST command). * Get cached size of a message (via previous LIST command).
@@ -387,6 +427,24 @@ public class POP3MailBox {
} }
} }
/**
* Timestamp.
*
* @since 0.9.13
*/
private void updateActivity() {
lastActive.set(System.currentTimeMillis());
}
/**
* Timestamp.
*
* @since 0.9.13
*/
long getLastActivity() {
return lastActive.get();
}
/** /**
* *
* @param response line starting with +OK * @param response line starting with +OK
@@ -628,6 +686,7 @@ public class POP3MailBox {
sendCmd1aNoWait(cmd); sendCmd1aNoWait(cmd);
socket.getOutputStream().flush(); socket.getOutputStream().flush();
String foo = DataHelper.readLine(socket.getInputStream()); String foo = DataHelper.readLine(socket.getInputStream());
updateActivity();
// Debug.debug(Debug.DEBUG, "sendCmd1a: read " + read + " bytes"); // Debug.debug(Debug.DEBUG, "sendCmd1a: read " + read + " bytes");
if (foo != null) { if (foo != null) {
lastLine = foo; lastLine = foo;
@@ -684,6 +743,7 @@ public class POP3MailBox {
} }
} }
String foo = DataHelper.readLine(in); String foo = DataHelper.readLine(in);
updateActivity();
if (foo == null) { if (foo == null) {
lastError = _("No response from server"); lastError = _("No response from server");
throw new IOException(lastError); throw new IOException(lastError);
@@ -747,6 +807,7 @@ public class POP3MailBox {
Debug.debug(Debug.DEBUG, "sendCmd1a(" + msg + ")"); Debug.debug(Debug.DEBUG, "sendCmd1a(" + msg + ")");
cmd += "\r\n"; cmd += "\r\n";
socket.getOutputStream().write(DataHelper.getASCII(cmd)); socket.getOutputStream().write(DataHelper.getASCII(cmd));
updateActivity();
} }
/** /**
@@ -829,6 +890,7 @@ public class POP3MailBox {
StringBuilder buf = new StringBuilder(512); StringBuilder buf = new StringBuilder(512);
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096); ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
while (DataHelper.readLine(input, buf)) { while (DataHelper.readLine(input, buf)) {
updateActivity();
int len = buf.length(); int len = buf.length();
if (len == 0) if (len == 0)
break; // huh? no \r? break; // huh? no \r?
@@ -867,6 +929,7 @@ public class POP3MailBox {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
StringBuilder buf = new StringBuilder(512); StringBuilder buf = new StringBuilder(512);
while (DataHelper.readLine(input, buf)) { while (DataHelper.readLine(input, buf)) {
updateActivity();
int len = buf.length(); int len = buf.length();
if (len == 0) if (len == 0)
break; // huh? no \r? break; // huh? no \r?
@@ -918,14 +981,25 @@ public class POP3MailBox {
} }
/** /**
* Close without waiting for response * Close without waiting for response,
* and remove any delayed tasks and resources.
*/
public void destroy() {
delayedDeleter.cancel();
close(false);
}
/**
* Close without waiting for response.
* Deletes all queued deletions.
*/ */
public void close() { public void close() {
close(false); close(false);
} }
/** /**
* Close and optionally waiting for response * Close and optionally wait for response.
* Deletes all queued deletions.
* @since 0.9.13 * @since 0.9.13
*/ */
private void close(boolean shouldWait) { private void close(boolean shouldWait) {
@@ -933,10 +1007,34 @@ public class POP3MailBox {
Debug.debug(Debug.DEBUG, "close()"); Debug.debug(Debug.DEBUG, "close()");
if (socket != null && socket.isConnected()) { if (socket != null && socket.isConnected()) {
try { try {
if (shouldWait) Collection<String> toDelete = delayedDeleter.getQueued();
sendCmd1a("QUIT"); Map<String, Integer> sendDelete = new HashMap<String, Integer>(toDelete.size());
else for (String uidl : toDelete) {
int id = getIDfromUIDL(uidl);
if (id >= 0) {
sendDelete.put(uidl, Integer.valueOf(id));
}
}
if (shouldWait) {
if (!sendDelete.isEmpty()) {
// Verify deleted, remove from the delete queue
// this does the quit and close
Collection<String> deleted = delete(sendDelete.keySet());
for (String uidl : deleted) {
delayedDeleter.removeQueued(uidl);
}
} else {
sendCmd1a("QUIT");
}
} else {
if (!sendDelete.isEmpty()) {
// spray and pray the deletions, don't remove from delete queue
for (Integer id : sendDelete.values()) {
sendCmd1aNoWait("DELE " + id);
}
}
sendCmd1aNoWait("QUIT"); sendCmd1aNoWait("QUIT");
}
socket.close(); socket.close();
} catch (IOException e) {} } catch (IOException e) {}
} }
@@ -1012,7 +1110,9 @@ public class POP3MailBox {
/** /**
* Close and reconnect. Takes a while. * Close and reconnect. Takes a while.
* UNUSED
*/ */
/****
public void performDelete() public void performDelete()
{ {
synchronized( synchronizer ) { synchronized( synchronizer ) {
@@ -1021,7 +1121,9 @@ public class POP3MailBox {
//connect(); //connect();
} }
} }
****/
/** for SendRecv */
private enum Mode { private enum Mode {
/** no extra lines (sendCmd1a) */ /** no extra lines (sendCmd1a) */
A1, A1,

View File

@@ -1,3 +1,8 @@
2014-04-23 zzz
* SusiMail:
- Queue deletions for a later thread
- Synch all folder access
2014-04-22 zzz 2014-04-22 zzz
* SusiMail: * SusiMail:
- Add persistent cache - Add persistent cache

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 = 8; public final static long BUILD = 9;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";