0001: /***
0002: * jwma Java WebMail
0003: * Copyright (c) 2000-2003 jwma team
0004: *
0005: * jwma is free software; you can distribute and use this source
0006: * under the terms of the BSD-style license received along with
0007: * the distribution.
0008: ***/package dtw.webmail.model;
0009:
0010: import dtw.webmail.JwmaKernel;
0011: import dtw.webmail.JwmaSession;
0012: import dtw.webmail.util.config.PostOffice;
0013: import org.apache.log4j.Logger;
0014:
0015: import javax.mail.Folder;
0016: import javax.mail.Message;
0017: import javax.mail.MessagingException;
0018: import javax.mail.Store;
0019: import java.util.ArrayList;
0020: import java.util.Date;
0021: import java.util.Iterator;
0022: import java.util.List;
0023:
0024: /**
0025: * Class implementing a wrapper for the mail store.
0026: * Offers methods and utilities to manage the store.
0027: *
0028: * @author Dieter Wimberger
0029: * @version 0.9.7 07/02/2003
0030: */
0031: public class JwmaStoreImpl implements JwmaStoreInfo {
0032:
0033: //logging
0034: private static Logger log = Logger.getLogger(JwmaStoreImpl.class);
0035:
0036: public static final boolean c_SubscribedOnly = true;
0037:
0038: //instance attributes & associations
0039: private PostOffice m_PostOffice;
0040: private JwmaSession m_Session;
0041: private Store m_Store;
0042: private Folder m_RootFolder;
0043: private JwmaFolderImpl m_TrashFolder;
0044: private Folder m_SentMailFolder;
0045: private Folder m_DraftFolder;
0046: private Folder m_ReadMailFolder;
0047: private JwmaFolderImpl m_JwmaRootFolder;
0048: private JwmaFolderImpl m_ActualFolder;
0049: private JwmaFolderImpl m_InboxFolder;
0050: private char m_FolderSeparator;
0051: private JwmaFolderList m_Folders;
0052:
0053: /**
0054: * Constructs a <tt>JwmaStoreImpl</tt> instance.
0055: *
0056: * @param session the <tt>JwmaSession</tt> instance this store
0057: * belongs to.
0058: * @param mstore the mail <tt>Store</tt> this instance should wrap.
0059: */
0060: private JwmaStoreImpl(JwmaSession session, Store mstore) {
0061: m_Store = mstore;
0062: m_Session = session;
0063: m_PostOffice = session.getPostOffice();
0064: }//JwmaStoreImpl
0065:
0066: /*** jwma special folders **********************************/
0067:
0068: /**
0069: * Returns the actual folder.
0070: *
0071: * @return the actual folder as <tt>JwmaFolderImpl</tt>.
0072: *
0073: * @see dtw.webmail.model.JwmaFolderImpl
0074: */
0075: public JwmaFolderImpl getActualFolder() {
0076: return m_ActualFolder;
0077: }//getActualFolder
0078:
0079: /**
0080: * Sets the actual folder.
0081: *
0082: * @param f the actual folder as <tt>JwmaFolderImpl</tt>.
0083: *
0084: *
0085: * @see dtw.webmail.model.JwmaFolderImpl.
0086: */
0087: private void setActualFolder(JwmaFolderImpl f) {
0088: m_ActualFolder = f;
0089: }//setActualFolder
0090:
0091: /**
0092: * Returns the <tt>JwmaInboxInfo</tt> instance that
0093: * can be used to retrieve information about the store's
0094: * INBOX folder (i.e. where new messages should be arriving).
0095: *
0096: * @return the store's INBOX folder as <tt>JwmaInboxInfo</tt>.
0097: *
0098: * @see dtw.webmail.model.JwmaInboxInfo
0099: */
0100: public JwmaInboxInfo getInboxInfo() {
0101: return (JwmaInboxInfo) m_InboxFolder;
0102: }//getInboxInfo
0103:
0104: /**
0105: * Returns the <tt>JwmaTrashInfo</tt> instance that
0106: * can be used to retrieve information about the store's
0107: * trash folder (i.e. where deleted messages end up first).
0108: *
0109: * @return the store's trash folder as <tt>JwmaTrashInfo</tt>.
0110: *
0111: * @see dtw.webmail.model.JwmaTrashInfo
0112: */
0113: public JwmaTrashInfo getTrashInfo() throws JwmaException {
0114: return ((JwmaTrashInfo) m_TrashFolder);
0115: }//getTrashInfo
0116:
0117: /**
0118: * Returns the trash folder.
0119: * <p>
0120: * The type of this folder will be
0121: * <tt>JwmaFolderImpl.TYPE_MESSAGE_CONTAINER</tt>.
0122: *
0123: * @return the trash folder as <tt>Folder</tt>.
0124: *
0125: * @see javax.mail.Folder
0126: */
0127: public Folder getTrashFolder() {
0128: return m_TrashFolder.getFolder();
0129: }//getTrashFolder
0130:
0131: /**
0132: * Sets the trash folder from a name or path
0133: * given as <tt>String</tt>.
0134: * <p>
0135: * This method will check and modify the
0136: * name as follows:
0137: * <ol>
0138: * <li>name is null or an empty string:<br>
0139: * <root folder path>+<folder separator>+ "trash"
0140: * </li>
0141: * <li>name does not start with <root folder path>:<br>
0142: * <root folder path>+<folder separator>+name
0143: * </li>
0144: * </ol>
0145: * The method will finally set the newly created folder name in the
0146: * associated preferences.
0147: *
0148: * @param name the name or full name of the draft folder
0149: * as <tt>String</tt>.
0150: *
0151: * @throws JwmaException if it fails to set the draft folder and
0152: * create a JwmaFolder instance with it.
0153: */
0154: public void setTrashFolder(String name) throws JwmaException {
0155:
0156: try {
0157:
0158: if (name == null || name.length() == 0) {
0159: name = m_RootFolder.getFullName()
0160: + getFolderSeparator() + "trash";
0161: } else if (!name.startsWith(m_RootFolder.getFullName())) {
0162: //Note: causes this special folder to be subfolder of root always.
0163: int lastindex = name.lastIndexOf(getFolderSeparator());
0164: if (lastindex > 0) {
0165: name = name.substring(lastindex, name.length());
0166: }
0167: name = m_RootFolder.getFullName()
0168: + getFolderSeparator() + name;
0169: }
0170: Folder trash = getFolder(name);
0171: if (!trash.exists()) {
0172: if (!trash.create(JwmaFolderImpl.TYPE_MAILBOX)) {
0173: name = m_RootFolder.getFullName()
0174: + getFolderSeparator() + "jwma-trash";
0175: trash = getFolder(name);
0176: if (!trash.exists()
0177: && !trash
0178: .create(JwmaFolderImpl.TYPE_MAILBOX)) {
0179: throw new JwmaException(
0180: "jwma.store.trashfolder");
0181: }
0182: }
0183: log.debug("setTrashFolder(): Created trash folder="
0184: + name);
0185: }
0186: //ensure subscription
0187: trash.setSubscribed(true);
0188: m_Session.getPreferences().setTrashFolder(trash.getName());
0189: m_TrashFolder = JwmaFolderImpl.createLight(trash);
0190: //ensure always fresh counts
0191: m_TrashFolder.setOnlineCounting(true);
0192: } catch (MessagingException ex) {
0193: throw new JwmaException(ex.getMessage()).setException(ex);
0194: }
0195: }//setDraftFolder
0196:
0197: /**
0198: * Returns the draft folder.
0199: * <p>
0200: * The type of this folder will be
0201: * <tt>JwmaFolderImpl.TYPE_MESSAGE_CONTAINER</tt>.
0202: *
0203: * @return the draft folder as <tt>Folder</tt>.
0204: *
0205: * @see javax.mail.Folder
0206: */
0207: public Folder getDraftFolder() {
0208: return m_DraftFolder;
0209: }//getDraftFolder
0210:
0211: /**
0212: * Sets the draft folder.
0213: * <p>
0214: * The type of this folder should be
0215: * <tt>JwmaFolderImpl.TYPE_MESSAGE_CONTAINER</tt>.
0216: *
0217: * @param f the draft folder as <tt>Folder</tt>.
0218: *
0219: * @see javax.mail.Folder
0220: */
0221: protected void setDraftFolder(Folder f) {
0222: m_DraftFolder = f;
0223: }//setDraftFolder
0224:
0225: /**
0226: * Sets the draft folder from a name or path
0227: * given as <tt>String</tt>.
0228: * <p>
0229: * This method will check and modify the
0230: * name as follows:
0231: * <ol>
0232: * <li>name is null or an empty string:<br>
0233: * <root folder path>+<folder separator>+ "draft"
0234: * </li>
0235: * <li>name does not start with <root folder path>:<br>
0236: * <root folder path>+<folder separator>+name
0237: * </li>
0238: * </ol>
0239: * The method will finally set the newly created folder name in the
0240: * associated preferences.
0241: *
0242: * @param name the name or full name of the draft folder
0243: * as <tt>String</tt>.
0244: *
0245: * @throws JwmaException if it fails to set the draft folder and
0246: * create a JwmaFolder instance with it.
0247: */
0248: public void setDraftFolder(String name) throws JwmaException {
0249:
0250: try {
0251: if (name == null || name.length() == 0) {
0252: name = m_RootFolder.getFullName()
0253: + getFolderSeparator() + "draft";
0254: } else if (!name.startsWith(m_RootFolder.getFullName())) {
0255: //Note: causes this special folder to be subfolder of root always.
0256: int lastindex = name.lastIndexOf(getFolderSeparator());
0257: if (lastindex > 0) {
0258: name = name.substring(lastindex, name.length());
0259: }
0260: name = m_RootFolder.getFullName()
0261: + getFolderSeparator() + name;
0262: }
0263: m_DraftFolder = getFolder(name);
0264: if (!m_DraftFolder.exists()) {
0265: if (!m_DraftFolder.create(JwmaFolderImpl.TYPE_MAILBOX)) {
0266: name = m_RootFolder.getFullName()
0267: + getFolderSeparator() + "jwma-draft";
0268: m_DraftFolder = getFolder(name);
0269: if (!m_DraftFolder.exists()
0270: && !m_DraftFolder
0271: .create(JwmaFolderImpl.TYPE_MAILBOX)) {
0272: throw new JwmaException(
0273: "jwma.store.draftfolder");
0274: }
0275: }
0276: log.debug("setDraftFolder(): Created draft folder="
0277: + name);
0278: }
0279: //ensure subscription
0280: m_DraftFolder.setSubscribed(true);
0281: m_Session.getPreferences().setDraftFolder(
0282: m_DraftFolder.getName());
0283: } catch (MessagingException ex) {
0284: throw new JwmaException(ex.getMessage()).setException(ex);
0285: }
0286: }//setDraftFolder
0287:
0288: /**
0289: * Returns the sent mail archive folder.
0290: * <p>
0291: * The type of this folder will be
0292: * <tt>JwmaFolderImpl.TYPE_MESSAGE_CONTAINER</tt>.
0293: *
0294: * @return the sent mail archive folder as <tt>Folder</tt>.
0295: *
0296: * @see javax.mail.Folder
0297: */
0298: public Folder getSentMailFolder() {
0299: return m_SentMailFolder;
0300: }//getSentMailFolder
0301:
0302: /**
0303: * Sets the sent mail archive folder.
0304: * <p>
0305: * The type of this folder will be
0306: * <tt>JwmaFolderImpl.TYPE_MESSAGE_CONTAINER</tt>.
0307: *
0308: * @param f the sent mail archive folder as <tt>Folder</tt>.
0309: *
0310: * @see javax.mail.Folder
0311: */
0312: protected void setSentMailFolder(Folder f) {
0313: m_SentMailFolder = f;
0314: }//setSentMailFolder
0315:
0316: /**
0317: * Sets the sent mail archive folder from a name or path
0318: * given as <tt>String</tt>.
0319: * <p>
0320: * This method will first check if the automatic archivation of
0321: * sent messages is activated. If, then it checks and modifies the
0322: * name as follows:
0323: * <ol>
0324: * <li>name is null or an empty string:<br>
0325: * <root folder path>+<folder separator>+ "sent-mail"
0326: * </li>
0327: * <li>name does not start with <root folder path>:<br>
0328: * <root folder path>+<folder separator>+name
0329: * </li>
0330: * </ol>
0331: * The method will finally set the newly created folder name in the
0332: * associated preferences.
0333: *
0334: * @param name the name or full name of the sent-mail archive folder
0335: * as <tt>String</tt>.
0336: *
0337: * @throws JwmaException if it fails to set the sent-mail folder and
0338: * create a JwmaFolder instance with it.
0339: */
0340: public void setSentMailFolder(String name) throws JwmaException {
0341:
0342: try {
0343: if (m_Session.getPreferences().isAutoArchiveSent()) {
0344: if (name == null || name.length() == 0) {
0345: name = m_RootFolder.getFullName()
0346: + getFolderSeparator() + "sent-mail";
0347: } else if (!name.startsWith(m_RootFolder.getFullName())) {
0348: //Note: causes this special folder to be subfolder of root always.
0349: int lastindex = name
0350: .lastIndexOf(getFolderSeparator());
0351: if (lastindex > 0) {
0352: name = name.substring(lastindex, name.length());
0353: }
0354: name = m_RootFolder.getFullName()
0355: + getFolderSeparator() + name;
0356: }
0357: m_SentMailFolder = getFolder(name);
0358: if (!m_SentMailFolder.exists()) {
0359: boolean success = m_SentMailFolder
0360: .create(JwmaFolderImpl.TYPE_MAILBOX);
0361: log
0362: .debug("setSentMailFolder(): Created sent-mail folder="
0363: + name + ":" + success);
0364: } else {
0365: log
0366: .debug("setSentMailFolder(): Set sent-mail folder="
0367: + name);
0368: }
0369: m_SentMailFolder.setSubscribed(true);
0370: m_Session.getPreferences().setSentMailArchive(
0371: m_SentMailFolder.getName());
0372: }
0373: } catch (MessagingException ex) {
0374: throw new JwmaException(ex.getMessage()).setException(ex);
0375: }
0376: }//setSentMailFolder
0377:
0378: /**
0379: * Returns the read mail archive folder.
0380: * <p>
0381: * The type of this folder will be
0382: * <tt>JwmaFolderImpl.TYPE_MESSAGE_CONTAINER</tt>.
0383: *
0384: * @return the read mail archive folder as <tt>JwmaFolderImpl</tt>.
0385: * @see dtw.webmail.model.JwmaFolderImpl
0386: */
0387: public Folder getReadMailFolder() {
0388: return m_ReadMailFolder;
0389: }//getReadMailFolder
0390:
0391: /**
0392: * Sets the read mail archive folder.
0393: * <p>
0394: * The type of this folder will be
0395: * <tt>JwmaFolderImpl.TYPE_MESSAGE_CONTAINER</tt>.
0396: *
0397: * @param f the read mail archive folder as <tt>Folder</tt>.
0398: *
0399: * @see javax.mail.Folder
0400: */
0401: protected void setReadMailFolder(Folder f) {
0402: m_ReadMailFolder = f;
0403: }//setReadMailFolder
0404:
0405: /**
0406: * Sets the read mail archive folder from a name or path
0407: * given as <tt>String</tt>.
0408: * <p>
0409: * This method will first check if the automatic archivation of
0410: * read messages is activated. If, then it checks and modifies the
0411: * name as follows:
0412: * <ol>
0413: * <li>name is null or an empty string:<br>
0414: * <root folder path>+<folder separator>+ "sent-mail"
0415: * </li>
0416: * <li>name does not start with <root folder path>:<br>
0417: * <root folder path>+<folder separator>+name
0418: * </li>
0419: * </ol>
0420: * The method will finally set the newly created folder name in the
0421: * associated preferences.
0422: *
0423: * @param name the name or full name of the sent-mail archive folder
0424: * as <tt>String</tt>.
0425: *
0426: * @throws JwmaException if it fails to set the read-mail folder and
0427: * create a JwmaFolder instance with it.
0428: */
0429: public void setReadMailFolder(String name) throws JwmaException {
0430:
0431: try {
0432: if (m_Session.getPreferences().isAutoMoveRead()) {
0433: if (name == null || name.length() == 0) {
0434: name = m_RootFolder.getFullName()
0435: + getFolderSeparator() + "read-mail";
0436: } else if (!name.startsWith(m_RootFolder.getFullName())) {
0437:
0438: int lastindex = name
0439: .lastIndexOf(getFolderSeparator());
0440: if (lastindex > 0) {
0441: name = name.substring(lastindex, name.length());
0442: } //Note: causes this special folder to be subfolder of root always.
0443: name = m_RootFolder.getFullName()
0444: + getFolderSeparator() + name;
0445: }
0446: m_ReadMailFolder = getFolder(name);
0447: if (!m_ReadMailFolder.exists()) {
0448: boolean success = m_ReadMailFolder
0449: .create(JwmaFolderImpl.TYPE_MAILBOX);
0450: log
0451: .debug("setReadMailFolder(): Created read-mail folder="
0452: + name + ":" + success);
0453: }
0454: m_ReadMailFolder.setSubscribed(true);
0455: m_Session.getPreferences().setReadMailArchive(
0456: m_ReadMailFolder.getName());
0457: }
0458: } catch (MessagingException ex) {
0459: throw new JwmaException(ex.getMessage()).setException(ex);
0460: }
0461: }//setReadMailFolder
0462:
0463: /**
0464: * Updates the root folder, if the path differs
0465: * from the actual root folder path.
0466: *
0467: * @param path the path of the new root folder as <tt>String</tt>.
0468: */
0469: public void updateRootFolder(String path) throws JwmaException {
0470: if (!m_RootFolder.getFullName().equals(path)) {
0471: m_Session.getPreferences().setRootFolder(path);
0472: prepare();
0473: }
0474: }//updateRootFolder
0475:
0476: /**
0477: * Put's a message into the read-mail archive,
0478: * if archivation is enabled.
0479: *
0480: * @param message the <tt>Message</tt> to be archived.
0481: *
0482: * @throws JwmaException if it fails to archive the message.
0483: *
0484: * @see javax.mail.Message
0485: */
0486: public void archiveSentMail(Message message) throws JwmaException {
0487:
0488: if (m_Session.getPreferences().isAutoArchiveSent()) {
0489: Folder archive = null;
0490: try {
0491:
0492: //Set the sent date
0493: message.setSentDate(new Date());
0494: //get the folder
0495: archive = getSentMailFolder();
0496: //open it read write
0497: archive.open(Folder.READ_WRITE);
0498: //save the message in archive, append only works as array
0499: Message[] tosave = { message };
0500: //append it
0501: archive.appendMessages(tosave);
0502: //close without expunging
0503: archive.close(false);
0504: } catch (MessagingException mex) {
0505: try {
0506: if (archive.isOpen()) {
0507: archive.close(false);
0508: }
0509: } catch (Exception unex) {
0510: //ignore, will be closed anyway
0511: }
0512: }
0513: }
0514: }//archiveSentMail
0515:
0516: /*** end jwma special folders ******************************/
0517:
0518: /*** folder management methods *****************************/
0519:
0520: /**
0521: * Resets the actual folder to the root folder.
0522: *
0523: * @return the prepared root folder as <tt>JwmaFolder</tt>.
0524: */
0525: public JwmaFolder resetToRootFolder() throws JwmaException {
0526: //set as actual
0527: setActualFolder(m_JwmaRootFolder);
0528: m_JwmaRootFolder.update(this );
0529: //return reference
0530: return m_JwmaRootFolder;
0531: }//resetToRootFolder
0532:
0533: /**
0534: * Sets a new actual folder from a given path.
0535: *
0536: * @return the new actual folder as <tt>JwmaFolder</tt>.
0537: *
0538: * @throws JwmaException if it fails to set the new folder and
0539: * create a JwmaFolder instance with it.
0540: */
0541: public JwmaFolder setActualFolder(String path) throws JwmaException {
0542: setActualFolder(JwmaFolderImpl.createJwmaFolderImpl(this ,
0543: getFolder(path)));
0544: return getActualFolder();
0545: }//setActualFolder
0546:
0547: /**
0548: * Returns a <tt>JwmaFolderImpl</tt> with the given path
0549: * from the store.
0550: *
0551: * @return the folder as <tt>JwmaFolderImpl</tt> and set as actual
0552: * folder(!).
0553: *
0554: * @throws JwmaException if a folder with the given path does not exist
0555: * or an error occurs when creating the wrapper instance.
0556: */
0557: private JwmaFolderImpl getJwmaFolder(String fullname)
0558: throws JwmaException {
0559:
0560: setActualFolder(JwmaFolderImpl.createJwmaFolderImpl(this ,
0561: getFolder(fullname)));
0562: return getActualFolder();
0563: }//getJwmaFolder
0564:
0565: /**
0566: * Returns a <tt>Folder</tt> with a given path
0567: * from the mail store.
0568: *
0569: * @return the folder as <tt>Folder</tt>.
0570: *
0571: * @throws JwmaException if a folder with the given path does not exist
0572: * on the store or a MessagingException occurs.
0573: */
0574: public Folder getFolder(String fullname) throws JwmaException {
0575: try {
0576: //FIXME: Microsoft Exchange returns "" as Default Folder, but asking for the
0577: //folder "" does not return any subfolders.
0578: if (fullname.length() != 0) {
0579: return m_Store.getFolder(fullname);
0580: } else {
0581: //assume to return the default folder...
0582: return m_Store.getDefaultFolder();
0583: }
0584: } catch (MessagingException mex) {
0585: throw new JwmaException("jwma.store.getfolder", true)
0586: .setException(mex);
0587: }
0588: }//getFolder
0589:
0590: /**
0591: * Creates a new folder on the store.
0592: *
0593: * @throws JwmaException if the folder already exists, or if it fails
0594: * to create the folder.
0595: */
0596: public void createFolder(String fullname, int type)
0597: throws JwmaException {
0598: try {
0599: //relative name to path
0600: if (fullname.indexOf(getFolderSeparator()) == -1) {
0601: //In case of the root folder being "" this can represent a problem
0602: //The MS Exchange does not take /fullname as valid...
0603: if (m_ActualFolder.getPath().length() > 0) {
0604: fullname = m_ActualFolder.getPath()
0605: + getFolderSeparator() + fullname;
0606: }
0607: }
0608: Folder newfolder = getFolder(fullname);
0609: if (newfolder.exists()) {
0610: throw new JwmaException(
0611: "jwma.store.createfolder.exists", true);
0612: } else {
0613: newfolder.create(type);
0614: //ensure new folder is subscribed
0615: newfolder.setSubscribed(true);
0616: //update store list & subfolder list
0617: JwmaFolderImpl folder = JwmaFolderImpl
0618: .createLight(newfolder);
0619: m_Folders.addFolderToList(folder);
0620: m_ActualFolder.addIfSubfolder(folder);
0621: }
0622: } catch (MessagingException mex) {
0623: throw new JwmaException("jwma.store.createfolder.failed",
0624: true).setException(mex);
0625: }
0626: }//createFolder
0627:
0628: /**
0629: * Deletes the given folders from the store.
0630: * <p>
0631: * Note that this method will not remove any special folder from
0632: * the store. Despite that, it is a convenience method, looping over
0633: * the array and calling <tt>deleteFolder()</tt>
0634: *
0635: * @param folders an array of strings; each <tt>String</tt>
0636: * representing the full path of a valid folder of the
0637: * actual store.
0638: *
0639: * @throws JwmaException if a folder does not exist,
0640: * or if an error occurs when deleting.
0641: *
0642: * @see #deleteFolder(String)
0643: */
0644: public void deleteFolders(String[] folders) throws JwmaException {
0645:
0646: for (int i = 0; i < folders.length; i++) {
0647: deleteFolder(folders[i]);
0648: }
0649: }//deleteFolders
0650:
0651: /**
0652: * Deletes a given folders from the store.
0653: * <p>
0654: * Note that this method will not remove the folder if it is a
0655: * special folder.
0656: *
0657: * @param fullname the folder's path as <tt>String</tt>.
0658: *
0659: * @throws JwmaException if a folder does not exist,
0660: * or if an error occurs when deleting.
0661: */
0662: public void deleteFolder(String fullname) throws JwmaException {
0663: try {
0664: Folder delfolder = getFolder(fullname);
0665: //ensure not to delete jwma special folders
0666: if (isSpecialFolder(fullname)) {
0667: throw new JwmaException(
0668: "jwma.store.deletefolder.systemfolder", true);
0669: } else {
0670: //UW does not update subscriptions
0671: delfolder.setSubscribed(false);
0672: delfolder.delete(true);
0673: //update cached store list
0674: m_Folders.removeFolderFromList(fullname);
0675: m_ActualFolder.removeIfSubfolder(fullname);
0676: }
0677: } catch (MessagingException mex) {
0678: throw new JwmaException("jwma.store.deletefolder.failed",
0679: true).setException(mex);
0680: }
0681: }//deleteFolder
0682:
0683: /**
0684: * Moves the given folder on the store.
0685: * <p>
0686: * Note that this method is a convenience method
0687: * it creates a single entry array and calls
0688: * <tt>moveFolders()</tt>.
0689: *
0690: * @param foldername the full path of the folder as <tt>String</tt>.
0691: * @param destfolder the full path of a valid folder on the
0692: * actual store.
0693: *
0694: * @throws JwmaException if a folder does not exist,
0695: * or if an error occurs when deleting.
0696: *
0697: * @see #moveFolders(String[],String)
0698: */
0699: public void moveFolder(String foldername, String destfolder)
0700: throws JwmaException {
0701: String[] folders = { foldername };
0702: moveFolders(folders, destfolder);
0703: }//moveFolder
0704:
0705: /**
0706: * Moves the given folders to the given destination folder.
0707: *
0708: * @param foldernames an array of strings; each <tt>String</tt>
0709: * representing the full path of a valid folder of the
0710: * actual store.
0711: * @param destfolder the full path of a valid folder of the
0712: * actual store.
0713: *
0714: * @return the new actual folder as <tt>JwmaFolder</tt>.
0715: *
0716: * @throws JwmaException if the source folders or the destination folder
0717: * do not exist, the destination is a subfolder of a source folder,
0718: * the destination cannot contain any subfolders,
0719: * or if an error occurs when moving.
0720: */
0721: public void moveFolders(String[] foldernames, String destfolder)
0722: throws JwmaException {
0723:
0724: try {
0725: //ensure existing destination folder
0726: if (!checkFolderExistence(destfolder)) {
0727: throw new JwmaException(
0728: "jwma.store.movefolder.destination.missing",
0729: true);
0730: }
0731: //ensure basically valid destination
0732: Folder dest = getFolder(destfolder);
0733: if (!(dest.getType() == JwmaFolderImpl.TYPE_FOLDER || dest
0734: .getType() == JwmaFolderImpl.TYPE_MIXED)) {
0735:
0736: throw new JwmaException(
0737: "jwma.store.movefolder.destination.foul", true);
0738: }
0739: //create list that does not contain any special folder
0740: List folders = getFolders(foldernames);
0741:
0742: //
0743: for (Iterator iter = folders.iterator(); iter.hasNext();) {
0744: Folder oldfolder = (Folder) iter.next();
0745: Folder newfolder = getFolder(destfolder
0746: + getFolderSeparator() + oldfolder.getName());
0747: if (newfolder.exists()) {
0748: throw new JwmaException(
0749: "jwma.store.movefolder.destination.exists",
0750: true
0751: //" (@ "+
0752: //newfolder.getFullName()+
0753: //")"
0754: );
0755: }
0756: //move but ensure a parent is not moved into
0757: // one of its childs
0758: if (!newfolder.getFullName().regionMatches(0,
0759: oldfolder.getFullName(), 0,
0760: oldfolder.getFullName().length())) {
0761: //UW does not change subscriptions on moving!
0762: if (oldfolder.isSubscribed()) {
0763: oldfolder.setSubscribed(false);
0764: }
0765: oldfolder.renameTo(newfolder);
0766: newfolder.setSubscribed(true);
0767: } else {
0768: throw new JwmaException(
0769: "jwma.store.movefolder.destination.subfolder",
0770: true);
0771: }
0772: }
0773: //update the cached list
0774: m_Folders.rebuild();
0775: m_ActualFolder.removeIfSubfolder(foldernames);
0776: } catch (MessagingException mex) {
0777: throw new JwmaException("jwma.store.movefolder.failed",
0778: true).setException(mex);
0779:
0780: }
0781: }//moveFolders
0782:
0783: public void updateFolderSubscription(String[] foldernames,
0784: boolean subscribe) throws JwmaException {
0785: //create list that does not contain any special folder
0786: try {
0787: List folders = getFolders(foldernames);
0788: for (Iterator iter = folders.iterator(); iter.hasNext();) {
0789: ((Folder) iter.next()).setSubscribed(subscribe);
0790: }
0791: } catch (Exception ex) {
0792: log.error("updateFolderSubscription()", ex);
0793: throw new JwmaException(
0794: "jwma.store.updatesubscription.failed", true)
0795: .setException(ex);
0796: }
0797: }//updateFolderSubscription
0798:
0799: public void unsubscribeFolders() {
0800:
0801: }//unsubscribeFolders
0802:
0803: /**
0804: * An utility method that collects all non special
0805: * folders of a given array of folder paths, into
0806: * a list of jvax.mail.Folder instances.
0807: */
0808: private List getFolders(String[] foldernames)
0809: throws MessagingException, JwmaException {
0810:
0811: ArrayList folders = new ArrayList(foldernames.length);
0812: Folder f = null;
0813: for (int i = 0; i < foldernames.length; i++) {
0814: f = getFolder(foldernames[i]);
0815: //add if existant and NOT the trash folder
0816: if (f.exists() && !isSpecialFolder(foldernames[i])) {
0817: folders.add(f);
0818: }
0819: }
0820: return folders;
0821: }//getFolders
0822:
0823: /**
0824: * Tests if a given path is a special jwma folder.
0825: */
0826: private boolean isSpecialFolder(String fullname) {
0827: return (fullname.equals(m_RootFolder.getFullName())
0828: || fullname.equals(m_InboxFolder.getPath())
0829: || fullname.equals(m_TrashFolder.getPath())
0830: || fullname.equals(m_DraftFolder.getFullName())
0831: || (m_Session.getPreferences().isAutoArchiveSent() && fullname
0832: .equals(m_SentMailFolder.getFullName())) || (m_Session
0833: .getPreferences().isAutoMoveRead() && fullname
0834: .equals(m_ReadMailFolder.getFullName())));
0835: }//isSpecialFolder
0836:
0837: /*** end folder management methods *************************/
0838:
0839: /*** Storeinfo implementation **********************************/
0840:
0841: public JwmaFolder[] listFolders(int type) {
0842: return m_Folders.createFolderArray(m_Folders.sublist(type,
0843: c_SubscribedOnly));
0844: }//listFolders
0845:
0846: public JwmaFolder[] listFolders(int type, boolean subscribed) {
0847: String[] excludes = {
0848: m_RootFolder.getFullName(),
0849: m_InboxFolder.getPath(),
0850: m_TrashFolder.getPath(),
0851: m_DraftFolder.getFullName(),
0852: ((m_Session.getPreferences().isAutoArchiveSent()) ? m_SentMailFolder
0853: .getFullName()
0854: : ""),
0855: ((m_Session.getPreferences().isAutoMoveRead()) ? m_ReadMailFolder
0856: .getFullName()
0857: : "") };
0858: return m_Folders.createFolderArray(m_Folders.sublist(type,
0859: excludes, subscribed));
0860: }//listFolders
0861:
0862: public JwmaFolder[] listFolderMoveTargets() {
0863: //sublist with actual folder filtered
0864: return m_Folders.createFolderArray(m_Folders.sublist(
0865: JwmaFolder.TYPE_FOLDER_CONTAINER, m_ActualFolder,
0866: c_SubscribedOnly));
0867: }//listFolderMoveTargets
0868:
0869: public JwmaFolder[] listMessageMoveTargets() {
0870: //sublist with actual folder filtered
0871: return m_Folders.createFolderArray(m_Folders.sublist(
0872: JwmaFolder.TYPE_MESSAGE_CONTAINER, m_ActualFolder,
0873: c_SubscribedOnly));
0874: }//listMessageMoveTargets
0875:
0876: /*** End display methods ***********************************/
0877:
0878: /*** Utility methods ***************************************/
0879:
0880: /**
0881: * Returns a reference to the associated preferences.
0882: *
0883: * @return the associated preferences <tt>JwmaPreferences</tt>.
0884: *
0885: * @see dtw.webmail.model.JwmaPreferences
0886: */
0887: public JwmaPreferences getPreferences() {
0888: return (JwmaPreferences) m_Session.getPreferences();
0889: }//getPreferences
0890:
0891: /**
0892: * Tests if the store is mixed mode, which means it can
0893: * hold folders that hold messages and subfolders at once.
0894: *
0895: * @return true if store is supporting mixed mode, false
0896: * otherwise.
0897: */
0898: public boolean isMixedMode() {
0899: return m_PostOffice.isType(PostOffice.TYPE_MIXED);
0900: }//isMixedMode
0901:
0902: public char getFolderSeparator() {
0903: return m_FolderSeparator;
0904: }//getFolderSeparator
0905:
0906: /**
0907: * Sets the folder separator character.
0908: *
0909: * @param the folder separator as <tt>char</tt>.
0910: */
0911: protected void setFolderSeparator(char c) {
0912: m_FolderSeparator = c;
0913: }//setFolderSeparator
0914:
0915: /**
0916: * Cleans up the store.
0917: * <p>
0918: * This method performs some of the do-automatic functions
0919: * within jwma:
0920: * <ol>
0921: * <li>Move read messages from the inbox to the read messages folder,
0922: * if the feature is enabled.
0923: * </li>
0924: * <li>Empty the trashbin.</li>
0925: * </ol>
0926: *
0927: * @throws JwmaException if cleanup routines fail to perform error free.
0928: */
0929: public boolean cleanup() throws JwmaException {
0930:
0931: //perform auto features like set in users prefs
0932: JwmaPreferencesImpl prefs = m_Session.getPreferences();
0933:
0934: //1. automove read from Inbox to readmessagesfolder if set
0935: if (prefs.isAutoMoveRead()) {
0936: //move the read messages
0937: m_InboxFolder.moveMessages(m_InboxFolder.getReadMessages(),
0938: m_ReadMailFolder.getFullName());
0939: }
0940:
0941: //2. autoempty trash if set
0942: if (prefs.isAutoEmpty()) {
0943: m_TrashFolder.update(this );
0944: m_TrashFolder.deleteAllMessages();
0945: return (m_TrashFolder.getMessageCount() != 0);
0946: }
0947:
0948: return true;
0949: }//cleanup
0950:
0951: /**
0952: * Closes the associated mail store.
0953: */
0954: public void close() {
0955: try {
0956: //close store
0957: m_Store.close();
0958: } catch (MessagingException mex) {
0959: log.error(JwmaKernel.getReference().getLogMessage(
0960: "jwma.store.closefailed"), mex);
0961: }
0962: }//close
0963:
0964: /**
0965: * Prepares all special folders.
0966: */
0967: public void prepare() throws JwmaException {
0968: log.debug("prepare()");
0969: try {
0970: JwmaPreferencesImpl prefs = m_Session.getPreferences();
0971:
0972: //if empty or null string, set store default
0973: //folder as root
0974: if (prefs.getRootFolder() == null
0975: || prefs.getRootFolder().equals("")) {
0976: m_RootFolder = m_Store.getDefaultFolder();
0977: } else {
0978: m_RootFolder = m_Store.getFolder(prefs.getRootFolder());
0979: if (!m_RootFolder.exists()) {
0980: m_RootFolder = m_Store.getDefaultFolder();
0981: }
0982: }
0983:
0984: //ensure the root folder exists
0985: if (!m_RootFolder.exists()) {
0986: throw new JwmaException("jwma.store.rootfolder");
0987: } else {
0988: prefs.setRootFolder(m_RootFolder.getFullName());
0989: log.debug("Prepare set root folder to:"
0990: + prefs.getRootFolder());
0991: }
0992:
0993: //set folder separator
0994: setFolderSeparator(m_RootFolder.getSeparator());
0995:
0996: //build folder list
0997: m_Folders = JwmaFolderList.createStoreList(m_RootFolder);
0998: //buildFolderList(m_RootFolder.list());
0999:
1000: //Inbox the folder that contains the incoming mail
1001: //this has to exist, regarding to the IMAP specification
1002: m_InboxFolder = getJwmaFolder("INBOX");
1003:
1004: //ensure always fresh counts
1005: m_InboxFolder.setOnlineCounting(true);
1006:
1007: //Trash
1008: setTrashFolder(prefs.getTrashFolder());
1009:
1010: //Draft
1011: setDraftFolder(prefs.getDraftFolder());
1012:
1013: //read-mail archive
1014: setReadMailFolder(prefs.getReadMailArchive());
1015:
1016: //sent-mail archive
1017: setSentMailFolder(prefs.getSentMailArchive());
1018:
1019: //store JwmaFolderImpl of root as last, to set the actual folder
1020: //to root
1021: m_JwmaRootFolder = getJwmaFolder(m_RootFolder.getFullName());
1022:
1023: } catch (MessagingException ex) {
1024: log.debug("prepare()", ex);
1025: throw new JwmaException("jwma.store.prepare")
1026: .setException(ex);
1027: }
1028: }//prepare
1029:
1030: /**
1031: * Tests if a folder with the given path exists
1032: * on the store.
1033: * <p>
1034: * Note that the method checks against the cached folder list.
1035: *
1036: * @param path the path of the folder as <tt>String</tt>.
1037: *
1038: * @return true if a folder with this path exists on the store,
1039: * false otherwise.
1040: *
1041: * @throws JwmaException if the folder already exists, or if it fails
1042: * to create the folder.
1043: *
1044: */
1045: public boolean checkFolderExistence(String path)
1046: throws JwmaException {
1047:
1048: if (path.equals(m_RootFolder.getFullName())
1049: || path.equals("INBOX")) {
1050: return true;
1051: }
1052: return m_Folders.contains(path);
1053: }//checkFolderExistence
1054:
1055: /**
1056: * Creates a new <tt>JwmaStoreImpl</tt> instance.
1057: * <p>
1058: * The method will also prepare the store for use.
1059: *
1060: * @param session the actual <tt>JwmaSession</tt>.
1061: * @param mstore the mail store that should be wrapped.
1062: *
1063: * @return the newly created <tt>JwmaStoreImpl</tt> instance.
1064: *
1065: * @throws JwmaException if preparing the store for use fails.
1066: *
1067: * @see #prepare()
1068: * @see dtw.webmail.JwmaSession
1069: * @see javax.mail.Store
1070: */
1071: public static JwmaStoreImpl createJwmaStoreImpl(
1072: JwmaSession session, Store mstore) throws JwmaException {
1073:
1074: JwmaStoreImpl store = new JwmaStoreImpl(session, mstore);
1075: //prepare this store
1076: store.prepare();
1077:
1078: return store;
1079: }//createJwmaStoreImpl
1080:
1081: }//JwmaStoreImpl
|