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    
021    package org.apache.james.jspf.terms;
022    
023    import org.apache.james.jspf.core.DNSLookupContinuation;
024    import org.apache.james.jspf.core.LogEnabled;
025    import org.apache.james.jspf.core.Logger;
026    import org.apache.james.jspf.core.MacroExpand;
027    import org.apache.james.jspf.core.MacroExpandEnabled;
028    import org.apache.james.jspf.core.SPF1Constants;
029    import org.apache.james.jspf.core.SPFCheckEnabled;
030    import org.apache.james.jspf.core.SPFChecker;
031    import org.apache.james.jspf.core.SPFCheckerExceptionCatcher;
032    import org.apache.james.jspf.core.SPFSession;
033    import org.apache.james.jspf.core.SPFTermsRegexps;
034    import org.apache.james.jspf.core.exceptions.NeutralException;
035    import org.apache.james.jspf.core.exceptions.NoneException;
036    import org.apache.james.jspf.core.exceptions.PermErrorException;
037    import org.apache.james.jspf.core.exceptions.TempErrorException;
038    
039    /**
040     * This class represent the incude mechanism
041     * 
042     */
043    public class IncludeMechanism implements Mechanism, ConfigurationEnabled, LogEnabled, SPFCheckEnabled, MacroExpandEnabled {
044    
045        private final class ExpandedChecker implements SPFChecker {
046          
047            /**
048            * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
049            */
050            public DNSLookupContinuation checkSPF(SPFSession spfData) throws PermErrorException,
051                    TempErrorException {
052    
053                // throws a PermErrorException that we can pass through
054                String host = macroExpand.expand(getHost(), spfData, MacroExpand.DOMAIN);
055                
056                spfData.setCurrentDomain(host);
057                
058                // On includes we should not use the explanation of the included domain
059                spfData.setIgnoreExplanation(true);
060                // set a null current result
061                spfData.setCurrentResult(null);
062                spfData.setCurrentResultExpanded(null);
063                
064                spfData.pushChecker(spfChecker);
065                
066                return null;
067            }
068        }
069    
070        private final class CleanupAndResultChecker implements SPFChecker, SPFCheckerExceptionCatcher {
071            private String previousResult;
072            private String previousResultExpanded;
073            private String previousDomain;
074    
075            private void restoreSession(SPFSession spfData) {
076                spfData.setIgnoreExplanation(false);
077                spfData.setCurrentDomain(previousDomain);
078                spfData.setCurrentResult(previousResult);
079                spfData.setCurrentResultExpanded(previousResultExpanded);
080            }
081            
082            /**
083             * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
084             */
085            public DNSLookupContinuation checkSPF(SPFSession spfData) throws PermErrorException,
086                    TempErrorException, NeutralException, NoneException {
087                
088                String currentResult = spfData.getCurrentResult();
089                
090                restoreSession(spfData);
091                
092                if (currentResult == null) {
093                    throw new TempErrorException("included checkSPF returned null");
094                } else if (currentResult.equals(SPF1Constants.PASS)) {
095                    // TODO this won't work asynchronously
096                    spfData.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.TRUE);
097                } else if (currentResult.equals(SPF1Constants.FAIL) || currentResult.equals(SPF1Constants.SOFTFAIL) || currentResult.equals(SPF1Constants.NEUTRAL)) {
098                    // TODO this won't work asynchronously
099                    spfData.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.FALSE);
100                } else {
101                    throw new TempErrorException("included checkSPF returned an Illegal result");
102                }
103    
104                return null;
105            }
106    
107            /**
108             * @see org.apache.james.jspf.core.SPFCheckerExceptionCatcher#onException(java.lang.Exception, org.apache.james.jspf.core.SPFSession)
109             */
110            public void onException(Exception exception, SPFSession session)
111                    throws PermErrorException, NoneException,
112                    TempErrorException, NeutralException {
113                
114                restoreSession(session);
115                
116                if (exception instanceof NeutralException) {
117                    throw new PermErrorException("included checkSPF returned NeutralException");
118                } else if (exception instanceof NoneException) {
119                    throw new PermErrorException("included checkSPF returned NoneException");
120                } else if (exception instanceof PermErrorException){
121                    throw (PermErrorException) exception;
122                } else if (exception instanceof TempErrorException){
123                    throw (TempErrorException) exception;
124                } else if (exception instanceof RuntimeException){
125                    throw (RuntimeException) exception;
126                } else {
127                    throw new IllegalStateException(exception.getMessage());
128                }
129            }
130    
131            public SPFChecker init(SPFSession spfSession) {
132                // TODO understand what exactly we have to do now that spfData is a session
133                // and contains much more than the input data.
134                // do we need to create a new session at all?
135                // do we need to backup the session attributes and restore them?
136                this.previousResult = spfSession.getCurrentResult();
137                this.previousDomain = spfSession.getCurrentDomain();
138                this.previousResultExpanded = spfSession.getCurrentResultExpanded();
139                return this;
140            }
141        }
142    
143        /**
144         * ABNF: include = "include" ":" domain-spec
145         */
146        public static final String REGEX = "[iI][nN][cC][lL][uU][dD][eE]" + "\\:"
147                + SPFTermsRegexps.DOMAIN_SPEC_REGEX;
148    
149        protected String host;
150        
151        protected Logger log;
152    
153        private SPFChecker spfChecker;
154    
155        private MacroExpand macroExpand;
156    
157        /**
158         * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
159         */
160        public DNSLookupContinuation checkSPF(SPFSession spfData) throws PermErrorException, TempErrorException, NoneException, NeutralException {
161            // update currentDepth
162            spfData.increaseCurrentDepth();
163            
164            SPFChecker cleanupAndResultHandler = new CleanupAndResultChecker().init(spfData);
165            spfData.pushChecker(cleanupAndResultHandler);
166            
167            spfData.pushChecker(new ExpandedChecker());
168            return macroExpand.checkExpand(getHost(), spfData, MacroExpand.DOMAIN);
169        }
170    
171        /**
172         * @see org.apache.james.jspf.terms.ConfigurationEnabled#config(Configuration)
173         */
174        public synchronized void config(Configuration params) throws PermErrorException {
175            if (params.groupCount() == 0) {
176                throw new PermErrorException("Include mechanism without an host");
177            }
178            host = params.group(1);
179        }
180    
181        /**
182         * @return Returns the host.
183         */
184        protected synchronized String getHost() {
185            return host;
186        }
187    
188        /**
189         * @see org.apache.james.jspf.core.LogEnabled#enableLogging(org.apache.james.jspf.core.Logger)
190         */
191        public void enableLogging(Logger logger) {
192            this.log = logger;
193        }
194    
195        /**
196         * @see java.lang.Object#toString()
197         */
198        public String toString() {
199            return "include:"+getHost();
200        }
201    
202        /**
203         * @see org.apache.james.jspf.core.SPFCheckEnabled#enableSPFChecking(org.apache.james.jspf.core.SPFChecker)
204         */
205        public void enableSPFChecking(SPFChecker checker) {
206            this.spfChecker = checker;
207        }
208    
209        /**
210         * @see org.apache.james.jspf.core.MacroExpandEnabled#enableMacroExpand(org.apache.james.jspf.core.MacroExpand)
211         */
212        public void enableMacroExpand(MacroExpand macroExpand) {
213            this.macroExpand = macroExpand;
214        }
215    }