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.dao;
017    
018    import java.sql.Connection;
019    import java.sql.PreparedStatement;
020    import java.sql.ResultSet;
021    import java.sql.SQLException;
022    import java.sql.Statement;
023    import java.util.Date;
024    import java.util.List;
025    
026    import org.apache.commons.lang.StringUtils;
027    import org.springframework.jdbc.core.PreparedStatementCreator;
028    import org.springframework.jdbc.core.RowMapper;
029    import org.springframework.jdbc.support.GeneratedKeyHolder;
030    import org.springframework.jdbc.support.KeyHolder;
031    
032    import com.hs.mail.imap.ImapConstants;
033    import com.hs.mail.imap.mailbox.Mailbox;
034    import com.hs.mail.imap.message.PhysMessage;
035    
036    /**
037     * 
038     * @author Won Chul Doh
039     * @since Mar 23, 2010
040     *
041     */
042    public class MySqlMailboxDao extends AbstractDao implements MailboxDao {
043    
044            public Mailbox getMailbox(long ownerID, String mailboxName) {
045                    String sql = "SELECT * FROM mailbox USE INDEX (fk_mailbox_user) WHERE ownerid = ? AND name = ?";
046                    return (Mailbox) queryForObject(sql, new Object[] { new Long(ownerID),
047                                    mailboxName }, mailboxRowMapper);
048            }
049    
050            public boolean mailboxExists(long ownerID, String mailboxName) {
051                    String sql = "SELECT COUNT(1) FROM mailbox USE INDEX (fk_mailbox_user) WHERE ownerid = ? AND name = ?";
052                    return queryForInt(sql, new Object[] { new Long(ownerID), mailboxName }) > 0;
053            }
054    
055            public Mailbox createMailbox(long ownerID, String mailboxName) {
056                    Mailbox result = doCreateMailbox(ownerID, mailboxName);
057                    forceCreate(ownerID, Mailbox.getParent(mailboxName));
058                    return result;
059            }
060    
061            public void renameMailbox(Mailbox source, String dest) {
062                    long ownerid = source.getOwnerID();
063    
064                    // If the server¡¯s hierarchy separator character appears in the
065                    // name, the server SHOULD create any superior hierarchical names
066                    // that are needed.
067                    forceCreate(ownerid, Mailbox.getParent(dest));
068    
069                    // If INBOX is renamed, then all messages in INBOX MUST be moved to
070                    // a new mailbox with the given name, leaving INBOX empty.
071                    String base = source.getName();
072                    if (ImapConstants.INBOX_NAME.equals(base)) {
073                            doCreateMailbox(ownerid, ImapConstants.INBOX_NAME);
074                    }
075                    doRenameMailbox(source.rename(base, dest));
076    
077                    // If source name has inferior hierarchical names, then the inferior
078                    // hierarchical names MUST also be renamed.
079                    List<Mailbox> children = getChildren(ownerid, base);
080                    for (Mailbox child : children) {
081                            doRenameMailbox(child.rename(base, dest));
082                    }
083            }
084    
085            private void forceCreate(long ownerID, String mailboxName) {
086                    while (!"".equals(mailboxName) && !mailboxExists(ownerID, mailboxName)) {
087                            doCreateMailbox(ownerID, mailboxName);
088                            mailboxName = Mailbox.getParent(mailboxName);
089                    }
090            }
091    
092            private Mailbox doCreateMailbox(final long ownerID, final String mailboxName) {
093                    final String sql = "INSERT INTO mailbox (name, ownerid, nextuid, uidvalidity) VALUES(?, ?, ?, ?)";
094                    final long uidValidity = System.currentTimeMillis();
095                    KeyHolder keyHolder = new GeneratedKeyHolder();
096                    getJdbcTemplate().update(new PreparedStatementCreator() {
097                            public PreparedStatement createPreparedStatement(Connection con)
098                                            throws SQLException {
099                                    PreparedStatement pstmt = con.prepareStatement(sql,
100                                                    Statement.RETURN_GENERATED_KEYS);
101                                    pstmt.setString(1, mailboxName);
102                                    pstmt.setLong(2, ownerID);
103                                    pstmt.setLong(3, 1);
104                                    pstmt.setLong(4, uidValidity);
105                                    return pstmt;
106                            }
107                    }, keyHolder);
108                    
109                    Mailbox mailbox = new Mailbox();
110                    mailbox.setMailboxID(keyHolder.getKey().longValue());
111                    mailbox.setOwnerID(ownerID);
112                    mailbox.setName(mailboxName);
113                    mailbox.setNextUID(1);
114                    mailbox.setUidValidity(uidValidity);
115                    
116                    return mailbox;
117            }
118    
119            public List<Mailbox> getChildren(long userID, long ownerID,
120                            String mailboxName, boolean subscribed) {
121                    if (subscribed)
122                            return getSubscriptions(userID, ownerID, mailboxName);
123                    else
124                            return getChildren(ownerID, mailboxName);
125            }
126            
127            private List<Mailbox> getChildren(long ownerID, String mailboxName) {
128                    if (StringUtils.isEmpty(mailboxName)) {
129                            String sql = "SELECT * FROM mailbox USE INDEX (fk_mailbox_user) WHERE ownerid = ? ORDER BY name";
130                            return getJdbcTemplate().query(sql,
131                                            new Object[] { new Long(ownerID) }, mailboxRowMapper);
132                    } else {
133                            String sql = "SELECT * FROM mailbox USE INDEX (fk_mailbox_user) WHERE ownerid = ? AND name LIKE ? ORDER BY name";
134                            return getJdbcTemplate().query(
135                                            sql,
136                                            new Object[] {
137                                                            new Long(ownerID),
138                                                            new StringBuilder(escape(mailboxName))
139                                                                            .append(Mailbox.folderSeparator).append('%')
140                                                                            .toString() }, mailboxRowMapper);
141                    }
142            }
143            
144            private List<Mailbox> getSubscriptions(long userID, long ownerID,
145                            String mailboxName) {
146                    if (StringUtils.isEmpty(mailboxName)) {
147                            String sql = "SELECT b.* FROM mailbox b, subscription s WHERE s.userid = ? AND b.ownerid = ? AND b.name = s.name ORDER BY b.name";
148                            return getJdbcTemplate().query(sql,
149                                            new Object[] { new Long(userID), new Long(ownerID) },
150                                            mailboxRowMapper);
151                    } else {
152                            String sql = "SELECT b.* FROM mailbox b, subscription s WHERE s.userid = ? AND b.ownerid = ? AND b.name LIKE ? AND b.name = s.name ORDER BY b.name";
153                            return getJdbcTemplate().query(
154                                            sql,
155                                            new Object[] {
156                                                            new Long(userID),
157                                                            new Long(ownerID),
158                                                            new StringBuilder(escape(mailboxName))
159                                                                            .append(Mailbox.folderSeparator).append('%')
160                                                                            .toString() }, mailboxRowMapper);
161                    }
162            }
163    
164            public int getChildCount(long ownerID, String mailboxName) {
165                    if (StringUtils.isEmpty(mailboxName)) {
166                            String sql = "SELECT COUNT(1) FROM mailbox USE INDEX (fk_mailbox_user) WHERE ownerid = ?";
167                            Object[] params = { new Long(ownerID) };
168                            return queryForInt(sql, params);
169                    } else {
170                            String sql = "SELECT COUNT(1) FROM mailbox USE INDEX (fk_mailbox_user) WHERE ownerid = ? AND name LIKE ?";
171                            Object[] params = {
172                                            new Long(ownerID),
173                                            new StringBuilder(escape(mailboxName)).append(
174                                                            Mailbox.folderSeparator).append('%').toString() };
175                            return queryForInt(sql, params);
176                    }
177            }
178            
179            public List<Long> getMailboxIDList(String mailboxName) {
180                    String sql = "SELECT mailboxid FROM mailbox WHERE name = ?";
181                    return (List<Long>) getJdbcTemplate().queryForList(sql,
182                                    new Object[] { mailboxName }, Long.class);
183            }
184    
185            public boolean isSubscribed(long userID, String mailboxName) {
186                    String sql = "SELECT COUNT(1) FROM subscription WHERE userid = ? AND name = ?";
187                    int count = getJdbcTemplate().queryForInt(sql,
188                                    new Object[] { new Long(userID), mailboxName });
189                    return (count > 0);
190            }
191    
192            public void addSubscription(long userID, long mailboxID, String mailboxName) {
193                    if (!isSubscribed(userID, mailboxName)) {
194                            String sql = "INSERT INTO subscription (userid, mailboxid, name) VALUES(?, ?, ?)";
195                            getJdbcTemplate().update(
196                                            sql,
197                                            new Object[] { new Long(userID), new Long(mailboxID),
198                                                            mailboxName });
199                    } else {
200                            // already subscribed to the mailbox, verified after attempt to
201                            // subscribe
202                    }
203            }
204    
205            public void deleteSubscription(long userID, String mailboxName) {
206                    String sql = "DELETE FROM subscription WHERE userid = ? AND name = ?";
207                    getJdbcTemplate().update(sql,
208                                    new Object[] { new Long(userID), mailboxName });
209            }
210    
211            private int doRenameMailbox(Mailbox mailbox) {
212                    String sql = "UPDATE mailbox SET name = ? WHERE mailboxid = ?";
213                    return getJdbcTemplate().update(
214                                    sql,
215                                    new Object[] { mailbox.getName(),
216                                                    new Long(mailbox.getMailboxID()) });
217            }
218    
219            public List<Long> getDeletedMessageIDList(long mailboxID) {
220                    String sql = "SELECT messageid FROM message WHERE mailboxid = ? AND deleted = 'Y'";
221                    return (List<Long>) getJdbcTemplate().queryForList(sql,
222                                    new Object[] { new Long(mailboxID) }, Long.class);
223            }
224    
225            public void deleteMailboxes(long ownerID) {
226                    String sql = "DELETE FROM mailbox WHERE ownerid = ?";
227                    getJdbcTemplate().update(sql, new Object[] { new Long(ownerID) });
228            }
229            
230            public void deleteMailbox(long ownerID, long mailboxID) {
231                    String sql = "DELETE FROM mailbox WHERE mailboxid = ?";
232                    getJdbcTemplate().update(sql, new Object[] { new Long(mailboxID) });
233            }
234    
235            public void forbidSelectMailbox(long ownerID, long mailboxID) {
236                    String sql = "UPDATE mailbox SET noselect = 'Y' WHERE mailboxid = ?";
237                    getJdbcTemplate().update(sql, new Object[] { new Long(mailboxID) });
238            }
239    
240            public List<PhysMessage> getDanglingMessageIDList(long ownerID) {
241                    String sql = "SELECT m.physmessageid, p.internaldate FROM mailbox b, message m, physmessage p WHERE b.ownerid = ? AND m.physmessageid = p.id AND m.mailboxid = b.mailboxid GROUP BY m.physmessageid HAVING COUNT(m.physmessageid) = 1";
242                    return (List<PhysMessage>) getJdbcTemplate().query(sql,
243                                    new Object[] { new Long(ownerID) }, new RowMapper() {
244                                            public Object mapRow(ResultSet rs, int rowNum)
245                                                            throws SQLException {
246                                                    PhysMessage pm = new PhysMessage();
247                                                    pm.setPhysMessageID(rs.getLong("physmessageid"));
248                                                    pm.setInternalDate(new Date(rs.getTimestamp("internaldate").getTime()));
249                                                    return pm;
250                                            }
251    
252                                    });
253            }
254            
255            public List<PhysMessage> getDanglingMessageIDList(long ownerID,
256                            long mailboxID) {
257                    String sql = "SELECT m.physmessageid, p.internaldate FROM message m, message n, physmessage p WHERE m.mailboxid = ? AND m.physmessageid = n.physmessageid AND m.physmessageid = p.id GROUP BY n.physmessageid HAVING COUNT(n.physmessageid) = 1";
258                    return (List<PhysMessage>) getJdbcTemplate().query(sql,
259                                    new Object[] { new Long(mailboxID) }, new RowMapper() {
260                                            public Object mapRow(ResultSet rs, int rowNum)
261                                                            throws SQLException {
262                                                    PhysMessage pm = new PhysMessage();
263                                                    pm.setPhysMessageID(rs.getLong("physmessageid"));
264                                                    pm.setInternalDate(new Date(rs.getTimestamp("internaldate").getTime()));
265                                                    return pm;
266                                            }
267    
268                                    });
269            }
270    
271            public void deleteMessages(long ownerID) {
272                    String sql = "DELETE m.*, k.* FROM message AS m LEFT JOIN keyword AS k ON (m.messageid = k.messageid) WHERE mailboxid IN (SELECT mailboxid FROM mailbox WHERE ownerid = ?)";
273                    getJdbcTemplate().update(sql, new Object[] { new Long(ownerID) });
274            }
275    
276            public void deleteMessages(long ownerID, long mailboxID) {
277                    String sql = "DELETE m.*, k.* FROM message AS m LEFT JOIN keyword AS k ON (m.messageid = k.messageid) WHERE m.mailboxid = ?";
278                    getJdbcTemplate().update(sql, new Object[] { new Long(mailboxID) });
279            }
280    
281            public int getMessageCount(long mailboxID) {
282                    String sql = "SELECT COUNT(1) FROM message WHERE mailboxid = ?";
283                    return queryForInt(sql, new Object[] { new Long(mailboxID) });
284            }
285            
286            public int getRecentMessageCount(long mailboxID) {
287                    String sql = "SELECT COUNT(1) FROM message WHERE mailboxid = ? AND recent = 'Y'";
288                    return queryForInt(sql, new Object[] { new Long(mailboxID) });
289            }
290            
291            public int getUnseenMessageCount(long mailboxID) {
292                    String sql = "SELECT COUNT(1) FROM message WHERE mailboxid = ? AND seen = 'N'";
293                    return getJdbcTemplate().queryForInt(sql,
294                                    new Object[] { new Long(mailboxID) });
295            }
296            
297            public long getFirstUnseenMessageID(long mailboxID) {
298                    String sql = "SELECT messageid FROM message WHERE mailboxid = ? AND seen = 'N' ORDER BY messageid LIMIT 1";
299                    return queryForLong(sql, new Object[] { new Long(mailboxID) });
300            }
301            
302            public List<Long> getGarbageMailboxList() {
303                    String sql = "SELECT m.mailboxid FROM mailbox AS m LEFT JOIN user AS u ON m.ownerid = u.id WHERE u.id IS NULL";
304                    return getJdbcTemplate().queryForList(sql, Long.class);
305            }
306            
307             private static RowMapper mailboxRowMapper = new RowMapper() {
308                     public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
309                            Mailbox mailbox = new Mailbox();
310                            mailbox.setMailboxID(rs.getLong("mailboxid"));
311                            mailbox.setOwnerID(rs.getLong("ownerid"));
312                            mailbox.setName(rs.getString("name"));
313                            mailbox.setNoInferiors("Y".equals(rs.getString("noinferiors")));
314                            mailbox.setNoSelect("Y".equals(rs.getString("noselect")));
315                            mailbox.setReadOnly("Y".equals(rs.getString("readonly")));
316                            mailbox.setNextUID(rs.getLong("nextuid"));
317                            mailbox.setUidValidity(rs.getLong("uidvalidity"));
318                            return mailbox;
319                    }
320             };
321     
322    }