mirror of https://github.com/moparisthebest/k-9
Issue 234 Issue 366 Issue 329
Hardening of POP3 and SMTP communication: SMTP: Decrement failure counter when no possible message send occurred. This way, K-9 will only stop attempting to send messages for which a possible successful send occurred, but K-9 could not detect. Any message that is known to have completely failed to send will be reattempted indefinitely. POP3: Some reworking of Exception handling. Also, if it is not possible to get a "message number" for the UID of the message that is being deleted, conclude that the message has already been deleted on the server. Mark this as a permanent error, so that it gets removed from the pending actions queue. MessagingController: Look for the permanentFailure flag on the MessagingException, and if a pending action raised a permanent failure, remove the pending action from the queue so that it will not be re-attempted nor block later requests.
This commit is contained in:
parent
388969d76c
commit
5711abdee5
|
@ -1229,6 +1229,8 @@ s * critical data as fast as possible, and then we'll fill in the de
|
||||||
* most likely due to a server or IO error and it must be retried before any
|
* most likely due to a server or IO error and it must be retried before any
|
||||||
* other command processes. This maintains the order of the commands.
|
* other command processes. This maintains the order of the commands.
|
||||||
*/
|
*/
|
||||||
|
try
|
||||||
|
{
|
||||||
if (PENDING_COMMAND_APPEND.equals(command.command)) {
|
if (PENDING_COMMAND_APPEND.equals(command.command)) {
|
||||||
processPendingAppend(command, account);
|
processPendingAppend(command, account);
|
||||||
}
|
}
|
||||||
|
@ -1246,13 +1248,24 @@ s * critical data as fast as possible, and then we'll fill in the de
|
||||||
}
|
}
|
||||||
localStore.removePendingCommand(command);
|
localStore.removePendingCommand(command);
|
||||||
Log.d(Email.LOG_TAG, "Done processing pending command '" + command + "'");
|
Log.d(Email.LOG_TAG, "Done processing pending command '" + command + "'");
|
||||||
|
}
|
||||||
|
catch (MessagingException me)
|
||||||
|
{
|
||||||
|
if (me.isPermanentFailure())
|
||||||
|
{
|
||||||
|
Log.e(Email.LOG_TAG, "Failure of command '" + command + "' was permanent, removing command from queue");
|
||||||
|
localStore.removePendingCommand(processingCommand);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw me;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (MessagingException me)
|
catch (MessagingException me)
|
||||||
{
|
{
|
||||||
addErrorMessage(account, me);
|
addErrorMessage(account, me);
|
||||||
|
|
||||||
Log.e(Email.LOG_TAG, "Could not process command '" + processingCommand + "'", me);
|
Log.e(Email.LOG_TAG, "Could not process command '" + processingCommand + "'", me);
|
||||||
throw me;
|
throw me;
|
||||||
}
|
}
|
||||||
|
@ -2122,6 +2135,17 @@ s * critical data as fast as possible, and then we'll fill in the de
|
||||||
processPendingCommands(account);
|
processPendingCommands(account);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
if (e instanceof MessagingException)
|
||||||
|
{
|
||||||
|
MessagingException me = (MessagingException)e;
|
||||||
|
if (me.isPermanentFailure() == false)
|
||||||
|
{
|
||||||
|
// Decrement the counter if the message could not possibly have been sent
|
||||||
|
int newVal = count.decrementAndGet();
|
||||||
|
Log.i(Email.LOG_TAG, "Decremented send count for message " + message.getUid() + " to " + newVal
|
||||||
|
+ "; no possible send");
|
||||||
|
}
|
||||||
|
}
|
||||||
message.setFlag(Flag.X_SEND_FAILED, true);
|
message.setFlag(Flag.X_SEND_FAILED, true);
|
||||||
Log.e(Email.LOG_TAG, "Failed to send message", e);
|
Log.e(Email.LOG_TAG, "Failed to send message", e);
|
||||||
for (MessagingListener l : getListeners()) {
|
for (MessagingListener l : getListeners()) {
|
||||||
|
|
|
@ -4,6 +4,8 @@ package com.android.email.mail;
|
||||||
public class MessagingException extends Exception {
|
public class MessagingException extends Exception {
|
||||||
public static final long serialVersionUID = -1;
|
public static final long serialVersionUID = -1;
|
||||||
|
|
||||||
|
boolean permanentFailure = false;
|
||||||
|
|
||||||
public MessagingException(String message) {
|
public MessagingException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
@ -11,4 +13,16 @@ public class MessagingException extends Exception {
|
||||||
public MessagingException(String message, Throwable throwable) {
|
public MessagingException(String message, Throwable throwable) {
|
||||||
super(message, throwable);
|
super(message, throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPermanentFailure()
|
||||||
|
{
|
||||||
|
return permanentFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPermanentFailure(boolean permanentFailure)
|
||||||
|
{
|
||||||
|
this.permanentFailure = permanentFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,16 +158,13 @@ public class Pop3Store extends Store {
|
||||||
* Run an additional test to see if UIDL is supported on the server. If it's not we
|
* Run an additional test to see if UIDL is supported on the server. If it's not we
|
||||||
* can't service this account.
|
* can't service this account.
|
||||||
*/
|
*/
|
||||||
try{
|
|
||||||
/*
|
/*
|
||||||
* If the server doesn't support UIDL it will return a - response, which causes
|
* If the server doesn't support UIDL it will return a - response, which causes
|
||||||
* executeSimpleCommand to throw a MessagingException, exiting this method.
|
* executeSimpleCommand to throw a MessagingException, exiting this method.
|
||||||
*/
|
*/
|
||||||
folder.executeSimpleCommand("UIDL");
|
folder.executeSimpleCommand("UIDL");
|
||||||
}
|
|
||||||
catch (IOException ioe) {
|
|
||||||
throw new MessagingException(null, ioe);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
folder.close(false);
|
folder.close(false);
|
||||||
}
|
}
|
||||||
|
@ -262,14 +259,10 @@ public class Pop3Store extends Store {
|
||||||
throw new MessagingException("Unable to open connection to POP server.", ioe);
|
throw new MessagingException("Unable to open connection to POP server.", ioe);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
String response = executeSimpleCommand("STAT");
|
String response = executeSimpleCommand("STAT");
|
||||||
String[] parts = response.split(" ");
|
String[] parts = response.split(" ");
|
||||||
mMessageCount = Integer.parseInt(parts[1]);
|
mMessageCount = Integer.parseInt(parts[1]);
|
||||||
}
|
|
||||||
catch (IOException ioe) {
|
|
||||||
throw new MessagingException("Unable to STAT folder.", ioe);
|
|
||||||
}
|
|
||||||
mUidToMsgMap.clear();
|
mUidToMsgMap.clear();
|
||||||
mMsgNumToMsgMap.clear();
|
mMsgNumToMsgMap.clear();
|
||||||
mUidToMsgNumMap.clear();
|
mUidToMsgNumMap.clear();
|
||||||
|
@ -448,6 +441,9 @@ public class Pop3Store extends Store {
|
||||||
HashSet<String> unindexedUids = new HashSet<String>();
|
HashSet<String> unindexedUids = new HashSet<String>();
|
||||||
for (String uid : uids) {
|
for (String uid : uids) {
|
||||||
if (mUidToMsgMap.get(uid) == null) {
|
if (mUidToMsgMap.get(uid) == null) {
|
||||||
|
if (Config.LOGD) {
|
||||||
|
Log.d(Email.LOG_TAG, "Need to index UID " + uid);
|
||||||
|
}
|
||||||
unindexedUids.add(uid);
|
unindexedUids.add(uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -468,6 +464,10 @@ public class Pop3Store extends Store {
|
||||||
Integer msgNum = Integer.valueOf(uidParts[0]);
|
Integer msgNum = Integer.valueOf(uidParts[0]);
|
||||||
String msgUid = uidParts[1];
|
String msgUid = uidParts[1];
|
||||||
if (unindexedUids.contains(msgUid)) {
|
if (unindexedUids.contains(msgUid)) {
|
||||||
|
if (Config.LOGD) {
|
||||||
|
Log.d(Email.LOG_TAG, "Got msgNum " + msgNum + " for UID " + msgUid);
|
||||||
|
}
|
||||||
|
|
||||||
Pop3Message message = mUidToMsgMap.get(msgUid);
|
Pop3Message message = mUidToMsgMap.get(msgUid);
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
message = new Pop3Message(msgUid, this);
|
message = new Pop3Message(msgUid, this);
|
||||||
|
@ -478,6 +478,9 @@ public class Pop3Store extends Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void indexMessage(int msgNum, Pop3Message message) {
|
private void indexMessage(int msgNum, Pop3Message message) {
|
||||||
|
if (Config.LOGD){
|
||||||
|
Log.d(Email.LOG_TAG, "Adding index for UID " + message.getUid() + " to msgNum " + msgNum);
|
||||||
|
}
|
||||||
mMsgNumToMsgMap.put(msgNum, message);
|
mMsgNumToMsgMap.put(msgNum, message);
|
||||||
mUidToMsgMap.put(message.getUid(), message);
|
mUidToMsgMap.put(message.getUid(), message);
|
||||||
mUidToMsgNumMap.put(message.getUid(), msgNum);
|
mUidToMsgNumMap.put(message.getUid(), msgNum);
|
||||||
|
@ -721,20 +724,18 @@ public class Pop3Store extends Store {
|
||||||
{
|
{
|
||||||
throw new MessagingException("Could not get message number for uid " + uids, ioe);
|
throw new MessagingException("Could not get message number for uid " + uids, ioe);
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
for (Message message : messages) {
|
for (Message message : messages) {
|
||||||
executeSimpleCommand(String.format("DELE %s",
|
|
||||||
mUidToMsgNumMap.get(message.getUid())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException ioe) {
|
|
||||||
throw new MessagingException("setFlags()", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Integer msgNum = mUidToMsgNumMap.get(message.getUid());
|
||||||
public void copyMessages(Message[] msgs, Folder folder) throws MessagingException {
|
if (msgNum == null)
|
||||||
throw new UnsupportedOperationException("copyMessages is not supported in POP3");
|
{
|
||||||
|
MessagingException me = new MessagingException("Could not delete message " + message.getUid()
|
||||||
|
+ " because no msgNum found; permanent error");
|
||||||
|
me.setPermanentFailure(true);
|
||||||
|
throw me;
|
||||||
|
}
|
||||||
|
executeSimpleCommand(String.format("DELE %s", msgNum));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// private boolean isRoundTripModeSuggested() {
|
// private boolean isRoundTripModeSuggested() {
|
||||||
|
@ -814,7 +815,7 @@ public class Pop3Store extends Store {
|
||||||
return capabilities;
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String executeSimpleCommand(String command) throws IOException, MessagingException {
|
private String executeSimpleCommand(String command) throws MessagingException {
|
||||||
try {
|
try {
|
||||||
open(OpenMode.READ_WRITE);
|
open(OpenMode.READ_WRITE);
|
||||||
if (Config.LOGV)
|
if (Config.LOGV)
|
||||||
|
@ -837,9 +838,9 @@ public class Pop3Store extends Store {
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (Exception e) {
|
||||||
closeIO();
|
closeIO();
|
||||||
throw e;
|
throw new MessagingException("Unable to execute POP3 command", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -228,7 +228,7 @@ public class SmtpTransport extends Transport {
|
||||||
close();
|
close();
|
||||||
open();
|
open();
|
||||||
Address[] from = message.getFrom();
|
Address[] from = message.getFrom();
|
||||||
|
boolean possibleSend = false;
|
||||||
try {
|
try {
|
||||||
executeSimpleCommand("MAIL FROM: " + "<" + from[0].getAddress() + ">");
|
executeSimpleCommand("MAIL FROM: " + "<" + from[0].getAddress() + ">");
|
||||||
for (Address address : message.getRecipients(RecipientType.TO)) {
|
for (Address address : message.getRecipients(RecipientType.TO)) {
|
||||||
|
@ -246,14 +246,21 @@ public class SmtpTransport extends Transport {
|
||||||
message.writeTo(
|
message.writeTo(
|
||||||
new EOLConvertingOutputStream(
|
new EOLConvertingOutputStream(
|
||||||
new BufferedOutputStream(mOut, 1024)));
|
new BufferedOutputStream(mOut, 1024)));
|
||||||
|
|
||||||
|
possibleSend = true; // After the "\r\n." is attempted, we may have sent the message
|
||||||
executeSimpleCommand("\r\n.");
|
executeSimpleCommand("\r\n.");
|
||||||
} catch (IOException ioe) {
|
} catch (Exception e) {
|
||||||
throw new MessagingException("Unable to send message", ioe);
|
MessagingException me = new MessagingException("Unable to send message", e);
|
||||||
|
me.setPermanentFailure(possibleSend);
|
||||||
|
throw me;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
Loading…
Reference in New Issue