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.core;
022    
023    import org.apache.james.jspf.core.exceptions.PermErrorException;
024    
025    import java.util.HashMap;
026    import java.util.Map;
027    import java.util.Stack;
028    
029    /**
030     * 
031     * This Class is used as a container between the other classes. All necessary
032     * values get stored here and get retrieved from here.
033     * 
034     */
035    
036    public class SPFSession implements MacroData {
037    
038        private String ipAddress = ""; // also used for (i)<sending-host>
039    
040        private String mailFrom = ""; // (s)<responsible-sender>
041    
042        private String hostName = ""; // (h)<sender-domain>
043    
044        private String currentSenderPart = ""; // (l)
045    
046        private String currentDomain = ""; // (d)<current-domain>
047    
048        private String inAddress = "in-addr"; // (v)
049    
050        private String clientDomain = null; // (p)
051    
052        private String senderDomain = ""; // (o)
053    
054        private String readableIP = null; // (c)
055    
056        private String receivingDomain = null; // (r)
057    
058        private int currentDepth = 0;
059    
060        /**
061         * The maximum mechanismn which are allowed to use
062         */
063        public static final int MAX_DEPTH = 10;
064    
065        private String explanation = null;
066    
067        private String currentResult = null;
068    
069        private boolean ignoreExplanation = false;
070        
071        private Map<String,Object> attributes = new HashMap<String,Object>();
072        
073        private Stack<SPFChecker> checkers = new Stack<SPFChecker>();
074        
075        private String currentResultExpanded;
076        
077        /**
078         * Build the SPFSession from the given parameters
079         * 
080         * @param mailFrom
081         *            The emailaddress of the sender
082         * @param heloDomain
083         *            The helo provided by the sender
084         * @param clientIP
085         *            The ipaddress of the client
086         * 
087         */
088        public SPFSession(String mailFrom, String heloDomain, String clientIP) {
089            super();
090            this.mailFrom = mailFrom.trim();
091            this.hostName = heloDomain.trim();
092           
093            try {
094                this.ipAddress = IPAddr.getProperIpAddress(clientIP.trim());
095                // get the in Address
096                this.inAddress = IPAddr.getInAddress(clientIP);
097            } catch (PermErrorException e) {
098                // ip was not rfc conform
099                this.setCurrentResultExpanded(e.getResult());
100            }
101    
102            // if nullsender is used postmaster@helo will be used as email
103            if (mailFrom.equals("")) {
104                this.currentSenderPart = "postmaster";
105                this.senderDomain = hostName;
106                this.mailFrom = currentSenderPart + "@" + hostName;
107            } else {
108                String[] fromParts = mailFrom.split("@");
109                // What to do when mailFrom is "@example.com" ?
110                if (fromParts.length > 1) {
111                    this.senderDomain = fromParts[fromParts.length -1];
112                    this.currentSenderPart = mailFrom.substring(0, mailFrom.length() - senderDomain.length() - 1);
113                    if (this.currentSenderPart.length() == 0) {
114                        this.currentSenderPart = "postmaster";
115                    }
116                } else {
117                    this.currentSenderPart = "postmaster";
118                    this.senderDomain = mailFrom;
119                }
120            }
121            this.currentDomain = this.senderDomain;
122        }
123    
124        /**
125         * @see org.apache.james.jspf.core.MacroData#getCurrentSenderPart()
126         */
127        public String getCurrentSenderPart() {
128            return currentSenderPart;
129        }
130    
131        /**
132         * @see org.apache.james.jspf.core.MacroData#getMailFrom()
133         */
134        public String getMailFrom() {
135            return mailFrom;
136        }
137    
138        /**
139         * @see org.apache.james.jspf.core.MacroData#getHostName()
140         */
141        public String getHostName() {
142            return hostName;
143        }
144    
145        /**
146         * @see org.apache.james.jspf.core.MacroData#getCurrentDomain()
147         */
148        public String getCurrentDomain() {
149            return currentDomain;
150        }
151    
152        /**
153         * @see org.apache.james.jspf.core.MacroData#getInAddress()
154         */
155        public String getInAddress() {
156            return inAddress;
157        }
158    
159        /**
160         * @see org.apache.james.jspf.core.MacroData#getClientDomain()
161         */
162        public String getClientDomain() {
163            return clientDomain;
164        }
165        
166        /**
167         * Sets the calculated clientDomain
168         * @param clientDomain the new clientDomain
169         */
170        public void setClientDomain(String clientDomain) {
171            this.clientDomain = clientDomain;
172        }
173    
174        /**
175         * @see org.apache.james.jspf.core.MacroData#getSenderDomain()
176         */
177        public String getSenderDomain() {
178            return senderDomain;
179        }
180    
181        /**
182         * Get the ipAddress which was used to connect
183         * 
184         * @return ipAddres 
185         */
186        public String getIpAddress() {
187            return ipAddress;
188        }
189        
190        /**
191         * @see org.apache.james.jspf.core.MacroData#getMacroIpAddress()
192         */
193        public String getMacroIpAddress() {
194            
195            if (IPAddr.isIPV6(ipAddress)) {
196                try {
197                    return IPAddr.getAddress(ipAddress).getNibbleFormat();
198                } catch (PermErrorException e) {
199                }
200            } 
201            
202            return ipAddress;
203    
204        }
205    
206        /**
207         * @see org.apache.james.jspf.core.MacroData#getTimeStamp()
208         */
209        public long getTimeStamp() {
210            return System.currentTimeMillis();
211        }
212    
213        /**
214         * @see org.apache.james.jspf.core.MacroData#getReadableIP()
215         */
216        public String getReadableIP() {
217            if (readableIP == null) {
218                readableIP = IPAddr.getReadableIP(ipAddress);
219            }
220            return readableIP;
221        }
222    
223        /**
224         * @see org.apache.james.jspf.core.MacroData#getReceivingDomain()
225         */
226        public String getReceivingDomain() {
227            return receivingDomain;
228        }
229        
230        /**
231         * Sets the new receiving domain
232         * 
233         * @param receivingDomain the new receiving domain
234         */
235        public void setReceivingDomain(String receivingDomain) {
236            this.receivingDomain = receivingDomain;
237        }
238        
239        /**
240         * Increase the current depth:
241         * 
242         * if we reach maximum calls we must throw a PermErrorException. See
243         * SPF-RFC Section 10.1. Processing Limits
244         */
245        public void increaseCurrentDepth() throws PermErrorException {
246            this.currentDepth++;
247            if (currentDepth > MAX_DEPTH)
248                throw new PermErrorException(
249                        "Maximum mechanism/modifiers calls done: "
250                            + currentDepth);
251        }
252    
253        /**
254         * Set the currentDomain
255         * 
256         * @param domain The current used domain
257         */
258        public void setCurrentDomain(String domain) {
259            this.currentDomain = domain;
260        }
261    
262        /**
263         * Set the explanation which will returned when a fail match
264         * 
265         * @param explanation
266         *            This String is set as explanation
267         */
268        public void setExplanation(String explanation) {
269            this.explanation = explanation;
270        }
271    
272        /**
273         * Get the explanation
274         * 
275         * @return explanation
276         */
277        public String getExplanation() {
278            return explanation;
279        }
280    
281        /**
282         * Set the current result
283         * 
284         * @param result
285         *            result
286         */
287        public void setCurrentResult(String result) {
288            this.currentResult = result;
289        }
290    
291        /**
292         * Get the current result
293         * 
294         * @return current result
295         */
296        public String getCurrentResult() {
297            return currentResult;
298        }
299        
300        /**
301         * Get set to true if the explanation should be ignored
302         * 
303         * @param ignoreExplanation true or false
304         */
305        public void setIgnoreExplanation(boolean ignoreExplanation) {
306            this.ignoreExplanation = ignoreExplanation; 
307        }
308        
309        /**
310         * Return true if the explanation should be ignored
311         * 
312         * @return true of false
313         */
314        public boolean ignoreExplanation() {
315            return ignoreExplanation;
316        }
317        
318        /**
319         * Retrieve a stored attribute
320         * 
321         * @param key the attribute key
322         * @return the stored attribute
323         */
324        public Object getAttribute(String key) {
325            return attributes.get(key);
326        }
327        
328        /**
329         * Sets a new attribute in the session
330         * 
331         * @param key attribute key
332         * @param value the value for this attribute
333         */
334        public void setAttribute(String key, Object value) {
335            this.attributes.put(key, value);
336        }
337        
338        /**
339         * Remove the attribute stored under the given key
340         * 
341         * @param key the key of the attribute
342         * @return object the attribute which was stored with the key
343         */
344        public Object removeAttribute(String key) {
345            return this.attributes.remove(key);
346        }
347    
348        /**
349         * Add the given SPFChecker on top of the stack
350         * 
351         * @param checker  
352         */
353        public void pushChecker(SPFChecker checker) {
354            checkers.push(checker);
355        }
356        
357        /**
358         * Remove the SPFChecker on the top and return it. If no SPFChecker is left
359         * null is returned
360         * 
361         * @return the last checker
362         */
363        public SPFChecker popChecker() {
364            if (checkers.isEmpty()) {
365                return null;
366            } else {
367                SPFChecker checker = checkers.pop();
368                return checker;
369            }
370        }
371    
372        /**
373         * @param result
374         */
375        public void setCurrentResultExpanded(String result) {
376            this.currentResultExpanded = result;
377        }
378    
379        /**
380         * @return current result converted/expanded
381         */
382        public String getCurrentResultExpanded() {
383            return currentResultExpanded;
384        }
385    
386    }