001 /* 002 * Copyright 2010 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package com.hs.mail.container.config; 017 018 import java.io.File; 019 import java.io.IOException; 020 import java.net.InetAddress; 021 import java.net.UnknownHostException; 022 import java.text.DecimalFormat; 023 import java.util.Calendar; 024 import java.util.Date; 025 import java.util.GregorianCalendar; 026 import java.util.HashSet; 027 import java.util.Locale; 028 import java.util.Properties; 029 import java.util.Set; 030 031 import javax.net.ssl.SSLContext; 032 033 import org.apache.commons.io.FileUtils; 034 import org.apache.commons.io.FilenameUtils; 035 import org.apache.commons.lang.ArrayUtils; 036 import org.apache.commons.lang.StringUtils; 037 import org.apache.log4j.Logger; 038 import org.springframework.beans.factory.InitializingBean; 039 040 import com.hs.mail.container.server.SSLContextFactory; 041 import com.hs.mail.util.InetAddressMatcher; 042 043 /** 044 * Provides a number of properties to the server. 045 * 046 * @author Won Chul Doh 047 * @since Jun 3, 2010 048 */ 049 public class Config implements InitializingBean { 050 051 static Logger console = Logger.getLogger("console"); 052 053 public static final String ZIPFILE_EXTENSION = "zip"; 054 055 private static final String DEF_CACHE_FIELDS = "Bcc,Cc,Date,From,In-Reply-To,Message-ID,Reply-To,Sender,Subject,To"; 056 057 private static DecimalFormat formatter = new DecimalFormat("0000000000"); 058 059 private static Properties properties; 060 private static File dataDirectory; 061 private static File tempDirectory; 062 private static File spoolDirectory; 063 private static String authScheme; 064 private static Set<String> defaultCacheFields; 065 private static long defaultQuota; 066 private static String[] domains; 067 private static String hostName; 068 private static String helloName; 069 private static InetAddressMatcher authorizedNetworks; 070 private static String postmaster; 071 private static long maxMessageSize; 072 private static int maxRcpt; 073 private static boolean saslAuthEnabled; 074 private static SSLContext context; 075 private static boolean initData = true; 076 077 public void setInitData(boolean initData) { 078 Config.initData = initData; 079 } 080 081 public static Properties getProperties() { 082 return properties; 083 } 084 085 public void setProperties(Properties properties) { 086 Config.properties = properties; 087 } 088 089 public static File getDataDirectory() { 090 return dataDirectory; 091 } 092 093 public static String getSubDirectory(Date date, long physmessageid) { 094 Calendar cal = Calendar.getInstance(); 095 cal.setTime(date); 096 return new StringBuilder() 097 .append("mail") 098 .append(File.separator) 099 .append(cal.get(GregorianCalendar.YEAR)) 100 .append(File.separator) 101 .append(cal.get(GregorianCalendar.MONTH) + 1) 102 .append(File.separator) 103 .append(cal.get(GregorianCalendar.DAY_OF_MONTH)) 104 .append(File.separator) 105 .append(Integer.parseInt( 106 formatter.format(physmessageid) 107 .substring(5, 8))) 108 .toString(); 109 } 110 111 public static File getDataFile(Date date, long physmessageid) throws IOException { 112 File directory = new File(dataDirectory, getSubDirectory(date, physmessageid)); 113 FileUtils.forceMkdir(directory); 114 File zipped = new File(directory, Long.toString(physmessageid) 115 + FilenameUtils.EXTENSION_SEPARATOR_STR + ZIPFILE_EXTENSION); 116 return (zipped.exists()) ? zipped : new File(directory, Long 117 .toString(physmessageid)); 118 } 119 120 public static File getTempDirectory() { 121 return tempDirectory; 122 } 123 124 public static File getSpoolDirectory() { 125 return spoolDirectory; 126 } 127 128 public static void setSpoolDirectory(File dir) { 129 spoolDirectory = dir; 130 } 131 132 public static File getSnapshotDirectory() { 133 return new File(spoolDirectory, "snapshot"); 134 } 135 136 public static String getAuthScheme() { 137 return authScheme; 138 } 139 140 public static Set<String> getDefaultCacheFields() { 141 return defaultCacheFields; 142 } 143 144 public static long getDefaultQuota() { 145 return defaultQuota; 146 } 147 148 public static String[] getDomains() { 149 return domains; 150 } 151 152 public static String getDefaultDomain() { 153 return domains[0]; 154 } 155 156 public static boolean isLocal(String domain) { 157 return ArrayUtils.contains(domains, domain); 158 } 159 160 public static String getHostName() { 161 return hostName; 162 } 163 164 public static String getHelloName() { 165 return helloName; 166 } 167 168 public static InetAddressMatcher getAuthorizedNetworks() { 169 return authorizedNetworks; 170 } 171 172 public static String getPostmaster() { 173 return postmaster; 174 } 175 176 public static long getMaxMessageSize() { 177 return maxMessageSize; 178 } 179 180 public static int getMaxRcptCount() { 181 return maxRcpt; 182 } 183 184 public static boolean isSaslAuthEnabled() { 185 return saslAuthEnabled; 186 } 187 188 public static SSLContext getSSLContext() { 189 return context; 190 } 191 192 public static String getProperty(String key, String defaultValue) { 193 return replaceByProperties(properties.getProperty(key, defaultValue)); 194 } 195 196 public static File getFileProperty(String key, String file) 197 throws IOException { 198 return new File(getProperty(key, file)); 199 } 200 201 public static long getNumberProperty(String key, long defaultValue) { 202 try { 203 String value = getProperty(key, Long.toString(defaultValue)); 204 return Long.valueOf(value); 205 } catch (NumberFormatException e) { 206 return defaultValue; 207 } 208 } 209 210 public static boolean getBooleanProperty(String key, boolean defaultValue) { 211 String value = getProperty(key, Boolean.toString(defaultValue)); 212 return "yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value); 213 } 214 215 public void afterPropertiesSet() throws Exception { 216 dataDirectory = getFileProperty("data_directory", "${app.home}" 217 + File.separator + "data"); 218 console.info("Data directory is: " + dataDirectory.getCanonicalPath()); 219 220 tempDirectory = getFileProperty("temp_directory", "${app.home}" 221 + File.separator + "temp"); 222 console.info("Temp directory is: " + tempDirectory.getCanonicalPath()); 223 224 spoolDirectory = getFileProperty("queue_directory", "${app.home}" 225 + File.separator + "spool"); 226 console.info("Spool directory is: " + spoolDirectory.getCanonicalPath()); 227 228 authScheme = getProperty("auth_scheme", null); 229 console.info("Authentication scheme is " 230 + ((authScheme != null) ? authScheme : "not specified")); 231 232 String fields = getProperty("default_cache_fields", DEF_CACHE_FIELDS); 233 defaultCacheFields = buildDefaultCacheFields(StringUtils.split(fields, ',')); 234 235 long quota = getNumberProperty("default_quota", 0); 236 defaultQuota = quota * 1024 * 1024; 237 console.info("Default quota is: " + quota + "MB"); 238 239 hostName = getProperty("myhostname", null); 240 if (null == hostName) { 241 try { 242 hostName = InetAddress.getLocalHost().getHostName(); 243 } catch (UnknownHostException ex) { 244 hostName = "localhost"; 245 } 246 } 247 console.info("Local host is: " + hostName); 248 249 String domain = getProperty("mydomain", null); 250 if (null == domain) { 251 domains = new String[] { StringUtils.substringAfter(hostName, ".") }; 252 } else { 253 domains = StringUtils.split(domain, ","); 254 } 255 for (int i = 0; i < domains.length; i++) { 256 console.info("Handling mail for: " + domains[i]); 257 } 258 259 String networks = getProperty("mynetworks", "0.0.0.0/0.0.0.0"); 260 authorizedNetworks = new InetAddressMatcher(networks); 261 console.info("SMTP relaying is allowded to: " + networks); 262 263 helloName = getProperty("smtp_helo_name", hostName); 264 265 postmaster = getProperty("postmaster", "postmaster"); 266 if (postmaster.indexOf('@') < 0) { 267 String domainName = null; 268 for (int i = 0; i < domains.length; i++) { 269 String serverName = domains[i].toLowerCase(Locale.US); 270 if (!"localhost".equals(serverName)) { 271 domainName = serverName; 272 } 273 } 274 postmaster = postmaster + "@" 275 + (domainName != null ? domainName : hostName); 276 } 277 console.info("Postmaster address is: " + postmaster); 278 279 maxMessageSize = getNumberProperty("message_size_limit", 10240000); 280 console.info("Maximum message size is: " + maxMessageSize); 281 282 maxRcpt = (int) getNumberProperty("smtp_recipient_limit", 0); 283 console.info("Maximun recipients count is: " + maxRcpt); 284 285 saslAuthEnabled = getBooleanProperty("smtp_sasl_auth_enable", false); 286 287 if (initData) { 288 FileUtils.forceMkdir(dataDirectory); 289 FileUtils.forceMkdir(tempDirectory); 290 FileUtils.forceMkdir(spoolDirectory); 291 FileUtils.forceMkdir(getSnapshotDirectory()); 292 buildSSLContext(); 293 } 294 } 295 296 private Set<String> buildDefaultCacheFields(String[] fields) { 297 Set<String> result = new HashSet<String>(); 298 for (int i = 0; i < fields.length; i++) { 299 result.add(fields[i]); 300 } 301 return result; 302 } 303 304 private void buildSSLContext() throws IOException { 305 if (context == null) { 306 String keyStore = getProperty("tls_keystore", null); 307 if (keyStore != null) { 308 String keyStorePassword = getProperty("tls_keypass", ""); 309 String certificatePassword = getProperty("tls_storepass", ""); 310 context = SSLContextFactory.createContext(keyStore, 311 keyStorePassword, certificatePassword); 312 } 313 } 314 } 315 316 public static String replaceByProperties(String source) { 317 if (null == source) 318 return null; 319 int end; 320 int start = source.indexOf("${"); 321 while (-1 != start) { 322 end = source.indexOf("}", start + 2); 323 if (end > 0) { 324 String propName = source.substring(start + 2, end); 325 String propValue = System.getProperty(propName); 326 if (null != propValue) { 327 int propValueLen = propValue.length(); 328 source = new StringBuffer(source.length()) 329 .append(source.substring(0, start)) 330 .append(propValue) 331 .append(source.substring(end + 1)).toString(); 332 start = source.indexOf("${", start + propValueLen); 333 } else { 334 start = source.indexOf("${", end + 1); 335 } 336 } else { 337 break; 338 } 339 } 340 return source; 341 } 342 343 }