001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  You may obtain a copy of the License at   *
009 *                                                              *
010 *   http://www.apache.org/licenses/LICENSE-2.0                 *
011 *                                                              *
012 * Unless required by applicable law or agreed to in writing,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019
020package org.apache.james.domainlist.lib;
021
022import java.net.InetAddress;
023import java.net.UnknownHostException;
024import java.util.ArrayList;
025import java.util.List;
026import java.util.Locale;
027
028import javax.inject.Inject;
029
030import org.apache.commons.configuration.ConfigurationException;
031import org.apache.commons.configuration.HierarchicalConfiguration;
032import org.apache.james.dnsservice.api.DNSService;
033import org.apache.james.domainlist.api.DomainList;
034import org.apache.james.domainlist.api.DomainListException;
035import org.apache.james.lifecycle.api.Configurable;
036import org.apache.james.lifecycle.api.LogEnabled;
037import org.slf4j.Logger;
038import org.slf4j.LoggerFactory;
039
040import com.google.common.annotations.VisibleForTesting;
041import com.google.common.collect.ImmutableList;
042
043/**
044 * All implementations of the DomainList interface should extends this abstract
045 * class
046 */
047public abstract class AbstractDomainList implements DomainList, LogEnabled, Configurable {
048
049    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDomainList.class);
050
051    protected static final String LOCALHOST = "localhost";
052
053    private DNSService dns;
054    private boolean autoDetect = true;
055    private boolean autoDetectIP = true;
056    private Logger logger;
057    private String defaultDomain;
058
059    @Inject
060    public void setDNSService(DNSService dns) {
061        this.dns = dns;
062    }
063
064    public void setLog(Logger logger) {
065        this.logger = logger;
066    }
067
068    protected Logger getLogger() {
069        return logger;
070    }
071
072    @Override
073    public void configure(HierarchicalConfiguration config) throws ConfigurationException {
074        configureDefaultDomain(config);
075
076        setAutoDetect(config.getBoolean("autodetect", true));
077        setAutoDetectIP(config.getBoolean("autodetectIP", true));
078    }
079
080    @VisibleForTesting void configureDefaultDomain(HierarchicalConfiguration config) throws ConfigurationException {
081        
082        try {
083            defaultDomain = config.getString("defaultDomain", LOCALHOST);
084
085            String hostName = InetAddress.getLocalHost().getHostName();
086            if (mayChangeDefaultDomain()) {
087                setDefaultDomain(hostName);
088            }
089        } catch (UnknownHostException e) {
090            LOGGER.warn("Unable to retrieve hostname.", e);
091        } catch (DomainListException e) {
092            LOGGER.error("An error occured while creating the default domain", e);
093        }
094    }
095
096    private boolean mayChangeDefaultDomain() {
097        return LOCALHOST.equals(defaultDomain);
098    }
099
100    private void setDefaultDomain(String defaultDomain) throws DomainListException {
101        if (!containsDomain(defaultDomain)) {
102            addDomain(defaultDomain);
103        }
104        this.defaultDomain = defaultDomain;
105    }
106
107    @Override
108    public String getDefaultDomain() throws DomainListException {
109        if (defaultDomain!= null) {
110            return defaultDomain;
111        } else {
112            throw new DomainListException("Null default domain. Domain list might not be configured yet.");
113        }
114    }
115
116    @Override
117    public List<String> getDomains() throws DomainListException {
118        List<String> domains = getDomainListInternal();
119        if (domains != null) {
120
121            // create mutable copy, some subclasses return ImmutableList
122            ArrayList<String> mutableDomains = new ArrayList<String>(domains);
123
124            String hostName;
125            try {
126                hostName = getDNSServer().getHostName(getDNSServer().getLocalHost());
127            } catch (UnknownHostException ue) {
128                hostName = "localhost";
129            }
130
131            getLogger().info("Local host is: " + hostName);
132
133            if (autoDetect && (!hostName.equals("localhost"))) {
134                mutableDomains.add(hostName.toLowerCase(Locale.US));
135            }
136
137            if (autoDetectIP) {
138                mutableDomains.addAll(getDomainsIP(mutableDomains, dns, getLogger()));
139            }
140
141            if (getLogger().isInfoEnabled()) {
142                for (String domain : mutableDomains) {
143                    getLogger().debug("Handling mail for: " + domain);
144                }
145            }
146
147            return ImmutableList.copyOf(mutableDomains);
148        }
149        return ImmutableList.of();// empty list
150    }
151
152    /**
153     * Return a List which holds all ipAddress of the domains in the given List
154     * 
155     * @param domains
156     *            List of domains
157     * @return domainIP List of ipaddress for domains
158     */
159    private static List<String> getDomainsIP(List<String> domains, DNSService dns, Logger log) {
160        List<String> domainIP = new ArrayList<String>();
161        if (domains.size() > 0) {
162            for (String domain : domains) {
163                List<String> domList = getDomainIP(domain, dns, log);
164
165                for (String aDomList : domList) {
166                    if (!domainIP.contains(aDomList)) {
167                        domainIP.add(aDomList);
168                    }
169                }
170            }
171        }
172        return domainIP;
173    }
174
175    /**
176     * @see #getDomainsIP(List, DNSService, Logger)
177     */
178    private static List<String> getDomainIP(String domain, DNSService dns, Logger log) {
179        List<String> domainIP = new ArrayList<String>();
180        try {
181            InetAddress[] addrs = dns.getAllByName(domain);
182            for (InetAddress addr : addrs) {
183                String ip = addr.getHostAddress();
184                if (!domainIP.contains(ip)) {
185                    domainIP.add(ip);
186                }
187            }
188        } catch (UnknownHostException e) {
189            log.error("Cannot get IP address(es) for " + domain);
190        }
191        return domainIP;
192    }
193
194    /**
195     * Set to true to autodetect the hostname of the host on which james is
196     * running, and add this to the domain service Default is true
197     * 
198     * @param autoDetect
199     *            set to <code>false</code> for disable
200     */
201    public synchronized void setAutoDetect(boolean autoDetect) {
202        getLogger().info("Set autodetect to: " + autoDetect);
203        this.autoDetect = autoDetect;
204    }
205
206    /**
207     * Set to true to lookup the ipaddresses for each given domain and add these
208     * to the domain service Default is true
209     * 
210     * @param autoDetectIP
211     *            set to <code>false</code> for disable
212     */
213    public synchronized void setAutoDetectIP(boolean autoDetectIP) {
214        getLogger().info("Set autodetectIP to: " + autoDetectIP);
215        this.autoDetectIP = autoDetectIP;
216    }
217
218    /**
219     * Return dnsServer
220     * 
221     * @return dns
222     */
223    protected DNSService getDNSServer() {
224        return dns;
225    }
226
227    /**
228     * Return domainList
229     * 
230     * @return List
231     */
232    protected abstract List<String> getDomainListInternal() throws DomainListException;
233
234}