public class MaildirHostSystem extends JamesImapHostSystem {

  public static final String META_DATA_DIRECTORY = "target/user-meta-data";
  private static final String MAILDIR_HOME = "target/Maildir";
  private static final ImapFeatures SUPPORTED_FEATURES = ImapFeatures.of();

  private final StoreMailboxManager mailboxManager;
  private final FakeAuthenticator userManager;
  private final MaildirMailboxSessionMapperFactory mailboxSessionMapperFactory;

  public static JamesImapHostSystem build() throws Exception {
    return new MaildirHostSystem();
  }

  public MaildirHostSystem() throws MailboxException {
    userManager = new FakeAuthenticator();
    JVMMailboxPathLocker locker = new JVMMailboxPathLocker();
    MaildirStore store = new MaildirStore(MAILDIR_HOME + "/%user", locker);
    mailboxSessionMapperFactory = new MaildirMailboxSessionMapperFactory(store);
    StoreSubscriptionManager sm = new StoreSubscriptionManager(mailboxSessionMapperFactory);

    MailboxACLResolver aclResolver = new UnionMailboxACLResolver();
    GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver();
    MessageParser messageParser = new MessageParser();

    mailboxManager =
        new StoreMailboxManager(
            mailboxSessionMapperFactory,
            userManager,
            locker,
            aclResolver,
            groupMembershipResolver,
            messageParser,
            new DefaultMessageId.Factory());
    mailboxManager.init();

    final ImapProcessor defaultImapProcessorFactory =
        DefaultImapProcessorFactory.createDefaultProcessor(
            mailboxManager,
            sm,
            new NoQuotaManager(),
            new DefaultQuotaRootResolver(mailboxSessionMapperFactory));
    configure(
        new DefaultImapDecoderFactory().buildImapDecoder(),
        new DefaultImapEncoderFactory().buildImapEncoder(),
        defaultImapProcessorFactory);
    (new File(MAILDIR_HOME)).mkdirs();
  }

  public boolean addUser(String user, String password) throws Exception {
    userManager.addUser(user, password);
    return true;
  }

