Package net.suberic.pooka

Source Code of net.suberic.pooka.StoreInfo

package net.suberic.pooka;

import javax.mail.*;
import javax.mail.event.*;
import javax.swing.event.EventListenerList;
import java.util.*;
import java.util.logging.Logger;
import java.util.logging.Level;

import net.suberic.pooka.gui.*;
import net.suberic.util.ValueChangeListener;
import net.suberic.util.thread.ActionThread;
import net.suberic.util.VariableBundle;
import net.suberic.util.Item;

/**
* This class does all of the work for a Store.  It keeps track of the
* StoreNode for the Store, as well as keeping the children of the store
* and the properties of the Store.
*/

public class StoreInfo implements ValueChangeListener, Item, NetworkConnectionListener {

  // logger
  Logger mLogger = null;

  // the actual connection information
  private Store store;
  private Session mSession;

  // The is the store ID.
  private String storeID;

  // Information for the StoreNode
  private StoreNode storeNode;
  private Vector<FolderInfo> children;

  // the status indicators
  private boolean connected = false;
  private boolean available = false;

  private int mPreferredStatus = FolderInfo.CONNECTED;

  // if this is a pop mailbox.
  private boolean popStore = false;

  private UserProfile defaultProfile;

  private NetworkConnection connection;

  // the connection information.
  private String user;
  private String password;
  private String server;
  private String protocol;
  private int port;
  private URLName url;

  private String sslSetting = "none";

  // the Thread for connections to this Store.
  private ActionThread storeThread;

  // the Trash folder for this Store, if any.
  private FolderInfo trashFolder;

  // whether or not this store synchronizes with the subscribed folders
  // automatically
  private boolean useSubscribed = false;

  // the connection listener for this store.
  private ConnectionListener connectionListener;

  // the Authenticator to use for this Store.
  private AuthenticatorUI mAuthenticator;

  /**
   * Creates a new StoreInfo from a Store ID.
   */
  public StoreInfo(String sid) {
    setStoreID(sid);

    configureStore();
  }

