1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.james.impl.vut;
23
24 import java.net.InetAddress;
25 import java.net.UnknownHostException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Map;
32
33 import javax.mail.internet.ParseException;
34
35 import org.apache.avalon.framework.configuration.Configurable;
36 import org.apache.avalon.framework.configuration.Configuration;
37 import org.apache.avalon.framework.configuration.ConfigurationException;
38 import org.apache.avalon.framework.logger.AbstractLogEnabled;
39 import org.apache.avalon.framework.logger.Logger;
40 import org.apache.avalon.framework.service.ServiceException;
41 import org.apache.avalon.framework.service.ServiceManager;
42 import org.apache.avalon.framework.service.Serviceable;
43 import org.apache.james.api.dnsservice.DNSService;
44 import org.apache.james.api.domainlist.DomainList;
45 import org.apache.james.api.vut.ErrorMappingException;
46 import org.apache.james.api.vut.VirtualUserTable;
47 import org.apache.james.api.vut.management.InvalidMappingException;
48 import org.apache.james.api.vut.management.VirtualUserTableManagement;
49 import org.apache.mailet.MailAddress;
50 import org.apache.oro.text.regex.MalformedPatternException;
51 import org.apache.oro.text.regex.Perl5Compiler;
52
53
54
55
56 public abstract class AbstractVirtualUserTable extends AbstractLogEnabled
57 implements VirtualUserTable, VirtualUserTableManagement, DomainList, Serviceable, Configurable {
58
59 private boolean autoDetect = true;
60 private boolean autoDetectIP = true;
61 private DNSService dns;
62
63
64 private int mappingLimit = 10;
65
66
67 private boolean recursive = true;
68
69
70
71
72 public void service(ServiceManager arg0) throws ServiceException {
73 dns = (DNSService)arg0.lookup(DNSService.ROLE);
74 }
75
76
77
78
79
80 public void configure(Configuration arg0) throws ConfigurationException {
81 Configuration recursiveConf = arg0.getChild("recursiveMapping", false);
82
83 if (recursiveConf != null) {
84 setRecursiveMapping(recursiveConf.getValueAsBoolean(true));
85 }
86
87 Configuration mappingLimitConf = arg0.getChild("mappingLimit", false);
88
89 if (mappingLimitConf != null ) {
90 try {
91 setMappingLimit(mappingLimitConf.getValueAsInteger(10));
92 } catch (IllegalArgumentException e) {
93 throw new ConfigurationException(e.getMessage());
94 }
95 }
96 }
97
98 public void setRecursiveMapping(boolean recursive) {
99 this.recursive = recursive;
100 }
101
102
103
104
105
106
107
108 public void setMappingLimit(int mappingLimit) throws IllegalArgumentException {
109 if (mappingLimit < 1) throw new IllegalArgumentException("The minimum mappingLimit is 1");
110 this.mappingLimit = mappingLimit;
111 }
112
113
114
115
116 public Collection getMappings(String user,String domain) throws ErrorMappingException {
117 return getMappings(user,domain,mappingLimit);
118 }
119
120
121 public Collection getMappings(String user,String domain,int mappingLimit) throws ErrorMappingException {
122
123
124 if (mappingLimit == 0) throw new ErrorMappingException("554 Too many mappings to process");
125
126 String targetString = mapAddress(user, domain);
127
128
129 if (targetString != null) {
130 Collection mappings = new ArrayList();
131 if (targetString.startsWith(VirtualUserTable.ERROR_PREFIX)) {
132 throw new ErrorMappingException(targetString.substring(VirtualUserTable.ERROR_PREFIX.length()));
133
134 } else {
135 Iterator map = VirtualUserTableUtil.mappingToCollection(targetString).iterator();
136
137 while (map.hasNext()) {
138 String target = map.next().toString();
139
140 if (target.startsWith(VirtualUserTable.REGEX_PREFIX)) {
141 try {
142 target = VirtualUserTableUtil.regexMap(new MailAddress(user,domain), target);
143 } catch (MalformedPatternException e) {
144 getLogger().error("Exception during regexMap processing: ", e);
145 } catch (ParseException e) {
146
147 getLogger().error("Exception during regexMap processing: ", e);
148 }
149 } else if (target.startsWith(VirtualUserTable.ALIASDOMAIN_PREFIX)) {
150 target = user + "@" + target.substring(VirtualUserTable.ALIASDOMAIN_PREFIX.length());
151 }
152
153 if (target == null) continue;
154
155 StringBuffer buf = new StringBuffer().append("Valid virtual user mapping ")
156 .append(user).append("@").append(domain)
157 .append(" to ").append(target);
158 getLogger().debug(buf.toString());
159
160
161 if (recursive) {
162
163 String userName = null;
164 String domainName = null;
165 String args[] = target.split("@");
166
167 if (args != null && args.length > 0) {
168
169 userName = args[0];
170 domainName = args[1];
171 } else {
172
173 userName = target;
174 domainName = domain;
175 }
176
177
178 if (userName.equalsIgnoreCase(user) && domainName.equalsIgnoreCase(domain)) {
179 return null;
180 }
181
182 Collection childMappings = getMappings(userName, domainName, mappingLimit -1);
183
184 if (childMappings == null) {
185
186 mappings.add(target);
187 } else {
188 mappings.addAll(childMappings);
189 }
190
191 } else {
192 mappings.add(target);
193 }
194 }
195 }
196 return mappings;
197 }
198 return null;
199 }
200
201
202
203
204 public synchronized boolean addRegexMapping(String user, String domain, String regex) throws InvalidMappingException {
205 try {
206 new Perl5Compiler().compile(regex);
207 } catch (MalformedPatternException e) {
208 throw new InvalidMappingException("Invalid regex: " + regex);
209 }
210
211 if (checkMapping(user,domain,regex) == true) {
212 getLogger().info("Add regex mapping => " + regex + " for user: " + user + " domain: " + domain);
213 return addMappingInternal(user, domain, VirtualUserTable.REGEX_PREFIX + regex);
214 } else {
215 return false;
216 }
217 }
218
219
220
221
222
223 public synchronized boolean removeRegexMapping(String user, String domain, String regex) throws InvalidMappingException {
224 getLogger().info("Remove regex mapping => " + regex + " for user: " + user + " domain: " + domain);
225 return removeMappingInternal(user,domain,VirtualUserTable.REGEX_PREFIX + regex);
226 }
227
228
229
230
231 public synchronized boolean addAddressMapping(String user, String domain, String address) throws InvalidMappingException {
232 if (address.indexOf('@') < 0) {
233 address = address + "@localhost";
234 }
235 try {
236 new MailAddress(address);
237 } catch (ParseException e) {
238 throw new InvalidMappingException("Invalid emailAddress: " + address);
239 }
240 if (checkMapping(user,domain,address) == true) {
241 getLogger().info("Add address mapping => " + address + " for user: " + user + " domain: " + domain);
242 return addMappingInternal(user, domain, address);
243 } else {
244 return false;
245 }
246 }
247
248
249
250
251 public synchronized boolean removeAddressMapping(String user, String domain, String address) throws InvalidMappingException {
252 if (address.indexOf('@') < 0) {
253 address = address + "@localhost";
254 }
255 getLogger().info("Remove address mapping => " + address + " for user: " + user + " domain: " + domain);
256 return removeMappingInternal(user,domain,address);
257 }
258
259
260
261
262 public synchronized boolean addErrorMapping(String user, String domain, String error) throws InvalidMappingException {
263 if (checkMapping(user,domain,error) == true) {
264 getLogger().info("Add error mapping => " + error + " for user: " + user + " domain: " + domain);
265 return addMappingInternal(user,domain, VirtualUserTable.ERROR_PREFIX + error);
266 } else {
267 return false;
268 }
269 }
270
271
272
273
274 public synchronized boolean removeErrorMapping(String user, String domain, String error) throws InvalidMappingException {
275 getLogger().info("Remove error mapping => " + error + " for user: " + user + " domain: " + domain);
276 return removeMappingInternal(user,domain,VirtualUserTable.ERROR_PREFIX + error);
277 }
278
279
280
281
282
283 public synchronized boolean addMapping(String user, String domain, String mapping) throws InvalidMappingException {
284 String map = mapping.toLowerCase();
285
286 if (map.startsWith(VirtualUserTable.ERROR_PREFIX)) {
287 return addErrorMapping(user,domain,map.substring(VirtualUserTable.ERROR_PREFIX.length()));
288 } else if (map.startsWith(VirtualUserTable.REGEX_PREFIX)) {
289 return addRegexMapping(user,domain,map.substring(VirtualUserTable.REGEX_PREFIX.length()));
290 } else if (map.startsWith(VirtualUserTable.ALIASDOMAIN_PREFIX)) {
291 if (user != null) throw new InvalidMappingException("User must be null for aliasDomain mappings");
292 return addAliasDomainMapping(domain,map.substring(VirtualUserTable.ALIASDOMAIN_PREFIX.length()));
293 } else {
294 return addAddressMapping(user,domain,map);
295 }
296 }
297
298
299
300
301 public synchronized boolean removeMapping(String user, String domain, String mapping) throws InvalidMappingException {
302 String map = mapping.toLowerCase();
303
304 if (map.startsWith(VirtualUserTable.ERROR_PREFIX)) {
305 return removeErrorMapping(user,domain,map.substring(VirtualUserTable.ERROR_PREFIX.length()));
306 } else if (map.startsWith(VirtualUserTable.REGEX_PREFIX)) {
307 return removeRegexMapping(user,domain,map.substring(VirtualUserTable.REGEX_PREFIX.length()));
308 } else if (map.startsWith(VirtualUserTable.ALIASDOMAIN_PREFIX)) {
309 if (user != null) throw new InvalidMappingException("User must be null for aliasDomain mappings");
310 return removeAliasDomainMapping(domain,map.substring(VirtualUserTable.ALIASDOMAIN_PREFIX.length()));
311 } else {
312 return removeAddressMapping(user,domain,map);
313 }
314 }
315
316
317
318
319 public Map getAllMappings() {
320 int count = 0;
321 Map mappings = getAllMappingsInternal();
322
323 if (mappings != null) {
324 count = mappings.size();
325 }
326 getLogger().debug("Retrieve all mappings. Mapping count: " + count);
327 return mappings;
328 }
329
330
331 private boolean checkMapping(String user,String domain, String mapping) {
332 Collection mappings = getUserDomainMappings(user,domain);
333 if (mappings != null && mappings.contains(mapping)) {
334 return false;
335 } else {
336 return true;
337 }
338 }
339
340
341
342
343
344 public List getDomains() {
345 List domains = getDomainsInternal();
346 if (domains != null) {
347
348 String hostName = null;
349 try {
350 hostName = dns.getHostName(dns.getLocalHost());
351 } catch (UnknownHostException ue) {
352 hostName = "localhost";
353 }
354
355 getLogger().info("Local host is: " + hostName);
356
357 hostName = hostName.toLowerCase(Locale.US);
358
359 if (autoDetect == true && hostName.equals("localhost") == false && domains.contains(hostName) == false) {
360 domains.add(hostName);
361 }
362
363 if (autoDetectIP == true) {
364 List ipList = getDomainsIP(domains,dns,getLogger());
365 for(int i = 0; i < ipList.size(); i++) {
366 if (domains.contains(ipList.get(i)) == false) {
367 domains.add(ipList.get(i));
368 }
369 }
370 }
371
372 if (getLogger().isInfoEnabled()) {
373 for (Iterator i = domains.iterator(); i.hasNext(); ) {
374 getLogger().info("Handling mail for: " + i.next());
375 }
376 }
377 return domains;
378 } else {
379 return null;
380 }
381 }
382
383
384
385
386
387
388
389 private static List getDomainsIP(List domains,DNSService dns,Logger log) {
390 List domainIP = new ArrayList();
391 if (domains.size() > 0 ) {
392 for (int i = 0; i < domains.size(); i++) {
393 List domList = getDomainIP(domains.get(i).toString(),dns,log);
394
395 for(int i2 = 0; i2 < domList.size();i2++) {
396 if(domainIP.contains(domList.get(i2)) == false) {
397 domainIP.add(domList.get(i2));
398 }
399 }
400 }
401 }
402 return domainIP;
403 }
404
405
406
407
408 private static List getDomainIP(String domain, DNSService dns, Logger log) {
409 List domainIP = new ArrayList();
410 try {
411 InetAddress[] addrs = dns.getAllByName(domain);
412 for (int j = 0; j < addrs.length ; j++) {
413 String ip = addrs[j].getHostAddress();
414 if (domainIP.contains(ip) == false) {
415 domainIP.add(ip);
416 }
417 }
418 } catch (UnknownHostException e) {
419 log.error("Cannot get IP address(es) for " + domain);
420 }
421 return domainIP;
422 }
423
424
425
426
427 public Collection getUserDomainMappings(String user, String domain) {
428 return getUserDomainMappingsInternal(user, domain);
429 }
430
431
432
433
434 public synchronized void setAutoDetect(boolean autoDetect) {
435 getLogger().info("Set autodetect to: " + autoDetect);
436 this.autoDetect = autoDetect;
437 }
438
439
440
441
442 public synchronized void setAutoDetectIP(boolean autoDetectIP) {
443 getLogger().info("Set autodetectIP to: " + autoDetectIP);
444 this.autoDetectIP = autoDetectIP;
445 }
446
447
448
449
450 public synchronized boolean addAliasDomainMapping(String aliasDomain, String realDomain) throws InvalidMappingException {
451 getLogger().info("Add domain mapping: " + aliasDomain + " => " + realDomain);
452 return addMappingInternal(null, aliasDomain, VirtualUserTable.ALIASDOMAIN_PREFIX + realDomain);
453 }
454
455
456
457
458 public synchronized boolean removeAliasDomainMapping(String aliasDomain, String realDomain) throws InvalidMappingException {
459 getLogger().info("Remove domain mapping: " + aliasDomain + " => " + realDomain);
460 return removeMappingInternal(null, aliasDomain, VirtualUserTable.ALIASDOMAIN_PREFIX + realDomain);
461 }
462
463
464
465
466
467
468
469
470 private String mapAddress(String user,String domain) {
471 String mappings = mapAddressInternal(user, domain);
472
473
474
475 if (mappings != null && mappings.indexOf(VirtualUserTable.ALIASDOMAIN_PREFIX) > -1) {
476 Collection mapCol = VirtualUserTableUtil.mappingToCollection(mappings);
477 Iterator mapIt = mapCol.iterator();
478
479 List col = new ArrayList(mapCol.size());
480
481 while (mapIt.hasNext()) {
482 int i = 0;
483 String mapping = mapIt.next().toString();
484
485 if (mapping.startsWith(VirtualUserTable.ALIASDOMAIN_PREFIX)) {
486 col.add(i,mapping);
487 i++;
488 } else {
489 col.add(mapping);
490 }
491 }
492 return VirtualUserTableUtil.CollectionToMapping(col);
493 } else {
494 return mappings;
495 }
496 }
497
498
499
500
501
502
503
504
505
506
507
508 protected abstract boolean addMappingInternal(String user, String domain, String mapping) throws InvalidMappingException;
509
510
511
512
513
514
515
516
517
518
519 protected abstract boolean removeMappingInternal(String user, String domain, String mapping) throws InvalidMappingException;
520
521
522
523
524
525
526 protected abstract List getDomainsInternal();
527
528
529
530
531
532
533
534
535 protected abstract Collection getUserDomainMappingsInternal(String user, String domain);
536
537
538
539
540
541
542 protected abstract Map getAllMappingsInternal();
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557 protected abstract String mapAddressInternal(String user, String domain);
558 }