/**
   * Performs the checks and processing necessary for the current bind operation (simple or SASL).
   */
  private void processBind() {
    // Check to see if the client has permission to perform the
    // bind.

    // FIXME: for now assume that this will check all permission
    // pertinent to the operation. This includes any controls
    // specified.
    try {
      if (!AccessControlConfigManager.getInstance().getAccessControlHandler().isAllowed(this)) {
        setResultCode(ResultCode.INVALID_CREDENTIALS);
        setAuthFailureReason(ERR_BIND_AUTHZ_INSUFFICIENT_ACCESS_RIGHTS.get());
        return;
      }
    } catch (DirectoryException e) {
      setResultCode(e.getResultCode());
      setAuthFailureReason(e.getMessageObject());
      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) {
      logger.traceException(de);

      setResponseData(de);
      return;
    }

    // Check to see if this is a simple bind or a SASL bind and process
    // accordingly.
    try {
      switch (getAuthenticationType()) {
        case SIMPLE:
          processSimpleBind();
          break;

        case SASL:
          processSASLBind();
          break;

        default:
          // Send a protocol error response to the client and disconnect.
          // We should never come here.
          setResultCode(ResultCode.PROTOCOL_ERROR);
      }
    } catch (DirectoryException de) {
      logger.traceException(de);

      if (de.getResultCode() == ResultCode.INVALID_CREDENTIALS) {
        setResultCode(ResultCode.INVALID_CREDENTIALS);
        setAuthFailureReason(de.getMessageObject());
      } else {
        setResponseData(de);
      }
    }
  }
  /**
   * Handles any controls contained in the request.
   *
   * @throws DirectoryException If there is a problem with any of the request controls.
   */
  private void handleRequestControls() throws DirectoryException {
    LocalBackendWorkflowElement.removeAllDisallowedControls(baseDN, this);

    List<Control> requestControls = getRequestControls();
    if (requestControls != null && !requestControls.isEmpty()) {
      for (Control c : requestControls) {
        String oid = c.getOID();

        if (OID_LDAP_ASSERTION.equals(oid)) {
          LDAPAssertionRequestControl assertControl =
              getRequestControl(LDAPAssertionRequestControl.DECODER);

          SearchFilter assertionFilter;
          try {
            assertionFilter = assertControl.getSearchFilter();
          } catch (DirectoryException de) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }

            throw new DirectoryException(
                de.getResultCode(),
                ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(de.getMessageObject()),
                de);
          }

          Entry entry;
          try {
            entry = DirectoryServer.getEntry(baseDN);
          } catch (DirectoryException de) {
            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }

            throw new DirectoryException(
                de.getResultCode(),
                ERR_SEARCH_CANNOT_GET_ENTRY_FOR_ASSERTION.get(de.getMessageObject()));
          }

          if (entry == null) {
            throw new DirectoryException(
                ResultCode.NO_SUCH_OBJECT, ERR_SEARCH_NO_SUCH_ENTRY_FOR_ASSERTION.get());
          }

          // Check if the current user has permission to make
          // this determination.
          if (!AccessControlConfigManager.getInstance()
              .getAccessControlHandler()
              .isAllowed(this, entry, assertionFilter)) {
            throw new DirectoryException(
                ResultCode.INSUFFICIENT_ACCESS_RIGHTS,
                ERR_CONTROL_INSUFFICIENT_ACCESS_RIGHTS.get(oid));
          }

          try {
            if (!assertionFilter.matchesEntry(entry)) {
              throw new DirectoryException(
                  ResultCode.ASSERTION_FAILED, ERR_SEARCH_ASSERTION_FAILED.get());
            }
          } catch (DirectoryException de) {
            if (de.getResultCode() == ResultCode.ASSERTION_FAILED) {
              throw de;
            }

            if (debugEnabled()) {
              TRACER.debugCaught(DebugLogLevel.ERROR, de);
            }

            throw new DirectoryException(
                de.getResultCode(),
                ERR_SEARCH_CANNOT_PROCESS_ASSERTION_FILTER.get(de.getMessageObject()),
                de);
          }
        } else if (OID_PROXIED_AUTH_V1.equals(oid)) {
          // Log usage of legacy proxy authz V1 control.
          addAdditionalLogItem(
              AdditionalLogItem.keyOnly(getClass(), "obsoleteProxiedAuthzV1Control"));

          // The requester must have the PROXIED_AUTH privilege in order to be
          // able to use this control.
          if (!clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) {
            throw new DirectoryException(
                ResultCode.AUTHORIZATION_DENIED, ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }

          ProxiedAuthV1Control proxyControl = getRequestControl(ProxiedAuthV1Control.DECODER);

          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getDN(authorizationEntry));
        } else if (OID_PROXIED_AUTH_V2.equals(oid)) {
          // The requester must have the PROXIED_AUTH privilege in order to be
          // able to use this control.
          if (!clientConnection.hasPrivilege(Privilege.PROXIED_AUTH, this)) {
            throw new DirectoryException(
                ResultCode.AUTHORIZATION_DENIED, ERR_PROXYAUTH_INSUFFICIENT_PRIVILEGES.get());
          }

          ProxiedAuthV2Control proxyControl = getRequestControl(ProxiedAuthV2Control.DECODER);

          Entry authorizationEntry = proxyControl.getAuthorizationEntry();
          setAuthorizationEntry(authorizationEntry);
          setProxiedAuthorizationDN(getDN(authorizationEntry));
        } else if (OID_PERSISTENT_SEARCH.equals(oid)) {
          final PersistentSearchControl ctrl = getRequestControl(PersistentSearchControl.DECODER);

          persistentSearch =
              new PersistentSearch(
                  this, ctrl.getChangeTypes(), ctrl.getChangesOnly(), ctrl.getReturnECs());
        } else if (OID_LDAP_SUBENTRIES.equals(oid)) {
          SubentriesControl subentriesControl = getRequestControl(SubentriesControl.DECODER);
          setReturnSubentriesOnly(subentriesControl.getVisibility());
        } else if (OID_LDUP_SUBENTRIES.equals(oid)) {
          // Support for legacy draft-ietf-ldup-subentry.
          addAdditionalLogItem(AdditionalLogItem.keyOnly(getClass(), "obsoleteSubentryControl"));

          setReturnSubentriesOnly(true);
        } else if (OID_MATCHED_VALUES.equals(oid)) {
          MatchedValuesControl matchedValuesControl =
              getRequestControl(MatchedValuesControl.DECODER);
          setMatchedValuesControl(matchedValuesControl);
        } else if (OID_ACCOUNT_USABLE_CONTROL.equals(oid)) {
          setIncludeUsableControl(true);
        } else if (OID_REAL_ATTRS_ONLY.equals(oid)) {
          setRealAttributesOnly(true);
        } else if (OID_VIRTUAL_ATTRS_ONLY.equals(oid)) {
          setVirtualAttributesOnly(true);
        } else if (OID_GET_EFFECTIVE_RIGHTS.equals(oid)
            && DirectoryServer.isSupportedControl(OID_GET_EFFECTIVE_RIGHTS)) {
          // Do nothing here and let AciHandler deal with it.
        }
        // NYI -- Add support for additional controls.

        else if (c.isCritical() && !backendSupportsControl(oid)) {
          throw new DirectoryException(
              ResultCode.UNAVAILABLE_CRITICAL_EXTENSION,
              ERR_SEARCH_UNSUPPORTED_CRITICAL_CONTROL.get(oid));
        }
      }
    }
  }
  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);
      }
    }
  }