  /**
   * This configures the store from the property information.
   */
  public void configureStore() {
    connected = false;
    available = false;

    protocol = Pooka.getProperty("Store." + storeID + ".protocol", "");

    if (protocol.equalsIgnoreCase("pop3")) {
      user = "";
      password = "";
      server = "localhost";
      if (Pooka.getProperty(getStoreProperty() + ".useMaildir", "unset").equalsIgnoreCase("true"))
        protocol = "maildir";
      else
        protocol = "mbox";
      port = -1;
      popStore = true;
    } else {
      popStore = false;
      user = Pooka.getProperty("Store." + storeID + ".user", "");
      password = Pooka.getProperty("Store." + storeID + ".password", "");
      String portValue = Pooka.getProperty("Store." + storeID + ".port", "");
      port = -1;
      if (!portValue.equals("")) {
        try {
          port = Integer.parseInt(portValue);
        } catch (Exception e) {
        }
      }
      if (!password.equals(""))
        password = net.suberic.util.gui.propedit.PasswordEditorPane.descrambleString(password);
      server = Pooka.getProperty("Store." + storeID + ".server", "");

      sslSetting = Pooka.getProperty(getStoreProperty() + ".SSL", "none");
      if (sslSetting.equalsIgnoreCase("true")) {
        Pooka.setProperty(getStoreProperty() + ".SSL", "ssl");
        sslSetting = "ssl";
      } else if (sslSetting.equalsIgnoreCase("false")) {
        Pooka.setProperty(getStoreProperty() + ".SSL", "none");
        sslSetting = "none";
      }

      if (sslSetting.equals("ssl")) {
        if (protocol.equals("imap"))
          protocol = "imaps";
      }
    }


    Properties p = loadProperties();

    if (protocol.equalsIgnoreCase("maildir")) {
      url = new URLName(protocol, server, port, p.getProperty("mail.store.maildir.baseDir"), user, password);
    } else {
      url = new URLName(protocol, server, port, "", user, password);
    }

    getLogger().fine("creating authenticator");
    mAuthenticator = Pooka.getUIFactory().createAuthenticatorUI();

    try {
      mSession = Session.getInstance(p, mAuthenticator);

      updateSessionDebug();

      store = mSession.getStore(url);
      available=true;
    } catch (NoSuchProviderException nspe) {
      Pooka.getUIFactory().showError(Pooka.getProperty("error.loadingStore", "Unable to load Store ") + getStoreID(), nspe);
      available=false;
    }

    // don't allow a StoreInfo to get created with an empty folderList.

    if (Pooka.getProperty("Store." + storeID + ".folderList", "").equals(""))
      Pooka.setProperty("Store." + storeID + ".folderList", "INBOX");

    // check to see if we're using the subscribed property.
    useSubscribed = Pooka.getProperty(getStoreProperty() + ".useSubscribed", "false").equalsIgnoreCase("true");

    Pooka.getResources().addValueChangeListener(this, getStoreProperty());
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".folderList");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".defaultProfile");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".protocol");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".user");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".password");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".server");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".port");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".connection");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".SSL");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".useSubscribed");
    Pooka.getResources().addValueChangeListener(this, getStoreProperty() + ".sessionDebug.level");

    // tell the log manager to listen to these settings.
    Pooka.getLogManager().addLogger(getStoreProperty());
    Pooka.getLogManager().addLogger(getStoreProperty() + ".sessionDebug");

    if (available) {
      connectionListener = new ConnectionListener() {

          public void disconnected(ConnectionEvent e) {
            getLogger().log(Level.FINE, "Store " + getStoreID() + " disconnected.");
            // if we think we're connected, then call disconnectStore.
            if (isConnected()) {
              try {
                if (Pooka.getUIFactory().getMessageNotificationManager() != null) {
                  Pooka.getUIFactory().getMessageNotificationManager().displayMessage("Disconnected", "Disconnected from store " + getStoreID(), net.suberic.pooka.gui.MessageNotificationManager.WARNING_MESSAGE_TYPE);
                }
                disconnectStore();
              } catch (MessagingException me) {
                getLogger().log(Level.FINE, "error disconnecting Store:  " + me.getMessage());
              }
            }

          }

          public void closed(ConnectionEvent e) {
            getLogger().log(Level.FINE, "Store " + getStoreID() + " closed.");

            // if we think we're connected, then call disconnectStore.
            if (isConnected()) {
              if (Pooka.getUIFactory().getMessageNotificationManager() != null) {
                Pooka.getUIFactory().getMessageNotificationManager().displayMessage("Disconnected", "Disconnected from store " + getStoreID(), net.suberic.pooka.gui.MessageNotificationManager.WARNING_MESSAGE_TYPE);
              }
              try {
                disconnectStore();
              } catch (MessagingException me) {
                getLogger().log(Level.FINE, "error disconnecting Store:  " + me.getMessage());
              }
            }

          }

          public void opened(ConnectionEvent e) {
            getLogger().log(Level.FINE, "Store " + getStoreID() + " opened.");
          }
        };

      store.addConnectionListener(connectionListener);

    }

    if (storeThread == null) {
      storeThread = new ActionThread(this.getStoreID() + " - ActionThread");
      storeThread.start();
    }

    String defProfileString = Pooka.getProperty(getStoreProperty() + ".defaultProfile", "");
    if (defProfileString.length() < 1 || defProfileString.equalsIgnoreCase(UserProfile.S_DEFAULT_PROFILE_KEY)) {
      defaultProfile = null;
    } else {
      defaultProfile = Pooka.getPookaManager().getUserProfileManager().getProfile(defProfileString);
    }

    connection = Pooka.getConnectionManager().getConnection(Pooka.getProperty(getStoreProperty() + ".connection", ""));
    if (connection == null) {
      connection = Pooka.getConnectionManager().getDefaultConnection();
    }

    if (connection != null) {
      connection.addConnectionListener(this);
    }

    updateChildren();

    String trashFolderName = Pooka.getProperty(getStoreProperty() + ".trashFolder", "");
    if (trashFolderName.length() > 0) {
      trashFolder = getChild(trashFolderName);
      if (trashFolder != null)
        trashFolder.setTrashFolder(true);
    }
  }

  /**
   * This loads in the default session properties for this Store's
   * Session.
   */
  public Properties loadProperties() {
    Properties p = new Properties(System.getProperties());

    String realProtocol = Pooka.getProperty("Store." + storeID + ".protocol", "");

    if (realProtocol.equalsIgnoreCase("imap")) {
      loadImapProperties(p);
    } else if (realProtocol.equalsIgnoreCase("pop3")) {
      loadPop3Properties(p);
      String useMaildir = Pooka.getProperty(getStoreProperty() + ".useMaildir", "unset");

      if (useMaildir.equals("unset")) {
        Pooka.setProperty(getStoreProperty() + ".useMaildir", "false");
        useMaildir="false";
      }

      if ( useMaildir.equalsIgnoreCase("false")) {
        loadMboxProperties(p);
      } else {
        loadMaildirProperties(p);
      }
    } else if (realProtocol.equalsIgnoreCase("maildir")) {
      loadMaildirProperties(p);
    } else if (realProtocol.equalsIgnoreCase("mbox")) {
      loadMboxProperties(p);
    }
    return p;
  }

  /**
   * Load all IMAP properties.
   */
  void loadImapProperties(Properties p) {
    p.setProperty("mail.imap.timeout", Pooka.getProperty(getStoreProperty() + ".timeout", Pooka.getProperty("Pooka.timeout", "-1")));
    p.setProperty("mail.imap.connectiontimeout", Pooka.getProperty(getStoreProperty() + ".connectionTimeout", Pooka.getProperty("Pooka.connectionTimeout", "-1")));

    p.setProperty("mail.imaps.timeout", Pooka.getProperty(getStoreProperty() + ".timeout", Pooka.getProperty("Pooka.timeout", "-1")));
    p.setProperty("mail.imaps.connectiontimeout", Pooka.getProperty(getStoreProperty() + ".connectionTimeout", Pooka.getProperty("Pooka.connectionTimeout", "-1")));

    // set up ssl
    if (sslSetting.equals("ssl")) {
      p.setProperty("mail.imaps.socketFactory.fallback", Pooka.getProperty(getStoreProperty() + ".SSL.fallback", "false"));
    } else if (sslSetting.equals("tlsrequired")) {
      p.setProperty("mail.imap.starttls.enable", "true");
    } else if (sslSetting.equals("tls")) {
      // failover is implemented in the connectStore() method.
      p.setProperty("mail.imap.starttls.enable", "true");
    }

    // use a dedicated store connection.
    p.setProperty("mail.imap.separatestoreconnection", "true");
    p.setProperty("mail.imaps.separatestoreconnection", "true");
  }

  /**
   * Load all POP3 properties.
   */
  void loadPop3Properties(Properties p) {
    if (Pooka.getProperty(getStoreProperty() + ".SSL", "false").equalsIgnoreCase("true")) {
      //p.setProperty("mail.pop3s.socketFactory.class", "net.suberic.pooka.ssl.PookaSSLSocketFactory");
      p.setProperty("mail.pop3s.socketFactory.fallback", Pooka.getProperty(getStoreProperty() + ".SSL.fallback", "false"));
      //p.setProperty("mail.pop3.socketFactory.port", Pooka.getProperty(getStoreProperty() + ".SSL.port", "995"));
    }
  }

  /**
   * Load all Maildir properties.
   */
  void loadMaildirProperties(Properties p) {

    String mailHome = Pooka.getProperty(getStoreProperty() + ".mailDir", "");
    if (mailHome.equals("")) {
      mailHome = Pooka.getProperty("Pooka.defaultMailSubDir", "");
      if (mailHome.equals(""))
        mailHome = Pooka.getPookaManager().getPookaRoot().getAbsolutePath() + java.io.File.separator + ".pooka";

      mailHome = mailHome + java.io.File.separator + storeID;
    }

    String userHomeName = Pooka.getPookaManager().getResourceManager().translateName(mailHome + java.io.File.separator + Pooka.getProperty("Pooka.subFolderName", "folders"));

    //p.setProperty("mail.store.maildir.imapEmulation", "true");
    p.setProperty("mail.store.maildir.baseDir", userHomeName);
    p.setProperty("mail.store.maildir.autocreatedir", "true");
  }

  /**
   * Load all Mbox properties.
   */
  void loadMboxProperties(Properties p) {
    /*
     * set the properties for mbox folders, and for the mbox backend of
     * a pop3 mailbox.  properties set are:
     *
     * mail.mbox.inbox:  the location of the INBOX for this mail store. for
     *   pop3 stores, this is the location of the local copy of the inbox.
     *   for mbox stores, this should be the local inbox file.
     * mail.mbox.userhome:  the location of all subfolders.
     */
    String mailHome = Pooka.getProperty(getStoreProperty() + ".mailDir", "");
    if (mailHome.equals("")) {
      mailHome = Pooka.getProperty("Pooka.defaultMailSubDir", "");
      if (mailHome.equals(""))
        mailHome = Pooka.getPookaManager().getPookaRoot().getAbsolutePath() + java.io.File.separator + ".pooka";

      mailHome = mailHome + java.io.File.separator + storeID;
    }

    mailHome = Pooka.getPookaManager().getResourceManager().translateName(mailHome);

    String inboxFileName;
    if (Pooka.getProperty(getStoreProperty() + ".protocol", "imap").equalsIgnoreCase("pop3")) {
      inboxFileName = mailHome + java.io.File.separator + Pooka.getProperty("Pooka.inboxName", "INBOX");
    } else {
      inboxFileName = Pooka.getProperty(getStoreProperty() + ".inboxLocation", "/var/spool/mail/" + System.getProperty("user.name"));
    }

    String userHomeName = mailHome + java.io.File.separator + Pooka.getProperty("Pooka.subFolderName", "folders");

    getLogger().log(Level.FINE, "for store " + getStoreID() + ", inboxFileName = " + inboxFileName + "; userhome = " + userHomeName);

    p.setProperty("mail.mbox.inbox", inboxFileName);
    p.setProperty("mail.mbox.userhome", userHomeName);
  }

  /**
   * This updates the children of the current store.  Generally called
   * when the folderList property is changed.
   */

  public void updateChildren() {

    Vector<FolderInfo> newChildren = new Vector<FolderInfo>();

    List<String> newChildNames = Pooka.getResources().getPropertyAsList(getStoreProperty() + ".folderList", "INBOX");

    for (String newFolderName: newChildNames) {
      FolderInfo childFolder = getChild(newFolderName);
      if (childFolder == null) {
        childFolder = Pooka.getResourceManager().createFolderInfo(this, newFolderName);
      }

      newChildren.add(0, childFolder);
    }

    children = newChildren;
    getLogger().log(Level.FINE, getStoreID() + ":  in configureStore.  children.size() = " + children.size());

    if (storeNode != null)
      storeNode.loadChildren();
  }

  /**
   * This goes through the list of children of this store and
   * returns the FolderInfo for the given childName, if one exists.
   * If none exists, or if the children Vector has not been loaded
   * yet, or if this is a leaf node, then this method returns null.
   */
  public FolderInfo getChild(String childName) {
    FolderInfo childFolder = null;
    String folderName  = null, subFolderName = null;

    if (children != null) {
      int divider = childName.indexOf('/');
      if (divider > 0) {
        folderName = childName.substring(0, divider);
        if (divider < childName.length() - 1)
          subFolderName = childName.substring(divider + 1);
      } else
        folderName = childName;

      for (int i = 0; i < children.size(); i++)
        if (((FolderInfo)children.elementAt(i)).getFolderName().equals(folderName))
          childFolder = (FolderInfo)children.elementAt(i);
    }

    if (childFolder != null && subFolderName != null)
      return childFolder.getChild(subFolderName);
    else
      return childFolder;
  }


  /**
   * This goes through the list of children of this store and
   * returns the FolderInfo that matches this folderID.
   * If none exists, or if the children Vector has not been loaded
   * yet, or if this is a leaf node, then this method returns null.
   */
  public FolderInfo getFolderById(String folderID) {
    FolderInfo childFolder = null;

    if (children != null) {
      for (int i = 0; i < children.size(); i++) {
        FolderInfo possibleMatch = ((FolderInfo)children.elementAt(i)).getFolderById(folderID);
        if (possibleMatch != null) {
          return possibleMatch;
        }
      }
    }

    return null;
  }

  /**
   * This handles the event that the StoreInfo is removed from Pooka.
   */
  public void remove() {
    // FIXME need to do a lot here.
    try {
      disconnectStore();
    } catch (Exception e) {
    }
    cleanup();
    // FIXME
    // Pooka.getResources().removePropertyTree(getStoreProperty())

  }

  /**
   * Cleans up all references to this StoreInfo.
   */
  public void cleanup() {
    Pooka.getResources().removeValueChangeListener(this);
    Pooka.getLogManager().removeLogger(getStoreProperty());

    if (children != null && children.size() > 0) {
      for (int i = 0; i < children.size(); i++)
        ((FolderInfo)children.elementAt(i)).cleanup();
    }

    if (store != null) {
      store.removeConnectionListener(connectionListener);
    }
    if (getStoreThread() != null) {
      getStoreThread().setStop(true);
    }
  }

  /**
   * This handles the changes if the source property is modified.
   *
   * As defined in net.suberic.util.ValueChangeListener.
   */

  public void valueChanged(String pChangedValue) {
    final String changedValue = pChangedValue;
    javax.swing.AbstractAction valueChangedAction = new javax.swing.AbstractAction() {
        public void actionPerformed(java.awt.event.ActionEvent ae) {
          // check to make sure that we still exist.
          List<String> storeList = Pooka.getResources().getPropertyAsList("Store", "");
          if (storeList.contains(getStoreID())) {
            if (changedValue.equals(getStoreProperty() + ".folderList")) {
              updateChildren();
            } else if (changedValue.equals(getStoreProperty() + ".defaultProfile")) {
              String defProfileString = Pooka.getProperty(getStoreProperty() + ".defaultProfile", "");
              if (defProfileString.length() < 1 || defProfileString.equalsIgnoreCase(UserProfile.S_DEFAULT_PROFILE_KEY)) {
                defaultProfile = null;
              } else {
                defaultProfile = Pooka.getPookaManager().getUserProfileManager().getProfile(defProfileString);
              }
            } else if (changedValue.equals(getStoreProperty() + ".protocol") || changedValue.equals(getStoreProperty() + ".user") || changedValue.equals(getStoreProperty() + ".password") || changedValue.equals(getStoreProperty() + ".server") || changedValue.equals(getStoreProperty() + ".port") || changedValue.equals(getStoreProperty() + ".SSL") ) {

              if (storeNode != null) {
                Enumeration childEnum = storeNode.children();
                Vector v = new Vector();
                while (childEnum.hasMoreElements())
                  v.add(childEnum.nextElement());

                storeNode.removeChildren(v);
              }

              children = null;

              try {
                disconnectStore();
              } catch (Exception e) { }

              getLogger().log(Level.FINE, "calling configureStore()");

              configureStore();
            } else if (changedValue.equals(getStoreProperty() + ".connection")) {
              connection.removeConnectionListener(StoreInfo.this);

              connection = Pooka.getConnectionManager().getConnection(Pooka.getProperty(getStoreProperty() + ".connection", ""));
              if (connection == null) {
                connection = Pooka.getConnectionManager().getDefaultConnection();
              }

              if (connection != null) {
                connection.addConnectionListener(StoreInfo.this);
              }
            } else if (changedValue.equals(getStoreProperty() + ".useSubscribed")) {
              useSubscribed = Pooka.getProperty(getStoreProperty() + ".useSubscribed", "false").equalsIgnoreCase("true");
            } else if (changedValue.equals(getStoreProperty() + ".sessionDebug.level")) {
              updateSessionDebug();
            }
          }
        }
      };

    // if we don't do the update synchronously on the store thread,
    // then subscribing to subfolders breaks.
    java.awt.event.ActionEvent actionEvent =  new java.awt.event.ActionEvent(this, 0, "value-changed");
    if (Thread.currentThread() == getStoreThread()) {
      valueChangedAction.actionPerformed(actionEvent);
    } else {
      getStoreThread().addToQueue(valueChangedAction, actionEvent);
    }

  }


  /**
   * Called when the status of the NetworkConnection changes.
   */
  public void connectionStatusChanged(NetworkConnection connection, int newStatus) {
    // mbox folders still don't care.
    if (! (protocol.equalsIgnoreCase("mbox") || protocol.equalsIgnoreCase("maildir"))) {
      if (newStatus == NetworkConnection.CONNECTED) {
        // we've connected.
        // we probably don't care.

      } else if (newStatus == NetworkConnection.DISCONNECTED) {
        // we're being disconnected.  close all the connections.
        try {
          disconnectStore();
        } catch (MessagingException me) {
          getLogger().log(Level.FINE, "Caught exception disconnecting Store " + getStoreID() + ":  " + me);
          if (getLogger().isLoggable(Level.FINE))
            me.printStackTrace();
          // else ignore
        }

      } else {
        // we've been cut off.  note it.
        try {
          disconnectStore();
        } catch (MessagingException me) {
          getLogger().log(Level.FINE, "Caught exception disconnecting Store " + getStoreID() + ":  " + me);
          if (getLogger().isLoggable(Level.FINE))
            me.printStackTrace();
          // else ignore
        }
      }
    }
  }
  /**
   * Remove the given String from the folderList property.
   *
   * Note that because this is also a ValueChangeListener to the
   * folderList property, this will also result in the FolderInfo being
   * removed from the children Vector.
   */
  void removeFromFolderList(String removeFolderName) {
    Vector folderNames = Pooka.getResources().getPropertyAsVector(getStoreProperty() + ".folderList", "");

    boolean first = true;
    StringBuffer newValue = new StringBuffer();
    String folderName;

    for (int i = 0; i < folderNames.size(); i++) {
      folderName = (String) folderNames.elementAt(i);

      if (! folderName.equals(removeFolderName)) {
        if (!first)
          newValue.append(":");

        newValue.append(folderName);
        first = false;
      }

    }

    Pooka.setProperty(getStoreProperty() + ".folderList", newValue.toString());
  }

  /**
   * This adds the given folderString to the folderList property.
   */
  void addToFolderList(String addFolderName) {
    String folderName;
    Vector folderNames = Pooka.getResources().getPropertyAsVector(getStoreProperty() + ".folderList", "");

    boolean found = false;

    for (int i = 0; i < folderNames.size(); i++) {
      folderName = (String) folderNames.elementAt(i);

      if (folderName.equals(addFolderName)) {
        found=true;
      }

    }

    if (!found) {
      String currentValue = Pooka.getProperty(getStoreProperty() + ".folderList");
      if (currentValue.equals(""))
        Pooka.setProperty(getStoreProperty() + ".folderList", addFolderName);
      else
        Pooka.setProperty(getStoreProperty() + ".folderList", currentValue + ":" + addFolderName);
    }

  }

  /**
   * This creates a folder if it doesn't exist already.  If it does exist,
   * but is not of the right type, or if there is a problem in creating the
   * folder, throws an error.
   */
  public void createSubFolder(String subFolderName, int type) throws MessagingException {
    Folder folder = store.getDefaultFolder();

    if (folder != null) {
      Folder subFolder = folder.getFolder(subFolderName);

      if (subFolder == null) {
        throw new MessagingException("Store returned null for subfolder " + subFolderName);
      }

      if (! subFolder.exists())
        subFolder.create(type);

      subscribeFolder(subFolderName);
    } else {
      throw new MessagingException("Failed to open store " + getStoreID() + " to create subfolder " + subFolderName);

    }
  }

  /**
   * This subscribes the Folder described by the given String to this
   * StoreInfo.
   */
  public void subscribeFolder(String folderName) {
    getLogger().log(Level.FINE, "subscribing folder " + folderName);

    String subFolderName = null;
    String childFolderName = null;
    int firstSlash = folderName.indexOf('/');
    while (firstSlash == 0) {
      folderName = folderName.substring(1);
      firstSlash = folderName.indexOf('/');
    }

    if (firstSlash > 0) {
      childFolderName = folderName.substring(0, firstSlash);
      if (firstSlash < folderName.length() -1)
        subFolderName = folderName.substring(firstSlash +1);
    } else
      childFolderName = folderName;

    getLogger().log(Level.FINE, "store " + getStoreID() + " subscribing folder " + childFolderName + "; sending " + subFolderName + " to child for subscription.");

    this.addToFolderList(childFolderName);

    FolderInfo childFolder = getChild(childFolderName);

    getLogger().log(Level.FINE, "got child folder '" + childFolder + "' for " + childFolderName);

    if (childFolder != null && subFolderName != null) {
      childFolder.subscribeFolder(subFolderName);
    }
  }

  /**
   * This method connects the Store, and sets the StoreInfo to know that
   * the Store should be connected.  You should use this method instead of
   * calling getStore().connect(), because if you use this method, then
   * the StoreInfo will try to keep the Store connected, and will try to
   * reconnect the Store if it gets disconnected before
   * disconnectStore is called.
   *
   * This method also calls updateChildren() to load the children of
   * the Store, if the children vector has not been loaded yet.
   */
  public void connectStore() throws MessagingException, OperationCancelledException {
    getLogger().log(Level.FINE, "trying to connect store " + getStoreID());

    if (store.isConnected()) {
      getLogger().log(Level.FINE, "store " + getStoreID() + " is already connected.");
      connected=true;
      return;
    } else {
      // test the connection and execute the precommand, if any.
      testConnection();
      executePrecommand();

      getLogger().log(Level.FINE, "connect store " + getStoreID() + ":  doing store.connect()");
      boolean connectSucceeded = false;
      while (! connectSucceeded) {
        try {
          getLogger().fine("running store.connect()");
          store.connect();
          connectSucceeded = true;
          mPreferredStatus = FolderInfo.CONNECTED;
          getLogger().fine("done with store.connect().");
          // if authentication is necessary, then the authenticator will
          // show, so will need to be closed.
          mAuthenticator.disposeAuthenticator();
        } catch (MessagingException me) {
          getLogger().fine("caught exception.");
          // cases here:
          // 1)  authenticator cancelled.
          if (mAuthenticator.isCancelled()) {
            getLogger().fine("operation was cancelled.");
            mAuthenticator.disposeAuthenticator();
            mPreferredStatus = FolderInfo.DISCONNECTED;
            throw new OperationCancelledException();
          }

          // 2) Interrupted IO exception -- try again.
          Exception nextEx = me.getNextException();

          if (nextEx != null && nextEx instanceof java.io.InterruptedIOException) {
            getLogger().fine("retrying--interruptedioexception");
          else {

            // 3) TLS exception -- fall back.
            if (nextEx != null && nextEx.toString().contains("SunCertPathBuilderException") && "tls".equalsIgnoreCase(sslSetting)) {
              getLogger().fine("falling back to no tls.");
              // fall back.
              Properties p = mSession.getProperties();
              p.setProperty("mail.imap.starttls.enable", "false");

              store = mSession.getStore(url);
            } else {
              // 4)  auth failed.  show error and retry.
              if (mAuthenticator.isShowing()) {
                mAuthenticator.setErrorMessage(me.getMessage(), me);
              } else {
                // 5) must have been some other error.  throw it.
                if (nextEx != null) {
                  if (nextEx instanceof java.net.UnknownHostException) {
                    throw new MessagingException(Pooka.getResources().formatMessage("error.login.unknownHostException", nextEx.getMessage()), me);
                    //} else if (nextEx instanceof java.io.UnknownHostException) {

                  }
                }
                throw me;
              }
            }
          }
        }
      }

      getLogger().log(Level.FINE, "connect store " + getStoreID() + ":  connection succeeded; connected = true.");
      connected=true;

      if (useSubscribed && protocol.equalsIgnoreCase("imap")) {
        synchSubscribed();
      }

      if (Pooka.getProperty("Pooka.openFoldersOnConnect", "true").equalsIgnoreCase("true")) {
        for (int i = 0; i < children.size(); i++) {
          doOpenFolders((FolderInfo) children.elementAt(i));
        }
      }
    }

  }

  /**
   * Tests the NetworkConnection status for this store.
   */
  private void testConnection() throws MessagingException {
    // don't test for connections for mbox providers.
    if (! (protocol.equalsIgnoreCase("mbox") || protocol.equalsIgnoreCase("maildir"))) {
      NetworkConnection currentConnection = getConnection();
      getLogger().log(Level.FINE, "connect store " + getStoreID() + ":  checking connection.");

      if (currentConnection != null) {
        if (currentConnection.getStatus() == NetworkConnection.DISCONNECTED) {
          getLogger().log(Level.FINE, "connect store " + getStoreID() + ":  connection not up.  trying to connect it..");

          currentConnection.connect(true, true);
        }

        if (connection.getStatus() != NetworkConnection.CONNECTED) {
          throw new MessagingException(Pooka.getProperty("error.connectionDown", "Connection down for Store:  ") + getItemID());
        } else {
          getLogger().log(Level.FINE, "connect store " + getStoreID() + ":  successfully opened connection.");

        }
      }
    }
  }

  /**
   * Execute the precommand if there is one.
   */
  private void executePrecommand() {
    String preCommand = Pooka.getProperty(getStoreProperty() + ".precommand", "");
    if (preCommand.length() > 0) {
      getLogger().log(Level.FINE, "connect store " + getStoreID() + ":  executing precommand.");

      try {
        Process p = Runtime.getRuntime().exec(preCommand);
        p.waitFor();
      } catch (Exception ex) {
        getLogger().log(Level.FINE, "Could not run precommand:");
          ex.printStackTrace();
      }
    }
  }

  /**
   *
   */
  private void doOpenFolders(FolderInfo fi) {
    if (Pooka.getProperty("Pooka.openFoldersInBackground", "false").equalsIgnoreCase("true")) {
      final FolderInfo current = fi;
      javax.swing.AbstractAction openFoldersAction = new javax.swing.AbstractAction() {
          public void actionPerformed(java.awt.event.ActionEvent e) {
            current.openAllFolders(Folder.READ_WRITE);
          }
        };

      openFoldersAction.putValue(javax.swing.Action.NAME, "file-open");
      openFoldersAction.putValue(javax.swing.Action.SHORT_DESCRIPTION, "file-open on folder " + fi.getFolderID());
      getStoreThread().addToQueue(openFoldersAction, new java.awt.event.ActionEvent(this, 0, "open-all"), ActionThread.PRIORITY_LOW);
    } else {
      fi.openAllFolders(Folder.READ_WRITE);
    }
  }

  /**
   * Opens the given folders in the UI.
   */
  public void openFolders(List<FolderInfo> folderList) {
    try {
      connectStore();
      for (FolderInfo fInfo: folderList) {
        final FolderNode fNode = fInfo.getFolderNode();

        fNode.openFolder(false, false);
      }
    } catch (MessagingException me) {
      // on failure still open caching folders.
      boolean showError = false;
      for (FolderInfo fInfo: folderList) {
        if (fInfo instanceof net.suberic.pooka.cache.CachingFolderInfo) {
          if (! (((net.suberic.pooka.cache.CachingFolderInfo) fInfo).getCacheHeadersOnly())) {
            final FolderNode fNode = fInfo.getFolderNode();

            fNode.openFolder(false, false);
          }
        } else {
          showError = true;
        }
      }
      if (showError) {
        Pooka.getUIFactory().showError(Pooka.getResources().formatMessage("error.Store.connecton.failed", getStoreID()), me);
      }
    } catch (OperationCancelledException oce) {
      // on failure still open caching folders.
      for (FolderInfo fInfo: folderList) {
        if (fInfo instanceof net.suberic.pooka.cache.CachingFolderInfo) {
          if (! (((net.suberic.pooka.cache.CachingFolderInfo) fInfo).getCacheHeadersOnly())) {
            final FolderNode fNode = fInfo.getFolderNode();

            fNode.openFolder(false, false);
          }
        }
      }
    }
  }

  /**
   * This method disconnects the Store.  If you connect to the Store using
   * connectStore() (which you should), then you should use this method
   * instead of calling getStore.disconnect().  If you don't, then the
   * StoreInfo will try to reconnect the store.
   */
  public void disconnectStore() throws MessagingException {
    getLogger().log(Level.FINE, "disconnecting store " + getStoreID());

    MessagingException storeException = null;
    if (!(store.isConnected())) {
      connected=false;
      closeAllFolders(false, false);
      return;
    } else {
      try {
        try {
          closeAllFolders(false, false);
        } catch (MessagingException folderMe) {
          storeException = folderMe;
        }
        store.close();
      } catch (MessagingException me) {
        if (storeException != null) {
          me.setNextException(storeException);
        }
        storeException = me;
        throw storeException;
      } finally {
        connected=false;
      }

      if (storeException != null)
        throw storeException;
    }
  }

  /**
   * Closes all of the Store's children.
   */
  public void closeAllFolders(boolean expunge, boolean shuttingDown) throws MessagingException {
    if (getStoreThread() != null && ! getStoreThread().getStopped()) {
      // if the store thread has exited, assume we're exiting, too.
      synchronized(getStoreThread().getRunLock()) {
        getLogger().log(Level.FINE, "closing all folders of store " + getStoreID());
        Vector folders = getChildren();
        if (folders != null) {
          for (int i = 0; i < folders.size(); i++) {
            ((FolderInfo) folders.elementAt(i)).closeAllFolders(expunge, shuttingDown);
          }
        }
      }
    }
  }

  /**
   * Stops the store thread.
   */
  public void stopStoreThread() {
    if (storeThread != null) {
      storeThread.setStop(true);
      //storeThread = null;
    }
  }

  /**
   * Gets all of the children folders of this StoreInfo which are both
   * Open and can contain Messages.
   */
  public Vector getAllFolders() {
    Vector returnValue = new Vector();
    Vector subFolders = getChildren();
    for (int i = 0; i < subFolders.size(); i++) {
      returnValue.addAll(((FolderInfo) subFolders.elementAt(i)).getAllFolders());
    }
    return returnValue;
  }

  /**
   * Synchronizes the locally stored subscribed folders list to the subscribed
   * folder information from the IMAP server.
   */
  public void synchSubscribed() throws MessagingException, OperationCancelledException {
    // require the inbox.  this is to work around a bug in which the inbox
    // doesn't show up in certain conditions.

    boolean foundInbox=false;

    Folder[] subscribedFolders = store.getDefaultFolder().list();

    ArrayList subscribedNames = new ArrayList();

    for (int i = 0; subscribedFolders != null && i < subscribedFolders.length; i++) {
      // sometimes listSubscribed() doesn't work.
      // and sometimes list() returns duplicate entries for some reason.
      String folderName = subscribedFolders[i].getName();
      if (folderName.equalsIgnoreCase("inbox")) {
        if (!foundInbox) {
          foundInbox=true;
          subscribedNames.add(folderName);
        }
      } else if (subscribedFolders[i].isSubscribed()) {
        if (! subscribedNames.contains(folderName))
          subscribedNames.add(folderName);
      }
    }

    // add subscribed namespaces.
    List tmpChildren = getChildren();
    if (tmpChildren != null) {
      // go through each.  check to see if it's a namespace.  if so,
      // add it, too.

      Iterator it = tmpChildren.iterator();
      while (it.hasNext()) {
        FolderInfo fi = (FolderInfo) it.next();
        String folderName = fi.getFolderName();
        if (fi.isNamespace() && ! subscribedNames.contains(folderName))
          subscribedNames.add(folderName);
      }
    }
    Collections.sort(subscribedNames);

    // keep the existing order when possible.
    List<String> currentSubscribed = Pooka.getResources().getPropertyAsList(getStoreProperty() + ".folderList", "");
    Iterator<String> currentIter = currentSubscribed.iterator();
    while(currentIter.hasNext()) {
      String folder = currentIter.next();
      if (! subscribedNames.contains(folder)) {
        currentSubscribed.remove(folder);
      } else {
        subscribedNames.remove(folder);
      }
    }

    currentSubscribed.addAll(subscribedNames);

    // this will update our children vector.
    Pooka.setProperty(getStoreProperty() + ".folderList", VariableBundle.convertToString(currentSubscribed));

    for (int i = 0; children != null && i < children.size(); i++) {
      FolderInfo fi = (FolderInfo) children.get(i);
      fi.synchSubscribed();
    }
  }

  // last connection check.
  long lastConnectionCheck = 0;

  /**
   * Checks connection for this store.
   */
  public boolean checkConnection() {
    // if we don't think we're connected, don't check.
    if (! isConnected())
      return false;

    // don't check if we've checked very recently.
    if (System.currentTimeMillis() - lastConnectionCheck > 20000) {
      getLogger().log(Level.FINER, "Checking connection for store " + getStoreID());
      Store realStore = getStore();

      if (realStore != null) {
        if (! realStore.isConnected()) {
          getLogger().log(Level.FINER, getStoreID() + ":  isConnected() returns false.  returning false.");
          return false;
        } else {
          return true;
        }
      } else {
        return false;
      }
    } else {
      return isConnected();
    }
  }

  /**
   * Shows the current status for this store and its thread.
   */
  public void showStatus() {
    StringBuffer statusBuffer = new StringBuffer();
    getLogger().log(Level.INFO, "Status for store " + getStoreID());
    statusBuffer.append("Status for store " + getStoreID() + "\r\n");
    boolean infoIsConnected = isConnected();
    getLogger().log(Level.INFO, "Connected:  " + infoIsConnected);
    statusBuffer.append("Connected:  " + infoIsConnected + "\r\n");

    if (storeThread != null) {
      String currentAction = storeThread.getCurrentActionName();
      getLogger().log(Level.INFO, "Current Action:  " + currentAction);
      statusBuffer.append("Current Action:  " + currentAction + "\r\n");
      int queueSize = storeThread.getQueueSize();
      getLogger().log(Level.INFO, "Action Queue Size:  " + queueSize);
      statusBuffer.append("Action Queue Size:  " + queueSize +"\r\n");
      if (storeThread.getQueueSize() > 0) {
        System.out.println("Queue:");
        java.util.List queue = storeThread.getQueue();
        for (int i = 0; i < queue.size(); i++) {
          net.suberic.util.thread.ActionThread.ActionEventPair current = (net.suberic.util.thread.ActionThread.ActionEventPair) queue.get(i);
          String queueString = "  queue[" + i + "]:  ";
          String entryDescription = (String) current.action.getValue(javax.swing.Action.SHORT_DESCRIPTION);
          if (entryDescription == null)
            entryDescription = (String) current.action.getValue(javax.swing.Action.NAME);
          if (entryDescription == null)
            entryDescription = "Unknown action";

          queueString = queueString + entryDescription;
          System.out.println(queueString);
          statusBuffer.append(queueString + "\r\n");
        }
      }
      statusBuffer.append("Stack Trace:\r\n");
      StackTraceElement[] stackTrace = storeThread.getStackTrace();
      for (StackTraceElement stackLine: stackTrace) {
        statusBuffer.append("  " + stackLine + "\r\n");
      }

    } else {
      getLogger().log(Level.INFO, "No Action Thread.");
      StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
      for (StackTraceElement stackLine: stackTrace) {
        statusBuffer.append("  " + stackLine + "\r\n");
      }
    }

    Pooka.getUIFactory().showMessage(statusBuffer.toString(), "Status for " + getStoreID());
  }

  // Accessor methods.

  public Store getStore() {
    return store;
  }

  private void setStore(Store newValue) {
    store=newValue;
  }

  /**
   * This returns the StoreID.
   */
  public String getStoreID() {
    return storeID;
  }

  /**
   * This returns the ItemID, which in this case is the StoreID.
   */
  public String getItemID() {
    return getStoreID();
  }

  /**
   * This sets the storeID.
   */
  private void setStoreID(String newValue) {
    storeID=newValue;
  }

  /**
   * This returns the property which defines this StoreInfo, such as
   * "Store.myStore".
   */
  public String getStoreProperty() {
    return "Store." + getStoreID();
  }

  /**
   * This returns the item property, which in this case is the same as
   * the storeProperty.
   */
  public String getItemProperty() {
    return getStoreProperty();
  }

  /**
   * Returns the protocol we're using.
   */
  public String getProtocol() {
    return protocol;
  }

  /**
   * Returns true if this is a pop3 store.
   */
  public boolean isPopStore() {
    return popStore;
  }

  public Vector getChildren() {
    return children;
  }

  public StoreNode getStoreNode() {
    return storeNode;
  }

  public void setStoreNode(StoreNode newValue) {
    storeNode = newValue;
  }

  public boolean isConnected() {
    return connected;
  }

  public boolean isAvailable() {
    return available;
  }

  public UserProfile getDefaultProfile() {
    return defaultProfile;
  }

  public NetworkConnection getConnection() {
    return connection;
  }

  public ActionThread getStoreThread() {
    return storeThread;
  }

  public void setStoreThread(ActionThread newValue) {
    storeThread=newValue;
  }

  public FolderInfo getTrashFolder() {
    return trashFolder;
  }

  /**
   * Returns the preferredStatus of this StoreInfo.
   */
  public int getPreferredStatus() {
    return mPreferredStatus;
  }

  /**
   * Sets the preferredStatus of this StoreInfo.
   */
  public void setPreferredStatus(int pPreferredStatus) {
    mPreferredStatus = pPreferredStatus;
  }

  /**
   * This returns whether or not this Store is set up to use the
   * TrashFolder.  If StoreProperty.useTrashFolder is set, return that as
   * a boolean.  Otherwise, return Pooka.useTrashFolder as a boolean.
   */
  public boolean useTrashFolder() {
    if (getTrashFolder() == null)
      return false;

    String prop = Pooka.getProperty(getStoreProperty() + ".useTrashFolder", "");
    if (!prop.equals(""))
      return (! prop.equalsIgnoreCase("false"));
    else
      return (! Pooka.getProperty("Pooka.useTrashFolder", "true").equalsIgnoreCase("true"));

  }


  public void setTrashFolder(FolderInfo newValue) {
    trashFolder = newValue;
  }

  /**
   * Returns the logger for this Store.
   */
  public Logger getLogger() {
    if (mLogger == null) {
      mLogger = Logger.getLogger(getStoreProperty());
    }
    return mLogger;
  }

  /**
   * Updates the debug status on the session.
   */
  void updateSessionDebug() {
    if (Pooka.getProperty("Pooka.sessionDebug", "false").equalsIgnoreCase("true") || (! Pooka.getProperty(getStoreProperty() + ".sessionDebug.logLevel", "OFF").equalsIgnoreCase("OFF"))) {
      mSession.setDebug(true);
    } else {
      mSession.setDebug(false);
    }
  }
}
TOP

Related Classes of net.suberic.pooka.StoreInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.