/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 *
 * License: GPL (version 3 or any later version).
 * See LICENSE for details. 
 * END COPYRIGHT BLOCK **/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/* vlv_srch.c */


#include "back-ldbm.h"
#include "vlv_srch.h"

/* Attributes for vlvSearch */
char* const type_vlvName = "cn";
char* const type_vlvBase = "vlvBase";
char* const type_vlvScope = "vlvScope";
char* const type_vlvFilter = "vlvFilter";

/* Attributes for vlvIndex */
char* const type_vlvSort = "vlvSort";
char* const type_vlvFilename = "vlvFilename";
char* const type_vlvEnabled = "vlvEnabled";
char* const type_vlvUses = "vlvUses";

static const char *file_prefix= "vlv#"; /* '#' used to avoid collision with real attributes */
static const char *file_suffix= LDBM_FILENAME_SUFFIX;

static int vlvIndex_createfilename(struct vlvIndex* pIndex, char **ppc);

static int vlvIndex_equal(const struct vlvIndex* p1, const sort_spec* sort_control);
static void vlvIndex_checkforindex(struct vlvIndex* p, backend *be);

/*
 * Create a new vlvSearch object
 */
struct vlvSearch*
vlvSearch_new()
{
    struct vlvSearch* p = (struct vlvSearch*)slapi_ch_calloc(1,sizeof(struct vlvSearch));
    if(p!=NULL)
    {
        p->vlv_e= NULL;
        p->vlv_dn= NULL;
        p->vlv_name= NULL;
        p->vlv_base= NULL;
        p->vlv_scope= LDAP_SCOPE_BASE;
        p->vlv_filter= NULL;
        p->vlv_slapifilter= NULL;
        p->vlv_index= NULL;
        p->vlv_next= NULL;
    }
    return p;
}

/*
 * Trim spaces off the end of the string
 */
static void
trimspaces(char *s)
{
	if (s) {
		PRUint32 i= strlen(s) - 1;
		while(i > 0 && isascii(s[i]) && isspace(s[i]))
			{
				s[i]= '\0';
				i--;
			}
	}
}

/*
 * Re-Initialise a vlvSearch object
 */
void
vlvSearch_reinit(struct vlvSearch* p, const struct backentry *base)
{
	if (p->vlv_initialized) {
		return; /* no work to do */
	}
	if (LDAP_SCOPE_ONELEVEL != p->vlv_scope) {
		/* Only kind we re-init is onelevel searches */
		return;
	}
	/* Now down to work */
	if (NULL != p->vlv_slapifilter) {
		slapi_filter_free(p->vlv_slapifilter,1);
	}
    p->vlv_slapifilter= slapi_str2filter( p->vlv_filter );
    filter_normalize(p->vlv_slapifilter);
    /* make (&(parentid=idofbase)(|(originalfilter)(objectclass=referral))) */
	{
    Slapi_Filter *fid2kids= NULL;
    Slapi_Filter *focref= NULL;
    Slapi_Filter *fand= NULL;
    Slapi_Filter *forr= NULL;
    p->vlv_slapifilter= create_onelevel_filter(p->vlv_slapifilter, base, 0 /* managedsait */, &fid2kids, &focref, &fand, &forr);
	}
}

/*
 * Initialise a vlvSearch object
 */
