send_ldap_search_entry_ext(
    Slapi_PBlock *pb,
    Slapi_Entry *e,
    LDAPControl **ectrls,
    char **attrs,
    int attrsonly,
    int send_result,
    int nentries,
    struct berval **urls)
{
    Connection *conn = NULL;
    BerElement *ber = NULL;
    int i, rc = 0, logit = 0;
    int alluserattrs;
    int noattrs;
    int some_named_attrs;
    int alloperationalattrs;
    Slapi_Operation *operation;
    int real_attrs_only = 0;
    LDAPControl **ctrlp = 0;
    Slapi_Entry *gerentry = NULL;
    Slapi_Entry *ecopy = NULL;
    LDAPControl **searchctrlp = NULL;


    slapi_pblock_get(pb, SLAPI_CONNECTION, &conn);
    slapi_pblock_get(pb, SLAPI_OPERATION, &operation);

    slapi_log_err(SLAPI_LOG_TRACE, "send_ldap_search_entry_ext", "=> (%s)\n",
                  e ? slapi_entry_get_dn_const(e) : "null");

    /* set current entry */
    slapi_pblock_set(pb, SLAPI_SEARCH_ENTRY_ORIG, e);
    /* set controls */
    slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, ectrls);

    /* call pre entry fn */
    rc = plugin_call_plugins(pb, SLAPI_PLUGIN_PRE_ENTRY_FN);
    if (rc) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext",
                      "Error %d returned by pre entry plugins for entry %s\n",
                      rc, e ? slapi_entry_get_dn_const(e) : "null");
        goto cleanup;
    }

    slapi_pblock_get(pb, SLAPI_SEARCH_ENTRY_COPY, &ecopy);
    if (ecopy) {
        e = ecopy; /* send back the altered entry */
    }
    slapi_pblock_get(pb, SLAPI_SEARCH_CTRLS, &searchctrlp);

    if (conn == NULL && e) {
        Slapi_Backend *pb_backend;
        slapi_pblock_get(pb, SLAPI_BACKEND, &pb_backend);

        if (operation->o_search_entry_handler != NULL) {
            if ((rc = (*operation->o_search_entry_handler)(
                     pb_backend, conn, operation, e)) == 0) {
                logit = 1;
                goto log_and_return;
            } else {
                goto cleanup;
            }
        }
        rc = 0;
        goto cleanup;
    }

#if !defined(DISABLE_ACL_CHECK)
    if (e && plugin_call_acl_plugin(pb, e, attrs, NULL, SLAPI_ACL_READ, ACLPLUGIN_ACCESS_READ_ON_ENTRY, NULL) != LDAP_SUCCESS) {
        slapi_log_err(SLAPI_LOG_ACL, "send_ldap_search_entry_ext", "Access to entry not allowed\n");
        rc = 1;
        goto cleanup;
    }
