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 }