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    }