#endif

    if (NULL == e) {
        rc = 1; /* everything is ok - don't send the result */
        goto cleanup;
    }

    if ((ber = der_alloc()) == NULL) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext", "ber_alloc failed\n");
        send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                         "ber_alloc", 0, NULL);
        rc = -1;
        goto cleanup;
    }

    rc = ber_printf(ber, "{it{s{", operation->o_msgid,
                    LDAP_RES_SEARCH_ENTRY, slapi_entry_get_dn_const(e));

    if (rc == -1) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext", "ber_printf failed 7\n");
        send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                         "ber_printf dn", 0, NULL);
        goto cleanup;
    }

    /*
     * in ldapv3, the special attribute "*" means all user attributes,
     * NULL means all user attributes, "1.1" means no attributes, and
     * "+" means all operational attributes (rfc3673)
     * operational attributes are only retrieved if they are named
     * specifically or when "+" is specified.
     * In the case of "1.1", if there are other requested attributes
     * then "1.1" should be ignored.
     */

    /* figure out if we want all user attributes or no attributes at all */
    alluserattrs = 0;
    noattrs = 0;
    some_named_attrs = 0;
    alloperationalattrs = 0;
    if (attrs == NULL) {
        alluserattrs = 1;
    } else {
        for (i = 0; attrs[i] != NULL; i++) {
            if (strcmp(LDAP_ALL_USER_ATTRS, attrs[i]) == 0) {
                alluserattrs = 1;
            } else if (strcmp(LDAP_NO_ATTRS, attrs[i]) == 0) {
                /* "1.1" is only valid if it's the only requested attribute */
                if (i == 0 && attrs[1] == NULL) {
                    noattrs = 1;
                }
            } else if (strcmp(LDAP_ALL_OPERATIONAL_ATTRS, attrs[i]) == 0) {
                alloperationalattrs = 1;
            } else {
                some_named_attrs = 1;
            }
        }
        if (i > 1 && noattrs) {
            /*
             * user has specified the special "1.1" noattrs attr
             * and some other stuff. this is not allowed, but
             * what should we do? we'll allow them to keep going.
             */
            slapi_log_err(SLAPI_LOG_TRACE, "send_ldap_search_entry_ext",
                          "Accepting illegal other attributes specified with special \"1.1\" attribute\n");
        }
    }


    /* determine whether we are to return virtual attributes */
    slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
    if (slapi_control_present(ctrlp, LDAP_CONTROL_REAL_ATTRS_ONLY, NULL, NULL))
        real_attrs_only = SLAPI_SEND_VATTR_FLAG_REALONLY;

    if (slapi_control_present(ctrlp, LDAP_CONTROL_VIRT_ATTRS_ONLY, NULL, NULL)) {
        if (real_attrs_only != SLAPI_SEND_VATTR_FLAG_REALONLY)
            real_attrs_only = SLAPI_SEND_VATTR_FLAG_VIRTUALONLY;
        else {
            /* we cannot service a request for virtual only and real only */
            send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                             "Both real and virtual attributes only controls", 0, NULL);
            rc = -1;
            goto cleanup;
        }
    }

    /* look through each attribute in the entry */
    if (alluserattrs || alloperationalattrs) {
        rc = send_all_attrs(e, attrs, operation, pb, ber, attrsonly, conn->c_ldapversion,
                            real_attrs_only, some_named_attrs, alloperationalattrs, alluserattrs);
    }

    /* if the client explicitly specified a list of attributes look through each attribute requested */
    if ((rc == 0) && (attrs != NULL) && !noattrs) {
        rc = send_specific_attrs(e, attrs, operation, pb, ber, attrsonly, conn->c_ldapversion, real_attrs_only);
    }

    /* Append effective rights to the stream of attribute list */
    if (operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS) {
        char *gerstr;
        char *entryrights;
        char *attributerights;
        char *p;

        slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &gerstr);

        /* Syntax check - see acleffectiverights.c */
        if (gerstr && (p = strchr(gerstr, '\n')) != NULL &&
            strncasecmp(gerstr, "entryLevelRights: ",
                        strlen("entryLevelRights: ")) == 0 &&
            strncasecmp(p + 1, "attributeLevelRights: ",
                        strlen("attributeLevelRights: ")) == 0) {
            entryrights = gerstr + strlen("entryLevelRights: ");
            *p = '\0';
            attributerights = p + 1 + strlen("attributeLevelRights: ");
            ber_printf(ber, "{s[o]}", "entryLevelRights", entryrights, strlen(entryrights));
            ber_printf(ber, "{s[o]}", "attributeLevelRights", attributerights, strlen(attributerights));
        }
    }

    if (rc != 0) {
        goto cleanup;
    }

    rc = ber_printf(ber, "}}");

    if (conn->c_ldapversion >= LDAP_VERSION3) {
        if (searchctrlp != NULL) {
            rc = write_controls(ber, searchctrlp);
        }
    }

    if (rc != -1) {
        rc = ber_printf(ber, "}");
    }

    if (rc == -1) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext", "ber_printf failed 8\n");
        send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                         "ber_printf entry end", 0, NULL);
        goto cleanup;
    }

    if (send_result) {
        send_ldap_result_ext(pb, LDAP_SUCCESS, NULL, NULL, nentries, urls, ber);
    }

    /* write only one pdu at a time - wait til it's our turn */
    if ((rc = flush_ber(pb, conn, operation, ber, _LDAP_SEND_ENTRY)) == 0) {
        logit = 1;
    }
    ber = NULL; /* flush_ber will always free the ber */

log_and_return:
    if (logit && operation_is_flag_set(operation, OP_FLAG_ACTION_LOG_ACCESS)) {

        log_entry(operation, e);

        if (send_result) {
            ber_tag_t tag;

            switch (operation->o_tag) {
            case LBER_DEFAULT:
                tag = LBER_SEQUENCE;
                break;

            case LDAP_REQ_SEARCH:
                tag = LDAP_RES_SEARCH_RESULT;
                break;

            case LDAP_REQ_DELETE:
                tag = LDAP_RES_DELETE;
                break;

            case LDAP_REFERRAL:
                if (conn != NULL && conn->c_ldapversion > LDAP_VERSION2) {
                    tag = LDAP_TAG_REFERRAL;
                    break;
                }
            /* FALLTHROUGH */

            default:
                tag = operation->o_tag + 1;
                break;
            }

            log_result(pb, operation, LDAP_SUCCESS, tag, nentries);
        }
    }
