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: ****************************************************************/package org.apache.james.transport.mailets;
019:
020: import org.apache.avalon.cornerstone.services.datasources.DataSourceSelector;
021: import org.apache.avalon.excalibur.datasource.DataSourceComponent;
022: import org.apache.avalon.framework.service.ServiceManager;
023: import org.apache.james.Constants;
024: import org.apache.james.util.JDBCUtil;
025: import org.apache.mailet.MailAddress;
026: import org.apache.mailet.MailetException;
027:
028: import javax.mail.MessagingException;
029:
030: import java.sql.Connection;
031: import java.sql.DatabaseMetaData;
032: import java.sql.PreparedStatement;
033: import java.sql.ResultSet;
034: import java.sql.SQLException;
035: import java.util.Collection;
036: import java.util.Iterator;
037: import java.util.Map;
038:
039: /**
040: * Implements a Virtual User Table for JAMES. Derived from the
041: * JDBCAlias mailet, but whereas that mailet uses a simple map from a
042: * source address to a destination address, this handles simple
043: * wildcard selection, verifies that a catchall address is for a domain
044: * in the Virtual User Table, and handles forwarding.
045: *
046: * JDBCVirtualUserTable does not provide any administation tools.
047: * You'll have to create the VirtualUserTable yourself. The standard
048: * configuration is as follows:
049: *
050: * CREATE TABLE VirtualUserTable
051: * (
052: * user varchar(64) NOT NULL default '',
053: * domain varchar(255) NOT NULL default '',
054: * target_address varchar(255) NOT NULL default '',
055: * PRIMARY KEY (user,domain)
056: * );
057: *
058: * The user column specifies the username of the virtual recipient, the domain
059: * column the domain of the virtual recipient, and the target_address column
060: * the email address of the real recipient. The target_address column can contain
061: * just the username in the case of a local user, and multiple recipients can be
062: * specified in a list separated by commas, semi-colons or colons.
063: *
064: * The standard query used with VirtualUserTable is:
065: *
066: * select VirtualUserTable.target_address from VirtualUserTable, VirtualUserTable as VUTDomains
067: * where (VirtualUserTable.user like ? or VirtualUserTable.user like "\%")
068: * and (VirtualUserTable.domain like ?
069: * or (VirtualUserTable.domain like "\%" and VUTDomains.domain like ?))
070: * order by concat(VirtualUserTable.user,'@',VirtualUserTable.domain) desc limit 1
071: *
072: * For a given [user, domain, domain] used with the query, this will
073: * match as follows (in precedence order):
074: *
075: * 1. user@domain - explicit mapping for user@domain
076: * 2. user@% - catchall mapping for user anywhere
077: * 3. %@domain - catchall mapping for anyone at domain
078: * 4. null - no valid mapping
079: *
080: * You need to set the connection. At the moment, there is a limit to
081: * what you can change regarding the SQL Query, because there isn't a
082: * means to specify where in the query to replace parameters. [TODO]
083: *
084: * <mailet match="All" class="JDBCVirtualUserTable">
085: * <table>db://maildb/VirtualUserTable</table>
086: * <sqlquery>sqlquery</sqlquery>
087: * </mailet>
088: */
089: public class JDBCVirtualUserTable extends AbstractVirtualUserTable {
090: protected DataSourceComponent datasource;
091:
092: /**
093: * The query used by the mailet to get the alias mapping
094: */
095: protected String query = null;
096:
097: /**
098: * The JDBCUtil helper class
099: */
100: private final JDBCUtil theJDBCUtil = new JDBCUtil() {
101: protected void delegatedLog(String logString) {
102: log("JDBCVirtualUserTable: " + logString);
103: }
104: };
105:
106: /**
107: * Initialize the mailet
108: */
109: public void init() throws MessagingException {
110: if (getInitParameter("table") == null) {
111: throw new MailetException(
112: "Table location not specified for JDBCVirtualUserTable");
113: }
114:
115: String tableURL = getInitParameter("table");
116:
117: String datasourceName = tableURL.substring(5);
118: int pos = datasourceName.indexOf("/");
119: String tableName = datasourceName.substring(pos + 1);
120: datasourceName = datasourceName.substring(0, pos);
121: Connection conn = null;
122:
123: try {
124: ServiceManager componentManager = (ServiceManager) getMailetContext()
125: .getAttribute(Constants.AVALON_COMPONENT_MANAGER);
126: // Get the DataSourceSelector service
127: DataSourceSelector datasources = (DataSourceSelector) componentManager
128: .lookup(DataSourceSelector.ROLE);
129: // Get the data-source required.
130: datasource = (DataSourceComponent) datasources
131: .select(datasourceName);
132:
133: conn = datasource.getConnection();
134:
135: // Check if the required table exists. If not, complain.
136: DatabaseMetaData dbMetaData = conn.getMetaData();
137: // Need to ask in the case that identifiers are stored, ask the DatabaseMetaInfo.
138: // Try UPPER, lower, and MixedCase, to see if the table is there.
139: if (!(theJDBCUtil.tableExists(dbMetaData, tableName))) {
140: StringBuffer exceptionBuffer = new StringBuffer(128)
141: .append("Could not find table '").append(
142: tableName).append("' in datasource '")
143: .append(datasourceName).append("'");
144: throw new MailetException(exceptionBuffer.toString());
145: }
146:
147: //Build the query
148: query = getInitParameter(
149: "sqlquery",
150: "select VirtualUserTable.target_address from VirtualUserTable, VirtualUserTable as VUTDomains where (VirtualUserTable.user like ? or VirtualUserTable.user like '\\%') and (VirtualUserTable.domain like ? or (VirtualUserTable.domain like '\\%' and VUTDomains.domain like ?)) order by concat(VirtualUserTable.user,'@',VirtualUserTable.domain) desc limit 1");
151: } catch (MailetException me) {
152: throw me;
153: } catch (Exception e) {
154: throw new MessagingException(
155: "Error initializing JDBCVirtualUserTable", e);
156: } finally {
157: theJDBCUtil.closeJDBCConnection(conn);
158: }
159: }
160:
161: /**
162: * Map any virtual recipients to real recipients using the configured
163: * JDBC connection, table and query.
164: *
165: * @param recipientsMap the mapping of virtual to real recipients
166: */
167: protected void mapRecipients(Map recipientsMap)
168: throws MessagingException {
169: Connection conn = null;
170: PreparedStatement mappingStmt = null;
171:
172: Collection recipients = recipientsMap.keySet();
173:
174: try {
175: conn = datasource.getConnection();
176: mappingStmt = conn.prepareStatement(query);
177:
178: for (Iterator i = recipients.iterator(); i.hasNext();) {
179: ResultSet mappingRS = null;
180: try {
181: MailAddress source = (MailAddress) i.next();
182: mappingStmt.setString(1, source.getUser());
183: mappingStmt.setString(2, source.getHost());
184: mappingStmt.setString(3, source.getHost());
185: mappingRS = mappingStmt.executeQuery();
186: if (mappingRS.next()) {
187: String targetString = mappingRS.getString(1);
188: recipientsMap.put(source, targetString);
189: }
190: } finally {
191: theJDBCUtil.closeJDBCResultSet(mappingRS);
192: }
193: }
194: } catch (SQLException sqle) {
195: throw new MessagingException("Error accessing database",
196: sqle);
197: } finally {
198: theJDBCUtil.closeJDBCStatement(mappingStmt);
199: theJDBCUtil.closeJDBCConnection(conn);
200: }
201: }
202:
203: public String getMailetInfo() {
204: return "JDBC Virtual User Table mailet";
205: }
206: }
|