001: //The contents of this file are subject to the Mozilla Public License Version 1.1
002: //(the "License"); you may not use this file except in compliance with the
003: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
004: //
005: //Software distributed under the License is distributed on an "AS IS" basis,
006: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
007: //for the specific language governing rights and
008: //limitations under the License.
009: //
010: //The Original Code is "The Columba Project"
011: //
012: //The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
013: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
014: //
015: //All Rights Reserved.
016:
017: package org.columba.mail.folder.imap;
018:
019: import java.util.logging.Logger;
020:
021: import org.columba.api.command.ICommand;
022: import org.columba.api.command.IStatusObservable;
023: import org.columba.core.command.Command;
024: import org.columba.core.command.CommandProcessor;
025: import org.columba.core.command.NullWorkerStatusController;
026: import org.columba.core.command.StatusObservableImpl;
027: import org.columba.core.connectionstate.ConnectionStateImpl;
028: import org.columba.core.filter.Filter;
029: import org.columba.mail.command.MailFolderCommandReference;
030: import org.columba.mail.config.AccountItem;
031: import org.columba.mail.config.FolderItem;
032: import org.columba.mail.config.IFolderItem;
033: import org.columba.mail.config.MailConfig;
034: import org.columba.mail.config.SpecialFoldersItem;
035: import org.columba.mail.folder.AbstractFolder;
036: import org.columba.mail.folder.IMailFolder;
037: import org.columba.mail.folder.RootFolder;
038: import org.columba.mail.gui.tree.FolderTreeModel;
039: import org.columba.mail.imap.FetchSubFolderListCommand;
040: import org.columba.mail.imap.IExistsChangedAction;
041: import org.columba.mail.imap.IFirstLoginAction;
042: import org.columba.mail.imap.IImapServer;
043: import org.columba.mail.imap.IMAPServer;
044: import org.columba.mail.imap.IMAPServerOwner;
045: import org.columba.mail.imap.IUpdateFlagAction;
046: import org.columba.mail.util.MailResourceLoader;
047: import org.columba.ristretto.imap.IMAPFlags;
048: import org.columba.ristretto.imap.ListInfo;
049:
050: /**
051: * Root folder for IMAP folders.
052: */
053: public class IMAPRootFolder extends AbstractFolder implements
054: RootFolder, IMAPServerOwner {
055:
056: /** JDK 1.4+ logging framework logger, used for logging. */
057: private static final Logger LOG = Logger
058: .getLogger("org.columba.mail.folder.imap");
059:
060: private static final String[] SPECIAL_FOLDER_NAMES = { "trash",
061: "drafts", "templates", "sent" };
062:
063: // private ImapOperator operator;
064: private AccountItem accountItem;
065:
066: private IImapServer server;
067:
068: private IMAPRootFolder this Folder = this ;
069:
070: /**
071: * parent directory for mail folders
072: *
073: * for example: "/home/donald/.columba/mail/"
074: */
075: private String parentPath;
076:
077: /**
078: * Status information updates are handled in using IStatusObservable.
079: * <p>
080: * Every command has to register its interest to this events before
081: * accessing the folder.
082: */
083: protected IStatusObservable observable;
084:
085: public IMAPRootFolder(FolderItem folderItem, String path) {
086: // super(node, folderItem);
087: super (folderItem);
088:
089: // remember parent path
090: // (this is necessary for IMAPRootFolder sync operations)
091: parentPath = path;
092:
093: observable = new StatusObservableImpl();
094:
095: accountItem = MailConfig.getInstance().getAccountList().uidGet(
096: folderItem.getInteger("account_uid"));
097:
098: updateConfiguration();
099: }
100:
101: public IMAPRootFolder(AccountItem accountItem, String path) {
102: // super(node, folderItem);
103: // super(getDefaultItem("IMAPRootFolder", getDefaultProperties()));
104: super (accountItem.get("name"), "IMAPRootFolder");
105: observable = new StatusObservableImpl();
106:
107: // remember parent path
108: // (this is necessary for IMAPRootFolder sync operations)
109: parentPath = path;
110:
111: this .accountItem = accountItem;
112:
113: getConfiguration().setInteger("account_uid",
114: accountItem.getInteger("uid"));
115:
116: updateConfiguration();
117: }
118:
119: /**
120: * @param type
121: */
122: // public IMAPRootFolder(String name, String type) {
123: // super(name, type);
124: //
125: // IFolderItem item = getConfiguration();
126: // item.setString("property", "accessrights", "system");
127: // item.setString("property", "subfolder", "true");
128: // }
129: public String getDefaultChild() {
130: return "IMAPFolder";
131: }
132:
133: /**
134: * @return observable containing status information
135: */
136: public IStatusObservable getObservable() {
137: return observable;
138: }
139:
140: protected void syncFolder(AbstractFolder parent, String name,
141: ListInfo info) throws Exception {
142:
143: if ((name.indexOf(server.getDelimiter()) != -1)
144: && (name.indexOf(server.getDelimiter()) != (name
145: .length() - 1))) {
146: // delimiter found
147: // -> recursively create all necessary folders to create
148: // -> the final folder
149: String subchild = name.substring(0, name.indexOf(server
150: .getDelimiter()));
151: AbstractFolder subFolder = (AbstractFolder) parent
152: .findChildWithName(subchild, false,
153: IMAPFolder.class);
154:
155: // if folder doesn't exist already
156: if (subFolder == null) {
157: subFolder = new IMAPFolder(subchild, "IMAPFolder",
158: getParentPath());
159: parent.add(subFolder);
160: parent.getConfiguration().getRoot().addElement(
161: subFolder.getConfiguration().getRoot());
162: FolderTreeModel.getInstance().insertNodeInto(subFolder,
163: parent, parent.getIndex(subFolder));
164:
165: ((IMAPFolder) subFolder).existsOnServer = true;
166: subFolder.getConfiguration().setString("selectable",
167: "false");
168:
169: // this is the final folder
170: // subFolder = addIMAPChildFolder(parent, info, subchild);
171: } else {
172: if (!((IMAPFolder) subFolder).existsOnServer) {
173: ((IMAPFolder) subFolder).existsOnServer = true;
174: subFolder.getConfiguration().setString(
175: "selectable", "false");
176: }
177: }
178:
179: // recursively go on
180: syncFolder(subFolder, name.substring(name.indexOf(server
181: .getDelimiter())
182: + server.getDelimiter().length()), info);
183: } else {
184: // no delimiter found
185: // -> this is already the final folder
186: // if folder doesn't exist already
187: AbstractFolder subFolder = (AbstractFolder) parent
188: .findChildWithName(name, false, IMAPFolder.class);
189:
190: if (subFolder == null) {
191: subFolder = new IMAPFolder(name, "IMAPFolder",
192: getParentPath());
193: parent.add(subFolder);
194: parent.getConfiguration().getRoot().addElement(
195: subFolder.getConfiguration().getRoot());
196: FolderTreeModel.getInstance().insertNodeInto(subFolder,
197: parent, parent.getIndex(subFolder));
198: }
199: ((IMAPFolder) subFolder).existsOnServer = true;
200:
201: // Check the Noselect flag
202: if (info.getParameter(ListInfo.NOSELECT)) {
203: subFolder.getConfiguration().setString("selectable",
204: "false");
205: } else {
206: subFolder.getConfiguration().setString("selectable",
207: "true");
208: }
209:
210: // Check the Noinferior flag
211: if (info.getParameter(ListInfo.NOINFERIORS)
212: && info.getDelimiter() != null) {
213: subFolder.getConfiguration().setString("noinferiors",
214: "true");
215: } else {
216: subFolder.getConfiguration().setString("noinferiors",
217: "false");
218: }
219: }
220: }
221:
222: protected void markAllSubfoldersAsExistOnServer(
223: AbstractFolder parent, boolean value) {
224: AbstractFolder child;
225:
226: for (int i = 0; i < parent.getChildCount(); i++) {
227: child = (AbstractFolder) parent.getChildAt(i);
228:
229: if (child instanceof IMAPFolder) {
230: markAllSubfoldersAsExistOnServer(child, value);
231: }
232: }
233:
234: if (parent instanceof IMAPFolder) {
235: ((IMAPFolder) parent).existsOnServer = value;
236: }
237: }
238:
239: private boolean removeNotMarkedSubfolders(AbstractFolder parent)
240: throws Exception {
241: AbstractFolder child;
242:
243: // first remove all subfolders recursively
244: for (int i = 0; i < parent.getChildCount(); i++) {
245: child = (AbstractFolder) parent.getChildAt(i);
246:
247: if (child instanceof IMAPFolder) {
248: if (removeNotMarkedSubfolders(child)) {
249: // A child got removed -> stay at this position to
250: // get the next
251: i--;
252: }
253: }
254: }
255:
256: // maybe remove this folder
257: if (parent instanceof IMAPFolder) {
258: if (!((IMAPFolder) parent).existsOnServer) {
259: FolderTreeModel.getInstance().removeNodeFromParent(
260: parent);
261: parent.removeFolder();
262: return true;
263: }
264: }
265:
266: return false;
267: }
268:
269: public void findSpecialFolders() {
270: SpecialFoldersItem folders = accountItem
271: .getSpecialFoldersItem();
272:
273: for (int i = 0; i < SPECIAL_FOLDER_NAMES.length; i++) {
274: // Find special
275: String specialUid = folders.get(SPECIAL_FOLDER_NAMES[i]);
276:
277: // if have already a suitable folder skip the search
278: if (this .findChildWithUID(specialUid, true) == null) {
279: // search for a folder thats on the IMAP account
280: // first try to find the local translation of special
281: AbstractFolder specialFolder = this .findChildWithName(
282: MailResourceLoader.getString("tree",
283: SPECIAL_FOLDER_NAMES[i]), true);
284:
285: if (specialFolder == null) {
286: // fall back to the english version
287: specialFolder = this .findChildWithName(
288: SPECIAL_FOLDER_NAMES[i], true);
289: }
290:
291: if (specialFolder != null) {
292: // we found a suitable folder -> set it
293: folders.setString(SPECIAL_FOLDER_NAMES[i],
294: specialFolder.getId());
295: }
296: }
297: }
298: }
299:
300: public void syncSubscribedFolders() throws Exception {
301: // first clear all flags
302: markAllSubfoldersAsExistOnServer(this , false);
303:
304: IMAPFolder inbox = (IMAPFolder) this .findChildWithName("INBOX",
305: false);
306: inbox.existsOnServer = true;
307:
308: try {
309: // create and tag all subfolders on server
310: ListInfo[] listInfo = getServer().fetchSubscribedFolders();
311:
312: for (int i = 0; i < listInfo.length; i++) {
313: ListInfo info = listInfo[i];
314: LOG.fine("delimiter=" + getServer().getDelimiter());
315:
316: String folderPath = info.getName();
317:
318: syncFolder(this , folderPath, info);
319: }
320:
321: } catch (Exception ex) {
322: ex.printStackTrace();
323: }
324:
325: // This fixes the strange behaviour of the courier imapserver
326: // which sets the \Noselect flag on INBOX
327: inbox.getConfiguration().setString("selectable", "true");
328:
329: removeNotMarkedSubfolders(this );
330:
331: findSpecialFolders();
332: }
333:
334: public IImapServer getServer() {
335: return server;
336: }
337:
338: public void updateConfiguration() {
339: try {
340: if (server != null)
341: server.logout();
342: } catch (Exception e1) {
343: // don't care
344: }
345: server = new IMAPServer(accountItem.getImapItem());
346: server.setObservable(observable);
347:
348: server.setFirstLoginAction(new IFirstLoginAction() {
349: public void actionPerformed() {
350: // If we are online sync the subscribed folders on first
351: // connection
352: if (ConnectionStateImpl.getInstance().isOnline()) {
353: ICommand c = new FetchSubFolderListCommand(
354: new MailFolderCommandReference(this Folder));
355: try {
356: // MainInterface.processor.addOp(c);
357: c.execute(NullWorkerStatusController
358: .getInstance());
359: c.updateGUI();
360: } catch (Exception e) {
361: e.printStackTrace();
362: }
363: }
364: }
365:
366: });
367:
368: server.setExistsChangedAction(new IExistsChangedAction() {
369:
370: public void actionPerformed(IMailFolder folder) {
371: // Trigger synchronization of the selected Folder
372: Command updateFolderCommand = new CheckForNewMessagesCommand(
373: null, new MailFolderCommandReference(folder));
374: CommandProcessor.getInstance().addOp(
375: updateFolderCommand);
376: }
377:
378: });
379:
380: server.setUpdateFlagAction(new IUpdateFlagAction() {
381:
382: public void actionPerformed(IMailFolder folder,
383: IMAPFlags flags) {
384: // Trigger synchronization of the IMAPFolder
385: Command updateFlagCommand = new UpdateFlagCommand(
386: new MailFolderCommandReference(folder), flags);
387: CommandProcessor.getInstance().addOp(updateFlagCommand);
388:
389: }
390:
391: });
392: }
393:
394: /**
395: * @see org.columba.mail.folder.Folder#searchMessages(org.columba.mail.filter.Filter,
396: * java.lang.Object, org.columba.api.command.IWorkerStatusController)
397: */
398: public Object[] searchMessages(Filter filter, Object[] uids)
399: throws Exception {
400: return null;
401: }
402:
403: /**
404: * @see org.columba.mail.folder.Folder#searchMessages(org.columba.mail.filter.Filter,
405: * org.columba.api.command.IWorkerStatusController)
406: */
407: public Object[] searchMessages(Filter filter) throws Exception {
408: return null;
409: }
410:
411: /**
412: * @return
413: */
414: public AccountItem getAccountItem() {
415: return accountItem;
416: }
417:
418: /*
419: * (non-Javadoc)
420: *
421: * @see org.columba.mail.folder.FolderTreeNode#addSubfolder(org.columba.mail.folder.FolderTreeNode)
422: */
423: public void addSubfolder(IMailFolder child) throws Exception {
424: if (child instanceof IMAPFolder) {
425: getServer().createMailbox(child.getName(), null);
426: }
427:
428: super .addSubfolder(child);
429: }
430:
431: public void setInbox(IMAPFolder inbox) throws Exception {
432: super .addSubfolder(inbox);
433: super .add(inbox);
434: }
435:
436: /*
437: * (non-Javadoc)
438: *
439: * @see org.columba.mail.folder.Folder#save()
440: */
441: public void save() throws Exception {
442: LOG.info("Logout from IMAPServer " + getName());
443:
444: getServer().logout();
445: }
446:
447: /*
448: * (non-Javadoc)
449: *
450: * @see org.columba.mail.folder.RootFolder#getTrashFolder()
451: */
452: public AbstractFolder getTrashFolder() {
453: AbstractFolder ret = findChildWithUID(accountItem
454: .getSpecialFoldersItem().get("trash"), true);
455:
456: // has the imap account no trash folder using the default trash folder
457: if (ret == null) {
458: ret = (AbstractFolder) FolderTreeModel.getInstance()
459: .getTrashFolder();
460: }
461:
462: return ret;
463: }
464:
465: /*
466: * (non-Javadoc)
467: *
468: * @see org.columba.mail.folder.RootFolder#getInbox()
469: */
470: public AbstractFolder getInboxFolder() {
471: return (IMAPFolder) this .findChildWithName("INBOX", false);
472: }
473:
474: /**
475: * Parent directory for mail folders.
476: * <p>
477: * For example: /home/donald/.columba/mail
478: *
479: * @return Returns the parentPath.
480: */
481: public String getParentPath() {
482: return parentPath;
483: }
484:
485: /**
486: * @see org.columba.mail.folder.AbstractFolder#supportsAddFolder()
487: */
488: public boolean supportsAddFolder(String folder) {
489: return true;
490: }
491: }
|