package mireka.smtp; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import mireka.transmission.immediate.Rfc821Status; import mireka.util.Multiline; /** * These class represents an SMTP status which includes enhanced status code. * * @see <a href="http://tools.ietf.org/html/rfc3463">RFC 3463 - Enhanced Mail * System Status Codes</a> * @see <a href="http://tools.ietf.org/html/rfc2034">RFC 2034 - SMTP Service * Extension for Returning Enhanced Error Codes</a> * @see <a * href="http://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xml">IANA * Enhanced Status Code Registry</a> */ public class EnhancedStatus implements MailSystemStatus { public static final EnhancedStatus TRANSIENT_SYSTEM_NOT_ACCEPTING_NETWORK_MESSAGES = new EnhancedStatus(421, "4.3.2", "System not accepting network messages"); public static final EnhancedStatus TRANSIENT_DIRECTORY_SERVER_FAILURE = new EnhancedStatus(450, "4.4.3", "Directory server failure"); public static final EnhancedStatus BAD_DESTINATION_SYSTEM_ADDRESS = new EnhancedStatus(550, "5.1.2", "Bad destination system address"); public static final EnhancedStatus PERMANENT_UNABLE_TO_ROUTE = new EnhancedStatus(550, "5.4.4", "Unable to route"); public static final EnhancedStatus TRANSIENT_LOCAL_ERROR_IN_PROCESSING = new EnhancedStatus(451, "4.3.0", "Local error in processing"); public static final EnhancedStatus MAIL_SYSTEM_FULL = new EnhancedStatus( 452, "4.3.1", "Mail system full"); public static final EnhancedStatus BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX = new EnhancedStatus(553, "5.1.3", "Bad destination mailbox address syntax"); public static final EnhancedStatus PERMANENT_INTERNAL_ERROR = new EnhancedStatus(554, "5.3.0", "Internal error"); public static final EnhancedStatus BAD_MESSAGE_BODY = new EnhancedStatus( 554, "5.6.0", "Message body is invalid"); public static final EnhancedStatus INCORRECT_CONFIGURATION = new EnhancedStatus(554, "5.3.5", "System incorrectly configured"); private final int smtpReplyCode; private final String enhancedStatusCode; private final String message; public EnhancedStatus(int smtpReplyCode, String enhancedStatusCode, String message) { if (smtpReplyCode <= 0 || enhancedStatusCode == null || message == null) throw new IllegalArgumentException(); this.smtpReplyCode = smtpReplyCode; this.enhancedStatusCode = enhancedStatusCode; this.message = message; } public EnhancedStatus(Rfc821Status response) { this.smtpReplyCode = response.getSmtpReplyCode(); this.message = response.getMessage(); this.enhancedStatusCode = approximateEnhancedCodeFromSmtpReplyCode(); } /** * Returns an enhanced status code which roughly correspond to * {@link #smtpReplyCode}. */ private String approximateEnhancedCodeFromSmtpReplyCode() { if (200 <= smtpReplyCode && smtpReplyCode <= 299) return "2.0.0"; else if (400 <= smtpReplyCode && smtpReplyCode <= 499) return "4.0.0"; else if (500 <= smtpReplyCode && smtpReplyCode <= 599) return "5.0.0"; else throw new RuntimeException("Unexpected: " + Integer.toString(smtpReplyCode)); } @Override public int getSmtpReplyCode() { return smtpReplyCode; } public String getEnhancedStatusCode() { return enhancedStatusCode; } @Override public String getMessage() { return message; } /** * it returns true, if repeating the action may help */ public boolean shouldRetry() { switch (getStatusClass()) { case TransientFailure: return true; case PermanentFailure: return false; default: throw new RuntimeException(getStatusClass().toString()); } } private StatusClass getStatusClass() { if (smtpReplyCode >= 200 && smtpReplyCode <= 299) return StatusClass.Success; else if (smtpReplyCode >= 400 && smtpReplyCode <= 499) return StatusClass.TransientFailure; else if (smtpReplyCode >= 500 && smtpReplyCode <= 599) return StatusClass.PermanentFailure; else throw new RuntimeException("Unexpected: " + Integer.toString(smtpReplyCode)); } public String getMessagePrefixedWithEnhancedStatusCode() { try { if (message.isEmpty()) return enhancedStatusCode; BufferedReader reader = new BufferedReader(new StringReader(message)); String line; StringBuilder buffer = new StringBuilder(); boolean firstLine = true; while (null != (line = reader.readLine())) { if (!firstLine) buffer.append("\r\n"); firstLine = false; buffer.append(enhancedStatusCode); buffer.append(' '); buffer.append(line); } return buffer.toString(); } catch (IOException e) { throw new RuntimeException(); // impossible } } @Override public String getDiagnosticCode() { return Multiline.prependStatusCodeToMessage(smtpReplyCode, getMessagePrefixedWithEnhancedStatusCode()); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((enhancedStatusCode == null) ? 0 : enhancedStatusCode.hashCode()); result = prime * result + ((message == null) ? 0 : message.hashCode()); result = prime * result + smtpReplyCode; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; EnhancedStatus other = (EnhancedStatus) obj; if (enhancedStatusCode == null) { if (other.enhancedStatusCode != null) return false; } else if (!enhancedStatusCode.equals(other.enhancedStatusCode)) return false; if (message == null) { if (other.message != null) return false; } else if (!message.equals(other.message)) return false; if (smtpReplyCode != other.smtpReplyCode) return false; return true; } @Override public String toString() { return smtpReplyCode + " " + enhancedStatusCode + " " + message; } public static enum StatusClass { Success(1), TransientFailure(4), PermanentFailure(5); private final int code; StatusClass(int code) { this.code = code; } public int code() { return code; } } }