001: /*
002: Copyright (C) 2004 Know Gate S.L. All rights reserved.
003: C/Oņa, 107 1š2 28050 Madrid (Spain)
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions
007: are met:
008:
009: 1. Redistributions of source code must retain the above copyright
010: notice, this list of conditions and the following disclaimer.
011:
012: 2. The end-user documentation included with the redistribution,
013: if any, must include the following acknowledgment:
014: "This product includes software parts from hipergate
015: (http://www.hipergate.org/)."
016: Alternately, this acknowledgment may appear in the software itself,
017: if and wherever such third-party acknowledgments normally appear.
018:
019: 3. The name hipergate must not be used to endorse or promote products
020: derived from this software without prior written permission.
021: Products derived from this software may not be called hipergate,
022: nor may hipergate appear in their name, without prior written
023: permission.
024:
025: This library is distributed in the hope that it will be useful,
026: but WITHOUT ANY WARRANTY; without even the implied warranty of
027: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
028:
029: You should have received a copy of hipergate License with this code;
030: if not, visit http://www.hipergate.org or mail to info@hipergate.org
031: */
032:
033: package com.knowgate.hipermail;
034:
035: import java.lang.UnsupportedOperationException;
036:
037: import java.io.File;
038: import java.io.IOException;
039: import java.io.InputStream;
040: import java.io.FileNotFoundException;
041:
042: import java.sql.DriverManager;
043: import java.sql.Connection;
044: import java.sql.SQLException;
045: import java.sql.PreparedStatement;
046: import java.sql.ResultSet;
047:
048: import java.net.ProtocolException;
049:
050: import javax.mail.Store;
051: import javax.mail.Session;
052: import javax.mail.URLName;
053: import javax.mail.Folder;
054: import javax.mail.MessagingException;
055: import javax.mail.StoreClosedException;
056: import javax.mail.AuthenticationFailedException;
057: import javax.mail.FolderNotFoundException;
058: import javax.mail.internet.MimeBodyPart;
059: import javax.mail.internet.MimeMessage;
060:
061: import com.knowgate.debug.DebugFile;
062: import com.knowgate.jdc.JDCConnection;
063: import com.knowgate.dataobjs.DB;
064: import com.knowgate.dataobjs.DBSubset;
065: import com.knowgate.misc.Environment;
066: import com.knowgate.misc.Gadgets;
067: import com.knowgate.hipergate.Category;
068: import com.knowgate.acl.*;
069: import com.knowgate.dfs.FileSystem;
070:
071: import javax.activation.CommandInfo;
072: import javax.activation.CommandMap;
073: import javax.activation.MailcapCommandMap;
074:
075: /**
076: * Manages local storage of mail messages at RDBMS and MBOX files
077: * @author Sergio Montoro Ten
078: * @version 2.2
079: */
080:
081: public class DBStore extends javax.mail.Store {
082:
083: private JDCConnection oConn;
084: private ACLUser oUser;
085: private URLName oURL;
086:
087: public DBStore(javax.mail.Session session, URLName url)
088: throws MessagingException {
089: super (session, url);
090:
091: Class cStore = null;
092:
093: try {
094: cStore = Class.forName("javax.mail.Store");
095: } catch (ClassNotFoundException cnf) {
096: }
097:
098: oURL = new URLName(url.getProtocol(), url.getHost(), url
099: .getPort(), url.getFile(), url.getUsername(), url
100: .getPassword());
101:
102: if (null != url.getFile()) {
103: File oDir = new File(url.getFile());
104:
105: if (!oDir.exists()) {
106: FileSystem oFS = new FileSystem();
107: try {
108: oFS.mkdirs(url.getFile());
109: } catch (Exception e) {
110: if (DebugFile.trace)
111: DebugFile.writeln(e.getClass().getName() + " "
112: + e.getMessage());
113: throw new MessagingException(e.getMessage(), e);
114: }
115: }
116: }
117:
118: oConn = null;
119: oUser = null;
120: }
121:
122: // ----------------------------------------------------------------------------------------
123:
124: /**
125: * Create new DBStore instance and open connection to the database
126: * @param oMailSession Session
127: * @param sProfile String
128: * @param sMBoxDir String
129: * @param sGuUser String
130: * @param sPwd String
131: * @return DBStore
132: * @throws MessagingException
133: */
134: public static DBStore open(Session oMailSession, String sProfile,
135: String sMBoxDir, String sGuUser, String sPwd)
136: throws MessagingException {
137: DBStore oNewInstance = new DBStore(oMailSession, new URLName(
138: "jdbc://", sProfile, -1, sMBoxDir, sGuUser, sPwd));
139: oNewInstance.connect(sProfile, sGuUser, sPwd);
140: return oNewInstance;
141: } // open
142:
143: // ---------------------------------------------------------------------------
144:
145: public JDCConnection getConnection() {
146: return oConn;
147: }
148:
149: // ---------------------------------------------------------------------------
150:
151: public Session getSession() {
152: return session;
153: }
154:
155: // ---------------------------------------------------------------------------
156:
157: public boolean isConnected() {
158: return (oConn != null);
159: }
160:
161: // ---------------------------------------------------------------------------
162:
163: /**
164: *
165: * @param host Name of profile file without extension { hipergate, real, test, demo }
166: * @param port Not used, must be -1
167: * @param user GUID of user to be authenticated
168: * @param password User password in clear text
169: * @return <b>true</b>
170: * @throws MessagingException
171: */
172: protected boolean protocolConnect(String host, int port,
173: String user, String password)
174: throws AuthenticationFailedException, MessagingException {
175:
176: if (DebugFile.trace) {
177: DebugFile.writeln("Begin DBStore.protocolConnect(" + host
178: + ", " + user + ", ...)");
179: DebugFile.incIdent();
180: }
181:
182: if (oConn != null || isConnected()) {
183: if (DebugFile.trace)
184: DebugFile.decIdent();
185: throw new MessagingException("DBStore ia already connected");
186: }
187:
188: String dburl = Environment.getProfileVar(host, "dburl");
189: String dbusr = Environment.getProfileVar(host, "dbuser");
190: String dbpwd = Environment.getProfileVar(host, "dbpassword");
191: String schema = Environment.getProfileVar(host, "schema", "");
192:
193: try {
194: if (DebugFile.trace)
195: DebugFile.writeln("DriverManager.getConnection("
196: + dburl + ", " + dbusr + ", ...)");
197:
198: if (schema.length() > 0)
199: oConn = new JDCConnection(DriverManager.getConnection(
200: dburl, dbusr, dbpwd), null, schema);
201: else
202: oConn = new JDCConnection(DriverManager.getConnection(
203: dburl, dbusr, dbpwd), null);
204:
205: oConn.setAutoCommit(false);
206:
207: short iAuth = ACL.autenticate(oConn, user, password,
208: ACL.PWD_CLEAR_TEXT);
209:
210: if (iAuth < 0) {
211: oConn.close();
212: oConn = null;
213: if (DebugFile.trace)
214: DebugFile.decIdent();
215: throw new AuthenticationFailedException(ACL
216: .getErrorMessage(iAuth)
217: + " (" + user + ")");
218: } else {
219: oUser = new ACLUser(oConn, user);
220: setConnected(true);
221: }
222: } catch (SQLException sqle) {
223: if (DebugFile.trace)
224: DebugFile.decIdent();
225: throw new MessagingException(sqle.getMessage(), sqle);
226: }
227:
228: if (DebugFile.trace) {
229: DebugFile.decIdent();
230: DebugFile.writeln("End DBStore.protocolConnect()");
231: }
232:
233: return true;
234: }
235:
236: // ---------------------------------------------------------------------------
237:
238: public void connect(String host, String user, String password)
239: throws MessagingException {
240:
241: protocolConnect(host, -1, user, password);
242: }
243:
244: // ---------------------------------------------------------------------------
245:
246: public void connect() throws MessagingException {
247: URLName oURLName = getURLName();
248:
249: protocolConnect(oURLName.getHost(), oURLName.getPort(),
250: oURLName.getUsername(), oURLName.getPassword());
251: }
252:
253: // ---------------------------------------------------------------------------
254:
255: public void close() throws MessagingException {
256: if (DebugFile.trace) {
257: DebugFile.writeln("Begin DBStore.close()");
258: DebugFile.incIdent();
259: }
260: if (null != oConn && isConnected()) {
261: try {
262: oConn.close();
263: oConn = null;
264: oUser = null;
265: setConnected(false);
266: } catch (SQLException sqle) {
267: throw new MessagingException(sqle.getMessage(), sqle);
268: }
269: } else {
270: throw new StoreClosedException(this , "Store already closed");
271: }
272: if (DebugFile.trace) {
273: DebugFile.decIdent();
274: DebugFile.writeln("End DBStore.close()");
275: }
276: }
277:
278: // ---------------------------------------------------------------------------
279:
280: /**
281: * Calls getFolder(oURL.getFile());
282: * @param oURL URLName
283: * @return DBFolder instance
284: * @throws StoreClosedException
285: * @throws FolderNotFoundException
286: * @throws MessagingException
287: */
288: public Folder getFolder(URLName oURL) throws StoreClosedException,
289: FolderNotFoundException, MessagingException {
290:
291: return getFolder(oURL.getFile());
292: }
293:
294: // ---------------------------------------------------------------------------
295:
296: /**
297: * <p>Get folder by guid or name</p>
298: * @param sFolderName String This parameter may be either the folder GUID or its name
299: * valid folder names are {inbox, outbox, drafts, sent, spam, deleted, received}
300: * @return DBFolder instance
301: * @throws StoreClosedException
302: * @throws FolderNotFoundException
303: * @throws MessagingException
304: */
305: public Folder getFolder(String sFolderName)
306: throws StoreClosedException, FolderNotFoundException,
307: MessagingException {
308:
309: if (DebugFile.trace) {
310: DebugFile.writeln("Begin DBStore.getFolder(" + sFolderName
311: + ")");
312: DebugFile.incIdent();
313: }
314:
315: if (sFolderName == null) {
316: if (DebugFile.trace)
317: DebugFile.decIdent();
318: throw new NullPointerException(
319: "DBStore.getFolder() folder name cannot be null");
320: }
321: if (sFolderName.length() == 0) {
322: if (DebugFile.trace)
323: DebugFile.decIdent();
324: throw new NullPointerException(
325: "DBStore.getFolder() folder name cannot be an empty string");
326: }
327:
328: if (!isConnected()) {
329: if (DebugFile.trace)
330: DebugFile.decIdent();
331: throw new StoreClosedException(this , "Store is closed");
332: }
333:
334: DBFolder oRetVal = new DBFolder(this , sFolderName);
335: boolean bExistsGuid = false;
336: PreparedStatement oStmt = null;
337: ResultSet oRSet = null;
338:
339: try {
340: if (sFolderName.length() == 32) {
341: if (DebugFile.trace)
342: DebugFile
343: .writeln("Connection.prepareStatement(SELECT NULL FROM "
344: + DB.k_categories
345: + " WHERE "
346: + DB.gu_category
347: + "='"
348: + sFolderName + "')");
349:
350: oStmt = this .getConnection().prepareStatement(
351: "SELECT NULL FROM " + DB.k_categories
352: + " WHERE " + DB.gu_category + "=?");
353: oStmt.setString(1, sFolderName);
354: oRSet = oStmt.executeQuery();
355: bExistsGuid = oRSet.next();
356: oRSet.close();
357: oStmt.close();
358: } else
359: bExistsGuid = false;
360:
361: if (bExistsGuid) {
362: oRetVal.getCategory().load(oConn,
363: new Object[] { sFolderName });
364: } else {
365: String sGuid = oUser.getMailFolder(oConn, sFolderName);
366:
367: if (null == sGuid) {
368: if (DebugFile.trace)
369: DebugFile.decIdent();
370: throw new FolderNotFoundException(oRetVal,
371: sFolderName);
372: }
373: oRetVal.getCategory().load(oConn,
374: new Object[] { sGuid });
375: }
376: } catch (SQLException sqle) {
377: if (DebugFile.trace)
378: DebugFile.decIdent();
379: throw new MessagingException(sqle.getMessage(), sqle);
380: }
381:
382: if (DebugFile.trace) {
383: DebugFile.decIdent();
384: DebugFile.writeln("End DBStore.getFolder(" + sFolderName
385: + ")");
386: }
387:
388: return oRetVal;
389: } // getFolder
390:
391: // ---------------------------------------------------------------------------
392:
393: /**
394: * Same as getFolder() but casting result to DBFolder
395: * @param sFolderName String
396: * @return DBFolder
397: * @throws StoreClosedException
398: * @throws FolderNotFoundException
399: * @throws MessagingException
400: */
401: public DBFolder getDBFolder(String sFolderName)
402: throws StoreClosedException, FolderNotFoundException,
403: MessagingException {
404: return (DBFolder) getFolder(sFolderName);
405: }
406:
407: // ---------------------------------------------------------------------------
408:
409: /**
410: * Get DBFolder and open it in the specified mode
411: * @param sFolderName String
412: * @param iMode int {DBFolder.READ_ONLY | DBFolder.READ_WRITE}
413: * @return DBFolder
414: * @throws StoreClosedException
415: * @throws FolderNotFoundException
416: * @throws MessagingException
417: */
418: public DBFolder openDBFolder(String sFolderName, int iMode)
419: throws StoreClosedException, FolderNotFoundException,
420: MessagingException {
421: DBFolder oFldr = (DBFolder) getFolder(sFolderName);
422: oFldr.open(iMode);
423: return oFldr;
424: }
425:
426: // ---------------------------------------------------------------------------
427:
428: /**
429: * Get inbox Folder
430: * @return DBFolder
431: * @throws StoreClosedException
432: * @throws FolderNotFoundException
433: * @throws MessagingException
434: */
435: public Folder getDefaultFolder() throws StoreClosedException,
436: FolderNotFoundException, MessagingException {
437:
438: return getFolder("inbox");
439: } // getDefaultFolder
440:
441: // ---------------------------------------------------------------------------
442:
443: public Folder[] getPersonalNamespaces()
444: throws StoreClosedException, FolderNotFoundException,
445: MessagingException {
446:
447: DBFolder[] aRetVal;
448:
449: if (!isConnected())
450: throw new StoreClosedException(this , "Store is closed");
451:
452: try {
453:
454: String sGuid = oUser.getMailRoot(oConn);
455:
456: if (null == sGuid)
457: throw new FolderNotFoundException(new DBFolder(this ,
458: "mailroot"), "mailroot");
459:
460: Category oMailRoot = new Category(sGuid);
461: DBSubset oChilds = oMailRoot.getChilds(oConn);
462: int iFolders = oChilds.getRowCount();
463:
464: if (0 == iFolders)
465: aRetVal = null;
466: else {
467: Object[] oPK = new Object[] { null };
468: DBFolder oFR;
469: aRetVal = new DBFolder[iFolders];
470: for (int f = 0; f < iFolders; f++) {
471: oPK[0] = oChilds.get(0, f);
472: oFR = new DBFolder(this , null);
473: oFR.getCategory().load(oConn, oPK);
474: aRetVal[f] = oFR;
475: } // next
476: } // fi
477: } catch (SQLException sqle) {
478: throw new MessagingException(sqle.getMessage(), sqle);
479: }
480: return aRetVal;
481: }
482:
483: // ---------------------------------------------------------------------------
484:
485: public Folder[] getSharedNamespaces() {
486: return null;
487: }
488:
489: // ---------------------------------------------------------------------------
490:
491: public Folder[] getUserNamespaces(String sUserId)
492: throws StoreClosedException, FolderNotFoundException,
493: MessagingException {
494: DBFolder[] aRetVal;
495:
496: if (!isConnected())
497: throw new StoreClosedException(this , "Store is closed");
498:
499: try {
500:
501: ACLUser oUsr = new ACLUser(sUserId);
502:
503: String sGuid = oUsr.getMailRoot(oConn);
504:
505: if (null == sGuid)
506: throw new FolderNotFoundException(new DBFolder(this ,
507: "mailroot"), "mailroot");
508:
509: Category oMailRoot = new Category(sGuid);
510: DBSubset oChilds = oMailRoot.getChilds(oConn);
511: int iFolders = oChilds.getRowCount();
512:
513: if (0 == iFolders)
514: aRetVal = null;
515: else {
516: Object[] oPK = new Object[] { null };
517: DBFolder oFR;
518: aRetVal = new DBFolder[iFolders];
519: for (int f = 0; f < iFolders; f++) {
520: oPK[0] = oChilds.get(0, f);
521: oFR = new DBFolder(this , null);
522: oFR.getCategory().load(oConn, oPK);
523: aRetVal[f] = oFR;
524: } // next
525: } // fi
526: } catch (SQLException sqle) {
527: throw new MessagingException(sqle.getMessage(), sqle);
528: }
529: return aRetVal;
530: } // getUserNamespaces
531:
532: // ---------------------------------------------------------------------------
533:
534: public URLName getURLName() {
535: return oURL;
536: }
537:
538: // ---------------------------------------------------------------------------
539:
540: public ACLUser getUser() {
541: return oUser;
542: }
543:
544: // ----------------------------------------------------------------------------------------
545:
546: /**
547: * Fetch a message from a POP3 or other remote folder into de local cache
548: * @param oIncomingFldr Incoming Folder (POP3, IMAP, or other)
549: * @param iMsgNum int Message number
550: * @return DBMimeMessage
551: * @throws MessagingException
552: */
553: public DBMimeMessage preFetchMessage(Folder oIncomingFldr,
554: int iMsgNum) throws MessagingException {
555:
556: if (DebugFile.trace) {
557: DebugFile.writeln("Begin DBStore.preFetchMessage([Folder],"
558: + String.valueOf(iMsgNum) + ")");
559: DebugFile.incIdent();
560: }
561:
562: if (null == oIncomingFldr)
563: throw new MessagingException("Unable to open inbox folder",
564: new NullPointerException(
565: "DBStore.preFetchMessage() Folder is null"));
566:
567: boolean bWasConnected = isConnected();
568: if (!bWasConnected)
569: connect();
570: DBFolder oInboxFldr = (DBFolder) getDefaultFolder();
571: oInboxFldr.open(Folder.READ_WRITE);
572: boolean bWasOpen = oIncomingFldr.isOpen();
573: if (!bWasOpen)
574: oIncomingFldr.open(Folder.READ_ONLY);
575: DBMimeMessage oMimeMsg = new DBMimeMessage(
576: (MimeMessage) oIncomingFldr.getMessage(iMsgNum));
577: oInboxFldr.appendMessage(oMimeMsg);
578: if (!bWasOpen)
579: oIncomingFldr.close(false);
580: if (!bWasConnected)
581: close();
582:
583: if (DebugFile.trace) {
584: DebugFile.decIdent();
585: DebugFile.writeln("End DBStore.preFetchMessage() : "
586: + oMimeMsg.getMessageGuid());
587: }
588:
589: return oMimeMsg;
590: } // prefetchMessage
591:
592: // ----------------------------------------------------------------------------------------
593:
594: public static String MBoxDirectory(String sProfile, int iDomainId,
595: String sWorkAreaGu) throws ProtocolException {
596: String sSep = System.getProperty("file.separator");
597: String sFileProtocol = Environment.getProfileVar(sProfile,
598: "fileprotocol", "file://");
599: String sMBoxDir;
600: if (sFileProtocol.equals("file://"))
601: sMBoxDir = sFileProtocol
602: + Environment.getProfilePath(sProfile, "storage")
603: + "domains" + sSep + String.valueOf(iDomainId)
604: + sSep + "workareas" + sSep + sWorkAreaGu;
605: else
606: throw new java.net.ProtocolException(sFileProtocol);
607: return sMBoxDir;
608: } // MBoxDirectory
609: }
|