You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ro...@apache.org on 2018/01/10 10:14:37 UTC

[37/53] [abbrv] james-project git commit: JAMES-2277 respect naming conventions

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DnsHelper.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DnsHelper.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DnsHelper.java
new file mode 100644
index 0000000..92cb4e6
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/DnsHelper.java
@@ -0,0 +1,49 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.util.Iterator;
+
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.dnsservice.api.TemporaryResolutionException;
+import org.apache.james.dnsservice.library.MXHostAddressIterator;
+import org.apache.mailet.HostAddress;
+
+@SuppressWarnings("deprecation")
+public class DnsHelper {
+
+    public static final boolean USE_SEVERAL_IP = false;
+    private final DNSService dnsServer;
+    private final RemoteDeliveryConfiguration configuration;
+
+    public DnsHelper(DNSService dnsServer, RemoteDeliveryConfiguration configuration) {
+        this.dnsServer = dnsServer;
+        this.configuration = configuration;
+    }
+
+    public Iterator<HostAddress> retrieveHostAddressIterator(String host) throws TemporaryResolutionException {
+        if (configuration.getGatewayServer().isEmpty()) {
+            return new MXHostAddressIterator(dnsServer.findMXRecords(host).iterator(), dnsServer, USE_SEVERAL_IP);
+        } else {
+            return new MXHostAddressIterator(configuration.getGatewayServer().iterator(), dnsServer, USE_SEVERAL_IP);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/EnhancedMessagingException.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/EnhancedMessagingException.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/EnhancedMessagingException.java
new file mode 100644
index 0000000..06f8a61
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/EnhancedMessagingException.java
@@ -0,0 +1,167 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.mail.MessagingException;
+import javax.mail.internet.InternetAddress;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+import com.sun.mail.smtp.SMTPAddressFailedException;
+import com.sun.mail.smtp.SMTPSendFailedException;
+import com.sun.mail.smtp.SMTPSenderFailedException;
+
+public class EnhancedMessagingException {
+
+    private static final Logger logger = LoggerFactory.getLogger(EnhancedMessagingException.class);
+    
+    private final MessagingException messagingException;
+    private final Optional<Integer> returnCode;
+    private final Optional<Integer> nestedReturnCode;
+
+    public EnhancedMessagingException(MessagingException messagingException) {
+        this.messagingException = messagingException;
+        this.returnCode = computeReturnCode();
+        this.nestedReturnCode = computeNestedReturnCode();
+    }
+
+    public boolean hasReturnCode() {
+        return returnCode.isPresent();
+    }
+
+    public boolean hasNestedReturnCode() {
+        return nestedReturnCode.isPresent();
+    }
+
+    public boolean isServerError() {
+        return isServerError(returnCode) || isServerError(nestedReturnCode);
+    }
+
+    private boolean isServerError(Optional<Integer> returnCode) {
+        return (returnCode.isPresent()
+            && returnCode.get() >= 500
+            && returnCode.get() <= 599)
+            || messageIndicatesServerException();
+    }
+
+    private boolean messageIndicatesServerException() {
+        return Optional.ofNullable(messagingException.getMessage())
+            .map(startWith5())
+            .orElse(false);
+    }
+
+    private Function<String, Boolean> startWith5() {
+        return message -> message.startsWith("5");
+    }
+
+    private Optional<Integer> computeReturnCode() {
+        if (messagingException instanceof SMTPAddressFailedException) {
+            SMTPAddressFailedException addressFailedException = (SMTPAddressFailedException) this.messagingException;
+            return Optional.of(addressFailedException.getReturnCode());
+        }
+        if (messagingException instanceof SMTPSendFailedException) {
+            SMTPSendFailedException sendFailedException = (SMTPSendFailedException) this.messagingException;
+            return Optional.of(sendFailedException.getReturnCode());
+        }
+        if (messagingException instanceof SMTPSenderFailedException) {
+            SMTPSenderFailedException senderFailedException = (SMTPSenderFailedException) this.messagingException;
+            return Optional.of(senderFailedException.getReturnCode());
+        }
+        if (messagingException.getClass().getName().endsWith(".SMTPSendFailedException")
+            || messagingException.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
+            try {
+                return Optional.of((Integer)invokeGetter(messagingException, "getReturnCode"));
+            } catch (ClassCastException | IllegalArgumentException | IllegalStateException e) {
+                logger.error("unexpected exception", e);
+            }
+        }
+        return Optional.empty();
+    }
+
+    public Optional<String> computeCommand() {
+        if (hasReturnCode()) {
+            try {
+                return Optional.of((String) invokeGetter(messagingException, "getCommand"));
+            } catch (ClassCastException | IllegalArgumentException | IllegalStateException e) {
+                logger.error("unexpected exception", e);
+            }
+        }
+        return Optional.empty();
+    }
+
+    public Optional<InternetAddress> computeAddress() {
+        if (hasReturnCode()) {
+            try {
+                return Optional.of((InternetAddress) invokeGetter(messagingException, "getAddress"));
+            } catch (ClassCastException | IllegalArgumentException | IllegalStateException e) {
+                logger.error("unexpected exception", e);
+            }
+        }
+        return Optional.empty();
+    }
+
+    public String computeAction() {
+        return messagingException.getClass().getName().endsWith(".SMTPAddressFailedException") ? "FAILED" : "SUCCEEDED";
+    }
+
+    public Optional<Integer> getReturnCode() {
+        return returnCode;
+    }
+
+    private Optional<Integer> computeNestedReturnCode() {
+        EnhancedMessagingException currentMessagingException = this;
+        while (true) {
+            Optional<Integer> returnCode = currentMessagingException.computeReturnCode();
+            if (returnCode.isPresent()) {
+                return returnCode;
+            }
+            if (currentMessagingException.hasNestedMessagingException()) {
+                currentMessagingException = currentMessagingException.getNestedMessagingException();
+            } else {
+                return Optional.empty();
+            }
+        }
+    }
+
+    private boolean hasNestedMessagingException() {
+        return messagingException.getNextException() != null
+            && messagingException.getNextException() instanceof MessagingException;
+    }
+
+    private EnhancedMessagingException getNestedMessagingException() {
+        Preconditions.checkState(hasNestedMessagingException());
+        return new EnhancedMessagingException((MessagingException) messagingException.getNextException());
+    }
+
+    private Object invokeGetter(Object target, String getter) {
+        try {
+            Method getAddress = target.getClass().getMethod(getter);
+            return getAddress.invoke(target);
+        } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            return new IllegalStateException("Exception invoking " + getter + " on a " + target.getClass() + " object");            
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/ExecutionResult.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/ExecutionResult.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/ExecutionResult.java
new file mode 100644
index 0000000..3dae211
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/ExecutionResult.java
@@ -0,0 +1,93 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.util.Optional;
+
+import com.google.common.base.Objects;
+
+public class ExecutionResult {
+
+    public enum ExecutionState {
+        SUCCESS,
+        PERMANENT_FAILURE,
+        TEMPORARY_FAILURE
+    }
+
+    public static ExecutionResult success() {
+        return new ExecutionResult(ExecutionState.SUCCESS, Optional.empty());
+    }
+
+    public static ExecutionResult temporaryFailure(Exception e) {
+        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.of(e));
+    }
+
+    public static ExecutionResult permanentFailure(Exception e) {
+        return new ExecutionResult(ExecutionState.PERMANENT_FAILURE, Optional.of(e));
+    }
+
+    public static ExecutionResult temporaryFailure() {
+        return new ExecutionResult(ExecutionState.TEMPORARY_FAILURE, Optional.empty());
+    }
+
+    public static ExecutionResult onFailure(boolean permanent, Exception exeption) {
+        if (permanent) {
+            return permanentFailure(exeption);
+        } else {
+            return temporaryFailure(exeption);
+        }
+    }
+
+    private final ExecutionState executionState;
+    private final Optional<Exception> exception;
+
+    public ExecutionResult(ExecutionState executionState, Optional<Exception> exception) {
+        this.executionState = executionState;
+        this.exception = exception;
+    }
+
+    public ExecutionState getExecutionState() {
+        return executionState;
+    }
+
+    public Optional<Exception> getException() {
+        return exception;
+    }
+
+    public boolean isPermanent() {
+        return executionState == ExecutionState.PERMANENT_FAILURE;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof ExecutionResult) {
+            ExecutionResult that = (ExecutionResult) o;
+            return Objects.equal(this.executionState, that.executionState)
+                && Objects.equal(this.exception, that.exception);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(executionState, exception);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/HeloNameProvider.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/HeloNameProvider.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/HeloNameProvider.java
new file mode 100644
index 0000000..9042fbb
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/HeloNameProvider.java
@@ -0,0 +1,53 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.domainlist.api.DomainListException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HeloNameProvider {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(HeloNameProvider.class);
+    public static final String LOCALHOST = "localhost";
+
+    private final String heloName;
+    private final DomainList domainList;
+
+    public HeloNameProvider(String heloName, DomainList domainList) {
+        this.heloName = heloName;
+        this.domainList = domainList;
+    }
+
+    public String getHeloName() {
+        if (heloName == null) {
+            // TODO: Maybe we should better just lookup the hostname via dns
+            try {
+                return domainList.getDefaultDomain();
+            } catch (DomainListException e) {
+                LOGGER.warn("Unable to access DomainList", e);
+                return LOCALHOST;
+            }
+        } else {
+            return heloName;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/InternetAddressConverter.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/InternetAddressConverter.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/InternetAddressConverter.java
new file mode 100644
index 0000000..c68b9ec
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/InternetAddressConverter.java
@@ -0,0 +1,39 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.util.Collection;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.james.core.MailAddress;
+
+import com.google.common.base.Preconditions;
+
+
+public class InternetAddressConverter {
+
+    public static InternetAddress[] convert(Collection<MailAddress> recipients) {
+        Preconditions.checkNotNull(recipients);
+        return recipients.stream()
+            .map(MailAddress::toInternetAddress)
+            .toArray(InternetAddress[]::new);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrer.java
new file mode 100644
index 0000000..9fbb4ba
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrer.java
@@ -0,0 +1,273 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.mail.Address;
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.dnsservice.api.DNSService;
+import org.apache.james.dnsservice.api.TemporaryResolutionException;
+import org.apache.mailet.HostAddress;
+import org.apache.mailet.Mail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+
+@SuppressWarnings("deprecation")
+public class MailDelivrer {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MailDelivrer.class);
+
+    private final RemoteDeliveryConfiguration configuration;
+    private final MailDelivrerToHost mailDelivrerToHost;
+    private final DnsHelper dnsHelper;
+    private final MessageComposer messageComposer;
+    private final Bouncer bouncer;
+
+    public MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DNSService dnsServer, Bouncer bouncer) {
+        this(configuration, mailDelivrerToHost, new DnsHelper(dnsServer, configuration), bouncer);
+    }
+
+    @VisibleForTesting
+    MailDelivrer(RemoteDeliveryConfiguration configuration, MailDelivrerToHost mailDelivrerToHost, DnsHelper dnsHelper, Bouncer bouncer) {
+        this.configuration = configuration;
+        this.mailDelivrerToHost = mailDelivrerToHost;
+        this.dnsHelper = dnsHelper;
+        this.messageComposer = new MessageComposer(configuration);
+        this.bouncer = bouncer;
+    }
+
+    /**
+     * We can assume that the recipients of this message are all going to the same mail server. We will now rely on the
+     * DNS server to do DNS MX record lookup and try to deliver to the multiple mail servers. If it fails, it should
+     * throw an exception.
+     *
+     * @param mail    org.apache.james.core.MailImpl
+     * @return boolean Whether the delivery was successful and the message can be deleted
+     */
+    public ExecutionResult deliver(Mail mail) {
+        try {
+            return tryDeliver(mail);
+        } catch (SendFailedException sfe) {
+            return handleSenderFailedException(mail, sfe);
+        } catch (MessagingException ex) {
+            // We check whether this is a 5xx error message, which indicates a permanent failure (like account doesn't exist
+            // or mailbox is full or domain is setup wrong). We fail permanently if this was a 5xx error
+            boolean isPermanent = new EnhancedMessagingException(ex).isServerError();
+            return logAndReturn(mail, ExecutionResult.onFailure(isPermanent, ex));
+        } catch (Exception ex) {
+            LOGGER.error("Generic exception = permanent failure: {}", ex.getMessage(), ex);
+            return logAndReturn(mail, ExecutionResult.permanentFailure(ex));
+        }
+    }
+
+    private ExecutionResult tryDeliver(Mail mail) throws MessagingException {
+        if (mail.getRecipients().isEmpty()) {
+            LOGGER.info("No recipients specified... not sure how this could have happened.");
+            return ExecutionResult.permanentFailure(new Exception("No recipients specified for " + mail.getName() + " sent by " + mail.getSender()));
+        }
+        if (configuration.isDebug()) {
+            LOGGER.debug("Attempting to deliver {}", mail.getName());
+        }
+
+        String host = retrieveTargetHostname(mail);
+        try {
+            // Figure out which servers to try to send to. This collection
+            // will hold all the possible target servers
+            Iterator<HostAddress> targetServers = dnsHelper.retrieveHostAddressIterator(host);
+            if (!targetServers.hasNext()) {
+                return handleNoTargetServer(mail, host);
+            }
+            return doDeliver(mail, InternetAddressConverter.convert(mail.getRecipients()), targetServers);
+        } catch (TemporaryResolutionException e) {
+            return logAndReturn(mail, ExecutionResult.temporaryFailure(new MessagingException("Temporary problem looking " +
+                "up mail server for host: " + host + ".  I cannot determine where to send this message.")));
+        }
+    }
+
+    private String retrieveTargetHostname(Mail mail) {
+        Preconditions.checkArgument(!mail.getRecipients().isEmpty(), "Mail should have recipients to attempt delivery");
+        MailAddress rcpt = Iterables.getFirst(mail.getRecipients(), null);
+        return rcpt.getDomain();
+    }
+
+    private ExecutionResult doDeliver(Mail mail, InternetAddress[] addr, Iterator<HostAddress> targetServers) throws MessagingException {
+        MessagingException lastError = null;
+
+        while (targetServers.hasNext()) {
+            try {
+                return mailDelivrerToHost.tryDeliveryToHost(mail, addr, targetServers.next());
+            } catch (SendFailedException sfe) {
+                lastError = handleSendFailExceptionOnMxIteration(mail, sfe);
+            } catch (MessagingException me) {
+                lastError = handleMessagingException(mail, me);
+                if (configuration.isDebug()) {
+                    LOGGER.debug(me.getMessage(), me.getCause());
+                } else {
+                    LOGGER.info(me.getMessage());
+                }
+            }
+        }
+        // If we encountered an exception while looping through,
+        // throw the last MessagingException we caught. We only
+        // do this if we were unable to send the message to any
+        // server. If sending eventually succeeded, we exit
+        // deliver() though the return at the end of the try
+        // block.
+        if (lastError != null) {
+            throw lastError;
+        }
+        return ExecutionResult.temporaryFailure();
+    }
+
+    private MessagingException handleMessagingException(Mail mail, MessagingException me) throws MessagingException {
+        LOGGER.debug("Exception delivering message ({}) - {}", mail.getName(), me.getMessage());
+        if ((me.getNextException() != null) && (me.getNextException() instanceof IOException)) {
+            // If it's an IO exception with no nested exception, it's probably
+            // some socket or weird I/O related problem.
+            return me;
+        } else {
+            // This was not a connection or I/O error particular to one SMTP server of an MX set. Instead, it is almost
+            // certainly a protocol level error. In this case we assume that this is an error we'd encounter with any of
+            // the SMTP servers associated with this MX record, and we pass the exception to the code in the outer block
+            // that determines its severity.
+            throw me;
+        }
+    }
+
+    @VisibleForTesting
+    ExecutionResult handleSenderFailedException(Mail mail, SendFailedException sfe) {
+        logSendFailedException(sfe);
+        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+        List<MailAddress> invalidAddresses = AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(sfe.getInvalidAddresses());
+        List<MailAddress> validUnsentAddresses = AddressesArrayToMailAddressListConverter.getAddressesAsMailAddress(sfe.getValidUnsentAddresses());
+        if (configuration.isDebug()) {
+            LOGGER.debug("Mail {} has initially recipients: {}", mail.getName(), mail.getRecipients());
+            if (!invalidAddresses.isEmpty()) {
+                LOGGER.debug("Invalid recipients: {}", invalidAddresses);
+            }
+            if (!validUnsentAddresses.isEmpty()) {
+                LOGGER.debug("Unsent recipients: {}", validUnsentAddresses);
+            }
+        }
+        if (!validUnsentAddresses.isEmpty()) {
+            if (!invalidAddresses.isEmpty()) {
+                mail.setRecipients(invalidAddresses);
+                bouncer.bounce(mail, sfe);
+            }
+            mail.setRecipients(validUnsentAddresses);
+            if (enhancedMessagingException.hasReturnCode()) {
+                boolean isPermanent = enhancedMessagingException.isServerError();
+                return logAndReturn(mail, ExecutionResult.onFailure(isPermanent, sfe));
+            } else {
+                return logAndReturn(mail, ExecutionResult.temporaryFailure(sfe));
+            }
+        }
+        if (!invalidAddresses.isEmpty()) {
+            mail.setRecipients(invalidAddresses);
+            return logAndReturn(mail, ExecutionResult.permanentFailure(sfe));
+        }
+
+        if (enhancedMessagingException.hasReturnCode() || enhancedMessagingException.hasNestedReturnCode()) {
+            if (enhancedMessagingException.isServerError()) {
+                return ExecutionResult.permanentFailure(sfe);
+            }
+        }
+        return ExecutionResult.temporaryFailure(sfe);
+    }
+
+    private ExecutionResult logAndReturn(Mail mail, ExecutionResult executionResult) {
+        LOGGER.debug(messageComposer.composeFailLogMessage(mail, executionResult));
+        return executionResult;
+    }
+
+    private MessagingException handleSendFailExceptionOnMxIteration(Mail mail, SendFailedException sfe) throws SendFailedException {
+        logSendFailedException(sfe);
+
+        if (sfe.getValidSentAddresses() != null) {
+            Address[] validSent = sfe.getValidSentAddresses();
+            if (validSent.length > 0) {
+                LOGGER.debug("Mail ({}) sent successfully for {}", mail.getName(), validSent);
+            }
+        }
+
+        EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+        if (enhancedMessagingException.isServerError()) {
+            throw sfe;
+        }
+
+        final Address[] validUnsentAddresses = sfe.getValidUnsentAddresses();
+        if (validUnsentAddresses != null && validUnsentAddresses.length > 0) {
+            if (configuration.isDebug()) {
+                LOGGER.debug("Send failed, {} valid addresses remain, continuing with any other servers", (Object) validUnsentAddresses);
+            }
+            return sfe;
+        } else {
+            // There are no valid addresses left to send, so rethrow
+            throw sfe;
+        }
+    }
+
+    private ExecutionResult handleNoTargetServer(Mail mail, String host) {
+        LOGGER.info("No mail server found for: {}", host);
+        MessagingException messagingException = new MessagingException("There are no DNS entries for the hostname " + host + ".  I cannot determine where to send this message.");
+        int retry = DeliveryRetriesHelper.retrieveRetries(mail);
+        if (retry >= configuration.getDnsProblemRetry()) {
+            return logAndReturn(mail, ExecutionResult.permanentFailure(messagingException));
+        } else {
+            return logAndReturn(mail, ExecutionResult.temporaryFailure(messagingException));
+        }
+    }
+
+    private void logSendFailedException(SendFailedException sfe) {
+        if (configuration.isDebug()) {
+            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(sfe);
+            if (enhancedMessagingException.hasReturnCode()) {
+                LOGGER.info("SMTP SEND FAILED: Command [{}] RetCode: [{}] Response[{}]", enhancedMessagingException.computeCommand(),
+                    enhancedMessagingException.getReturnCode(), sfe.getMessage());
+            } else {
+                LOGGER.info("Send failed", sfe);
+            }
+            logLevels(sfe);
+        }
+    }
+
+    private void logLevels(MessagingException me) {
+        Exception ne;
+        while ((ne = me.getNextException()) != null && ne instanceof MessagingException) {
+            me = (MessagingException) ne;
+            EnhancedMessagingException enhancedMessagingException = new EnhancedMessagingException(me);
+            if (me.getClass().getName().endsWith(".SMTPAddressFailedException") || me.getClass().getName().endsWith(".SMTPAddressSucceededException")) {
+                LOGGER.debug("ADDRESS :[{}] Address:[{}] Command : [{}] RetCode[{}] Response [{}]",
+                    enhancedMessagingException.computeAction(), me, enhancedMessagingException.computeAddress(),
+                    enhancedMessagingException.computeCommand(), enhancedMessagingException.getReturnCode());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java
new file mode 100644
index 0000000..5be0f62
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MailDelivrerToHost.java
@@ -0,0 +1,136 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import org.apache.mailet.HostAddress;
+import org.apache.mailet.Mail;
+import org.apache.mailet.MailetContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.mail.smtp.SMTPTransport;
+
+@SuppressWarnings("deprecation")
+public class MailDelivrerToHost {
+    private static final Logger LOGGER = LoggerFactory.getLogger(MailDelivrerToHost.class);
+    public static final String BIT_MIME_8 = "8BITMIME";
+
+    private final RemoteDeliveryConfiguration configuration;
+    private final Converter7Bit converter7Bit;
+    private final Session session;
+
+    public MailDelivrerToHost(RemoteDeliveryConfiguration remoteDeliveryConfiguration, MailetContext mailetContext) {
+        this.configuration = remoteDeliveryConfiguration;
+        this.converter7Bit = new Converter7Bit(mailetContext);
+        this.session = Session.getInstance(configuration.createFinalJavaxProperties());
+    }
+
+    public ExecutionResult tryDeliveryToHost(Mail mail, InternetAddress[] addr, HostAddress outgoingMailServer) throws MessagingException {
+        Properties props = getPropertiesForMail(mail);
+        LOGGER.debug("Attempting delivery of {} to host {} at {} from {}",
+            mail.getName(), outgoingMailServer.getHostName(), outgoingMailServer.getHost(), props.get("mail.smtp.from"));
+
+        // Many of these properties are only in later JavaMail versions
+        // "mail.smtp.ehlo"           //default true
+        // "mail.smtp.auth"           //default false
+        // "mail.smtp.dsn.ret"        //default to nothing... appended as RET= after MAIL FROM line.
+        // "mail.smtp.dsn.notify"     //default to nothing... appended as NOTIFY= after RCPT TO line.
+
+        SMTPTransport transport = null;
+        try {
+            transport = (SMTPTransport) session.getTransport(outgoingMailServer);
+            transport.setLocalHost(props.getProperty("mail.smtp.localhost", configuration.getHeloNameProvider().getHeloName()));
+            connect(outgoingMailServer, transport);
+            transport.sendMessage(adaptToTransport(mail.getMessage(), transport), addr);
+            LOGGER.debug("Mail ({})  sent successfully to {} at {} from {} for {}", mail.getName(), outgoingMailServer.getHostName(),
+                outgoingMailServer.getHost(), props.get("mail.smtp.from"), mail.getRecipients());
+        } finally {
+            closeTransport(mail, outgoingMailServer, transport);
+        }
+        return ExecutionResult.success();
+    }
+
+    private Properties getPropertiesForMail(Mail mail) {
+        Properties props = session.getProperties();
+        if (mail.getSender() == null) {
+            props.put("mail.smtp.from", "<>");
+        } else {
+            String sender = mail.getSender().toString();
+            props.put("mail.smtp.from", sender);
+        }
+        return props;
+    }
+
+    private void connect(HostAddress outgoingMailServer, SMTPTransport transport) throws MessagingException {
+        if (configuration.getAuthUser() != null) {
+            transport.connect(outgoingMailServer.getHostName(), configuration.getAuthUser(), configuration.getAuthPass());
+        } else {
+            transport.connect();
+        }
+    }
+
+    private MimeMessage adaptToTransport(MimeMessage message, SMTPTransport transport) throws MessagingException {
+        if (shouldAdapt(transport)) {
+            try {
+                converter7Bit.convertTo7Bit(message);
+            } catch (IOException e) {
+                LOGGER.error("Error during the conversion to 7 bit.", e);
+            }
+        }
+        return message;
+    }
+
+    private boolean shouldAdapt(SMTPTransport transport) {
+        // If the transport is a SMTPTransport (from sun) some performance enhancement can be done.
+        // If the transport is not the one developed by Sun we are not sure of how it handles the 8 bit mime stuff, so I
+        // convert the message to 7bit.
+        return !transport.getClass().getName().endsWith(".SMTPTransport")
+            || !transport.supportsExtension(BIT_MIME_8);
+        // if the message is already 8bit or binary and the server doesn't support the 8bit extension it has to be converted
+        // to 7bit. Javamail api doesn't perform that conversion, but it is required to be a rfc-compliant smtp server.
+    }
+
+    private void closeTransport(Mail mail, HostAddress outgoingMailServer, SMTPTransport transport) {
+        if (transport != null) {
+            try {
+                // James-899: transport.close() sends QUIT to the server; if that fails
+                // (e.g. because the server has already closed the connection) the message
+                // should be considered to be delivered because the error happened outside
+                // of the mail transaction (MAIL, RCPT, DATA).
+                transport.close();
+            } catch (MessagingException e) {
+                LOGGER.error("Warning: could not close the SMTP transport after sending mail ({}) to {} at {} for {}; " +
+                        "probably the server has already closed the connection. Message is considered to be delivered. Exception: {}",
+                    mail.getName(), outgoingMailServer.getHostName(), outgoingMailServer.getHost(), mail.getRecipients(), e.getMessage());
+            }
+            transport = null;
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MessageComposer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MessageComposer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MessageComposer.java
new file mode 100644
index 0000000..2dfa0a9
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/MessageComposer.java
@@ -0,0 +1,141 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.mailet.Mail;
+
+public class MessageComposer {
+
+    private final RemoteDeliveryConfiguration configuration;
+
+    public MessageComposer(RemoteDeliveryConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    /**
+     * Try to return a usefull logString created of the Exception which was
+     * given. Return null if nothing usefull could be done
+     *
+     * @param e The MessagingException to use
+     * @return logString
+     */
+    public String fromException(Exception e) {
+        if (e.getClass().getName().endsWith(".SMTPSendFailedException")) {
+            return "RemoteHost said: " + e.getMessage();
+        } else if (e instanceof SendFailedException) {
+            SendFailedException exception = (SendFailedException) e;
+
+            // No error
+            if (exception.getInvalidAddresses().length == 0 && exception.getValidUnsentAddresses().length == 0) {
+                return null;
+            }
+
+            Exception ex;
+            StringBuilder sb = new StringBuilder();
+            boolean smtpExFound = false;
+            sb.append("RemoteHost said:");
+
+            if (e instanceof MessagingException) {
+                while ((ex = ((MessagingException) e).getNextException()) != null && ex instanceof MessagingException) {
+                    e = ex;
+                    if (ex.getClass().getName().endsWith(".SMTPAddressFailedException")) {
+                        try {
+                            InternetAddress ia = (InternetAddress) invokeGetter(ex, "getAddress");
+                            sb.append(" ( ").append(ia).append(" - [").append(ex.getMessage().replaceAll("\\n", "")).append("] )");
+                            smtpExFound = true;
+                        } catch (IllegalStateException ise) {
+                            // Error invoking the getAddress method
+                        } catch (ClassCastException cce) {
+                            // The getAddress method returned something
+                            // different than InternetAddress
+                        }
+                    }
+                }
+            }
+            if (!smtpExFound) {
+                boolean invalidAddr = false;
+                sb.append(" ( ");
+
+                if (exception.getInvalidAddresses().length > 0) {
+                    sb.append(Arrays.toString(exception.getInvalidAddresses()));
+                    invalidAddr = true;
+                }
+                if (exception.getValidUnsentAddresses().length > 0) {
+                    if (invalidAddr) {
+                        sb.append(" ");
+                    }
+                    sb.append(Arrays.toString(exception.getValidUnsentAddresses()));
+                }
+                sb.append(" - [");
+                sb.append(exception.getMessage().replaceAll("\\n", ""));
+                sb.append("] )");
+            }
+            return sb.toString();
+        }
+        return null;
+    }
+
+    public String composeFailLogMessage(Mail mail, ExecutionResult executionResult) {
+        StringWriter sout = new StringWriter();
+        PrintWriter out = new PrintWriter(sout, true);
+        out.print(permanentAsString(executionResult.isPermanent()) + " exception delivering mail (" + mail.getName()
+            + ")" + retrieveExceptionLog(executionResult.getException().orElse(null)) + ": ");
+        if (configuration.isDebug()) {
+            if (executionResult.getException().isPresent()) {
+                executionResult.getException().get().printStackTrace(out);
+            }
+        }
+        return sout.toString();
+    }
+
+    private String permanentAsString(boolean permanent) {
+        if (permanent) {
+            return "Permanent";
+        }
+        return "Temporary";
+    }
+
+    private String retrieveExceptionLog(Exception ex) {
+        String exceptionLog = fromException(ex);
+        if (exceptionLog != null) {
+            return ". " + exceptionLog;
+        }
+        return "";
+    }
+
+    private Object invokeGetter(Object target, String getter) {
+        try {
+            Method getAddress = target.getClass().getMethod(getter);
+            return getAddress.invoke(target);
+        } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            return new IllegalStateException("Exception invoking " + getter + " on a " + target.getClass() + " object");
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java
new file mode 100644
index 0000000..63f5451
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliveryConfiguration.java
@@ -0,0 +1,320 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.mailet.MailetConfig;
+import org.apache.mailet.base.MailetUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+
+public class RemoteDeliveryConfiguration {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(RemoteDeliveryConfiguration.class);
+
+    public static final String DELIVERY_THREADS = "deliveryThreads";
+    public static final String USE_PRIORITY = "usePriority";
+    public static final String MAX_DNS_PROBLEM_RETRIES = "maxDnsProblemRetries";
+    public static final String HELO_NAME = "heloName";
+    public static final String JAVAX_PREFIX = "mail.";
+    public static final String BIND = "bind";
+    public static final String GATEWAY_PASSWORD = "gatewayPassword";
+    public static final String GATEWAY_USERNAME_COMPATIBILITY = "gatewayusername";
+    public static final String GATEWAY_USERNAME = "gatewayUsername";
+    public static final String GATEWAY_PORT = "gatewayPort";
+    public static final String GATEWAY = "gateway";
+    public static final String SSL_ENABLE = "sslEnable";
+    public static final String START_TLS = "startTLS";
+    public static final String BOUNCE_PROCESSOR = "bounceProcessor";
+    public static final String SENDPARTIAL = "sendpartial";
+    public static final String TIMEOUT = "timeout";
+    public static final String CONNECTIONTIMEOUT = "connectiontimeout";
+    public static final String OUTGOING = "outgoing";
+    public static final String MAX_RETRIES = "maxRetries";
+    public static final String DELAY_TIME = "delayTime";
+    public static final String DEBUG = "debug";
+    public static final int DEFAULT_SMTP_TIMEOUT = 180000;
+    public static final String DEFAULT_OUTGOING_QUEUE_NAME = "outgoing";
+    public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
+    public static final int DEFAULT_DNS_RETRY_PROBLEM = 0;
+    public static final int DEFAULT_MAX_RETRY = 5;
+    public static final String ADDRESS_PORT_SEPARATOR = ":";
+
+    private final boolean isDebug;
+    private final boolean usePriority;
+    private final boolean startTLS;
+    private final boolean isSSLEnable;
+    private final boolean isBindUsed;
+    private final boolean sendPartial;
+    private final int maxRetries;
+    private final long smtpTimeout;
+    private final int dnsProblemRetry;
+    private final int connectionTimeout;
+    private final int workersThreadCount;
+    private final List<Long> delayTimes;
+    private final HeloNameProvider heloNameProvider;
+    private final String outGoingQueueName;
+    private final String bindAddress;
+    private final String bounceProcessor;
+    private final Collection<String> gatewayServer;
+    private final String authUser;
+    private final String authPass;
+    private final Properties javaxAdditionalProperties;
+
+    public RemoteDeliveryConfiguration(MailetConfig mailetConfig, DomainList domainList) {
+        isDebug = MailetUtil.getInitParameter(mailetConfig, DEBUG).orElse(false);
+        startTLS = MailetUtil.getInitParameter(mailetConfig, START_TLS).orElse(false);
+        isSSLEnable = MailetUtil.getInitParameter(mailetConfig, SSL_ENABLE).orElse(false);
+        usePriority = MailetUtil.getInitParameter(mailetConfig, USE_PRIORITY).orElse(false);
+        sendPartial = MailetUtil.getInitParameter(mailetConfig, SENDPARTIAL).orElse(false);
+        outGoingQueueName = Optional.ofNullable(mailetConfig.getInitParameter(OUTGOING)).orElse(DEFAULT_OUTGOING_QUEUE_NAME);
+        bounceProcessor = mailetConfig.getInitParameter(BOUNCE_PROCESSOR);
+        bindAddress = mailetConfig.getInitParameter(BIND);
+
+        DelaysAndMaxRetry delaysAndMaxRetry = computeDelaysAndMaxRetry(mailetConfig);
+        maxRetries = delaysAndMaxRetry.getMaxRetries();
+        delayTimes = delaysAndMaxRetry.getExpandedDelays();
+        smtpTimeout = computeSmtpTimeout(mailetConfig);
+        connectionTimeout = computeConnectionTimeout(mailetConfig);
+        dnsProblemRetry = computeDnsProblemRetry(mailetConfig);
+        heloNameProvider = new HeloNameProvider(mailetConfig.getInitParameter(HELO_NAME), domainList);
+        workersThreadCount = Integer.valueOf(mailetConfig.getInitParameter(DELIVERY_THREADS));
+
+        String gatewayPort = mailetConfig.getInitParameter(GATEWAY_PORT);
+        String gateway = mailetConfig.getInitParameter(GATEWAY);
+        gatewayServer = computeGatewayServers(gatewayPort, gateway);
+        if (gateway != null) {
+            authUser = computeGatewayUser(mailetConfig);
+            authPass = mailetConfig.getInitParameter(GATEWAY_PASSWORD);
+        } else {
+            authUser = null;
+            authPass = null;
+        }
+        isBindUsed = bindAddress != null;
+        javaxAdditionalProperties = computeJavaxProperties(mailetConfig);
+    }
+
+    private Properties computeJavaxProperties(MailetConfig mailetConfig) {
+        Properties result = new Properties();
+        // deal with <mail.*> attributes, passing them to javamail
+        result.putAll(
+            ImmutableList.copyOf(mailetConfig.getInitParameterNames())
+                .stream()
+                .filter(propertyName -> propertyName.startsWith(JAVAX_PREFIX))
+                .map(propertyName -> Pair.of(propertyName, mailetConfig.getInitParameter(propertyName)))
+                .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)));
+        return result;
+    }
+
+    private int computeDnsProblemRetry(MailetConfig mailetConfig) {
+        String dnsRetry = mailetConfig.getInitParameter(MAX_DNS_PROBLEM_RETRIES);
+        if (!Strings.isNullOrEmpty(dnsRetry)) {
+            return Integer.valueOf(dnsRetry);
+        } else {
+            return DEFAULT_DNS_RETRY_PROBLEM;
+        }
+    }
+
+    private int computeConnectionTimeout(MailetConfig mailetConfig) {
+        try {
+            return Integer.valueOf(
+                Optional.ofNullable(mailetConfig.getInitParameter(CONNECTIONTIMEOUT))
+                    .orElse(String.valueOf(DEFAULT_CONNECTION_TIMEOUT)));
+        } catch (Exception e) {
+            LOGGER.warn("Invalid timeout setting: {}", mailetConfig.getInitParameter(TIMEOUT));
+            return DEFAULT_CONNECTION_TIMEOUT;
+        }
+    }
+
+    private long computeSmtpTimeout(MailetConfig mailetConfig) {
+        try {
+            if (mailetConfig.getInitParameter(TIMEOUT) != null) {
+                return Integer.valueOf(mailetConfig.getInitParameter(TIMEOUT));
+            } else {
+                return DEFAULT_SMTP_TIMEOUT;
+            }
+        } catch (Exception e) {
+            LOGGER.warn("Invalid timeout setting: {}", mailetConfig.getInitParameter(TIMEOUT));
+            return DEFAULT_SMTP_TIMEOUT;
+        }
+    }
+
+    private DelaysAndMaxRetry computeDelaysAndMaxRetry(MailetConfig mailetConfig) {
+        try {
+            int intendedMaxRetries = Integer.valueOf(
+                Optional.ofNullable(mailetConfig.getInitParameter(MAX_RETRIES))
+                    .orElse(String.valueOf(DEFAULT_MAX_RETRY)));
+            return DelaysAndMaxRetry.from(intendedMaxRetries, mailetConfig.getInitParameter(DELAY_TIME));
+        } catch (Exception e) {
+            LOGGER.warn("Invalid maxRetries setting: {}", mailetConfig.getInitParameter(MAX_RETRIES));
+            return DelaysAndMaxRetry.defaults();
+        }
+    }
+
+    private String computeGatewayUser(MailetConfig mailetConfig) {
+        // backward compatibility with 2.3.x
+        String user = mailetConfig.getInitParameter(GATEWAY_USERNAME);
+        if (user == null) {
+            return mailetConfig.getInitParameter(GATEWAY_USERNAME_COMPATIBILITY);
+        }
+        return user;
+    }
+
+    private List<String> computeGatewayServers(String gatewayPort, String gateway) {
+        if (gateway != null) {
+            ImmutableList.Builder<String> builder = ImmutableList.builder();
+            Iterable<String> gatewayParts = Splitter.on(',').split(gateway);
+            for (String gatewayPart : gatewayParts) {
+                builder.add(parsePart(gatewayPort, gatewayPart));
+            }
+            return builder.build();
+        } else {
+            return ImmutableList.of();
+        }
+    }
+
+    private String parsePart(String gatewayPort, String gatewayPart) {
+        String address = gatewayPart.trim();
+        if (!address.contains(ADDRESS_PORT_SEPARATOR) && gatewayPort != null) {
+            return address + ADDRESS_PORT_SEPARATOR + gatewayPort;
+        }
+        return address;
+    }
+
+    public Properties createFinalJavaxProperties() {
+        Properties props = new Properties();
+        props.put("mail.debug", "false");
+        // Reactivated: javamail 1.3.2 should no more have problems with "250 OK" messages
+        // (WAS "false": Prevents problems encountered with 250 OK Messages)
+        props.put("mail.smtp.ehlo", "true");
+        // By setting this property to true the transport is allowed to send 8 bit data to the server (if it supports
+        // the 8bitmime extension).
+        props.setProperty("mail.smtp.allow8bitmime", "true");
+        props.put("mail.smtp.timeout", String.valueOf(smtpTimeout));
+        props.put("mail.smtp.connectiontimeout", String.valueOf(connectionTimeout));
+        props.put("mail.smtp.sendpartial", String.valueOf(sendPartial));
+        props.put("mail.smtp.localhost", heloNameProvider.getHeloName());
+        props.put("mail.smtp.starttls.enable", String.valueOf(startTLS));
+        props.put("mail.smtp.ssl.enable", String.valueOf(isSSLEnable));
+        if (isBindUsed()) {
+            // undocumented JavaMail 1.2 feature, smtp transport will use
+            // our socket factory, which will also set the local address
+            props.put("mail.smtp.socketFactory.class", RemoteDeliverySocketFactory.class.getClass());
+            // Don't fallback to the standard socket factory on error, do throw an exception
+            props.put("mail.smtp.socketFactory.fallback", "false");
+        }
+        if (authUser != null) {
+            props.put("mail.smtp.auth", "true");
+        }
+        props.putAll(javaxAdditionalProperties);
+        return props;
+    }
+
+    public boolean isDebug() {
+        return isDebug;
+    }
+
+    public List<Long> getDelayTimes() {
+        return delayTimes;
+    }
+
+    public int getMaxRetries() {
+        return maxRetries;
+    }
+
+    public long getSmtpTimeout() {
+        return smtpTimeout;
+    }
+
+    public boolean isSendPartial() {
+        return sendPartial;
+    }
+
+    public int getConnectionTimeout() {
+        return connectionTimeout;
+    }
+
+    public int getWorkersThreadCount() {
+        return workersThreadCount;
+    }
+
+    public Collection<String> getGatewayServer() {
+        return gatewayServer;
+    }
+
+    public String getAuthUser() {
+        return authUser;
+    }
+
+    public String getAuthPass() {
+        return authPass;
+    }
+
+    public boolean isBindUsed() {
+        return isBindUsed;
+    }
+
+    public String getBounceProcessor() {
+        return bounceProcessor;
+    }
+
+    public boolean isUsePriority() {
+        return usePriority;
+    }
+
+    public boolean isStartTLS() {
+        return startTLS;
+    }
+
+    public boolean isSSLEnable() {
+        return isSSLEnable;
+    }
+
+    public HeloNameProvider getHeloNameProvider() {
+        return heloNameProvider;
+    }
+
+    public String getOutGoingQueueName() {
+        return outGoingQueueName;
+    }
+
+    public Properties getJavaxAdditionalProperties() {
+        return javaxAdditionalProperties;
+    }
+
+    public int getDnsProblemRetry() {
+        return dnsProblemRetry;
+    }
+
+    public String getBindAddress() {
+        return bindAddress;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliverySocketFactory.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliverySocketFactory.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliverySocketFactory.java
new file mode 100644
index 0000000..60722a2
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/RemoteDeliverySocketFactory.java
@@ -0,0 +1,138 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.net.SocketFactory;
+
+/**
+ * <p>
+ * It is used by RemoteDelivery in order to make possible to bind the client
+ * socket to a specific ip address.
+ * </p>
+ * <p>
+ * This is not a nice solution because the ip address must be shared by all
+ * RemoteDelivery instances. It would be better to modify JavaMail (current
+ * version 1.3) to support a corresonding property, e.g. mail.smtp.bindAdress.
+ * </p>
+ * <p>
+ * This used to not extend javax.net.SocketFactory descendant, because
+ * <ol>
+ * <li>
+ * it was not necessary because JavaMail 1.2 uses reflection when accessing this
+ * class;</li>
+ * <li>
+ * it was not desirable because it would require java 1.4.</li>
+ * </ol>
+ * </p>
+ * <p>
+ * But since James 2.3.0a1:
+ * <ol>
+ * <li>we require Java 1.4 so the dependency on SocketFactory is not really an
+ * issue;</li>
+ * <li>Javamail 1.4 cast the object returned by getDefault to SocketFactory and
+ * fails to create the socket if we don't extend SocketFactory.</li>
+ * </ol>
+ * </p>
+ * <p>
+ * <strong>Note</strong>: Javamail 1.4 should correctly support
+ * mail.smtp.localaddr so we could probably get rid of this class and simply add
+ * that property to the Session.
+ * </p>
+ */
+public class RemoteDeliverySocketFactory extends SocketFactory {
+
+    /**
+     * @param addr
+     *            the ip address or host name the delivery socket will bind to
+     */
+    public static void setBindAdress(String addr) throws UnknownHostException {
+        if (addr == null) {
+            bindAddress = null;
+        } else {
+            bindAddress = InetAddress.getByName(addr);
+        }
+    }
+
+    /**
+     * the same as the similarly named javax.net.SocketFactory operation.
+     */
+    public static SocketFactory getDefault() {
+        return new RemoteDeliverySocketFactory();
+    }
+
+    /**
+     * the same as the similarly named javax.net.SocketFactory operation. Just
+     * to be safe, it is not used by JavaMail 1.3. This is the only method used
+     * by JavaMail 1.4.
+     */
+    public Socket createSocket() throws IOException {
+        Socket s = new Socket();
+        s.bind(new InetSocketAddress(bindAddress, 0));
+        return s;
+    }
+
+    /**
+     * the same as the similarly named javax.net.SocketFactory operation. This
+     * is the one which is used by JavaMail 1.3. This is not used by JavaMail
+     * 1.4.
+     */
+    public Socket createSocket(String host, int port) throws IOException {
+        return new Socket(host, port, bindAddress, 0);
+    }
+
+    /**
+     * the same as the similarly named javax.net.SocketFactory operation. Just
+     * to be safe, it is not used by JavaMail 1.3. This is not used by JavaMail
+     * 1.4.
+     */
+    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException {
+        return new Socket(host, port, clientHost == null ? bindAddress : clientHost, clientPort);
+    }
+
+    /**
+     * the same as the similarly named javax.net.SocketFactory operation. Just
+     * to be safe, it is not used by JavaMail 1.3. This is not used by JavaMail
+     * 1.4.
+     */
+    public Socket createSocket(InetAddress host, int port) throws IOException {
+        return new Socket(host, port, bindAddress, 0);
+    }
+
+    /**
+     * the same as the similarly named javax.net.SocketFactory operation. Just
+     * to be safe, it is not used by JavaMail 1.3. This is not used by JavaMail
+     * 1.4.
+     */
+    public Socket createSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort) throws IOException {
+        return new Socket(address, port, clientAddress == null ? bindAddress : clientAddress, clientPort);
+    }
+
+    /**
+     * it should be set by setBindAdress(). Null means the socket is bind to the
+     * default address.
+     */
+    private static InetAddress bindAddress;
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Repeat.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Repeat.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Repeat.java
new file mode 100644
index 0000000..6926c2d
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remote/delivery/Repeat.java
@@ -0,0 +1,38 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.transport.mailets.remote.delivery;
+
+import java.util.List;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class Repeat {
+
+    @SuppressWarnings("unchecked")
+    public static <T> List<T> repeat(T element, int times) {
+        Preconditions.checkArgument(times >= 0, "Times argument should be strictly positive");
+        return ImmutableList.copyOf(
+            Iterables.limit(
+                Iterables.cycle(element), times));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
deleted file mode 100644
index 9fbf7c8..0000000
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/AddressesArrayToMailAddressListConverter.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.transport.mailets.remoteDelivery;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-
-import javax.mail.Address;
-import javax.mail.internet.AddressException;
-
-import org.apache.james.core.MailAddress;
-import org.apache.james.util.OptionalUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.github.steveash.guavate.Guavate;
-import com.google.common.collect.ImmutableList;
-
-public class AddressesArrayToMailAddressListConverter {
-    private static final Logger LOGGER = LoggerFactory.getLogger(AddressesArrayToMailAddressListConverter.class);
-
-    public static List<MailAddress> getAddressesAsMailAddress(Address[] addresses) {
-        if (addresses == null) {
-            return ImmutableList.of();
-        }
-        return Arrays.asList(addresses)
-            .stream()
-            .map(address -> toMailAddress(address))
-            .flatMap(OptionalUtils::toStream)
-            .collect(Guavate.toImmutableList());
-    }
-
-    private static Optional<MailAddress> toMailAddress(Address address) {
-        try {
-            return Optional.of(new MailAddress(address.toString()));
-        } catch (AddressException e) {
-            LOGGER.debug("Can't parse unsent address {}", address, e);
-            return Optional.empty();
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
deleted file mode 100644
index 5b40cc1..0000000
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Bouncer.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.transport.mailets.remoteDelivery;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.ConnectException;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-
-import javax.mail.MessagingException;
-import javax.mail.SendFailedException;
-
-import org.apache.james.core.MailAddress;
-import org.apache.mailet.Mail;
-import org.apache.mailet.MailetContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class Bouncer {
-    private static final Logger LOGGER = LoggerFactory.getLogger(Bouncer.class);
-
-    public static final String DELIVERY_ERROR = "delivery-error";
-    private final RemoteDeliveryConfiguration configuration;
-    private final MailetContext mailetContext;
-
-    public Bouncer(RemoteDeliveryConfiguration configuration, MailetContext mailetContext) {
-        this.configuration = configuration;
-        this.mailetContext = mailetContext;
-    }
-
-    public void bounce(Mail mail, Exception ex) {
-        if (mail.getSender() == null) {
-            LOGGER.debug("Null Sender: no bounce will be generated for {}", mail.getName());
-        } else {
-            if (configuration.getBounceProcessor() != null) {
-                mail.setAttribute(DELIVERY_ERROR, getErrorMsg(ex));
-                try {
-                    mailetContext.sendMail(mail, configuration.getBounceProcessor());
-                } catch (MessagingException e) {
-                    LOGGER.warn("Exception re-inserting failed mail: ", e);
-                }
-            } else {
-                bounceWithMailetContext(mail, ex);
-            }
-        }
-    }
-
-
-    private void bounceWithMailetContext(Mail mail, Exception ex) {
-        LOGGER.debug("Sending failure message {}", mail.getName());
-        try {
-            mailetContext.bounce(mail, explanationText(mail, ex));
-        } catch (MessagingException me) {
-            LOGGER.warn("Encountered unexpected messaging exception while bouncing message", me);
-        } catch (Exception e) {
-            LOGGER.warn("Encountered unexpected exception while bouncing message", e);
-        }
-    }
-
-    public String explanationText(Mail mail, Exception ex) {
-        StringWriter sout = new StringWriter();
-        PrintWriter out = new PrintWriter(sout, true);
-        out.println("Hi. This is the James mail server at " + resolveMachineName() + ".");
-        out.println("I'm afraid I wasn't able to deliver your message to the following addresses.");
-        out.println("This is a permanent error; I've given up. Sorry it didn't work out. Below");
-        out.println("I include the list of recipients and the reason why I was unable to deliver");
-        out.println("your message.");
-        out.println();
-        for (MailAddress mailAddress : mail.getRecipients()) {
-            out.println(mailAddress);
-        }
-        if (ex instanceof MessagingException) {
-            if (((MessagingException) ex).getNextException() == null) {
-                out.println(sanitizeExceptionMessage(ex));
-            } else {
-                Exception ex1 = ((MessagingException) ex).getNextException();
-                if (ex1 instanceof SendFailedException) {
-                    out.println("Remote mail server told me: " + sanitizeExceptionMessage(ex1));
-                } else if (ex1 instanceof UnknownHostException) {
-                    out.println("Unknown host: " + sanitizeExceptionMessage(ex1));
-                    out.println("This could be a DNS server error, a typo, or a problem with the recipient's mail server.");
-                } else if (ex1 instanceof ConnectException) {
-                    // Already formatted as "Connection timed out: connect"
-                    out.println(sanitizeExceptionMessage(ex1));
-                } else if (ex1 instanceof SocketException) {
-                    out.println("Socket exception: " + sanitizeExceptionMessage(ex1));
-                } else {
-                    out.println(sanitizeExceptionMessage(ex1));
-                }
-            }
-        }
-        out.println();
-        return sout.toString();
-    }
-
-    private String sanitizeExceptionMessage(Exception e) {
-        if (e.getMessage() == null) {
-            return "null";
-        } else {
-            return e.getMessage().trim();
-        }
-    }
-
-    private String resolveMachineName() {
-        try {
-            return configuration.getHeloNameProvider().getHeloName();
-        } catch (Exception e) {
-            return "[address unknown]";
-        }
-    }
-
-    public String getErrorMsg(Exception ex) {
-        if (ex instanceof MessagingException) {
-            return getNestedExceptionMessage((MessagingException) ex);
-        } else {
-            return sanitizeExceptionMessage(ex);
-        }
-    }
-
-    public String getNestedExceptionMessage(MessagingException me) {
-        if (me.getNextException() == null) {
-            return sanitizeExceptionMessage(me);
-        } else {
-            Exception ex1 = me.getNextException();
-            return sanitizeExceptionMessage(ex1);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/52c18ef6/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java
deleted file mode 100644
index ab798e6..0000000
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/remoteDelivery/Converter7Bit.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one   *
- * or more contributor license agreements.  See the NOTICE file *
- * distributed with this work for additional information        *
- * regarding copyright ownership.  The ASF licenses this file   *
- * to you under the Apache License, Version 2.0 (the            *
- * "License"); you may not use this file except in compliance   *
- * with the License.  You may obtain a copy of the License at   *
- *                                                              *
- *   http://www.apache.org/licenses/LICENSE-2.0                 *
- *                                                              *
- * Unless required by applicable law or agreed to in writing,   *
- * software distributed under the License is distributed on an  *
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
- * KIND, either express or implied.  See the License for the    *
- * specific language governing permissions and limitations      *
- * under the License.                                           *
- ****************************************************************/
-
-package org.apache.james.transport.mailets.remoteDelivery;
-
-import java.io.IOException;
-
-import javax.mail.MessagingException;
-import javax.mail.internet.MimeMultipart;
-import javax.mail.internet.MimePart;
-
-import org.apache.mailet.MailetContext;
-
-public class Converter7Bit {
-
-    private final MailetContext mailetContext;
-
-    public Converter7Bit(MailetContext mailetContext) {
-        this.mailetContext = mailetContext;
-    }
-
-    public MimePart convertTo7Bit(MimePart part) throws MessagingException, IOException {
-        if (part.isMimeType("multipart/*")) {
-            MimeMultipart parts = (MimeMultipart) part.getContent();
-            int count = parts.getCount();
-            for (int i = 0; i < count; i++) {
-                convertTo7Bit((MimePart) parts.getBodyPart(i));
-            }
-        } else if ("8bit".equals(part.getEncoding())) {
-            // The content may already be in encoded the form (likely with mail
-            // created from a
-            // stream). In that case, just changing the encoding to
-            // quoted-printable will mangle
-            // the result when this is transmitted. We must first convert the
-            // content into its
-            // native format, set it back, and only THEN set the transfer
-            // encoding to force the
-            // content to be encoded appropriately.
-
-            // if the part doesn't contain text it will be base64 encoded.
-            String contentTransferEncoding = part.isMimeType("text/*") ? "quoted-printable" : "base64";
-            part.setContent(part.getContent(), part.getContentType());
-            part.setHeader("Content-Transfer-Encoding", contentTransferEncoding);
-            part.addHeader("X-MIME-Autoconverted", "from 8bit to " + contentTransferEncoding + " by " + mailetContext.getServerInfo());
-        }
-        return part;
-    }
-
-}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org