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    }