/**
   * Process this search operation against a local backend.
   *
   * @param wfe The local backend work-flow element.
   * @throws CanceledOperationException if this operation should be cancelled
   */
  public void processLocalSearch(LocalBackendWorkflowElement wfe)
      throws CanceledOperationException {
    this.backend = wfe.getBackend();
    this.clientConnection = getClientConnection();

    // Check for a request to cancel this operation.
    checkIfCanceled(false);

    try {
      BooleanHolder executePostOpPlugins = new BooleanHolder(false);
      processSearch(executePostOpPlugins);

      // Check for a request to cancel this operation.
      checkIfCanceled(false);

      // Invoke the post-operation search plugins.
      if (executePostOpPlugins.value) {
        PluginResult.PostOperation postOpResult =
            DirectoryServer.getPluginConfigManager().invokePostOperationSearchPlugins(this);
        if (!postOpResult.continueProcessing()) {
          setResultCode(postOpResult.getResultCode());
          appendErrorMessage(postOpResult.getErrorMessage());
          setMatchedDN(postOpResult.getMatchedDN());
          setReferralURLs(postOpResult.getReferralURLs());
        }
      }
    } finally {
      LocalBackendWorkflowElement.filterNonDisclosableMatchedDN(this);
    }
  }
  private void processSearch(BooleanHolder executePostOpPlugins) throws CanceledOperationException {
    // Process the search base and filter to convert them from their raw forms
    // as provided by the client to the forms required for the rest of the
    // search processing.
    baseDN = getBaseDN();
    filter = getFilter();

    if (baseDN == null || filter == null) {
      return;
    }

    // Check to see if there are any controls in the request. If so, then
    // see if there is any special processing required.
    try {
      handleRequestControls();
    } catch (DirectoryException de) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, de);
      }

      setResponseData(de);
      return;
    }

    // Check to see if the client has permission to perform the
    // search.

    // FIXME: for now assume that this will check all permission
    // pertinent to the operation. This includes proxy authorization
    // and any other controls specified.
    try {
      if (!AccessControlConfigManager.getInstance().getAccessControlHandler().isAllowed(this)) {
        setResultCode(ResultCode.INSUFFICIENT_ACCESS_RIGHTS);
        appendErrorMessage(ERR_SEARCH_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get(String.valueOf(baseDN)));
        return;
      }
    } catch (DirectoryException e) {
      setResultCode(e.getResultCode());
      appendErrorMessage(e.getMessageObject());
      return;
    }

    // Check for a request to cancel this operation.
    checkIfCanceled(false);

    // Invoke the pre-operation search plugins.
    executePostOpPlugins.value = true;
    PluginResult.PreOperation preOpResult =
        DirectoryServer.getPluginConfigManager().invokePreOperationSearchPlugins(this);
    if (!preOpResult.continueProcessing()) {
      setResultCode(preOpResult.getResultCode());
      appendErrorMessage(preOpResult.getErrorMessage());
      setMatchedDN(preOpResult.getMatchedDN());
      setReferralURLs(preOpResult.getReferralURLs());
      return;
    }

    // Check for a request to cancel this operation.
    checkIfCanceled(false);

    // Get the backend that should hold the search base. If there is none,
    // then fail.
    if (backend == null) {
      setResultCode(ResultCode.NO_SUCH_OBJECT);
      appendErrorMessage(ERR_SEARCH_BASE_DOESNT_EXIST.get(String.valueOf(baseDN)));
      return;
    }

    // We'll set the result code to "success". If a problem occurs, then it
    // will be overwritten.
    setResultCode(ResultCode.SUCCESS);

    try {
      // If there's a persistent search, then register it with the server.
      boolean processSearchNow = true;
      if (persistentSearch != null) {
        // If we're only interested in changes, then we do not actually want
        // to process the search now.
        processSearchNow = !persistentSearch.isChangesOnly();

        // The Core server maintains the count of concurrent persistent searches
        // so that all the backends (Remote and Local) are aware of it. Verify
        // with the core if we have already reached the threshold.
        if (!DirectoryServer.allowNewPersistentSearch()) {
          setResultCode(ResultCode.ADMIN_LIMIT_EXCEEDED);
          appendErrorMessage(ERR_MAX_PSEARCH_LIMIT_EXCEEDED.get());
          return;
        }
        backend.registerPersistentSearch(persistentSearch);
        persistentSearch.enable();
      }

      if (processSearchNow) {
        // Process the search in the backend and all its subordinates.
        backend.search(this);
      }
    } catch (DirectoryException de) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.VERBOSE, de);
      }

      setResponseData(de);

      if (persistentSearch != null) {
        persistentSearch.cancel();
        setSendResponse(true);
      }

      return;
    } catch (CanceledOperationException coe) {
      if (persistentSearch != null) {
        persistentSearch.cancel();
        setSendResponse(true);
      }

      throw coe;
    } catch (Exception e) {
      if (debugEnabled()) {
        TRACER.debugCaught(DebugLogLevel.ERROR, e);
      }

      setResultCode(DirectoryServer.getServerErrorResultCode());
      appendErrorMessage(ERR_SEARCH_BACKEND_EXCEPTION.get(getExceptionMessage(e)));

      if (persistentSearch != null) {
        persistentSearch.cancel();
        setSendResponse(true);
      }
    }
  }
  /**
   * Process this bind operation in a local backend.
   *
   * @param wfe The local backend work-flow element.
   */
  public void processLocalBind(LocalBackendWorkflowElement wfe) {
    this.backend = wfe.getBackend();

    // Initialize a number of variables for use during the bind processing.
    clientConnection = getClientConnection();
    returnAuthzID = false;
    executePostOpPlugins = false;
    sizeLimit = DirectoryServer.getSizeLimit();
    timeLimit = DirectoryServer.getTimeLimit();
    lookthroughLimit = DirectoryServer.getLookthroughLimit();
    idleTimeLimit = DirectoryServer.getIdleTimeLimit();
    bindDN = getBindDN();
    saslMechanism = getSASLMechanism();
    authPolicyState = null;
    pwPolicyErrorType = null;
    pwPolicyControlRequested = false;
    isGraceLogin = false;
    isFirstWarning = false;
    mustChangePassword = false;
    pwPolicyWarningType = null;
    pwPolicyWarningValue = -1;
    pluginConfigManager = DirectoryServer.getPluginConfigManager();

    processBind();

    // Update the user's account with any password policy changes that may be
    // required.
    try {
      if (authPolicyState != null) {
        authPolicyState.finalizeStateAfterBind();
      }
    } catch (DirectoryException de) {
      logger.traceException(de);

      setResponseData(de);
    }

    // Invoke the post-operation bind plugins.
    if (executePostOpPlugins) {
      PluginResult.PostOperation postOpResult =
          pluginConfigManager.invokePostOperationBindPlugins(this);
      if (!postOpResult.continueProcessing()) {
        setResultCode(postOpResult.getResultCode());
        appendErrorMessage(postOpResult.getErrorMessage());
        setMatchedDN(postOpResult.getMatchedDN());
        setReferralURLs(postOpResult.getReferralURLs());
      }
    }

    // Update the authentication information for the user.
    AuthenticationInfo authInfo = getAuthenticationInfo();
    if (getResultCode() == ResultCode.SUCCESS && authInfo != null) {
      clientConnection.setAuthenticationInfo(authInfo);
      clientConnection.setSizeLimit(sizeLimit);
      clientConnection.setTimeLimit(timeLimit);
      clientConnection.setIdleTimeLimit(idleTimeLimit);
      clientConnection.setLookthroughLimit(lookthroughLimit);
      clientConnection.setMustChangePassword(mustChangePassword);

      if (returnAuthzID) {
        addResponseControl(new AuthorizationIdentityResponseControl(authInfo.getAuthorizationDN()));
      }
    }

    // See if we need to send a password policy control to the client.  If so,
    // then add it to the response.
    if (getResultCode() == ResultCode.SUCCESS) {
      if (pwPolicyControlRequested) {
        PasswordPolicyResponseControl pwpControl =
            new PasswordPolicyResponseControl(
                pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType);
        addResponseControl(pwpControl);
      } else {
        if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) {
          addResponseControl(new PasswordExpiredControl());
        } else if (pwPolicyWarningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) {
          addResponseControl(new PasswordExpiringControl(pwPolicyWarningValue));
        } else if (mustChangePassword) {
          addResponseControl(new PasswordExpiredControl());
        }
      }
    } else {
      if (pwPolicyControlRequested) {
        PasswordPolicyResponseControl pwpControl =
            new PasswordPolicyResponseControl(
                pwPolicyWarningType, pwPolicyWarningValue, pwPolicyErrorType);
        addResponseControl(pwpControl);
      } else {
        if (pwPolicyErrorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) {
          addResponseControl(new PasswordExpiredControl());
        }
      }
    }
  }