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 package org.apache.james.rrt.lib;
020
021 import java.util.ArrayList;
022 import java.util.Collection;
023 import java.util.HashMap;
024 import java.util.Iterator;
025 import java.util.Map;
026 import java.util.StringTokenizer;
027 import java.util.regex.Matcher;
028 import java.util.regex.Pattern;
029 import java.util.regex.PatternSyntaxException;
030
031 import org.apache.james.rrt.api.RecipientRewriteTable;
032 import org.apache.mailet.MailAddress;
033
034 /**
035 * This helper class contains methods for the RecipientRewriteTable implementations
036 */
037 public class RecipientRewriteTableUtil {
038
039 private RecipientRewriteTableUtil() {
040 }
041
042 // @deprecated QUERY is deprecated - SQL queries are now located in
043 // sqlResources.xml
044 public static String QUERY = "select RecipientRewriteTable.target_address from RecipientRewriteTable, RecipientRewriteTable as VUTDomains where (RecipientRewriteTable.user like ? or RecipientRewriteTable.user like '\\%') and (RecipientRewriteTable.domain like ? or (RecipientRewriteTable.domain like '%*%' and VUTDomains.domain like ?)) order by concat(RecipientRewriteTable.user,'@',RecipientRewriteTable.domain) desc limit 1";
045
046 /**
047 * Processes regex virtual user mapping
048 *
049 * If a mapped target string begins with the prefix regex:, it must be
050 * formatted as regex:<regular-expression>:<parameterized-string>, e.g.,
051 * regex:(.*)@(.*):${1}@tld
052 *
053 * @param address
054 * the MailAddress to be mapped
055 * @param targetString
056 * a String specifying the mapping
057 * @throws MalformedPatternException
058 */
059 public static String regexMap(MailAddress address, String targetString) {
060 String result = null;
061 int identifierLength = RecipientRewriteTable.REGEX_PREFIX.length();
062
063 int msgPos = targetString.indexOf(':', identifierLength + 1);
064
065 // Throw exception on invalid format
066 if (msgPos < identifierLength + 1)
067 throw new PatternSyntaxException("Regex should be formatted as regex:<regular-expression>:<parameterized-string>", targetString, 0);
068
069 // log("regex: targetString = " + targetString);
070 // log("regex: msgPos = " + msgPos);
071 // log("regex: compile " + targetString.substring("regex:".length(),
072 // msgPos));
073 // log("regex: address = " + address.toString());
074 // log("regex: replace = " + targetString.substring(msgPos + 1));
075
076 Pattern pattern = Pattern.compile(targetString.substring(identifierLength, msgPos));
077 Matcher match = pattern.matcher(address.toString());
078
079 if (match.matches()) {
080 Map<String, String> parameters = new HashMap<String, String>(match.groupCount());
081 for (int i = 1; i < match.groupCount(); i++) {
082 parameters.put(Integer.toString(i), match.group(i));
083 }
084 result = replaceParameters(targetString.substring(msgPos + 1), parameters);
085 }
086 return result;
087 }
088
089 /**
090 * Returns a named string, replacing parameters with the values set.
091 *
092 * @param str
093 * the name of the String resource required.
094 * @param parameters
095 * a map of parameters (name-value string pairs) which are
096 * replaced where found in the input strings
097 * @return the requested resource
098 */
099 static public String replaceParameters(String str, Map<String, String> parameters) {
100 if (str != null && parameters != null) {
101 // Do parameter replacements for this string resource.
102 StringBuffer replaceBuffer = new StringBuffer(64);
103 for (Map.Entry<String, String> entry : parameters.entrySet()) {
104 replaceBuffer.setLength(0);
105 replaceBuffer.append("${").append(entry.getKey()).append("}");
106 str = substituteSubString(str, replaceBuffer.toString(), entry.getValue());
107 }
108 }
109
110 return str;
111 }
112
113 /**
114 * Replace substrings of one string with another string and return altered
115 * string.
116 *
117 * @param input
118 * input string
119 * @param find
120 * the string to replace
121 * @param replace
122 * the string to replace with
123 * @return the substituted string
124 */
125 static private String substituteSubString(String input, String find, String replace) {
126 int find_length = find.length();
127 int replace_length = replace.length();
128
129 StringBuffer output = new StringBuffer(input);
130 int index = input.indexOf(find);
131 int outputOffset = 0;
132
133 while (index > -1) {
134 output.replace(index + outputOffset, index + outputOffset + find_length, replace);
135 outputOffset = outputOffset + (replace_length - find_length);
136
137 index = input.indexOf(find, index + find_length);
138 }
139
140 String result = output.toString();
141 return result;
142 }
143
144 /**
145 * Returns the real recipient given a virtual username and domain.
146 *
147 * @param user
148 * the virtual user
149 * @param domain
150 * the virtual domain
151 * @return the real recipient address, or <code>null</code> if no mapping
152 * exists
153 */
154 public static String getTargetString(String user, String domain, Map<String, String> mappings) {
155 StringBuffer buf;
156 String target;
157
158 // Look for exact (user@domain) match
159 buf = new StringBuffer().append(user).append("@").append(domain);
160 target = (String) mappings.get(buf.toString());
161 if (target != null) {
162 return target;
163 }
164
165 // Look for user@* match
166 buf = new StringBuffer().append(user).append("@*");
167 target = (String) mappings.get(buf.toString());
168 if (target != null) {
169 return target;
170 }
171
172 // Look for *@domain match
173 buf = new StringBuffer().append("*@").append(domain);
174 target = (String) mappings.get(buf.toString());
175 if (target != null) {
176 return target;
177 }
178
179 return null;
180 }
181
182 /**
183 * Returns the character used to delineate multiple addresses.
184 *
185 * @param targetString
186 * the string to parse
187 * @return the character to tokenize on
188 */
189 public static String getSeparator(String targetString) {
190 return (targetString.indexOf(',') > -1 ? "," : (targetString.indexOf(';') > -1 ? ";" : ((targetString.indexOf(RecipientRewriteTable.ERROR_PREFIX) > -1 || targetString.indexOf(RecipientRewriteTable.REGEX_PREFIX) > -1 || targetString.indexOf(RecipientRewriteTable.ALIASDOMAIN_PREFIX) > -1) ? "" : ":")));
191 }
192
193 /**
194 * Returns a Map which contains the mappings
195 *
196 * @param mapping
197 * A String which contains a list of mappings
198 * @return Map which contains the mappings
199 */
200 public static Map<String, String> getXMLMappings(String mapping) {
201 Map<String, String> mappings = new HashMap<String, String>();
202 StringTokenizer tokenizer = new StringTokenizer(mapping, ",");
203 while (tokenizer.hasMoreTokens()) {
204 String mappingItem = tokenizer.nextToken();
205 int index = mappingItem.indexOf('=');
206 String virtual = mappingItem.substring(0, index).trim().toLowerCase();
207 String real = mappingItem.substring(index + 1).trim().toLowerCase();
208 mappings.put(virtual, real);
209 }
210 return mappings;
211 }
212
213 /**
214 * Return a Collection which holds the extracted mappings of the given
215 * String
216 *
217 * @param rawMapping
218 * @deprecated Use mappingToCollection(String rawMapping)
219 */
220 public static Collection<String> getMappings(String rawMapping) {
221 return mappingToCollection(rawMapping);
222 }
223
224 /**
225 * Convert a raw mapping String to a Collection
226 *
227 * @param rawMapping
228 * the mapping String
229 * @return map a collection which holds all mappings
230 */
231 public static ArrayList<String> mappingToCollection(String rawMapping) {
232 ArrayList<String> map = new ArrayList<String>();
233 StringTokenizer tokenizer = new StringTokenizer(rawMapping, RecipientRewriteTableUtil.getSeparator(rawMapping));
234 while (tokenizer.hasMoreTokens()) {
235 final String raw = tokenizer.nextToken().trim();
236 map.add(raw);
237 }
238 return map;
239 }
240
241 /**
242 * Convert a Collection which holds mappings to a raw mapping String
243 *
244 * @param map
245 * the Collection
246 * @return mapping the mapping String
247 */
248 public static String CollectionToMapping(Collection<String> map) {
249 StringBuffer mapping = new StringBuffer();
250 Iterator<String> mappings = map.iterator();
251 while (mappings.hasNext()) {
252 mapping.append(mappings.next());
253 if (mappings.hasNext()) {
254 mapping.append(";");
255 }
256 }
257 return mapping.toString();
258 }
259
260 }