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 }