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.terms;
021    
022    import org.apache.james.jspf.core.DNSLookupContinuation;
023    import org.apache.james.jspf.core.MacroExpand;
024    import org.apache.james.jspf.core.MacroExpandEnabled;
025    import org.apache.james.jspf.core.SPFCheckEnabled;
026    import org.apache.james.jspf.core.SPFChecker;
027    import org.apache.james.jspf.core.SPFCheckerExceptionCatcher;
028    import org.apache.james.jspf.core.SPFSession;
029    import org.apache.james.jspf.core.SPFTermsRegexps;
030    import org.apache.james.jspf.core.exceptions.NeutralException;
031    import org.apache.james.jspf.core.exceptions.NoneException;
032    import org.apache.james.jspf.core.exceptions.PermErrorException;
033    import org.apache.james.jspf.core.exceptions.TempErrorException;
034    
035    /**
036     * This class represent the redirect modifier
037     * 
038     */
039    public class RedirectModifier extends GenericModifier implements
040            SPFCheckEnabled, MacroExpandEnabled {
041    
042        private final class ExpandedChecker implements SPFChecker {
043            
044            /**
045             * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
046             */
047            public DNSLookupContinuation checkSPF(SPFSession spfData)
048                    throws PermErrorException, NoneException,
049                    TempErrorException, NeutralException {
050                String host = getHost();
051    
052                // throws a PermErrorException that we can pass
053                // through
054                host = macroExpand.expand(host, spfData,
055                        MacroExpand.DOMAIN);
056    
057                spfData.setCurrentDomain(host);
058    
059                spfData.pushChecker(spfChecker);
060                return null;
061            }
062        }
063    
064        private final class CleanupChecker implements SPFChecker, SPFCheckerExceptionCatcher {
065          
066            /**
067            * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
068            */
069            public DNSLookupContinuation checkSPF(SPFSession spfData)
070                    throws PermErrorException, TempErrorException,
071                    NeutralException, NoneException {
072                // After the redirect we should not use the
073                // explanation from the orginal record
074                spfData.setIgnoreExplanation(true);
075                return null;
076            }
077            
078            /**
079             * @see org.apache.james.jspf.core.SPFCheckerExceptionCatcher#onException(java.lang.Exception, org.apache.james.jspf.core.SPFSession)
080             */
081            public void onException(Exception exception, SPFSession session)
082                    throws PermErrorException, NoneException,
083                    TempErrorException, NeutralException {
084                
085                session.setIgnoreExplanation(true);
086    
087                // remove every checker until the initialized one
088                
089                if (exception instanceof NeutralException) {
090                    throw new PermErrorException(
091                    "included checkSPF returned NeutralException");
092    
093                } else if (exception instanceof NoneException) {
094                    // no spf record assigned to the redirect domain
095                    throw new PermErrorException(
096                            "included checkSPF returned NoneException");
097                } else if (exception instanceof PermErrorException){
098                    throw (PermErrorException) exception;
099                } else if (exception instanceof TempErrorException){
100                    throw (TempErrorException) exception;
101                } else if (exception instanceof RuntimeException){
102                    throw (RuntimeException) exception;
103                } else {
104                    throw new IllegalStateException(exception.getMessage());
105                }
106            }
107        }
108    
109        /**
110         * ABNF: redirect = "redirect" "=" domain-spec
111         */
112        public static final String REGEX = "[rR][eE][dD][iI][rR][eE][cC][tT]"
113                + "\\=" + SPFTermsRegexps.DOMAIN_SPEC_REGEX;
114    
115        private SPFChecker spfChecker;
116    
117        private MacroExpand macroExpand;
118    
119        private SPFChecker cleanupChecker = new CleanupChecker();
120    
121        private SPFChecker expandedChecker = new ExpandedChecker();
122    
123        /**
124         * Set the host which should be used for redirection and set it in SPF1Data
125         * so it can be accessed easy later if needed
126         * 
127         * @param spfData
128         *            The SPF1Data which should used
129         * @return the result of this processing
130         * @throws PermErrorException
131         *             if an error is in the redirect modifier
132         * @throws TempErrorException
133         *             if an DNS problem accurred
134         * @throws NoneException
135         * @throws NeutralException
136         */
137        protected DNSLookupContinuation checkSPFLogged(SPFSession spfData)
138                throws PermErrorException, TempErrorException, NeutralException,
139                NoneException {
140            // the redirect modifier is used only when we had no previous matches
141            if (spfData.getCurrentResult() == null) {
142    
143                // update currentDepth
144                spfData.increaseCurrentDepth();
145                
146                spfData.pushChecker(cleanupChecker);
147                
148                spfData.pushChecker(expandedChecker);
149                return macroExpand.checkExpand(getHost(), spfData, MacroExpand.DOMAIN);
150            }
151            return null;
152        }
153    
154        /**
155         * @see java.lang.Object#toString()
156         */
157        public String toString() {
158            return "redirect=" + getHost();
159        }
160    
161        /**
162         * @see org.apache.james.jspf.core.SPFCheckEnabled#enableSPFChecking(org.apache.james.jspf.core.SPFChecker)
163         */
164        public void enableSPFChecking(SPFChecker checker) {
165            this.spfChecker = checker;
166        }
167    
168        /**
169         * @see org.apache.james.jspf.core.MacroExpandEnabled#enableMacroExpand(org.apache.james.jspf.core.MacroExpand)
170         */
171        public void enableMacroExpand(MacroExpand macroExpand) {
172            this.macroExpand = macroExpand;
173        }
174    
175    }