void
vlvSearch_init(struct vlvSearch* p, Slapi_PBlock *pb, const Slapi_Entry *e, ldbm_instance *inst)
{
    /* VLV specification */
    /* Need to copy the entry here because this one is in the cache, 
     * not forever ! */
    p->vlv_e= slapi_entry_dup( e ); 
    p->vlv_dn= slapi_sdn_dup(slapi_entry_get_sdn_const(e));
    p->vlv_name= slapi_entry_attr_get_charptr(e,type_vlvName);
    p->vlv_base= slapi_sdn_new_dn_passin(slapi_entry_attr_get_charptr(e,type_vlvBase));
    p->vlv_scope= slapi_entry_attr_get_int(e,type_vlvScope);
    p->vlv_filter= slapi_entry_attr_get_charptr(e,type_vlvFilter);
    p->vlv_initialized = 1;

    /* JCM: Should perform some validation and report errors to the error log */
    /* JCM: Add brackets around the filter if none are there... */
    trimspaces(p->vlv_name);
    trimspaces(p->vlv_filter);

    if(strlen(p->vlv_filter)>0)
    {
        /* Convert the textual filter, into a Slapi_Filter structure */
        p->vlv_slapifilter= slapi_str2filter( p->vlv_filter );
        filter_normalize(p->vlv_slapifilter);
    }

    /* JCM: Really should convert the slapifilter into a string and use that. */

    /* Convert the filter based on the scope of the search */
    switch(p->vlv_scope)
    {
	case LDAP_SCOPE_BASE:
        /* Don't need to alter the filter */
		break;
	case LDAP_SCOPE_ONELEVEL:
        {
    	/*
    	 * Get the base object for the search.
    	 * The entry "" will never be contained in the database,
    	 * so treat it as a special case.
    	 */
        struct backentry *e= NULL;
    	if ( !slapi_sdn_isempty(p->vlv_base)) {
            Slapi_Backend *oldbe = NULL;
            entry_address addr;
            back_txn txn = {NULL};

            /* switch context to the target backend */
            slapi_pblock_get(pb, SLAPI_BACKEND, &oldbe);
            slapi_pblock_set(pb, SLAPI_BACKEND, inst->inst_be);
            slapi_pblock_set(pb, SLAPI_PLUGIN, inst->inst_be->be_database);
            slapi_pblock_get(pb, SLAPI_TXN, &txn.back_txn_txn);

            addr.sdn = p->vlv_base;
            addr.uniqueid = NULL;
            e = find_entry( pb, inst->inst_be, &addr, &txn );
            /* Check to see if the entry is absent. If it is, mark this search
             * as not initialized */
            if (NULL == e) {
                p->vlv_initialized = 0;
		/* We crash on anyhow, and rely on the fact that the filter
                 * we create is bogus to prevent chaos */
            }

            /* switch context back to the DSE backend */
            slapi_pblock_set(pb, SLAPI_BACKEND, oldbe);
            slapi_pblock_set(pb, SLAPI_PLUGIN, oldbe->be_database);
        }

    	/* make (&(parentid=idofbase)(|(originalfilter)(objectclass=referral))) */
        {
            Slapi_Filter *fid2kids= NULL;
            Slapi_Filter *focref= NULL;
            Slapi_Filter *fand= NULL;
            Slapi_Filter *forr= NULL;
        p->vlv_slapifilter= create_onelevel_filter(p->vlv_slapifilter, e, 0 /* managedsait */, &fid2kids, &focref, &fand, &forr);
        /* jcm: fid2kids, focref, fand, and forr get freed when we free p->vlv_slapifilter */
            CACHE_RETURN(&inst->inst_cache,&e);
        }
        }
		break;
	case LDAP_SCOPE_SUBTREE:
        {
        /* make (|(originalfilter)(objectclass=referral))) */
        /* No need for scope-filter since we apply a scope test before the filter test */
    	Slapi_Filter *focref= NULL;
    	Slapi_Filter *forr= NULL;
        p->vlv_slapifilter= create_subtree_filter(p->vlv_slapifilter,  0 /* managedsait */, &focref, &forr);
        /* jcm: focref and forr get freed when we free p->vlv_slapifilter */
        }
		break;
    }
}

/*
 * Destroy an existing vlvSearch object
 */
void
vlvSearch_delete(struct vlvSearch** ppvs)
{
    if(ppvs!=NULL && *ppvs!=NULL)
    {
        struct vlvIndex *pi, *ni;
        slapi_sdn_free(&((*ppvs)->vlv_dn));
        slapi_ch_free((void**)&((*ppvs)->vlv_name));
        slapi_sdn_free(&((*ppvs)->vlv_base));
        slapi_ch_free((void**)&((*ppvs)->vlv_filter));
        slapi_filter_free((*ppvs)->vlv_slapifilter,1);
        for(pi= (*ppvs)->vlv_index;pi!=NULL;)
        {
            ni= pi->vlv_next;
            if(pi->vlv_be != NULL) {
                vlvIndex_go_offline(pi,pi->vlv_be);
            }
            vlvIndex_delete(&pi);
            pi= ni;
        }
        slapi_ch_free((void**)ppvs);
        *ppvs= NULL;
    }
}

