1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-27 03:32:16 -05:00

Improve send failure handling

We now no longer parse the exception message in MessagingController to find
out if it was a permanent SMTP failure.
Overall the code is still a mess, but the error handling should be a little
bit better and more readable now.
This commit is contained in:
cketti 2015-06-22 00:37:48 +02:00
parent ee7a95b750
commit 9d44f0e062
2 changed files with 63 additions and 36 deletions

View File

@ -491,7 +491,6 @@ public class SmtpTransport extends Transport {
private void sendMessageTo(List<String> addresses, Message message) private void sendMessageTo(List<String> addresses, Message message)
throws MessagingException { throws MessagingException {
boolean possibleSend = false;
close(); close();
open(); open();
@ -503,13 +502,11 @@ public class SmtpTransport extends Transport {
// the size of messages, count the message's size before sending it // the size of messages, count the message's size before sending it
if (mLargestAcceptableMessage > 0 && message.hasAttachments()) { if (mLargestAcceptableMessage > 0 && message.hasAttachments()) {
if (message.calculateSize() > mLargestAcceptableMessage) { if (message.calculateSize() > mLargestAcceptableMessage) {
MessagingException me = new MessagingException("Message too large for server"); throw new MessagingException("Message too large for server", true);
//TODO this looks rather suspicious... shouldn't it be true?
me.setPermanentFailure(possibleSend);
throw me;
} }
} }
boolean entireMessageSent = false;
Address[] from = message.getFrom(); Address[] from = message.getFrom();
try { try {
executeSimpleCommand("MAIL FROM:" + "<" + from[0].getAddress() + ">" executeSimpleCommand("MAIL FROM:" + "<" + from[0].getAddress() + ">"
@ -527,20 +524,14 @@ public class SmtpTransport extends Transport {
// We use BufferedOutputStream. So make sure to call flush() ! // We use BufferedOutputStream. So make sure to call flush() !
msgOut.flush(); msgOut.flush();
possibleSend = true; // After the "\r\n." is attempted, we may have sent the message entireMessageSent = true; // After the "\r\n." is attempted, we may have sent the message
executeSimpleCommand("\r\n."); executeSimpleCommand("\r\n.");
} catch (NegativeSmtpReplyException e) {
throw e;
} catch (Exception e) { } catch (Exception e) {
MessagingException me = new MessagingException("Unable to send message", e); MessagingException me = new MessagingException("Unable to send message", e);
me.setPermanentFailure(entireMessageSent);
// "5xx text" -responses are permanent failures
String msg = e.getMessage();
if (msg != null && msg.startsWith("5")) {
Log.w(LOG_TAG, "handling 5xx SMTP error code as a permanent failure");
possibleSend = false;
}
//TODO this looks rather suspicious... why is possibleSend used, and why are 5xx NOT permanent (in contrast to the log text)
me.setPermanentFailure(possibleSend);
throw me; throw me;
} finally { } finally {
close(); close();
@ -775,11 +766,15 @@ public class SmtpTransport extends Transport {
private final String mReplyText; private final String mReplyText;
public NegativeSmtpReplyException(int replyCode, String replyText) { public NegativeSmtpReplyException(int replyCode, String replyText) {
super("Negative SMTP reply: " + replyCode + " " + replyText); super("Negative SMTP reply: " + replyCode + " " + replyText, isPermanentSmtpError(replyCode));
mReplyCode = replyCode; mReplyCode = replyCode;
mReplyText = replyText; mReplyText = replyText;
} }
private static boolean isPermanentSmtpError(int replyCode) {
return replyCode >= 500 && replyCode <= 599;
}
public int getReplyCode() { public int getReplyCode() {
return mReplyCode; return mReplyCode;
} }

View File

@ -3367,6 +3367,7 @@ public class MessagingController implements Runnable {
public void sendPendingMessagesSynchronous(final Account account) { public void sendPendingMessagesSynchronous(final Account account) {
Folder localFolder = null; Folder localFolder = null;
Exception lastFailure = null; Exception lastFailure = null;
boolean wasPermanentFailure = false;
try { try {
Store localStore = account.getLocalStore(); Store localStore = account.getLocalStore();
localFolder = localStore.getFolder( localFolder = localStore.getFolder(
@ -3463,38 +3464,40 @@ public class MessagingController implements Runnable {
processPendingCommands(account); processPendingCommands(account);
} }
} catch (Exception e) { } catch (CertificateValidationException e) {
// 5.x.x errors from the SMTP server are "PERMFAIL" lastFailure = e;
// move the message over to drafts rather than leaving it in the outbox wasPermanentFailure = false;
// This is a complete hack, but is worlds better than the previous
// "don't even bother" functionality
if (getRootCauseMessage(e).startsWith("5")) {
localFolder.moveMessages(Collections.singletonList(message), (LocalFolder) localStore.getFolder(account.getDraftsFolderName()));
}
notifyUserIfCertificateProblem(context, e, account, false); notifyUserIfCertificateProblem(context, e, account, false);
addErrorMessage(account, "Failed to send message", e); handleSendFailure(account, localStore, localFolder, message, e, wasPermanentFailure);
message.setFlag(Flag.X_SEND_FAILED, true); } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Failed to send message", e);
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxFailed(account, localFolder.getName(), getRootCauseMessage(e));
}
lastFailure = e; lastFailure = e;
wasPermanentFailure = e.isPermanentFailure();
handleSendFailure(account, localStore, localFolder, message, e, wasPermanentFailure);
} catch (Exception e) {
lastFailure = e;
wasPermanentFailure = true;
handleSendFailure(account, localStore, localFolder, message, e, wasPermanentFailure);
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(K9.LOG_TAG, "Failed to fetch message for sending", e);
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxFailed(account, localFolder.getName(), getRootCauseMessage(e));
}
addErrorMessage(account, "Failed to fetch message for sending", e);
lastFailure = e; lastFailure = e;
wasPermanentFailure = false;
Log.e(K9.LOG_TAG, "Failed to fetch message for sending", e);
addErrorMessage(account, "Failed to fetch message for sending", e);
notifySynchronizeMailboxFailed(account, localFolder, e);
} }
} }
for (MessagingListener l : getListeners()) { for (MessagingListener l : getListeners()) {
l.sendPendingMessagesCompleted(account); l.sendPendingMessagesCompleted(account);
} }
if (lastFailure != null) { if (lastFailure != null) {
if (getRootCauseMessage(lastFailure).startsWith("5")) { if (wasPermanentFailure) {
notifySendPermFailed(account, lastFailure); notifySendPermFailed(account, lastFailure);
} else { } else {
notifySendTempFailed(account, lastFailure); notifySendTempFailed(account, lastFailure);
@ -3517,6 +3520,35 @@ public class MessagingController implements Runnable {
} }
} }
private void handleSendFailure(Account account, Store localStore, Folder localFolder, Message message,
Exception exception, boolean permanentFailure) throws MessagingException {
Log.e(K9.LOG_TAG, "Failed to send message", exception);
if (permanentFailure) {
moveMessageToDraftsFolder(account, localFolder, localStore, message);
}
addErrorMessage(account, "Failed to send message", exception);
message.setFlag(Flag.X_SEND_FAILED, true);
notifySynchronizeMailboxFailed(account, localFolder, exception);
}
private void moveMessageToDraftsFolder(Account account, Folder localFolder, Store localStore, Message message)
throws MessagingException {
LocalFolder draftsFolder = (LocalFolder) localStore.getFolder(account.getDraftsFolderName());
localFolder.moveMessages(Collections.singletonList(message), draftsFolder);
}
private void notifySynchronizeMailboxFailed(Account account, Folder localFolder, Exception exception) {
String folderName = localFolder.getName();
String errorMessage = getRootCauseMessage(exception);
for (MessagingListener listener : getListeners()) {
listener.synchronizeMailboxFailed(account, folderName, errorMessage);
}
}
public void getAccountStats(final Context context, final Account account, public void getAccountStats(final Context context, final Account account,
final MessagingListener listener) { final MessagingListener listener) {