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  package org.apache.james.transport.matchers;
23  
24  import org.apache.avalon.cornerstone.services.datasources.DataSourceSelector;
25  import org.apache.avalon.excalibur.datasource.DataSourceComponent;
26  import org.apache.avalon.framework.service.ServiceManager;
27  import org.apache.james.Constants;
28  import org.apache.james.api.user.JamesUser;
29  import org.apache.james.api.user.UsersRepository;
30  import org.apache.james.transport.mailets.WhiteListManager;
31  import org.apache.james.util.sql.JDBCUtil;
32  import org.apache.james.util.sql.SqlResources;
33  import org.apache.mailet.base.GenericMatcher;
34  import org.apache.mailet.Mail;
35  import org.apache.mailet.MailAddress;
36  
37  import javax.mail.MessagingException;
38  
39  import java.io.File;
40  import java.sql.Connection;
41  import java.sql.PreparedStatement;
42  import java.sql.ResultSet;
43  import java.sql.SQLException;
44  import java.util.Collection;
45  import java.util.HashMap;
46  import java.util.Iterator;
47  import java.util.Locale;
48  import java.util.Map;
49  import java.util.StringTokenizer;
50  
51  /**
52   * <P>Matches recipients having the mail sender in the recipient's private whitelist .</P>
53   * <P> The recipient name is always converted to its primary name (handling aliases).</P>
54   * <P>Configuration string: The database name containing the white list table.</P>
55   * <P>Example:</P>
56   * <PRE><CODE>
57   *    &lt;mailet match="IsInWhiteList=db://maildb" class="ToProcessor"&gt;
58   *       &lt;processor&gt; transport &lt;/processor&gt;
59   *    &lt;/mailet&gt;
60   * </CODE></PRE>
61   * @see org.apache.james.transport.mailets.WhiteListManager
62   * @version SVN $Revision: $ $Date: $
63   * @since 2.3.0
64   */
65  public class IsInWhiteList extends GenericMatcher {
66  
67      private String selectByPK;
68      
69      private DataSourceComponent datasource;
70      
71      /** The user repository for this mail server.  Contains all the users with inboxes
72       * on this server.
73       */
74      private UsersRepository localusers;
75  
76      /**
77       * The JDBCUtil helper class
78       */
79      private final JDBCUtil theJDBCUtil = new JDBCUtil() {
80          protected void delegatedLog(String logString) {
81              log("IsInWhiteList: " + logString);
82          }
83      };
84      
85      /**
86       * Contains all of the sql strings for this component.
87       */
88      private SqlResources sqlQueries = new SqlResources();
89  
90      /**
91       * Holds value of property sqlFile.
92       */
93      private File sqlFile;
94  
95       /**
96       * Holds value of property sqlParameters.
97       */
98      private Map sqlParameters = new HashMap();
99  
100     /**
101      * Getter for property sqlParameters.
102      * @return Value of property sqlParameters.
103      */
104     private Map getSqlParameters() {
105 
106         return this.sqlParameters;
107     }
108 
109     /**
110      * @see org.apache.mailet.GenericMailet#init()
111      */
112     public void init() throws javax.mail.MessagingException {
113         String repositoryPath = null;
114         StringTokenizer st = new StringTokenizer(getCondition(), ", \t", false);
115         if (st.hasMoreTokens()) {
116             repositoryPath = st.nextToken().trim();
117         }
118         if (repositoryPath != null) {
119             log("repositoryPath: " + repositoryPath);
120         }
121         else {
122             throw new MessagingException("repositoryPath is null");
123         }
124 
125         ServiceManager serviceManager = (ServiceManager) getMailetContext().getAttribute(Constants.AVALON_COMPONENT_MANAGER);
126 
127         try {
128             // Get the DataSourceSelector block
129             DataSourceSelector datasources = (DataSourceSelector) serviceManager.lookup(DataSourceSelector.ROLE);
130             // Get the data-source required.
131             int stindex =   repositoryPath.indexOf("://") + 3;
132             String datasourceName = repositoryPath.substring(stindex);
133             datasource = (DataSourceComponent) datasources.select(datasourceName);
134         } catch (Exception e) {
135             throw new MessagingException("Can't get datasource", e);
136         }
137 
138          try {
139             // Get the UsersRepository
140             localusers = (UsersRepository)serviceManager.lookup(UsersRepository.ROLE);
141         } catch (Exception e) {
142             throw new MessagingException("Can't get the local users repository", e);
143         }
144 
145         try {
146             initSqlQueries(datasource.getConnection(), getMailetContext());
147         } catch (Exception e) {
148             throw new MessagingException("Exception initializing queries", e);
149         }        
150         
151         selectByPK = sqlQueries.getSqlString("selectByPK", true);
152     }
153 
154     /**
155      * @see org.apache.mailet.GenericMatcher#match(Mail)
156      */
157     public Collection match(Mail mail) throws MessagingException {
158         // check if it's a local sender
159         MailAddress senderMailAddress = mail.getSender();
160         if (senderMailAddress == null) {
161             return null;
162         }
163         if (getMailetContext().isLocalEmail(senderMailAddress)) {
164             // is a local sender, so return
165             return null;
166         }
167         
168         String senderUser = senderMailAddress.getUser();
169         String senderHost = senderMailAddress.getHost();
170         
171         senderUser = senderUser.toLowerCase(Locale.US);
172         senderHost = senderHost.toLowerCase(Locale.US);
173         
174         Collection recipients = mail.getRecipients();
175                 
176         Collection inWhiteList = new java.util.HashSet();
177         
178         Connection conn = null;
179         PreparedStatement selectStmt = null;
180         ResultSet selectRS = null;
181         try {
182             
183             for (Iterator i = recipients.iterator(); i.hasNext(); ) {
184                 try {
185                     MailAddress recipientMailAddress = (MailAddress)i.next();
186                     String recipientUser = recipientMailAddress.getUser().toLowerCase(Locale.US);
187                     String recipientHost = recipientMailAddress.getHost().toLowerCase(Locale.US);
188                     
189                     if (!getMailetContext().isLocalServer(recipientHost)) {
190                         // not a local recipient, so skip
191                         continue;
192                     }
193                     
194                     recipientUser = getPrimaryName(recipientUser);
195                     
196                     if (conn == null) {
197                         conn = datasource.getConnection();
198                     }
199                     
200                     if (selectStmt == null) {
201                         selectStmt = conn.prepareStatement(selectByPK);
202                     }
203                     selectStmt.setString(1, recipientUser);
204                     selectStmt.setString(2, recipientHost);
205                     selectStmt.setString(3, senderUser);
206                     selectStmt.setString(4, senderHost);
207                     selectRS = selectStmt.executeQuery();
208                     if (selectRS.next()) {
209                         //This address was already in the list
210                         inWhiteList.add(recipientMailAddress);
211                     }
212                                         
213                 } finally {
214                     theJDBCUtil.closeJDBCResultSet(selectRS);
215                 }
216                 
217             }
218             
219             return inWhiteList;
220             
221         } catch (SQLException sqle) {
222             log("Error accessing database", sqle);
223             throw new MessagingException("Exception thrown", sqle);
224         } finally {
225             theJDBCUtil.closeJDBCStatement(selectStmt);
226             theJDBCUtil.closeJDBCConnection(conn);
227         }
228     }
229 
230     /** Gets the main name of a local customer, handling alias */
231     private String getPrimaryName(String originalUsername) {
232         String username;
233         try {
234             username = localusers.getRealName(originalUsername);
235             JamesUser user = (JamesUser) localusers.getUserByName(username);
236             if (user.getAliasing()) {
237                 username = user.getAlias();
238             }
239         }
240         catch (Exception e) {
241             username = originalUsername;
242         }
243         return username;
244     }
245     
246     /**
247      * Initializes the sql query environment from the SqlResources file.
248      * Will look for conf/sqlResources.xml.
249      * Will <I>not</I> create the database resources, if missing
250      * (this task is done, if needed, in the {@link WhiteListManager}
251      * initialization routine).
252      * @param conn The connection for accessing the database
253      * @param mailetContext The current mailet context,
254      * for finding the conf/sqlResources.xml file
255      * @throws Exception If any error occurs
256      */
257     private void initSqlQueries(Connection conn, org.apache.mailet.MailetContext mailetContext) throws Exception {
258         try {
259             if (conn.getAutoCommit()) {
260                 conn.setAutoCommit(false);
261             }
262             
263             this.sqlFile = new File((String) mailetContext.getAttribute("confDir"), "sqlResources.xml").getCanonicalFile();
264             sqlQueries.init(this.sqlFile, "WhiteList" , conn, getSqlParameters());
265             
266         } finally {
267             theJDBCUtil.closeJDBCConnection(conn);
268         }
269     }
270     
271 }