/*
 * Add a search to a list.
 *
 * We add it to the end of the list because there could
 * be other threads traversing the list at this time.
 */
void
vlvSearch_addtolist(struct vlvSearch* p, struct vlvSearch** pplist)
{
    if(pplist!=NULL && p!=NULL)
    {
        p->vlv_next= NULL;
        if(*pplist==NULL)
        {
            *pplist= p;
        }
        else
        {
            struct vlvSearch* last= *pplist;
            for(;last->vlv_next!=NULL;last=last->vlv_next);
            last->vlv_next= p;
        }
    }
}


/*
 * Compare two VLV Searches to see if they're the same, based on their VLV Search specification.
 */
static struct vlvIndex *
vlvSearch_equal(const struct vlvSearch* p1, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
{
	struct vlvIndex *pi= NULL;
    int r= (slapi_sdn_compare(p1->vlv_base,base)==0);
    if(r) r= (p1->vlv_scope==scope);
    if(r) r= (strcasecmp(p1->vlv_filter,filter)==0);
    if(r)
    {
        pi= p1->vlv_index;
		r= 0;
        for(;!r && pi!=NULL;)
        {
            r= vlvIndex_equal(pi, sort_control);
			if(!r)
			{
				pi= pi->vlv_next;
			}
        }
    }
    return pi;
}

/*
 * Find an enabled VLV Search in a list which matches the
 * description provided in "base, scope, filter, sort_control"
 */
struct vlvIndex*
vlvSearch_findenabled(backend *be,struct vlvSearch* plist, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
{
    struct vlvSearch *t= plist;
	struct vlvIndex *pi= NULL;
    for(; (t!=NULL) && (pi == NULL); t= t->vlv_next)
    {
		pi= vlvSearch_equal(t,base,scope,filter,sort_control);
        if(pi!=NULL)
        {
            if(!vlvIndex_enabled(pi))
            {
                /*
                 * A VLV Spec which matched the search criteria was found.
                 * But it hasn't been enabled yet.  Check to see if the
                 * index is there.  But, only check once every 60 seconds.
                 */
            	time_t curtime = current_time();
                if(curtime>pi->vlv_lastchecked+60)
                {
                    vlvIndex_checkforindex(pi, be);
                    pi->vlv_lastchecked= current_time();
                }
            }
            if(!vlvIndex_enabled(pi))
            {
                pi= NULL;
            }
        }
    }
    return pi;
}

/*
 * Find a VLV Search in a list which matches the name
 */
struct vlvIndex*
vlvSearch_findname(const struct vlvSearch* plist, const char *name)
{
    const struct vlvSearch* t= plist;
    for(; t!=NULL ; t= t->vlv_next)
    {
        struct vlvIndex *pi= t->vlv_index;
        for(;pi!=NULL;pi= pi->vlv_next)
        {
            if(strcasecmp(pi->vlv_name,name)==0)
            {
                return pi;
            }
        }
    }
    return NULL;
}

/*
 * Find a VLV Search in a list which matches the index name
 */
struct vlvIndex*
vlvSearch_findindexname(const struct vlvSearch* plist, const char *name)
{
    const struct vlvSearch* t= plist;
    if (NULL == name) {
        return NULL;
    }
    for(; t!=NULL ; t= t->vlv_next)
    {
        struct vlvIndex *pi= t->vlv_index;
        for(;pi!=NULL;pi= pi->vlv_next)
        {
            if(strcasecmp(pi->vlv_attrinfo->ai_type,name)==0)
            {
                return pi;
            }
        }
    }
    return NULL;
}

/*
 * Get a list of VLV Index names.
 * The returned pointer must be freed with slapi_ch_free
 */
char *
vlvSearch_getnames(const struct vlvSearch* plist)
{
    /* Work out how long the string will be */
    char *text;
    int length= 5; /* enough to hold 'none' */ 
    const struct vlvSearch* t= plist;
    for(; t!=NULL ; t= t->vlv_next)
    {
        struct vlvIndex *pi= t->vlv_index;
        for(;pi!=NULL;pi= pi->vlv_next)
        {
            length+= strlen(pi->vlv_name) + 4;
        }
    }
    /* Build a comma delimited list of Index names */
    text= slapi_ch_malloc(length);
    if(length==5)
    {
        strcpy(text,"none");
    }
    else
    {
        text[0]= '\0';
        t= plist;
        for(; t!=NULL ; t= t->vlv_next)
        {
            struct vlvIndex *pi= t->vlv_index;
            for(;pi!=NULL;pi= pi->vlv_next)
            {
                sprintf(text + strlen(text),"'%s', ",pi->vlv_name);
            }
        }
    }
    return text;
}

/*
 * Find a VLV Search in a list, based on the DN.
 */
struct vlvSearch*
vlvSearch_finddn(const struct vlvSearch* plist, const Slapi_DN *dn)
{
    const struct vlvSearch* curr= plist;
    for(; curr!=NULL && slapi_sdn_compare(curr->vlv_dn,dn)!=0; curr= curr->vlv_next);
    return (struct vlvSearch*)curr;
}

/*
 * Remove a VLV Search from a list, based on the DN.
 */
void
vlvSearch_removefromlist(struct vlvSearch** pplist, const Slapi_DN *dn)
{
    int done= 0;
    struct vlvSearch* prev= NULL;
    struct vlvSearch* curr= *pplist;
    while(curr!=NULL && !done)
    {
        if(slapi_sdn_compare(curr->vlv_dn,dn)==0)
        {
            if(curr==*pplist)
            {
                *pplist= curr->vlv_next;
            } 
            else
            {
                if (prev) prev->vlv_next= curr->vlv_next;
            }
            done= 1;
        }
        else
        {
            prev= curr;
            curr= curr->vlv_next;
        }
    }
}

/*
 * Access Control Check to see if the client is allowed to use this VLV Search.
 */
int
vlvSearch_accessallowed(struct vlvSearch *p, Slapi_PBlock *pb)
{
	char	*attrs[2] = { NULL, NULL};

	attrs[0] = type_vlvName;
    return (plugin_call_acl_plugin ( pb, (Slapi_Entry*)p->vlv_e, attrs, NULL, 
			  	  SLAPI_ACL_READ, ACLPLUGIN_ACCESS_READ_ON_VLV, NULL ) );
}

const Slapi_DN *vlvSearch_getBase(struct vlvSearch* p)
{
    return p->vlv_base;
}

int vlvSearch_getScope(struct vlvSearch* p)
{
    return p->vlv_scope;
}

Slapi_Filter *vlvSearch_getFilter(struct vlvSearch* p)
{
    return p->vlv_slapifilter;
}

int vlvSearch_isVlvSearchEntry(Slapi_Entry *e)
{
    return slapi_entry_attr_hasvalue(e, "objectclass", "vlvsearch");
}

void vlvSearch_addIndex(struct vlvSearch *pSearch, struct vlvIndex *pIndex)
{
    pIndex->vlv_next= NULL;
    if(pSearch->vlv_index==NULL)
    {
        pSearch->vlv_index= pIndex;
    }
    else
    {
        struct vlvIndex* last= pSearch->vlv_index;
        for(;last->vlv_next!=NULL;last=last->vlv_next);
        last->vlv_next= pIndex;
    }
}

/* ============================================================================================== */

/*
 * Create a new vlvIndex object
 */
struct vlvIndex*
vlvIndex_new()
{
    struct vlvIndex* p = (struct vlvIndex*)slapi_ch_calloc(1,sizeof(struct vlvIndex));
    if(p!=NULL)
    {
        p->vlv_sortspec= NULL;
        p->vlv_attrinfo= attrinfo_new();
        p->vlv_sortkey= NULL;
        p->vlv_filename= NULL;
	    p->vlv_mrpb= NULL;
        p->vlv_indexlength_lock= PR_NewLock();
        p->vlv_indexlength_cached= 0;
        p->vlv_indexlength= 0;
        p->vlv_online = 1;
        p->vlv_enabled = 0;
        p->vlv_lastchecked= 0;
        p->vlv_uses= 0;
        p->vlv_search= NULL;
        p->vlv_next= NULL;
    }
    return p;
}

/*
 * Destroy an existing vlvIndex object
 */
void
vlvIndex_delete(struct vlvIndex** ppvs)
{
    if(ppvs!=NULL && *ppvs!=NULL)
    {
        slapi_ch_free((void**)&((*ppvs)->vlv_sortspec));
        {
            int n;
            for(n=0;(*ppvs)->vlv_sortkey[n]!=NULL;n++)
            {
                if((*ppvs)->vlv_mrpb[n] != NULL) { 
                    destroy_matchrule_indexer((*ppvs)->vlv_mrpb[n]);
                    slapi_pblock_destroy((*ppvs)->vlv_mrpb[n]);
                }
            }
        }
        internal_ldap_free_sort_keylist((*ppvs)->vlv_sortkey);
        dblayer_erase_index_file_nolock((*ppvs)->vlv_be, (*ppvs)->vlv_attrinfo, 1 /* chkpt if not busy */);
        attrinfo_delete(&((*ppvs)->vlv_attrinfo));
        slapi_ch_free((void**)&((*ppvs)->vlv_name));
        slapi_ch_free((void**)&((*ppvs)->vlv_filename));
        slapi_ch_free((void**)&((*ppvs)->vlv_mrpb));
        PR_DestroyLock((*ppvs)->vlv_indexlength_lock);
        slapi_ch_free((void**)ppvs);
        *ppvs= NULL;
    }
}

/*
 * Initialise a vlvSearch object
 */
void
vlvIndex_init(struct vlvIndex* p, backend *be, struct vlvSearch* pSearch, const Slapi_Entry *e)
{
    struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
    char *filename= NULL;

    if (NULL == p)
        return;

    /* JCM: Should perform some validation and report errors to the error log */
    /* JCM: Add brackets around the filter if none are there... */
    p->vlv_sortspec= slapi_entry_attr_get_charptr(e,type_vlvSort);
    trimspaces(p->vlv_sortspec);

    p->vlv_name= slapi_entry_attr_get_charptr(e,type_vlvName);
    trimspaces(p->vlv_name);

    p->vlv_search= pSearch;

    /* Convert the textual sort specification into a keylist structure */
    internal_ldap_create_sort_keylist(&(p->vlv_sortkey),p->vlv_sortspec);
    {
        /*
         * For each sort attribute find the appropriate syntax plugin,
         * and if it has a matching rule, create a matching rule indexer object.
         */
        int n;
        for(n=0;p->vlv_sortkey[n]!=NULL;n++);
        p->vlv_mrpb= (Slapi_PBlock**)slapi_ch_calloc(n+1,sizeof(Slapi_PBlock*));
        for(n=0;p->vlv_sortkey[n]!=NULL;n++)
        {
            if(p->vlv_sortkey[n]->sk_matchruleoid!=NULL)
            {
                create_matchrule_indexer(&p->vlv_mrpb[n],p->vlv_sortkey[n]->sk_matchruleoid,p->vlv_sortkey[n]->sk_attrtype);
            }

        }

    }

    /* Create an index filename for the search */
    if(vlvIndex_createfilename(p,&filename))
    {
        p->vlv_filename= slapi_ch_smprintf("%s%s%s",file_prefix,filename,file_suffix);

        /* Create an attrinfo structure */
        p->vlv_attrinfo->ai_type= slapi_ch_smprintf("%s%s",file_prefix,filename);
        p->vlv_attrinfo->ai_indexmask= INDEX_VLV;

        /* Check if the index file actually exists */
        if(li!=NULL)
        {
            vlvIndex_checkforindex(p, be);
        }
        p->vlv_lastchecked= current_time();
    }
    slapi_ch_free((void**)&filename);
}

/*
 * Determine how many {key,data} pairs there are in the VLV Index.
 * We only work out the length of the index once, then we cache
 * it and maintain it.
 */
PRUint32
vlvIndex_get_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
{
    if (NULL == p)
        return 0;

    if(!p->vlv_indexlength_cached)
    {
        DBC *dbc = NULL;
        DB_TXN *db_txn = NULL;
        int err= 0;
        if (NULL != txn)
        {
            db_txn = txn->back_txn_txn;
        }
        err = db->cursor(db, db_txn, &dbc, 0);
        if(err==0)
        {
            DBT key= {0};
            DBT data= {0};
            key.flags= DB_DBT_MALLOC;
            data.flags= DB_DBT_MALLOC;
            err= dbc->c_get(dbc,&key,&data,DB_LAST);
            if(err==0)
            {
                slapi_ch_free(&(key.data));
                slapi_ch_free(&(data.data));
                err= dbc->c_get(dbc,&key,&data,DB_GET_RECNO);
                if(err==0)
                {
                    PR_Lock(p->vlv_indexlength_lock);
                    p->vlv_indexlength_cached= 1;
                    p->vlv_indexlength= *((db_recno_t*)data.data);
                    PR_Unlock(p->vlv_indexlength_lock);
                    slapi_ch_free(&(data.data));
                }
            }
            dbc->c_close(dbc);
        }
        else
        {
            /* couldn't get cursor??? */
        }
    }
    return p->vlv_indexlength;
}

/*
 * Increment the index length count.
 * We keep track of the index length for efficiency.
 */
void
vlvIndex_increment_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
{
    if (NULL == p)
        return;

    if(p->vlv_indexlength_cached)
    {
        PR_Lock(p->vlv_indexlength_lock);
        p->vlv_indexlength++;
        PR_Unlock(p->vlv_indexlength_lock);
    }
    else
    {
        p->vlv_indexlength= vlvIndex_get_indexlength(p, db, txn);
    }
}

/*
 * Decrement the index length count.
 * We keep track of the index length for efficiency.
 */
void
vlvIndex_decrement_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
{
    if (NULL == p)
        return;

    if(p->vlv_indexlength_cached)
    {
        /* jcm: Check for underflow? */
        PR_Lock(p->vlv_indexlength_lock);
        p->vlv_indexlength--;
        PR_Unlock(p->vlv_indexlength_lock);
    }
    else
    {
        p->vlv_indexlength= vlvIndex_get_indexlength(p, db, txn);
    }
}

/*
 * Increment the usage counter
 */
void
vlvIndex_incrementUsage(struct vlvIndex* p)
{
    if (NULL == p)
        return;
    p->vlv_uses++;
}

/*
 * Get the filename of the index.
 */
const char *
vlvIndex_filename(const struct vlvIndex* p)
{
    if (NULL == p)
        return NULL;
    return p->vlv_filename;
}

/*
 * Check if the index is available.
 */
int vlvIndex_enabled(const struct vlvIndex* p)
{
    if (NULL == p)
        return 0;
    return p->vlv_enabled;
}

int vlvIndex_online(const struct vlvIndex *p)
{
    if (NULL == p)
        return 0;
    return p->vlv_online;
}

void vlvIndex_go_offline(struct vlvIndex *p, backend *be)
{
    if (NULL == p)
        return;
    p->vlv_online = 0;
    p->vlv_enabled = 0;
    p->vlv_indexlength = 0;
    p->vlv_attrinfo->ai_indexmask |= INDEX_OFFLINE;
    dblayer_erase_index_file_nolock(be, p->vlv_attrinfo, 1 /* chkpt if not busy */);
}

void vlvIndex_go_online(struct vlvIndex *p, backend *be)
{
    if (NULL == p)
        return;
    p->vlv_attrinfo->ai_indexmask &= ~INDEX_OFFLINE;
    p->vlv_online = 1;
    vlvIndex_checkforindex(p, be);
}


/*
 * Access Control Check to see if the client is allowed to use this VLV Index.
 */
int
vlvIndex_accessallowed(struct vlvIndex *p, Slapi_PBlock *pb)
{
    if (NULL == p)
        return 0;
    return vlvSearch_accessallowed(p->vlv_search, pb);
}

const Slapi_DN *vlvIndex_getBase(struct vlvIndex* p)
{
    if (NULL == p)
        return NULL;
    return vlvSearch_getBase(p->vlv_search);
}

int vlvIndex_getScope(struct vlvIndex* p)
{
    if (NULL == p)
        return 0;
    return vlvSearch_getScope(p->vlv_search);
}

Slapi_Filter *vlvIndex_getFilter(struct vlvIndex* p)
{
    if (NULL == p)
        return NULL;
    return vlvSearch_getFilter(p->vlv_search);
}

const char *vlvIndex_getName(struct vlvIndex* p)
{
    if (NULL == p)
        return NULL;
    return p->vlv_name;
}

/*
 *  JCM: Could also match reverse sense of index and use in reverse.
 */
static int
vlvIndex_equal(const struct vlvIndex* p1, const sort_spec* sort_control)
{
    int r= 1;
    const sort_spec *t1= sort_control;
    LDAPsortkey *t2= p1->vlv_sortkey[0];
    int n= 1;
    for(;t1!=NULL && t2!=NULL && r;t1= t1->next,t2=p1->vlv_sortkey[n],n++)
    {
        r= (t1->order && t2->sk_reverseorder) || (!t1->order && !t2->sk_reverseorder);
        if(r) r= (strcasecmp(t1->type, t2->sk_attrtype)==0);
        if(r)
        {
            if(t1->matchrule==NULL && t2->sk_matchruleoid==NULL)
            {
                r= 1;
            }
            else if(t1->matchrule!=NULL && t2->sk_matchruleoid!=NULL)
            {
                r= (strcasecmp(t1->matchrule, t2->sk_matchruleoid)==0);
            }
            else
            {
                r= 0;
            }
        }
    }
    if(r) r= (t1==NULL && t2==NULL);
    return r;
}

/*
 * Check if the index file actually exists,
 * and set vlv_enabled appropriately
 */
static void
vlvIndex_checkforindex(struct vlvIndex* p, backend *be)
{
    DB *db = NULL;

    /* if the vlv index is offline (being generated), don't even look */
    if (! p->vlv_online)
        return;

    if (dblayer_get_index_file(be, p->vlv_attrinfo, &db, 0) == 0) {
        p->vlv_enabled = 1;
        dblayer_release_index_file( be, p->vlv_attrinfo, db );
    } else {
        p->vlv_enabled = 0;
    }
}

int vlvIndex_isVlvIndexEntry(Slapi_Entry *e)
{
    return slapi_entry_attr_hasvalue(e, "objectclass", "vlvindex");
}

/*
 * Create the filename for the index.
 * Extract all the alphanumeric characters from the descriptive name.
 * Convert to all lower case.
 */
static int
vlvIndex_createfilename(struct vlvIndex* pIndex, char **ppc)
{
    int filenameValid= 1;
    unsigned int i;
    char *p, *filename;
    filename= slapi_ch_malloc(strlen(pIndex->vlv_name) + 1);
    p= filename;
    for(i=0;i<strlen(pIndex->vlv_name);i++)
    {
        if(isalnum(pIndex->vlv_name[i]))
        {
            *p= TOLOWER( pIndex->vlv_name[i] );
            p++;
        }
    }
    *p= '\0';
    if(strlen(filename)==0)
    {
        LDAPDebug( LDAP_DEBUG_ANY, "Couldn't generate valid filename from Virtual List View Index Name (%s).  Need some alphabetical characters.\n", pIndex->vlv_name, 0, 0);
        filenameValid= 0;
    }
    /* JCM: Check if this file clashes with another VLV Index filename */
    *ppc= filename;
    return filenameValid;
}

int
vlv_isvlv(char *filename)
{
    if (0 == strncmp(filename, file_prefix, 4))
        return 1;
    return 0;
}

void
internal_ldap_free_sort_keylist(LDAPsortkey **sortKeyList)
{
#if defined(USE_OPENLDAP)
    ldap_free_sort_keylist((LDAPSortKey **)sortKeyList);
#else
    ldap_free_sort_keylist(sortKeyList);
#endif
}

int
internal_ldap_create_sort_keylist(LDAPsortkey ***sortKeyList, const char *string_rep)
{
#if defined(USE_OPENLDAP)
    return ldap_create_sort_keylist((LDAPSortKey ***)sortKeyList, (char *)string_rep);
#else
    return ldap_create_sort_keylist(sortKeyList, string_rep);
#endif
}
