hilmer 2003/12/12 14:48:19 Modified: src/java/org/apache/james Tag: branch_2_1_fcs James.java src/java/org/apache/james/dnsserver Tag: branch_2_1_fcs DNSServer.java src/java/org/apache/james/services Tag: branch_2_1_fcs DNSServer.java src/java/org/apache/james/transport/mailets Tag: branch_2_1_fcs RemoteDelivery.java src/java/org/apache/mailet Tag: branch_2_1_fcs MailetContext.java Added: src/java/org/apache/james/dnsserver Tag: branch_2_1_fcs SMTPHostAddressesImpl.java Log: PR:BUG 24885 Submitted by:"Richard O. Hammer" <[EMAIL PROTECTED]> Reviewed by: Soeren Hilmer <[EMAIL PROTECTED]> This commit fixes BUG 24885. James did not handle multihomedness of SMTP hosts. The fix adds a new method and inner class to MailetContext of the mailet api. Revision Changes Path No revision No revision 1.35.4.12 +25 -1 james-server/src/java/org/apache/james/James.java Index: James.java =================================================================== RCS file: /home/cvs/james-server/src/java/org/apache/james/James.java,v retrieving revision 1.35.4.11 retrieving revision 1.35.4.12 diff -u -r1.35.4.11 -r1.35.4.12 --- James.java 20 Oct 2003 06:03:15 -0000 1.35.4.11 +++ James.java 12 Dec 2003 22:48:18 -0000 1.35.4.12 @@ -945,4 +945,28 @@ success = localusers.addUser(user); return success; } + + /** + * Performs DNS lookups as needed to find servers which should or might + * support SMTP. + * Returns one SMTPHostAddresses for each such host discovered + * by DNS. If no host is found for domainName, the Iterator + * returned will be empty and the first call to hasNext() will return + * false. + * @param domainName the String domain for which SMTP host addresses are + * sought. + * @return an Iterator in which the Objects returned by next() + * are instances of SMTPHostAddresses. + */ + public Iterator getSMTPHostAddresses(String domainName) { + DNSServer dnsServer = null; + try { + dnsServer = (DNSServer) compMgr.lookup( DNSServer.ROLE ); + } catch ( final ComponentException cme ) { + getLogger().error("Fatal configuration error - DNS Servers lost!", cme ); + throw new RuntimeException("Fatal configuration error - DNS Servers lost!"); + } + return dnsServer.getSMTPHostAddresses(domainName); + } + } No revision No revision 1.9.4.9 +138 -0 james-server/src/java/org/apache/james/dnsserver/DNSServer.java Index: DNSServer.java =================================================================== RCS file: /home/cvs/james-server/src/java/org/apache/james/dnsserver/DNSServer.java,v retrieving revision 1.9.4.8 retrieving revision 1.9.4.9 diff -u -r1.9.4.8 -r1.9.4.9 --- DNSServer.java 29 Aug 2003 05:22:13 -0000 1.9.4.8 +++ DNSServer.java 12 Dec 2003 22:48:19 -0000 1.9.4.9 @@ -70,6 +70,7 @@ import org.xbill.DNS.FindServer; import org.xbill.DNS.Message; import org.xbill.DNS.MXRecord; +import org.xbill.DNS.ARecord; import org.xbill.DNS.Name; import org.xbill.DNS.Rcode; import org.xbill.DNS.Record; @@ -380,4 +381,141 @@ return (pa == pb) ? (512 - random.nextInt(1024)) : pa - pb; } } + + /** + * Performs DNS lookups as needed to find servers which should or might + * support SMTP. Returns one SMTPHostAddresses for each such host + * discovered by DNS. If no host is found for domainName, the Iterator + * returned will be empty and the first call to hasNext() will return + * false. + * @param domainName the String domain for which SMTP host addresses are + * sought. + * @return an Enumeration in which the Objects returned by next() + * are instances of SMTPHostAddresses. + */ + public Iterator getSMTPHostAddresses(final String domainName) { + return new Iterator() { + private Iterator mxHosts = new MxSorter(domainName); + + public boolean hasNext(){ + return mxHosts.hasNext(); + } + + public Object next(){ + String nextHostname = (String)mxHosts.next(); + Record[] aRecords = lookup(nextHostname, Type.A); + return new SMTPHostAddressesImpl(aRecords, nextHostname); + } + + public void remove () { + throw new UnsupportedOperationException ("remove not supported by this iterator"); + } + }; + } + + /** A way to get mail hosts to try. If any MX hosts are found for the + * domain name with which this is constructed, then these MX hostnames + * are returned in priority sorted order, lowest priority numbers coming + * first. And, whenever multiple hosts have the same priority then these + * are returned in a randomized order within that priority group, as + * specified in RFC 2821, Section 5. + * + * If no MX hosts are found for the domain name, then a DNS search is + * performed for an A record. If an A record is found then domainName itself + * will be returned by the Iterator, and it will be the only object in + * the Iterator. If however no A record is found (in addition to no MX + * record) then the Iterator constructed will be empty; the first call to + * its hasNext() will return false. + * + * This behavior attempts to satisfy the requirements of RFC 2821, Section 5. + */ + private class MxSorter implements Iterator { + private int priorListPriority = Integer.MIN_VALUE; + private ArrayList equiPriorityList = new ArrayList(); + private Record[] mxRecords; + private Random rnd = new Random (); + + /* The implementation of this class attempts to achieve efficiency by + * performing no more sorting of the rawMxRecords than necessary. In the + * large majority of cases the first attempt, made by a client of this class + * to connect to an SMTP server for a given domain, will succeed. As such, + * in most cases only one call will be made to this Iterator's + * next(), and in that majority of cases there will have been no need + * to sort the array of MX Records. This implementation would, however, be + * relatively inefficient in the case where all hosts fail, when every + * Object is called out of a long Iterator. + */ + + private MxSorter(String domainName) { + mxRecords = lookup(domainName, Type.MX); + if (mxRecords == null || mxRecords.length == 0) { + //no MX records were found, so try to use the domainName + Record[] aRecords = lookup(domainName, Type.A); + if(aRecords != null && aRecords.length > 0) { + equiPriorityList.add(domainName); + } + } + } + + /** + * Sets presentPriorityList to contain all hosts + * which have the least priority greater than pastPriority. + * When this is called, both (rawMxRecords.length > 0) and + * (presentPriorityList.size() == 0), by contract. + * In the case where this is called repeatedly, so that priorListPriority + * has already become the highest of the priorities in the rawMxRecords, + * then this returns without having added any elements to + * presentPriorityList; presentPriorityList.size remains zero. + */ + private void createPriorityList(){ + int leastPriorityFound = Integer.MAX_VALUE; + /* We loop once through the rawMxRecords, finding the lowest priority + * greater than priorListPriority, and collecting all the hostnames + * with that priority into equiPriorityList. + */ + for (int i = 0; i < mxRecords.length; i++) { + MXRecord thisRecord = (MXRecord)mxRecords[i]; + int thisRecordPriority = thisRecord.getPriority(); + if (thisRecordPriority > priorListPriority) { + if (thisRecordPriority < leastPriorityFound) { + equiPriorityList.clear(); + leastPriorityFound = thisRecordPriority; + equiPriorityList.add(thisRecord.getTarget().toString()); + } else if (thisRecordPriority == leastPriorityFound) { + equiPriorityList.add(thisRecord.getTarget().toString()); + } + } + } + priorListPriority = leastPriorityFound; + } + + public boolean hasNext(){ + if (equiPriorityList.size() > 0){ + return true; + }else if (mxRecords != null && mxRecords.length > 0){ + createPriorityList(); + return equiPriorityList.size() > 0; + } else{ + return false; + } + } + + public Object next(){ + if (hasNext()){ + /* this randomization is done to comply with RFC-2821 */ + /* Note: java.util.Random.nextInt(limit) is about twice as fast as (int)(Math.random()*limit) */ + int getIndex = rnd.nextInt(equiPriorityList.size()); + Object returnElement = equiPriorityList.get(getIndex); + equiPriorityList.remove(getIndex); + return returnElement; + }else{ + throw new NoSuchElementException(); + } + } + + public void remove () { + throw new UnsupportedOperationException ("remove not supported by this iterator"); + } + } + } No revision Index: DNSServer.java =================================================================== RCS file: /home/cvs/james-server/src/java/org/apache/james/dnsserver/DNSServer.java,v retrieving revision 1.9.4.8 retrieving revision 1.9.4.9 diff -u -r1.9.4.8 -r1.9.4.9 --- DNSServer.java 29 Aug 2003 05:22:13 -0000 1.9.4.8 +++ DNSServer.java 12 Dec 2003 22:48:19 -0000 1.9.4.9 @@ -70,6 +70,7 @@ import org.xbill.DNS.FindServer; import org.xbill.DNS.Message; import org.xbill.DNS.MXRecord; +import org.xbill.DNS.ARecord; import org.xbill.DNS.Name; import org.xbill.DNS.Rcode; import org.xbill.DNS.Record; @@ -380,4 +381,141 @@ return (pa == pb) ? (512 - random.nextInt(1024)) : pa - pb; } } + + /** + * Performs DNS lookups as needed to find servers which should or might + * support SMTP. Returns one SMTPHostAddresses for each such host + * discovered by DNS. If no host is found for domainName, the Iterator + * returned will be empty and the first call to hasNext() will return + * false. + * @param domainName the String domain for which SMTP host addresses are + * sought. + * @return an Enumeration in which the Objects returned by next() + * are instances of SMTPHostAddresses. + */ + public Iterator getSMTPHostAddresses(final String domainName) { + return new Iterator() { + private Iterator mxHosts = new MxSorter(domainName); + + public boolean hasNext(){ + return mxHosts.hasNext(); + } + + public Object next(){ + String nextHostname = (String)mxHosts.next(); + Record[] aRecords = lookup(nextHostname, Type.A); + return new SMTPHostAddressesImpl(aRecords, nextHostname); + } + + public void remove () { + throw new UnsupportedOperationException ("remove not supported by this iterator"); + } + }; + } + + /** A way to get mail hosts to try. If any MX hosts are found for the + * domain name with which this is constructed, then these MX hostnames + * are returned in priority sorted order, lowest priority numbers coming + * first. And, whenever multiple hosts have the same priority then these + * are returned in a randomized order within that priority group, as + * specified in RFC 2821, Section 5. + * + * If no MX hosts are found for the domain name, then a DNS search is + * performed for an A record. If an A record is found then domainName itself + * will be returned by the Iterator, and it will be the only object in + * the Iterator. If however no A record is found (in addition to no MX + * record) then the Iterator constructed will be empty; the first call to + * its hasNext() will return false. + * + * This behavior attempts to satisfy the requirements of RFC 2821, Section 5. + */ + private class MxSorter implements Iterator { + private int priorListPriority = Integer.MIN_VALUE; + private ArrayList equiPriorityList = new ArrayList(); + private Record[] mxRecords; + private Random rnd = new Random (); + + /* The implementation of this class attempts to achieve efficiency by + * performing no more sorting of the rawMxRecords than necessary. In the + * large majority of cases the first attempt, made by a client of this class + * to connect to an SMTP server for a given domain, will succeed. As such, + * in most cases only one call will be made to this Iterator's + * next(), and in that majority of cases there will have been no need + * to sort the array of MX Records. This implementation would, however, be + * relatively inefficient in the case where all hosts fail, when every + * Object is called out of a long Iterator. + */ + + private MxSorter(String domainName) { + mxRecords = lookup(domainName, Type.MX); + if (mxRecords == null || mxRecords.length == 0) { + //no MX records were found, so try to use the domainName + Record[] aRecords = lookup(domainName, Type.A); + if(aRecords != null && aRecords.length > 0) { + equiPriorityList.add(domainName); + } + } + } + + /** + * Sets presentPriorityList to contain all hosts + * which have the least priority greater than pastPriority. + * When this is called, both (rawMxRecords.length > 0) and + * (presentPriorityList.size() == 0), by contract. + * In the case where this is called repeatedly, so that priorListPriority + * has already become the highest of the priorities in the rawMxRecords, + * then this returns without having added any elements to + * presentPriorityList; presentPriorityList.size remains zero. + */ + private void createPriorityList(){ + int leastPriorityFound = Integer.MAX_VALUE; + /* We loop once through the rawMxRecords, finding the lowest priority + * greater than priorListPriority, and collecting all the hostnames + * with that priority into equiPriorityList. + */ + for (int i = 0; i < mxRecords.length; i++) { + MXRecord thisRecord = (MXRecord)mxRecords[i]; + int thisRecordPriority = thisRecord.getPriority(); + if (thisRecordPriority > priorListPriority) { + if (thisRecordPriority < leastPriorityFound) { + equiPriorityList.clear(); + leastPriorityFound = thisRecordPriority; + equiPriorityList.add(thisRecord.getTarget().toString()); + } else if (thisRecordPriority == leastPriorityFound) { + equiPriorityList.add(thisRecord.getTarget().toString()); + } + } + } + priorListPriority = leastPriorityFound; + } + + public boolean hasNext(){ + if (equiPriorityList.size() > 0){ + return true; + }else if (mxRecords != null && mxRecords.length > 0){ + createPriorityList(); + return equiPriorityList.size() > 0; + } else{ + return false; + } + } + + public Object next(){ + if (hasNext()){ + /* this randomization is done to comply with RFC-2821 */ + /* Note: java.util.Random.nextInt(limit) is about twice as fast as (int)(Math.random()*limit) */ + int getIndex = rnd.nextInt(equiPriorityList.size()); + Object returnElement = equiPriorityList.get(getIndex); + equiPriorityList.remove(getIndex); + return returnElement; + }else{ + throw new NoSuchElementException(); + } + } + + public void remove () { + throw new UnsupportedOperationException ("remove not supported by this iterator"); + } + } + } No revision Index: DNSServer.java =================================================================== RCS file: /home/cvs/james-server/src/java/org/apache/james/dnsserver/DNSServer.java,v retrieving revision 1.9.4.8 retrieving revision 1.9.4.9 diff -u -r1.9.4.8 -r1.9.4.9 --- DNSServer.java 29 Aug 2003 05:22:13 -0000 1.9.4.8 +++ DNSServer.java 12 Dec 2003 22:48:19 -0000 1.9.4.9 @@ -70,6 +70,7 @@ import org.xbill.DNS.FindServer; import org.xbill.DNS.Message; import org.xbill.DNS.MXRecord; +import org.xbill.DNS.ARecord; import org.xbill.DNS.Name; import org.xbill.DNS.Rcode; import org.xbill.DNS.Record; @@ -380,4 +381,141 @@ return (pa == pb) ? (512 - random.nextInt(1024)) : pa - pb; } } + + /** + * Performs DNS lookups as needed to find servers which should or might + * support SMTP. Returns one SMTPHostAddresses for each such host + * discovered by DNS. If no host is found for domainName, the Iterator + * returned will be empty and the first call to hasNext() will return + * false. + * @param domainName the String domain for which SMTP host addresses are + * sought. + * @return an Enumeration in which the Objects returned by next() + * are instances of SMTPHostAddresses. + */ + public Iterator getSMTPHostAddresses(final String domainName) { + return new Iterator() { + private Iterator mxHosts = new MxSorter(domainName); + + public boolean hasNext(){ + return mxHosts.hasNext(); + } + + public Object next(){ + String nextHostname = (String)mxHosts.next(); + Record[] aRecords = lookup(nextHostname, Type.A); + return new SMTPHostAddressesImpl(aRecords, nextHostname); + } + + public void remove () { + throw new UnsupportedOperationException ("remove not supported by this iterator"); + } + }; + } + + /** A way to get mail hosts to try. If any MX hosts are found for the + * domain name with which this is constructed, then these MX hostnames + * are returned in priority sorted order, lowest priority numbers coming + * first. And, whenever multiple hosts have the same priority then these + * are returned in a randomized order within that priority group, as + * specified in RFC 2821, Section 5. + * + * If no MX hosts are found for the domain name, then a DNS search is + * performed for an A record. If an A record is found then domainName itself + * will be returned by the Iterator, and it will be the only object in + * the Iterator. If however no A record is found (in addition to no MX + * record) then the Iterator constructed will be empty; the first call to + * its hasNext() will return false. + * + * This behavior attempts to satisfy the requirements of RFC 2821, Section 5. + */ + private class MxSorter implements Iterator { + private int priorListPriority = Integer.MIN_VALUE; + private ArrayList equiPriorityList = new ArrayList(); + private Record[] mxRecords; + private Random rnd = new Random (); + + /* The implementation of this class attempts to achieve efficiency by + * performing no more sorting of the rawMxRecords than necessary. In the + * large majority of cases the first attempt, made by a client of this class + * to connect to an SMTP server for a given domain, will succeed. As such, + * in most cases only one call will be made to this Iterator's + * next(), and in that majority of cases there will have been no need + * to sort the array of MX Records. This implementation would, however, be + * relatively inefficient in the case where all hosts fail, when every + * Object is called out of a long Iterator. + */ + + private MxSorter(String domainName) { + mxRecords = lookup(domainName, Type.MX); + if (mxRecords == null || mxRecords.length == 0) { + //no MX records were found, so try to use the domainName + Record[] aRecords = lookup(domainName, Type.A); + if(aRecords != null && aRecords.length > 0) { + equiPriorityList.add(domainName); + } + } + } + + /** + * Sets presentPriorityList to contain all hosts + * which have the least priority greater than pastPriority. + * When this is called, both (rawMxRecords.length > 0) and + * (presentPriorityList.size() == 0), by contract. + * In the case where this is called repeatedly, so that priorListPriority + * has already become the highest of the priorities in the rawMxRecords, + * then this returns without having added any elements to + * presentPriorityList; presentPriorityList.size remains zero. + */ + private void createPriorityList(){ + int leastPriorityFound = Integer.MAX_VALUE; + /* We loop once through the rawMxRecords, finding the lowest priority + * greater than priorListPriority, and collecting all the hostnames + * with that priority into equiPriorityList. + */ + for (int i = 0; i < mxRecords.length; i++) { + MXRecord thisRecord = (MXRecord)mxRecords[i]; + int thisRecordPriority = thisRecord.getPriority(); + if (thisRecordPriority > priorListPriority) { + if (thisRecordPriority < leastPriorityFound) { + equiPriorityList.clear(); + leastPriorityFound = thisRecordPriority; + equiPriorityList.add(thisRecord.getTarget().toString()); + } else if (thisRecordPriority == leastPriorityFound) { + equiPriorityList.add(thisRecord.getTarget().toString()); + } + } + } + priorListPriority = leastPriorityFound; + } + + public boolean hasNext(){ + if (equiPriorityList.size() > 0){ + return true; + }else if (mxRecords != null && mxRecords.length > 0){ + createPriorityList(); + return equiPriorityList.size() > 0; + } else{ + return false; + } + } + + public Object next(){ + if (hasNext()){ + /* this randomization is done to comply with RFC-2821 */ + /* Note: java.util.Random.nextInt(limit) is about twice as fast as (int)(Math.random()*limit) */ + int getIndex = rnd.nextInt(equiPriorityList.size()); + Object returnElement = equiPriorityList.get(getIndex); + equiPriorityList.remove(getIndex); + return returnElement; + }else{ + throw new NoSuchElementException(); + } + } + + public void remove () { + throw new UnsupportedOperationException ("remove not supported by this iterator"); + } + } + } 1.1.2.1 +117 -0 james-server/src/java/org/apache/james/dnsserver/Attic/SMTPHostAddressesImpl.java No revision No revision 1.4.4.3 +16 -0 james-server/src/java/org/apache/james/services/DNSServer.java Index: DNSServer.java =================================================================== RCS file: /home/cvs/james-server/src/java/org/apache/james/services/DNSServer.java,v retrieving revision 1.4.4.2 retrieving revision 1.4.4.3 diff -u -r1.4.4.2 -r1.4.4.3 --- DNSServer.java 8 Mar 2003 21:54:06 -0000 1.4.4.2 +++ DNSServer.java 12 Dec 2003 22:48:19 -0000 1.4.4.3 @@ -59,6 +59,7 @@ package org.apache.james.services; import java.util.Collection; +import java.util.Iterator; /** * Provides abstraction for DNS resolutions. The interface is Mail specific. @@ -83,4 +84,19 @@ * @return collection of strings representing MX record values. */ Collection findMXRecords(String hostname); + + + /** + * Performs DNS lookups as needed to find servers which should or might + * support SMTP. Returns one SMTPHostAddresses for each such host + * discovered by DNS. If no host is found for domainName, the Iterator + * returned will be empty and the first call to hasNext() will return + * false. + * @param domainName the String domain for which SMTP host addresses are + * sought. + * @return an Enumeration in which the Objects returned by next() + * are instances of SMTPHostAddresses. + */ + Iterator getSMTPHostAddresses(String domainName); + } No revision No revision 1.33.4.15 +167 -94 james-server/src/java/org/apache/james/transport/mailets/RemoteDelivery.java Index: RemoteDelivery.java =================================================================== RCS file: /home/cvs/james-server/src/java/org/apache/james/transport/mailets/RemoteDelivery.java,v retrieving revision 1.33.4.14 retrieving revision 1.33.4.15 diff -u -r1.33.4.14 -r1.33.4.15 --- RemoteDelivery.java 16 Nov 2003 21:47:24 -0000 1.33.4.14 +++ RemoteDelivery.java 12 Dec 2003 22:48:19 -0000 1.33.4.15 @@ -97,6 +97,7 @@ import org.apache.james.services.SpoolRepository; import org.apache.mailet.GenericMailet; import org.apache.mailet.Mail; +import org.apache.mailet.MailetContext; import org.apache.mailet.MailAddress; import org.apache.oro.text.regex.MalformedPatternException; @@ -335,7 +336,11 @@ } if (isDebug) log("Adding SMTP gateway: " + server) ; - gatewayServer.add(server); + try { + gatewayServer.add(new GatewaySMTPHostAddresses(server)); + } catch (UnknownHostException uhe) { + log("Invalid gateway address:" + uhe.getMessage()); + } } } @@ -419,14 +424,14 @@ //Figure out which servers to try to send to. This collection // will hold all the possible target servers - Collection targetServers = null; + Iterator targetServers = null; if (gatewayServer == null) { MailAddress rcpt = (MailAddress) recipients.iterator().next(); String host = rcpt.getHost(); //Lookup the possible targets - targetServers = getMailetContext().getMailServers(host); - if (targetServers.size() == 0) { + targetServers = getMailetContext().getSMTPHostAddresses(host); + if (!targetServers.hasNext()) { log("No mail server found for: " + host); StringBuffer exceptionBuffer = new StringBuffer(128) @@ -436,105 +441,124 @@ return failMessage(mail, new MessagingException(exceptionBuffer.toString()), false); } } else { - targetServers = gatewayServer; + targetServers = gatewayServer.iterator(); } MessagingException lastError = null; - Iterator i = targetServers.iterator(); - while ( i.hasNext()) { - try { - String outgoingMailServer = i.next().toString (); - StringBuffer logMessageBuffer = - new StringBuffer(256) - .append("Attempting delivery of ") - .append(mail.getName()) - .append(" to host ") - .append(outgoingMailServer) - .append(" to addresses ") - .append(Arrays.asList(addr)); - log(logMessageBuffer.toString()); - URLName urlname = new URLName("smtp://" + outgoingMailServer); - - 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); - } - - //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. - - Transport transport = null; + while ( targetServers.hasNext() ) { + MailetContext.SMTPHostAddresses thisHost = + (MailetContext.SMTPHostAddresses)targetServers.next(); + String thisHostName = thisHost.getHostname(); + InetAddress[] addresses = thisHost.getAddresses(); + for (int addressIndex = 0; addressIndex < addresses.length; addressIndex++){ try { - transport = session.getTransport(urlname); + InetAddress thisAddress = addresses[addressIndex]; + int thisPort = thisHost.getPort(thisAddress); + + StringBuffer outgoingMailServerBuf = + new StringBuffer(256).append(thisAddress) + .append(':') + .append(thisPort); + if (outgoingMailServerBuf.charAt(0) == '/') { + outgoingMailServerBuf.deleteCharAt(0); + } + String outgoingMailServer = outgoingMailServerBuf.toString(); + + StringBuffer logMessageBuffer = + new StringBuffer(256) + .append("Attempting delivery of ") + .append(mail.getName()) + .append(" to host ") + .append(thisHostName) + .append(" at ") + .append(outgoingMailServer) + .append(" to addresses ") + .append(Arrays.asList(addr)); + log(logMessageBuffer.toString()); + URLName urlname = new URLName("smtp://" + outgoingMailServer); + + 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); + } + + //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. + Transport transport = null; try { - transport.connect(); - } catch (MessagingException me) { - // Any error on connect should cause the mailet to attempt - // to connect to the next SMTP server associated with this - // MX record. Just log the exception. We'll worry about - // failing the message at the end of the loop. - log(me.getMessage()); + transport = session.getTransport(urlname); + try { + transport.connect(); + } catch (MessagingException me) { + // Any error on connect should cause the mailet to attempt + // to connect to the next SMTP server associated with this + // MX record. Just log the exception. We'll worry about + // failing the message at the end of the loop. + log(me.getMessage()); + continue; + } + transport.sendMessage(message, addr); + } finally { + if (transport != null) { + transport.close(); + transport = null; + } + } + logMessageBuffer = + new StringBuffer(256) + .append("Mail (") + .append(mail.getName()) + .append(") sent successfully to ") + .append(thisHostName) + .append(" at ") + .append(outgoingMailServer); + log(logMessageBuffer.toString()); + return true; + } catch (SendFailedException sfe) { + if (sfe.getValidSentAddresses() == null + || sfe.getValidSentAddresses().length < 1) { + if (isDebug) log("Send failed, continuing with any other servers"); + lastError = sfe; continue; + } else { + // If any mail was sent then the outgoing + // server config must be ok, therefore rethrow + throw sfe; } - transport.sendMessage(message, addr); - } finally { - if (transport != null) { - transport.close(); - transport = null; + } catch (MessagingException me) { + //MessagingException are horribly difficult to figure out what actually happened. + StringBuffer exceptionBuffer = + new StringBuffer(256) + .append("Exception delivering message (") + .append(mail.getName()) + .append(") - ") + .append(me.getMessage()); + log(exceptionBuffer.toString()); + if ((me.getNextException() != null) && + (me.getNextException() instanceof java.io.IOException)) { + //This is more than likely a temporary failure + + // If it's an IO exception with no nested exception, it's probably + // some socket or weird I/O related problem. + lastError = me; + continue; } + // 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; } - logMessageBuffer = - new StringBuffer(256) - .append("Mail (") - .append(mail.getName()) - .append(") sent successfully to ") - .append(outgoingMailServer); - log(logMessageBuffer.toString()); - return true; - } catch (SendFailedException sfe) { - if (sfe.getValidSentAddresses() == null - || sfe.getValidSentAddresses().length < 1) { - if (isDebug) log("Send failed, continuing with any other servers"); - lastError = sfe; - continue; - } else { - // If any mail was sent then the outgoing - // server config must be ok, therefore rethrow - throw sfe; - } - } catch (MessagingException me) { - //MessagingException are horribly difficult to figure out what actually happened. - StringBuffer exceptionBuffer = - new StringBuffer(256) - .append("Exception delivering message (") - .append(mail.getName()) - .append(") - ") - .append(me.getMessage()); - log(exceptionBuffer.toString()); - if ((me.getNextException() != null) && - (me.getNextException() instanceof java.io.IOException)) { - //This is more than likely a temporary failure - - // If it's an IO exception with no nested exception, it's probably - // some socket or weird I/O related problem. - lastError = me; - continue; - } - // 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; - } + } //end for } // end while //If we encountered an exception while looping through, //throw the last MessagingException we caught. We only @@ -1092,5 +1116,54 @@ return buf.toString(); } } + + /** + * This implementation of MailetContext.SMTPHostAddresses is used for holding gateway information + */ + private class GatewaySMTPHostAddresses implements MailetContext.SMTPHostAddresses + { + protected InetAddress[] ipAddresses; + protected int port; + + /** + * @param server gateway to use the String is of the form "address<:port>" + */ + public GatewaySMTPHostAddresses (String server) throws UnknownHostException + { + int idx = server.indexOf(':'); + if ( idx > 0) { + port = Integer.parseInt (server.substring(idx+1)); + server = server.substring(0,idx); + } else { + port = 25; + } + ipAddresses = new InetAddress[1]; + ipAddresses[0] = InetAddress.getByName(server); + } + + /** + * @return the hostName of the SMTP server (from the MX record lookup) + */ + public String getHostname() { + return "Gateway"; + } + + /** + * @return an array with the ip addresses of the hostname. An array is + * used because a host can have multiple homes (addresses) + */ + public InetAddress[] getAddresses() { + return ipAddresses; + } + + + /** + * @param address for which we need the port to use in SMTP connection + * @return the port number to use for the given address (this will usually be 25 for SMTP) + */ + public int getPort(InetAddress address) { + return port; + } + } } No revision No revision 1.5.4.6 +39 -0 james-server/src/java/org/apache/mailet/MailetContext.java Index: MailetContext.java =================================================================== RCS file: /home/cvs/james-server/src/java/org/apache/mailet/MailetContext.java,v retrieving revision 1.5.4.5 retrieving revision 1.5.4.6 diff -u -r1.5.4.5 -r1.5.4.6 --- MailetContext.java 21 Sep 2003 19:09:36 -0000 1.5.4.5 +++ MailetContext.java 12 Dec 2003 22:48:19 -0000 1.5.4.6 @@ -62,6 +62,7 @@ import javax.mail.internet.MimeMessage; import java.util.Collection; import java.util.Iterator; +import java.net.InetAddress; /** * Defines a set of methods that a mailet or matcher uses to communicate @@ -288,4 +289,42 @@ */ void storeMail(MailAddress sender, MailAddress recipient, MimeMessage msg) throws MessagingException; + + /** + * Performs DNS lookups as needed to find servers which should or might + * support SMTP. + * Returns one SMTPHostAddresses for each such host discovered + * by DNS. If no host is found for domainName, the Iterator + * returned will be empty and the first call to hasNext() will return + * false. + * @param domainName the String domain for which SMTP host addresses are + * sought. + * @return an Iterator in which the Objects returned by next() + * are instances of SMTPHostAddresses. + */ + Iterator getSMTPHostAddresses(String domainName); + + /** + * The Iterator returned by getSMTPHostAddresses(host) holds instances + * of this interface. + */ + interface SMTPHostAddresses { + /** + * @return the hostName of the SMTP server (from the MX record lookup) + */ + String getHostname(); + + /** + * @return an array with the ip addresses of the hostname. An array is + * used because a host can have multiple homes (addresses) + */ + InetAddress[] getAddresses(); + + /** + * @param address for which we need the port to use in SMTP connection + * @return the port number to use for the given address (this will usually be 25 for SMTP) + */ + int getPort(InetAddress address); + } + }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]