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    
020    package org.apache.james.jspf.policies.local;
021    
022    import org.apache.james.jspf.core.Logger;
023    import org.apache.james.jspf.core.SPF1Record;
024    import org.apache.james.jspf.core.SPFRecordParser;
025    import org.apache.james.jspf.core.exceptions.NeutralException;
026    import org.apache.james.jspf.core.exceptions.NoneException;
027    import org.apache.james.jspf.core.exceptions.PermErrorException;
028    import org.apache.james.jspf.core.exceptions.SPFResultException;
029    import org.apache.james.jspf.core.exceptions.TempErrorException;
030    import org.apache.james.jspf.policies.PolicyPostFilter;
031    
032    import java.util.Collections;
033    import java.util.HashMap;
034    import java.util.Iterator;
035    import java.util.Map;
036    
037    /**
038     * Class to support Fallback feature
039     */
040    public class FallbackPolicy implements PolicyPostFilter {
041    
042        private Map<String,SPF1Record> entryMap;
043    
044        private SPFRecordParser parser;
045    
046        private Logger log;
047    
048        public FallbackPolicy(Logger log, SPFRecordParser parser) {
049            this.log = log;
050            entryMap = Collections.synchronizedMap(new HashMap<String,SPF1Record>());
051            this.parser = parser;
052        }
053    
054        /**
055         * Add a entry.
056         * 
057         * @param rawHost
058         *            the host or ipaddress for which the entry should be added.
059         * @param rawSpfRecord
060         *            the spfRecord to add
061         * @throws IllegalArgumentException
062         *             get thrown on invalid spfRecord
063         */
064        public void addEntry(String rawHost, String rawSpfRecord)
065                throws IllegalArgumentException {
066            String host;
067            try {
068                log.debug("Start parsing SPF-Record: " + rawSpfRecord);
069                SPF1Record spfRecord = parser.parse(rawSpfRecord);
070                if (rawHost.startsWith("*")) {
071                    host = rawHost.substring(1);
072                    log.debug("Convert host " + rawHost + " to " + host);
073                } else if (rawHost.endsWith("*")) {
074                    int length = rawHost.length();
075                    host = rawHost.substring(length - 1, length);
076                    log.debug("Convert host " + rawHost + " to " + host);
077                } else {
078                    host = rawHost;
079                }
080    
081                entryMap.put(host, spfRecord);
082            } catch (SPFResultException e) {
083                throw new IllegalArgumentException("Invalid SPF-Record: "
084                        + rawSpfRecord);
085            }
086    
087        }
088    
089        /**
090         * Clear all entries
091         * 
092         */
093        public void clearEntrys() {
094            log.debug("Clear all entries");
095            entryMap.clear();
096        }
097    
098        /**
099         * Remove entry
100         * 
101         * @param host
102         *            The host
103         */
104        public void removeEntry(String host) {
105            log.debug("Remove fallback entry for host: " + host);
106            synchronized (entryMap) {
107                entryMap.remove(getRawEntry(host));
108            }
109        }
110    
111        /**
112         * @see org.apache.james.jspf.policies.PolicyPostFilter#getSPFRecord(java.lang.String, org.apache.james.jspf.core.SPF1Record)
113         */
114        public SPF1Record getSPFRecord(String currentDomain, SPF1Record res) throws PermErrorException, TempErrorException, NoneException, NeutralException {
115            if (res == null) {
116                return getMySPFRecord(currentDomain);
117            } else {
118                return res;
119            }
120        }
121        
122        /**
123         * Return the SPF1Record for the given host
124         * 
125         * @param host
126         *            the hostname or ipaddress
127         * @return the SPF1Record of null if no SPF1Record was found in fallback for
128         *         the given host
129         */
130        protected SPF1Record getMySPFRecord(String host) {
131            SPF1Record entry = null;
132    
133            synchronized (entryMap) {
134                entry = getRawEntry(host);
135            }
136    
137            if (entry != null) {
138                return entry;
139            } else {
140                return null;
141            }
142        }
143    
144        /**
145         * Return the Object stored in the map which match the given host. Keep in
146         * mind that this method should only called in a synchronized method or
147         * block
148         * 
149         * @param host
150         *            the host
151         * @return the stored object for the given host or null
152         */
153        private SPF1Record getRawEntry(String host) {
154            Iterator<String> fallBackIt = entryMap.keySet().iterator();
155    
156            while (fallBackIt.hasNext()) {
157                String rawHost = fallBackIt.next();
158    
159                if ((rawHost.startsWith(".") && host.startsWith(rawHost))
160                        || rawHost.endsWith(".") && host.endsWith(rawHost)) {
161                    return entryMap.get(rawHost);
162                }
163            }
164            return null;
165        }
166    
167    }