  @Override
  public void resetData() throws Exception {
    resetUserMetaData();
    try {
      FileUtils.deleteDirectory(new File(MAILDIR_HOME));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void resetUserMetaData() throws Exception {
    File dir = new File(META_DATA_DIRECTORY);
    if (dir.exists()) {
      FileUtils.deleteDirectory(dir);
    }
    dir.mkdirs();
  }

  @Override
  public void createMailbox(MailboxPath mailboxPath) throws Exception {
    new MailboxCreationDelegate(mailboxManager).createMailbox(mailboxPath);
  }

  @Override
  public boolean supports(Feature... features) {
    return SUPPORTED_FEATURES.supports(features);
  }
}
/** Task executes MPT scripts against a server running on a given port and host. */
public class MailProtocolTestTask extends Task implements Monitor {

  private static final ImapFeatures SUPPORTED_FEATURES = ImapFeatures.of(Feature.NAMESPACE_SUPPORT);

  private boolean quiet = false;
  private File script;
  private Union scripts;
  private int port = 0;
  private String host = "127.0.0.1";
  private boolean skip = false;
  private String shabang = null;
  private final Collection<AddUser> users = new ArrayList<AddUser>();
  private String errorProperty;

  /**
   * Gets the error property.
   *
   * @return name of the ant property to be set on error, null if the script should terminate on
   *     error
   */
  public String getErrorProperty() {
    return errorProperty;
  }

  /**
   * Sets the error property.
   *
   * @param errorProperty name of the ant property to be set on error, nul if the script should
   *     terminate on error
   */
  public void setErrorProperty(String errorProperty) {
    this.errorProperty = errorProperty;
  }

  /**
   * Should progress output be suppressed?
   *
   * @return true if progress information should be suppressed, false otherwise
   */
  public boolean isQuiet() {
    return quiet;
  }

  /**
   * Sets whether progress output should be suppressed/
   *
   * @param quiet true if progress information should be suppressed, false otherwise
   */
  public void setQuiet(boolean quiet) {
    this.quiet = quiet;
  }

  /**
   * Should the execution be skipped?
   *
   * @return true if exection should be skipped, otherwise false
   */
  public boolean isSkip() {
    return skip;
  }

  /**
   * Sets execution skipping.
   *
   * @param skip true to skip excution
   */
  public void setSkip(boolean skip) {
    this.skip = skip;
  }

  /**
   * Gets the host (either name or number) against which this test will run.
   *
   * @return host, not null
   */
  public String getHost() {
    return host;
  }

  /**
   * Sets the host (either name or number) against which this test will run.
   *
   * @param host not null
   */
  public void setHost(String host) {
    this.host = host;
  }

  /**
   * Gets the port against which this test will run.
   *
   * @return port number
   */
  public int getPort() {
    return port;
  }

  /**
   * Sets the port aginst which this test will run.
   *
   * @param port port number
   */
  public void setPort(int port) {
    this.port = port;
  }

  /**
   * Gets the script to execute.
   *
   * @return file containing test script
   */
  public File getScript() {
    return script;
  }

  /**
   * Sets the script to execute.
   *
   * @param script not null
   */
  public void setScript(File script) {
    this.script = script;
  }

  /**
   * Gets script shabang. This will be substituted for the first server response.
   *
   * @return script shabang, or null for no shabang
   */
  public String getShabang() {
    return shabang;
  }

  /**
   * Sets the script shabang. When not null, this value will be used to be substituted for the first
   * server response.
   *
   * @param shabang script shabang, or null for no shabang.
   */
  public void setShabang(String shabang) {
    this.shabang = shabang;
  }

  @Override
  public void execute() throws BuildException {
    if (port <= 0) {
      throw new BuildException("Port must be set to a positive integer");
    }

    if (scripts == null && script == null) {
      throw new BuildException("Scripts must be specified as an embedded resource collection");
    }

    if (scripts != null && script != null) {
      throw new BuildException(
          "Scripts can be specified either by the script attribute or as resource collections but not both.");
    }

    for (AddUser user : users) {
      user.validate();
    }

    if (skip) {
      log("Skipping excution");
    } else if (errorProperty == null) {
      doExecute();
    } else {
      try {
        doExecute();
      } catch (BuildException e) {
        final Project project = getProject();
        project.setProperty(errorProperty, e.getMessage());
        log(e, Project.MSG_DEBUG);
      }
    }
  }

  public void add(ResourceCollection resources) {
    if (scripts == null) {
      scripts = new Union();
    }
    scripts.add(resources);
  }

  private void doExecute() throws BuildException {
    for (AddUser userAdder : users) {
      userAdder.execute();
    }

    final ExternalHostSystem host =
        new ExternalHostSystem(SUPPORTED_FEATURES, getHost(), getPort(), this, getShabang(), null);
    final ProtocolSessionBuilder builder = new ProtocolSessionBuilder();

    if (scripts == null) {
      scripts = new Union();
      scripts.add(new FileResource(script));
    }

    for (Iterator<?> it = scripts.iterator(); it.hasNext(); ) {
      final Resource resource = (Resource) it.next();
      try {
        final Runner runner = new Runner();

        try {

          final InputStream inputStream = resource.getInputStream();
          final String name = resource.getName();
          builder.addProtocolLines(
              name == null ? "[Unknown]" : name, inputStream, runner.getTestElements());
          runner.runSessions(host);

        } catch (UnsupportedOperationException e) {
          log("Resource cannot be read: " + resource.getName(), Project.MSG_WARN);
        }
      } catch (IOException e) {
        throw new BuildException("Cannot load script " + resource.getName(), e);
      } catch (Exception e) {
        log(e.getMessage(), Project.MSG_ERR);
        throw new BuildException(
            "[FAILURE] in script " + resource.getName() + "\n" + e.getMessage(), e);
      }
    }
  }

  public AddUser createAddUser() {
    final AddUser result = new AddUser();
    users.add(result);
    return result;
  }

  /** Adds a user. */
  public class AddUser {

    private int port;
    private String user;
    private String passwd;
    private File script;
    private String scriptText;

    /**
     * Gets the port against which the user addition script should be executed.
     *
     * @return port number
     */
    public int getPort() {
      return port;
    }

    /**
     * Sets the port against which the user addition script should be executed.
     *
     * @param port port number
     */
    public void setPort(int port) {
      this.port = port;
    }

    /**
     * Gets the password for the user.
     *
     * @return password not null
     */
    public String getPasswd() {
      return passwd;
    }

    /**
     * Sets the password for the user. This will be passed in the user creation script.
     *
     * @param passwd not null
     */
    public void setPasswd(String passwd) {
      this.passwd = passwd;
    }

    /**
     * Gets the name of the user to be created.
     *
     * @return user name, not null
     */
    public String getUser() {
      return user;
    }

    /**
     * Sets the name of the user to be created.
     *
     * @param user not null
     */
    public void setUser(String user) {
      this.user = user;
    }

    /**
     * Sets user addition script.
     *
     * @param scriptText not null
     */
    public void addText(String scriptText) {
      this.scriptText = getProject().replaceProperties(scriptText);
    }

    /**
     * Gets the file containing the user creation script.
     *
     * @return not null
     */
    public File getScript() {
      return script;
    }

    /**
     * Sets the file containing the user creation script.
     *
     * @param script not null
     */
    public void setScript(File script) {
      this.script = script;
    }

    /** Validates mandatory fields have been filled. */
    void validate() throws BuildException {
      if (script == null && scriptText == null) {
        throw new BuildException(
            "Either the 'script' attribute must be set, or the body must contain the text of the script");
      }

      if (script != null && scriptText != null) {
        throw new BuildException("Choose either script text or script attribute but not both.");
      }

      if (port <= 0) {
        throw new BuildException(
            "'port' attribute must be set on AddUser to the port against which the script should run.");
      }
    }

    /**
     * Creates a user.
     *
     * @throws BuildException
     */
    void execute() throws BuildException {
      validate();
      try {
        final File scriptFile = getScript();
        final Reader reader;
        if (scriptFile == null) {
          reader = new StringReader(scriptText);
        } else {
          reader = new FileReader(scriptFile);
        }
        final ScriptedUserAdder adder =
            new ScriptedUserAdder(getHost(), getPort(), MailProtocolTestTask.this);
        adder.addUser(getUser(), getPasswd(), reader);
      } catch (Exception e) {
        log(e.getMessage(), Project.MSG_ERR);
        throw new BuildException("User addition failed: \n" + e.getMessage(), e);
      }
    }
  }

  public void note(String message) {
    if (quiet) {
      log(message, Project.MSG_DEBUG);
    } else {
      log(message, Project.MSG_INFO);
    }
  }

  public void debug(char character) {
    log("'" + character + "'", Project.MSG_DEBUG);
  }

  public void debug(String message) {
    log(message, Project.MSG_DEBUG);
  }
}
 @Override
 public boolean supports(Feature... features) {
   return SUPPORTED_FEATURES.supports(features);
 }