1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.james.jspf.parser;
22
23 import org.apache.james.jspf.core.Logger;
24 import org.apache.james.jspf.core.SPF1Constants;
25 import org.apache.james.jspf.core.SPF1Record;
26 import org.apache.james.jspf.core.SPFRecordParser;
27 import org.apache.james.jspf.core.exceptions.NeutralException;
28 import org.apache.james.jspf.core.exceptions.NoneException;
29 import org.apache.james.jspf.core.exceptions.PermErrorException;
30 import org.apache.james.jspf.terms.Configuration;
31 import org.apache.james.jspf.terms.Directive;
32 import org.apache.james.jspf.terms.Mechanism;
33 import org.apache.james.jspf.terms.Modifier;
34
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public class RFC4408SPF1Parser implements SPFRecordParser {
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94 private static final String QUALIFIER_PATTERN = "[" + "\\"
95 + SPF1Constants.PASS + "\\" + SPF1Constants.FAIL + "\\"
96 + SPF1Constants.NEUTRAL + "\\" + SPF1Constants.SOFTFAIL + "]";
97
98 private Pattern termsSeparatorPattern = null;
99
100 private Pattern termPattern = null;
101
102 private int TERM_STEP_REGEX_QUALIFIER_POS;
103
104 private int TERM_STEP_REGEX_MECHANISM_POS;
105
106 private int TERM_STEP_REGEX_MODIFIER_POS;
107
108 private List matchResultPositions;
109
110 private Logger log;
111
112 private TermsFactory termsFactory;
113
114
115
116
117
118
119
120 public RFC4408SPF1Parser(Logger logger, TermsFactory termsFactory) {
121 this.log = logger;
122 this.termsFactory = termsFactory;
123
124
125
126
127 String MECHANISM_REGEX = createRegex(termsFactory.getMechanismsCollection());
128
129
130
131
132 String MODIFIER_REGEX = "(" + createRegex(termsFactory.getModifiersCollection()) + ")";
133
134
135
136
137 String DIRECTIVE_REGEX = "(" + QUALIFIER_PATTERN + "?)("
138 + MECHANISM_REGEX + ")";
139
140
141
142
143 String TERM_REGEX = "(?:" + MODIFIER_REGEX + "|" + DIRECTIVE_REGEX
144 + ")";
145
146
147
148
149 String TERMS_SEPARATOR_REGEX = "[ ]+";
150
151 termsSeparatorPattern = Pattern.compile(TERMS_SEPARATOR_REGEX);
152 termPattern = Pattern.compile(TERM_REGEX);
153
154 initializePositions();
155 }
156
157
158
159
160
161
162 private void initializePositions() {
163 ArrayList matchResultPositions = new ArrayList();
164
165
166 int posIndex = 0;
167 matchResultPositions.ensureCapacity(posIndex + 1);
168 matchResultPositions.add(posIndex, null);
169
170 Iterator i;
171
172 TERM_STEP_REGEX_MODIFIER_POS = ++posIndex;
173 matchResultPositions.ensureCapacity(posIndex + 1);
174 matchResultPositions.add(TERM_STEP_REGEX_MODIFIER_POS, null);
175 i = termsFactory.getModifiersCollection().iterator();
176 while (i.hasNext()) {
177 TermDefinition td = (TermDefinition) i.next();
178 int size = td.getMatchSize() + 1;
179 for (int k = 0; k < size; k++) {
180 posIndex++;
181 matchResultPositions.ensureCapacity(posIndex + 1);
182 matchResultPositions.add(posIndex, td);
183 }
184 }
185
186 TERM_STEP_REGEX_QUALIFIER_POS = ++posIndex;
187 matchResultPositions.ensureCapacity(posIndex + 1);
188 matchResultPositions.add(posIndex, null);
189
190 TERM_STEP_REGEX_MECHANISM_POS = ++posIndex;
191 matchResultPositions.ensureCapacity(posIndex + 1);
192 matchResultPositions.add(TERM_STEP_REGEX_MECHANISM_POS, null);
193 i = termsFactory.getMechanismsCollection().iterator();
194 while (i.hasNext()) {
195 TermDefinition td = (TermDefinition) i.next();
196 int size = td.getMatchSize() + 1;
197 for (int k = 0; k < size; k++) {
198 posIndex++;
199 matchResultPositions.ensureCapacity(posIndex + 1);
200 matchResultPositions.add(posIndex, td);
201 }
202 }
203
204 if (log.isDebugEnabled()) {
205 log.debug("Parsing catch group positions: Modifiers["
206 + TERM_STEP_REGEX_MODIFIER_POS + "] Qualifier["
207 + TERM_STEP_REGEX_QUALIFIER_POS + "] Mechanism["
208 + TERM_STEP_REGEX_MECHANISM_POS + "]");
209 for (int k = 0; k < matchResultPositions.size(); k++) {
210 log
211 .debug(k
212 + ") "
213 + (matchResultPositions.get(k) != null ? ((TermDefinition) matchResultPositions
214 .get(k)).getPattern().pattern()
215 : null));
216 }
217 }
218
219 this.matchResultPositions = Collections.synchronizedList(matchResultPositions);
220 }
221
222
223
224
225
226
227
228
229
230
231
232
233 private String createRegex(Collection commandMap) {
234 StringBuffer modifierRegex = new StringBuffer();
235 Iterator i = commandMap.iterator();
236 boolean first = true;
237 while (i.hasNext()) {
238 if (first) {
239 modifierRegex.append("(?:(");
240 first = false;
241 } else {
242 modifierRegex.append(")|(");
243 }
244 Pattern pattern = ((TermDefinition) i.next()).getPattern();
245 modifierRegex.append(pattern.pattern());
246 }
247 modifierRegex.append("))");
248 return modifierRegex.toString();
249 }
250
251
252
253
254 public SPF1Record parse(String spfRecord) throws PermErrorException,
255 NoneException, NeutralException {
256
257 log.debug("Start parsing SPF-Record: " + spfRecord);
258
259 SPF1Record result = new SPF1Record();
260
261
262 if (spfRecord.toLowerCase().startsWith(SPF1Constants.SPF_VERSION1 + " ") || spfRecord.equalsIgnoreCase(SPF1Constants.SPF_VERSION1)) {
263 if (!spfRecord.toLowerCase().startsWith(SPF1Constants.SPF_VERSION1 + " ")) throw new NeutralException("Empty SPF Record");
264 } else {
265 throw new NoneException("No valid SPF Record: " + spfRecord);
266 }
267
268
269 String[] terms = termsSeparatorPattern.split(spfRecord.replaceFirst(
270 SPF1Constants.SPF_VERSION1, ""));
271
272
273 for (int i = 0; i < terms.length; i++) {
274 if (terms[i].length() > 0) {
275 Matcher termMatcher = termPattern.matcher(terms[i]);
276 if (!termMatcher.matches()) {
277 throw new PermErrorException("Term [" + terms[i]
278 + "] is not syntactically valid: "
279 + termPattern.pattern());
280 }
281
282
283
284 String modifierString = termMatcher
285 .group(TERM_STEP_REGEX_MODIFIER_POS);
286
287 if (modifierString != null) {
288
289 Modifier mod = (Modifier) lookupAndCreateTerm(termMatcher,
290 TERM_STEP_REGEX_MODIFIER_POS);
291
292 if (mod.enforceSingleInstance()) {
293 Iterator it = result.getModifiers().iterator();
294 while (it.hasNext()) {
295 if (it.next().getClass().equals(mod.getClass())) {
296 throw new PermErrorException("More than one "
297 + modifierString
298 + " found in SPF-Record");
299 }
300 }
301 }
302
303 result.getModifiers().add(mod);
304
305 } else {
306
307 String qualifier = termMatcher
308 .group(TERM_STEP_REGEX_QUALIFIER_POS);
309
310 Object mech = lookupAndCreateTerm(termMatcher,
311 TERM_STEP_REGEX_MECHANISM_POS);
312
313 result.getDirectives().add(
314 new Directive(qualifier, (Mechanism) mech, log.getChildLogger(qualifier+"directive")));
315
316 }
317
318 }
319 }
320
321 return result;
322 }
323
324
325
326
327
328
329
330
331
332 private Object lookupAndCreateTerm(Matcher res, int start)
333 throws PermErrorException {
334 for (int k = start + 1; k < res.groupCount(); k++) {
335 if (res.group(k) != null && k != TERM_STEP_REGEX_QUALIFIER_POS) {
336 TermDefinition c = (TermDefinition) matchResultPositions.get(k);
337 Configuration subres = new MatcherBasedConfiguration(res, k, c
338 .getMatchSize());
339 try {
340 return termsFactory.createTerm(c.getTermDef(), subres);
341 } catch (InstantiationException e) {
342 e.printStackTrace();
343
344 throw new IllegalStateException("Unexpected error creating term: " + e.getMessage());
345 }
346
347 }
348 }
349 return null;
350 }
351
352 }