cleanup:
    slapi_entry_free(gerentry);
    slapi_pblock_get(pb, SLAPI_SEARCH_ENTRY_COPY, &ecopy);
    slapi_pblock_set(pb, SLAPI_SEARCH_ENTRY_COPY, NULL);
    slapi_entry_free(ecopy);
    slapi_pblock_get(pb, SLAPI_SEARCH_CTRLS, &searchctrlp);
    slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, NULL);
    if (searchctrlp != ectrls) {
        ldap_controls_free(searchctrlp);
    }
    ber_free(ber, 1);
    slapi_log_err(SLAPI_LOG_TRACE, "send_ldap_search_entry_ext", "<= %d\n", rc);

    return (rc);
}
send_ldap_search_entry_ext(
    Slapi_PBlock *pb,
    Slapi_Entry *e,
    LDAPControl **ectrls,
    char **attrs,
    int attrsonly,
    int send_result,
    int nentries,
    struct berval **urls)
{
    Connection *conn = NULL;
    BerElement *ber = NULL;
    int i, rc = 0, logit = 0;
    int alluserattrs;
    int noattrs;
    int some_named_attrs;
    int alloperationalattrs;
    Slapi_Operation *operation;
    int real_attrs_only = 0;
    LDAPControl **ctrlp = 0;
    Slapi_Entry *gerentry = NULL;
    Slapi_Entry *ecopy = NULL;
    LDAPControl **searchctrlp = NULL;


    slapi_pblock_get(pb, SLAPI_CONNECTION, &conn);
    slapi_pblock_get(pb, SLAPI_OPERATION, &operation);

    slapi_log_err(SLAPI_LOG_TRACE, "send_ldap_search_entry_ext", "=> (%s)\n",
                  e ? slapi_entry_get_dn_const(e) : "null");

     
    slapi_pblock_set(pb, SLAPI_SEARCH_ENTRY_ORIG, e);
     
    slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, ectrls);

     
    rc = plugin_call_plugins(pb, SLAPI_PLUGIN_PRE_ENTRY_FN);
    if (rc) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext",
                      "Error %d returned by pre entry plugins for entry %s\n",
                      rc, e ? slapi_entry_get_dn_const(e) : "null");
        goto cleanup;
    }

    slapi_pblock_get(pb, SLAPI_SEARCH_ENTRY_COPY, &ecopy);
    if (ecopy) {
        e = ecopy;  
    }
    slapi_pblock_get(pb, SLAPI_SEARCH_CTRLS, &searchctrlp);

    if (conn == NULL && e) {
        Slapi_Backend *pb_backend;
        slapi_pblock_get(pb, SLAPI_BACKEND, &pb_backend);

        if (operation->o_search_entry_handler != NULL) {
            if ((rc = (*operation->o_search_entry_handler)(
                     pb_backend, conn, operation, e)) == 0) {
                logit = 1;
                goto log_and_return;
            } else {
                goto cleanup;
            }
        }
        rc = 0;
        goto cleanup;
    }

#if !defined(DISABLE_ACL_CHECK)
    if (e && plugin_call_acl_plugin(pb, e, attrs, NULL, SLAPI_ACL_READ, ACLPLUGIN_ACCESS_READ_ON_ENTRY, NULL) != LDAP_SUCCESS) {
        slapi_log_err(SLAPI_LOG_ACL, "send_ldap_search_entry_ext", "Access to entry not allowed\n");
        rc = 1;
        goto cleanup;
    }
