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