View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  
21  
22  
23  package org.apache.james.smtpserver.core.filter.fastfail;
24  
25  import java.util.Iterator;
26  import java.util.Map;
27  
28  import javax.mail.MessagingException;
29  import javax.mail.internet.MimeMessage;
30  
31  import org.apache.avalon.framework.configuration.Configurable;
32  import org.apache.avalon.framework.configuration.Configuration;
33  import org.apache.avalon.framework.configuration.ConfigurationException;
34  import org.apache.avalon.framework.logger.AbstractLogEnabled;
35  import org.apache.james.dsn.DSNStatus;
36  import org.apache.james.smtpserver.ConnectHandler;
37  import org.apache.james.smtpserver.MessageHandler;
38  import org.apache.james.smtpserver.SMTPSession;
39  import org.apache.james.smtpserver.junkscore.ComposedJunkScore;
40  import org.apache.james.smtpserver.junkscore.JunkScore;
41  import org.apache.james.smtpserver.junkscore.JunkScoreImpl;
42  
43  /**
44   * Check if a configured JunkScore is reached and perform an action. Valid actions are: reject, compose, header. 
45   * 
46   * -Reject action reject the mail if the limit is reached.
47   * -Compose action stores the junkScore values in the mail attributes
48   * -Header action create headers which holds the junkScore for each check
49   */
50  public class JunkScoreHandler extends AbstractLogEnabled implements ConnectHandler, MessageHandler,Configurable {
51  
52      private double maxScore = 0;
53      private String action;
54      private static final String REJECT_ACTION = "reject";
55      private static final String COMPOSE_ACTION = "compose";
56      private static final String HEADER_ACTION = "header";
57  
58      /**
59       * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
60       */
61      public void configure(Configuration arg0) throws ConfigurationException {
62          Configuration maxScoreConfig = arg0.getChild("maxScore");
63  
64          if (maxScoreConfig != null) {
65              setMaxScore(maxScoreConfig.getValueAsDouble(0));
66          }
67          
68          Configuration actionConfig = arg0.getChild("action");
69          if (actionConfig != null) {
70              setAction(actionConfig.getValue().toLowerCase());
71          } else {
72              throw new ConfigurationException("Please configure the action");
73          }
74      }
75      
76      /**
77       * Set the max JunkScore
78       * 
79       * @param maxScore the score
80       */
81      public void setMaxScore(double maxScore) {
82          this.maxScore = maxScore;
83      }
84      
85      /**
86       * Set the action to perform if the JunkScore limit is reached
87       * 
88       * @param action the action
89       * @throws ConfigurationException if invalid action is used
90       */
91      public void setAction(String action) throws ConfigurationException {
92          if (!action.equals(REJECT_ACTION) && !action.equals(COMPOSE_ACTION) && !action.equals(HEADER_ACTION)) 
93              throw new ConfigurationException("Illegal action: " + action);
94       
95          this.action = action;  
96      }
97      
98      /**
99       * @see org.apache.james.smtpserver.MessageHandler#onMessage(org.apache.james.smtpserver.SMTPSession)
100      */
101     public void onMessage(SMTPSession session) {
102         checkScore(session);
103     }
104 
105     /**
106      * Check if the JunkScore limit is reached and perform the configured action
107      * 
108      * @param session the SMTPSession
109      */
110     private void checkScore(SMTPSession session) {
111         JunkScore score1 = (JunkScore) session.getConnectionState().get(JunkScore.JUNK_SCORE_SESSION);
112         JunkScore score2 = (JunkScore) session.getState().get(JunkScore.JUNK_SCORE);
113         JunkScore composed = new ComposedJunkScore(score1,score2);
114         
115         if (action.equals(COMPOSE_ACTION)) {
116             // Save the scores attribute to maybe use it later!
117             session.getMail().setAttribute(JunkScore.JUNK_SCORE_SESSION_ATTR, String.valueOf(score1.getCompleteStoredScores()));
118             session.getMail().setAttribute(JunkScore.JUNK_SCORE_ATTR, String.valueOf(score2.getCompleteStoredScores()));
119             session.getMail().setAttribute(JunkScore.JUNK_SCORE_COMPOSED_ATTR, String.valueOf(composed.getCompleteStoredScores()));
120         } else if (action.equals(REJECT_ACTION)) {
121             if (maxScore <  composed.getCompleteStoredScores()) {
122 
123                 
124                 String responseString = "554 "
125                     + DSNStatus.getStatus(DSNStatus.PERMANENT,
126                             DSNStatus.SECURITY_OTHER)
127                     + " This message reach the spam hits treshold. Please contact the Postmaster if the email is not SPAM. Message rejected";
128                 StringBuffer buffer = new StringBuffer(256).append(
129                     "Rejected message from ").append(
130                     session.getState().get(SMTPSession.SENDER)
131                             .toString()).append(" from host ")
132                     .append(session.getRemoteHost()).append(" (")
133                     .append(session.getRemoteIPAddress()).append(
134                             ") " + responseString).append(
135                             ". Required rejection hits: "
136                                     + maxScore
137                                     + " hits: " + composed.getCompleteStoredScores());
138                 getLogger().info(buffer.toString());
139                 
140                 session.writeResponse(responseString);
141                 session.setStopHandlerProcessing(true);
142                 session.abortMessage();
143             }
144         } else if (action.equals(HEADER_ACTION)) {
145             try {
146                 MimeMessage message = session.getMail().getMessage();
147                 Map scores = composed.getStoredScores();
148                 Iterator itScores = scores.keySet().iterator();
149         
150                 StringBuffer header = new StringBuffer();
151                 while (itScores.hasNext()) {
152                     String key = itScores.next().toString();
153                     header.append(key);
154                     header.append("=");
155                     header.append(scores.get(key));
156                     header.append("; ");
157                 }
158         
159                 message.setHeader("X-JUNKSCORE", header.toString());
160                 message.setHeader("X-JUNKSCORE-COMPOSED", String.valueOf(composed.getCompleteStoredScores()));
161         
162             } catch (MessagingException e) {
163                 getLogger().info("Unable to add Junkscore to header: " + e.getMessage());
164             }
165         }
166     }
167         
168     /**
169      * @see org.apache.james.smtpserver.ConnectHandler#onConnect(org.apache.james.smtpserver.SMTPSession)
170      */
171     public void onConnect(SMTPSession session) {
172         session.getState().put(JunkScore.JUNK_SCORE, new JunkScoreImpl());
173         session.getConnectionState().put(JunkScore.JUNK_SCORE_SESSION, new JunkScoreImpl());
174     } 
175 }