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.imap.user; 017 018 import java.io.File; 019 import java.io.IOException; 020 import java.util.List; 021 022 import javax.mail.Quota; 023 import javax.security.auth.callback.CallbackHandler; 024 import javax.security.auth.login.AccountNotFoundException; 025 import javax.security.auth.login.CredentialException; 026 import javax.security.auth.login.LoginContext; 027 import javax.security.auth.login.LoginException; 028 029 import org.apache.commons.collections.CollectionUtils; 030 import org.apache.commons.io.FileUtils; 031 import org.apache.log4j.Logger; 032 import org.springframework.dao.DataAccessException; 033 import org.springframework.transaction.PlatformTransactionManager; 034 import org.springframework.transaction.TransactionStatus; 035 import org.springframework.transaction.support.TransactionCallback; 036 import org.springframework.transaction.support.TransactionCallbackWithoutResult; 037 import org.springframework.transaction.support.TransactionTemplate; 038 import org.springframework.util.Assert; 039 040 import com.hs.mail.container.config.Config; 041 import com.hs.mail.imap.dao.DaoFactory; 042 import com.hs.mail.imap.dao.MailboxDao; 043 import com.hs.mail.imap.dao.MessageDao; 044 import com.hs.mail.imap.dao.UserDao; 045 import com.hs.mail.imap.message.PhysMessage; 046 import com.hs.mail.security.login.BasicCallbackHandler; 047 import com.hs.mail.smtp.message.MailAddress; 048 049 /** 050 * 051 * @author Won Chul Doh 052 * @since Jun 24, 2010 053 * 054 */ 055 public class DefaultUserManager implements UserManager { 056 057 private static Logger logger = Logger.getLogger(UserManager.class); 058 059 private TransactionTemplate transactionTemplate; 060 061 public void setTransactionManager(PlatformTransactionManager transactionManager) { 062 Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); 063 this.transactionTemplate = new TransactionTemplate(transactionManager); 064 } 065 066 public TransactionTemplate getTransactionTemplate() { 067 return transactionTemplate; 068 } 069 070 /** 071 * Authenticate the given user against the given password. When 072 * authenticated, the ID of the user will be supplied. 073 * 074 * @param username 075 * user name 076 * @param password 077 * password supplied 078 * @return id of the user when authenticated 079 * @throws LoginException 080 * when the user does not exist or not authenticated 081 */ 082 public long login(String username, String password) throws LoginException { 083 String address = toAddress(username); 084 User user = DaoFactory.getUserDao().getUserByAddress(address); 085 if (user == null) { 086 throw new AccountNotFoundException("Account for " + username 087 + " not found"); 088 } 089 if (Config.getAuthScheme() != null) { 090 CallbackHandler callbackHandler = new BasicCallbackHandler(address, 091 password.toCharArray()); 092 LoginContext lc = new LoginContext(Config.getAuthScheme(), 093 callbackHandler); 094 lc.login(); 095 } else { 096 if (!password.equals(user.getPassword())) { 097 throw new CredentialException("Incorrect password for " 098 + username); 099 } 100 } 101 return user.getID(); 102 } 103 104 public User getUser(long id) { 105 return DaoFactory.getUserDao().getUser(id); 106 } 107 108 public long getUserID(String address) { 109 return DaoFactory.getUserDao().getUserID(address); 110 } 111 112 public User getUserByAddress(String address) { 113 return DaoFactory.getUserDao().getUserByAddress(address); 114 } 115 116 public int getUserCount(String domain) { 117 return DaoFactory.getUserDao().getUserCount(domain); 118 } 119 120 public List<User> getUserList(String domain, int page, int pageSize) { 121 return DaoFactory.getUserDao().getUserList(domain, page, pageSize); 122 } 123 124 public long addUser(final User user) { 125 return (Long) getTransactionTemplate().execute( 126 new TransactionCallback() { 127 public Object doInTransaction(TransactionStatus status) { 128 try { 129 return DaoFactory.getUserDao().addUser(user); 130 } catch (DataAccessException ex) { 131 status.setRollbackOnly(); 132 throw ex; 133 } 134 } 135 }); 136 } 137 138 public int updateUser(final User user) { 139 return (Integer) getTransactionTemplate().execute( 140 new TransactionCallback() { 141 public Object doInTransaction(TransactionStatus status) { 142 try { 143 return DaoFactory.getUserDao().updateUser(user); 144 } catch (DataAccessException ex) { 145 status.setRollbackOnly(); 146 throw ex; 147 } 148 } 149 }); 150 } 151 152 public void deleteUser(final long id) { 153 getTransactionTemplate().execute( 154 new TransactionCallbackWithoutResult() { 155 public void doInTransactionWithoutResult( 156 TransactionStatus status) { 157 try { 158 UserDao dao = DaoFactory.getUserDao(); 159 dao.deleteUser(id); 160 emptyMailboxes(id); 161 } catch (DataAccessException ex) { 162 status.setRollbackOnly(); 163 throw ex; 164 } 165 } 166 }); 167 } 168 169 public void emptyUser(final long id) { 170 getTransactionTemplate().execute( 171 new TransactionCallbackWithoutResult() { 172 public void doInTransactionWithoutResult( 173 TransactionStatus status) { 174 try { 175 emptyMailboxes(id); 176 } catch (DataAccessException ex) { 177 status.setRollbackOnly(); 178 throw ex; 179 } 180 } 181 }); 182 } 183 184 private void emptyMailboxes(long ownerID) { 185 MailboxDao dao = DaoFactory.getMailboxDao(); 186 List<PhysMessage> danglings = dao.getDanglingMessageIDList(ownerID); 187 dao.deleteMessages(ownerID); 188 dao.deleteMailboxes(ownerID); 189 if (CollectionUtils.isNotEmpty(danglings)) { 190 for (PhysMessage pm : danglings) { 191 deletePhysicalMessage(pm); 192 } 193 } 194 } 195 196 private void deletePhysicalMessage(PhysMessage pm) { 197 MessageDao dao = DaoFactory.getMessageDao(); 198 dao.deletePhysicalMessage(pm.getPhysMessageID()); 199 try { 200 File file = Config.getDataFile(pm.getInternalDate(), pm.getPhysMessageID()); 201 FileUtils.forceDelete(file); 202 } catch (IOException ex) { 203 logger.warn(ex.getMessage(), ex); // Ignore - What we can do? 204 } 205 } 206 207 public Alias getAlias(long id) { 208 return DaoFactory.getUserDao().getAlias(id); 209 } 210 211 public int getAliasCount(String domain) { 212 return DaoFactory.getUserDao().getAliasCount(domain); 213 } 214 215 public List<Alias> getAliasList(String domain, int page, int pageSize) { 216 return DaoFactory.getUserDao().getAliasList(domain, page, pageSize); 217 } 218 219 public List<Alias> expandAlias(String alias) { 220 return DaoFactory.getUserDao().expandAlias(alias); 221 } 222 223 public long addAlias(final Alias alias) { 224 return (Long) getTransactionTemplate().execute( 225 new TransactionCallback() { 226 public Object doInTransaction(TransactionStatus status) { 227 try { 228 return DaoFactory.getUserDao().addAlias(alias); 229 } catch (DataAccessException ex) { 230 status.setRollbackOnly(); 231 throw ex; 232 } 233 } 234 }); 235 } 236 237 public int updateAlias(final Alias alias) { 238 return (Integer) getTransactionTemplate().execute( 239 new TransactionCallback() { 240 public Object doInTransaction(TransactionStatus status) { 241 try { 242 return DaoFactory.getUserDao().updateAlias(alias); 243 } catch (DataAccessException ex) { 244 status.setRollbackOnly(); 245 throw ex; 246 } 247 } 248 }); 249 } 250 251 public void deleteAlias(final long id) { 252 getTransactionTemplate().execute( 253 new TransactionCallbackWithoutResult() { 254 public void doInTransactionWithoutResult( 255 TransactionStatus status) { 256 try { 257 UserDao dao = DaoFactory.getUserDao(); 258 dao.deleteAlias(id); 259 } catch (DataAccessException ex) { 260 status.setRollbackOnly(); 261 throw ex; 262 } 263 } 264 }); 265 } 266 267 public long getQuotaUsage(long ownerID) { 268 return DaoFactory.getUserDao().getQuotaUsage(ownerID); 269 } 270 271 public Quota getQuota(long ownerID, String quotaRoot) { 272 Quota quota = DaoFactory.getUserDao().getQuota(ownerID, quotaRoot); 273 if (quota.resources[0].limit == 0) { 274 quota.resources[0].limit = Config.getDefaultQuota(); 275 } 276 return quota; 277 } 278 279 public void setQuota(final long ownerID, final Quota quota) { 280 getTransactionTemplate().execute( 281 new TransactionCallbackWithoutResult() { 282 public void doInTransactionWithoutResult(TransactionStatus status) { 283 try { 284 DaoFactory.getUserDao().setQuota(ownerID, quota); 285 } catch (DataAccessException ex) { 286 status.setRollbackOnly(); 287 throw ex; 288 } 289 } 290 }); 291 } 292 293 public File getUserHome(MailAddress user) { 294 String str = user.getUser(); 295 StringBuilder sb = new StringBuilder( 296 (user.getHost() != null) ? user.getHost() : Config.getDefaultDomain()) 297 .append(File.separator) 298 .append("users") 299 .append(File.separator) 300 .append(str.charAt(0)) 301 .append(str.charAt(str.length() - 1)) 302 .append(File.separator) 303 .append(str); 304 return new File(Config.getDataDirectory(), sb.toString()); 305 } 306 307 public String getUserSieveScript(MailAddress user) { 308 File script = new File(getUserHome(user), "sieve"); 309 if (script.exists()) { 310 try { 311 return FileUtils.readFileToString(script); 312 } catch (IOException e) { 313 } 314 } 315 return null; 316 } 317 318 private String toAddress(String user) { 319 if (user.indexOf('@') != -1) 320 return user; 321 else 322 return new StringBuffer(user) 323 .append('@') 324 .append(Config.getDefaultDomain()) 325 .toString(); 326 } 327 328 }