#endif

    if (NULL == e) {
        rc = 1;  
        goto cleanup;
    }

    if ((ber = der_alloc()) == NULL) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext", "ber_alloc failed\n");
        send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                         "ber_alloc", 0, NULL);
        rc = -1;
        goto cleanup;
    }

    rc = ber_printf(ber, "{it{s{", operation->o_msgid,
                    LDAP_RES_SEARCH_ENTRY, slapi_entry_get_dn_const(e));

    if (rc == -1) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext", "ber_printf failed 7\n");
        send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                         "ber_printf dn", 0, NULL);
        goto cleanup;
    }

     

     
    alluserattrs = 0;
    noattrs = 0;
    some_named_attrs = 0;
    alloperationalattrs = 0;
    if (attrs == NULL) {
        alluserattrs = 1;
    } else {
        for (i = 0; attrs[i] != NULL; i++) {
            if (strcmp(LDAP_ALL_USER_ATTRS, attrs[i]) == 0) {
                alluserattrs = 1;
            } else if (strcmp(LDAP_NO_ATTRS, attrs[i]) == 0) {
                 
                if (i == 0 && attrs[1] == NULL) {
                    noattrs = 1;
                }
            } else if (strcmp(LDAP_ALL_OPERATIONAL_ATTRS, attrs[i]) == 0) {
                alloperationalattrs = 1;
            } else {
                some_named_attrs = 1;
            }
        }
        if (i > 1 && noattrs) {
             
            slapi_log_err(SLAPI_LOG_TRACE, "send_ldap_search_entry_ext",
                          "Accepting illegal other attributes specified with special \"1.1\" attribute\n");
        }
    }


     
    slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
    if (slapi_control_present(ctrlp, LDAP_CONTROL_REAL_ATTRS_ONLY, NULL, NULL))
        real_attrs_only = SLAPI_SEND_VATTR_FLAG_REALONLY;

    if (slapi_control_present(ctrlp, LDAP_CONTROL_VIRT_ATTRS_ONLY, NULL, NULL)) {
        if (real_attrs_only != SLAPI_SEND_VATTR_FLAG_REALONLY)
            real_attrs_only = SLAPI_SEND_VATTR_FLAG_VIRTUALONLY;
        else {
             
            send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                             "Both real and virtual attributes only controls", 0, NULL);
            rc = -1;
            goto cleanup;
        }
    }

     
    if (alluserattrs || alloperationalattrs) {
        rc = send_all_attrs(e, attrs, operation, pb, ber, attrsonly, conn->c_ldapversion,
                            real_attrs_only, some_named_attrs, alloperationalattrs, alluserattrs);
    }

     
    if ((rc == 0) && (attrs != NULL) && !noattrs) {
        rc = send_specific_attrs(e, attrs, operation, pb, ber, attrsonly, conn->c_ldapversion, real_attrs_only);
    }

     
    if (operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS) {
        char *gerstr;
        char *entryrights;
        char *attributerights;
        char *p;

        slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &gerstr);

         
        if (gerstr && (p = strchr(gerstr, '\n')) != NULL &&
            strncasecmp(gerstr, "entryLevelRights: ",
                        strlen("entryLevelRights: ")) == 0 &&
            strncasecmp(p + 1, "attributeLevelRights: ",
                        strlen("attributeLevelRights: ")) == 0) {
            entryrights = gerstr + strlen("entryLevelRights: ");
            *p = '\0';
            attributerights = p + 1 + strlen("attributeLevelRights: ");
            ber_printf(ber, "{s[o]}", "entryLevelRights", entryrights, strlen(entryrights));
            ber_printf(ber, "{s[o]}", "attributeLevelRights", attributerights, strlen(attributerights));
        }
    }

    if (rc != 0) {
        goto cleanup;
    }

    rc = ber_printf(ber, "}}");

    if (conn->c_ldapversion >= LDAP_VERSION3) {
        if (searchctrlp != NULL) {
            rc = write_controls(ber, searchctrlp);
        }
    }

    if (rc != -1) {
        rc = ber_printf(ber, "}");
    }

    if (rc == -1) {
        slapi_log_err(SLAPI_LOG_ERR, "send_ldap_search_entry_ext", "ber_printf failed 8\n");
        send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
                         "ber_printf entry end", 0, NULL);
        goto cleanup;
    }

    if (send_result) {
        send_ldap_result_ext(pb, LDAP_SUCCESS, NULL, NULL, nentries, urls, ber);
    }

     
    if ((rc = flush_ber(pb, conn, operation, ber, _LDAP_SEND_ENTRY)) == 0) {
        logit = 1;
    }
    ber = NULL;  

log_and_return:
    if (logit && operation_is_flag_set(operation, OP_FLAG_ACTION_LOG_ACCESS)) {

        log_entry(operation, e);

        if (send_result) {
            ber_tag_t tag;

            switch (operation->o_tag) {
            case LBER_DEFAULT:
                tag = LBER_SEQUENCE;
                break;

            case LDAP_REQ_SEARCH:
                tag = LDAP_RES_SEARCH_RESULT;
                break;

            case LDAP_REQ_DELETE:
                tag = LDAP_RES_DELETE;
                break;

            case LDAP_REFERRAL:
                if (conn != NULL && conn->c_ldapversion > LDAP_VERSION2) {
                    tag = LDAP_TAG_REFERRAL;
                    break;
                }
             

            default:
                tag = operation->o_tag + 1;
                break;
            }

            log_result(pb, operation, LDAP_SUCCESS, tag, nentries);
        }
    }
cleanup:
    slapi_entry_free(gerentry);
    slapi_pblock_get(pb, SLAPI_SEARCH_ENTRY_COPY, &ecopy);
    slapi_pblock_set(pb, SLAPI_SEARCH_ENTRY_COPY, NULL);
    slapi_entry_free(ecopy);
    slapi_pblock_get(pb, SLAPI_SEARCH_CTRLS, &searchctrlp);
    slapi_pblock_set(pb, SLAPI_SEARCH_CTRLS, NULL);
    if (searchctrlp != ectrls) {
        ldap_controls_free(searchctrlp);
    }
    ber_free(ber, 1);
    slapi_log_err(SLAPI_LOG_TRACE, "send_ldap_search_entry_ext", "<= %d\n", rc);

    return (rc);
}