/****************************************************************************

   mod_dav_pgsql (DASL) for apache 2.X
   CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
   Author : sean kim (sean@solutionbox.co.kr)

   $Id: search.c,v 1.2 2006/09/20 09:22:59 sean Exp $

   Redistribution and use in source and binary forms, with or with out
   modification, are not permitted in outside of SolutionBox Inc.

 ****************************************************************************/

/*
** DASL SEARCH method
*/

#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_core.h>		/* for ap_construct_url */
#include <apr_strings.h>
#include <apr_hash.h>

#include "dav_repos.h"
#include "dbms.h"
#include "util.h"

/**
 * Used to hold the mappings from dead properties to the temporary
 * table aliases they should be associated with.
 */
struct dead_prop_list
{
    char *prop;
    struct dead_prop_list *next;
};

typedef struct
{
    char *query;
    char *where_cond;
    char *orderby;
    char *limit;
    char *err_msg;
}
search_ctx;

typedef struct dead_prop_list dead_prop_list;

int build_query(request_rec * r, apr_xml_doc * doc, search_ctx * sctx,
		dav_repos_resource * search_result);
int build_xml_response(dav_repos_resource * query_results,
		       dav_response ** res);
int parse_select(request_rec * r, search_ctx * sctx,
		 apr_xml_elem * select_elem, dead_prop_list ** sel_list);
int parse_from(request_rec * r, dav_repos_resource * search_result,
	       search_ctx * sctx,
	       apr_xml_elem * from_elem, dead_prop_list * sel_list,
	       dead_prop_list * where_list);

int parse_where(request_rec * r, search_ctx * sctx, apr_xml_elem * where_elem,
		dead_prop_list ** where_list);
int parse_orderby(request_rec * r, search_ctx * sctx,
		  apr_xml_elem * orderby_elem);
int parse_limit(request_rec * r, search_ctx * sctx,
		apr_xml_elem * limit_elem);
int is_dead_prop(const char *prop);

static dav_error *dav_repos_set_option_head(request_rec * r)
{

    TRACE();

    /*DASL: <DAV:basicsearch>
     * DASL: <http://foo.bar.com/syntax1> 
     * DASL: <http://akuma.com/syntax2>
     */
    /*
       apr_table_addn(r->headers_out, "DASL",  "<http://dav.cse.ucsc.edu/syntax1>");
       apr_table_addn(r->headers_out, "DASL",  "<http://foo.bar.com/syntax2>");
     */
    apr_table_addn(r->headers_out, "DASL", "<DAV:basicsearch>");
    return NULL;
}

/**
 * Handles the DASL SEARCH method
 * @param r The method request record
 * @return The HTTP response code
 */
static dav_error *dav_repos_search_resource(request_rec * r,
					    dav_response ** res)
{
	int result;
	apr_xml_doc *doc = NULL;
	apr_xml_doc *response_body = NULL;
	dav_repos_resource *search_result = NULL;
	search_ctx *sctx = apr_pcalloc(r->pool, sizeof(*sctx));

	/* FIXME : 
	*/
	sctx->err_msg = "unknown: Plesae specify your error!";
	TRACE();

	if ((result = ap_xml_parse_input(r, &doc)) != 0) {
		return solbox_dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
				"ap_xml_parse_input error");
	}

	/*initialize search result structure */
	search_result = (dav_repos_resource *) apr_pcalloc(r->pool,
			sizeof(*search_result));
	search_result->p = r->pool;

	/* We need XML doc */
	if (doc == NULL || doc->root == NULL) {
		return solbox_dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, "doc is NULL");
	}

	/* Build query */
	if ((result = build_query(r, doc, sctx,
					search_result)) != HTTP_OK) {

		return solbox_dav_new_error(r->pool, result, 0, sctx->err_msg);
	}

	/* Seek DBMS */
	if ((dbms_search(sctx->query, search_result)) != 0) {
		return solbox_dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
				"DBMS_SEARCH error");
	}

	/** FIXME : It might be too slow
	*/
	/* Get properties in a form of property link for every resource */
	dbms_fill_dead_property(search_result);

	/* Make result */
	response_body = (apr_xml_doc *) apr_pcalloc(r->pool,
			sizeof(*response_body));

	if ((result =
				build_xml_response(search_result, res) != HTTP_OK)) {
		return solbox_dav_new_error(r->pool, HTTP_BAD_REQUEST, 0,
				"An error occurred while building XML response!");
	}

    return NULL;
}


/**
 * Builds the SQL query from the XML body of the request
 * @param r The method request record
 * @param doc The XML doc being parsed
 * @param query The SQL query being built
 * @param search_result The result record(s) returned from the DB query
 * @param db_handle Pointer to the DB
 * @return The HTTP response code
 */
int build_query(request_rec * r, apr_xml_doc * doc, search_ctx * sctx,
		dav_repos_resource * search_result)
{
    int result = -23;
    apr_xml_elem *cur_elem = NULL;
    apr_xml_elem *from_elem = NULL;
    apr_xml_elem *select_elem = NULL;
    apr_xml_elem *where_elem = NULL;
    apr_xml_elem *order_elem = NULL;
    apr_xml_elem *limit_elem = NULL;
    dead_prop_list *sel_list = NULL;
    dead_prop_list *where_list = NULL;

    TRACE();

    /* basicsearch */
    cur_elem = dav_find_child(doc->root, "basicsearch");
    if (cur_elem == NULL) {
	sctx->err_msg =
	    apr_pstrdup(r->pool,
			"Requested search grammar not supported."
			" We support only <basicsearch>");
	return HTTP_BAD_REQUEST;
    }

    /* initialize the query */
    sctx->query = apr_pstrdup(r->pool, "SELECT distinct ");

    /* Find elements */
    select_elem = dav_find_child(cur_elem, "select");
    from_elem = dav_find_child(cur_elem, "from");
    where_elem = dav_find_child(cur_elem, "where");
    order_elem = dav_find_child(cur_elem, "orderby");
    limit_elem = dav_find_child(cur_elem, "limit");

    /* process the select portion */
    if (select_elem == NULL) {
	sctx->err_msg =
	    apr_pstrdup(r->pool, "We don't have <select> element");
	return HTTP_BAD_REQUEST;
    }
    /* Parse select element */
    if ((result = parse_select(r, sctx, select_elem, &sel_list)) != HTTP_OK) {
	return result;
    }

    /* process the from portion */
    if (from_elem == NULL) {
	sctx->err_msg = apr_pstrdup(r->pool, "We don't have <from> element");
	return HTTP_BAD_REQUEST;
    }

    /* process the from portion */
    if (where_elem == NULL || where_elem->first_child == NULL) {
	sctx->err_msg =
	    apr_pstrdup(r->pool, "We don't have <where> element or child");
	return HTTP_BAD_REQUEST;
    }

    if ((result = parse_where(r, sctx, where_elem->first_child,
			      &where_list)) != HTTP_OK) {
	return result;
    }

    /* FIXME : What is this?? */
    sctx->where_cond = apr_pstrcat(r->pool, " AND ", sctx->where_cond, NULL);

    if (order_elem
	&& (result = parse_orderby(r, sctx, order_elem)) != HTTP_OK) {
	return result;
    }

    if (limit_elem && limit_elem->first_child) {
	const char *limit_cdata =
	    dav_xml_get_cdata(limit_elem->first_child, r->pool, 1);
	sctx->limit = apr_pstrcat(r->pool, " LIMIT ", limit_cdata, NULL);
    }

    if ((result = parse_from(r, search_result, sctx, from_elem,
			     sel_list, where_list)) != HTTP_OK) {
	return result;
    }

    /* Combine all queries */
    sctx->query = apr_pstrcat(r->pool, sctx->query, sctx->where_cond,
			      sctx->orderby, sctx->limit, NULL);

    DBG0(sctx->query);

    return HTTP_OK;
}


/**
 * Processes the SELECT portion of the XML query
 * @param r The method request record
 * @param query The SQL query being constructed
 * @param cur_elem The current XML element of WHERE clause being processed
 * @param assoc The association list of dead props and their tables
 * @return The HTTP response code
 */
int parse_select(request_rec * r, search_ctx * sctx, apr_xml_elem * cur_elem,
		 dead_prop_list ** dead_props)
{
    dead_prop_list *tmp = NULL;

    TRACE();

    if (cur_elem->first_child) {
	cur_elem = cur_elem->first_child;
    }
    else {
	sctx->err_msg = apr_pstrdup(r->pool, "PARSE_SELECT 1");
    }

    if (cur_elem->name == NULL) {
	sctx->err_msg = apr_pstrdup(r->pool, "No select element name");
    }

    if (apr_strnatcmp(cur_elem->name, "allprop") == 0) {
	sctx->query = apr_pstrcat(r->pool, sctx->query,
				  "dasl_resource.serialno, ",
				  "dasl_resource.URI, ",
				  "dasl_resource.creationdate, ",
				  "dasl_resource.displayname, ",
				  "dasl_resource.getcontentlanguage, ",
				  "dasl_resource.getcontenttype, ",
				  "dasl_resource.getcontentlength, ",
				  "dasl_resource.getetag, ",
				  "dasl_resource.getlastmodified, ",
				  "dasl_resource.lockdiscovery, ",
				  "dasl_resource.resourcetype, ",
				  "dasl_resource.source, ",
				  "dasl_resource.supportedlock ", NULL);

	return HTTP_OK;
    }

    if (apr_strnatcmp(cur_elem->name, "prop") == 0) {
	sctx->query = apr_pstrcat(r->pool, sctx->query,
				  "dasl_resource.serialno, dasl_resource.URI",
				  NULL);
	/* No prop information */
	if (!cur_elem->first_child || cur_elem->first_child->first_child) {
	    sctx->err_msg = apr_pstrdup(r->pool, "No prop information");
	    return HTTP_BAD_REQUEST;
	}

	/* gather up the properties to search for */

	for (cur_elem = cur_elem->first_child->first_child;
	     cur_elem; cur_elem = cur_elem->next) {
	    if ((is_dead_prop(cur_elem->name) == 23)) {
		tmp = (dead_prop_list *) apr_pcalloc(r->pool, sizeof(*tmp));
		tmp->next = *dead_props;
		tmp->prop = (char *) apr_pstrdup(r->pool, cur_elem->name);
		*dead_props = tmp;
	    }
	    else {
		sctx->query = apr_pstrcat(r->pool, sctx->query,
					  ", dasl_resource.", cur_elem->name,
					  NULL);
	    }

	}

	return HTTP_OK;
    }

    /* Unkonw element name */
    sctx->err_msg = apr_psprintf(r->pool, "Unkonw element name(%s) in select."
				 "Use <allprop> or <prop>", cur_elem->name);

    return HTTP_BAD_REQUEST;
}


/**
 * Processes the ORDER BY portion of the XML query
 * @param r The method request record
 * @param query The SQL query being constructed
 * @param cur_elem The current XML element of WHERE clause being processed
 * @return The HTTP response code 
 */
int parse_from(request_rec * r, dav_repos_resource * search_result,
	       search_ctx * sctx,
	       apr_xml_elem * from_elem, dead_prop_list * sel_list,
	       dead_prop_list * where_list)
{
    int result = -1;
    apr_xml_elem *cur_elem = NULL;
    const char *href = NULL;
    const char *depth = NULL;
    dead_prop_list *tmp = NULL;

    TRACE();

    if (sel_list) {
	/* PK */
	sctx->query = apr_pstrcat(r->pool, sctx->query, ", t.name, t.value",
				  " FROM dasl_resource left join dasl_property t USING (serialno) ",
				  NULL);
    }
    else {
	sctx->query =
	    apr_pstrcat(r->pool, sctx->query, " FROM dasl_resource", NULL);
    }

    if ((tmp = where_list)) {
	while (tmp) {
	    /* PK */
	    sctx->query =
		apr_pstrcat(r->pool, sctx->query, " left join dasl_property ",
			    tmp->prop, "_t", " USING (serialno) ", NULL);
	    tmp = tmp->next;
	}
    }
    sctx->query = apr_pstrcat(r->pool, sctx->query, " ", NULL);

    /* ignore the SCOPE elem */
    if (from_elem->first_child && from_elem->first_child->first_child) {
	cur_elem = from_elem->first_child->first_child;
    }
    else {
	sctx->err_msg = apr_pstrdup(r->pool, "SCOPE");
	return HTTP_BAD_REQUEST;
    }

    /* Read Href CDATA */
    href = dav_xml_get_cdata(cur_elem, r->pool, 1);
    if (href == NULL || strlen(href) == 0) {
	sctx->err_msg = apr_pstrdup(r->pool, "NO Href");
	return HTTP_BAD_REQUEST;
    }

    /* If it is full URL, we need only path */
    if (ap_is_url(href)) {
	apr_uri_t uptr;

	if (apr_uri_parse(r->pool, href, &uptr) != APR_SUCCESS ||
	    uptr.path == NULL) {
	    sctx->err_msg = apr_pstrdup(r->pool, "Malformed HREF");
	    return HTTP_BAD_REQUEST;
	}
	search_result->m_uri = apr_pstrdup(r->pool, uptr.path);
    }
    else if (*href == '/') {	/* absolute href */
	search_result->m_uri = apr_pstrdup(r->pool, href);
    }
    else {
	sctx->err_msg =
	    apr_pstrdup(r->pool, "HREF must be full URL or absolute URI");
	return HTTP_BAD_REQUEST;
    }

     /*PK*/
	sctx->query = apr_pstrcat(r->pool, sctx->query, "WHERE (1=1) ", NULL);
    /* PK
       if (where_list) {
       sctx->query = apr_pstrcat(r->pool, sctx->query, "dasl_resource.serialno = ",
       where_list->prop, "_t.serialno ", NULL);
       for (tmp = where_list; tmp; tmp = tmp->next) {
       sctx->query =
       apr_pstrcat(r->pool, sctx->query, "AND dasl_resource.serialno = ",
       tmp->prop, "_t.serialno ", NULL);
       }
       }
     */

    if (sel_list) {
	/* PK
	   if (where_list == NULL) {
	   sctx->query = apr_pstrcat(r->pool, sctx->query,
	   "dasl_resource.serialno = dasl_property.serialno ",
	   "AND dasl_resource.serialno = t.serialno ",
	   NULL);
	   }
	   else {
	   sctx->query = apr_pstrcat(r->pool, sctx->query,
	   "AND dasl_resource.serialno = t.serialno ",
	   NULL);
	   }
	 */

	tmp = sel_list;
	sctx->query = apr_pstrcat(r->pool, sctx->query, "AND (t.name = ",
				  sel_list->prop, NULL);

	for (tmp = sel_list; tmp; tmp = tmp->next) {
	    if ((tmp->prop)) {
		sctx->query =
		    apr_pstrcat(r->pool, sctx->query, " OR t.name = ",
				tmp->prop, NULL);
	    }
	}

	sctx->query = apr_pstrcat(r->pool, sctx->query, ")", NULL);
    }				/* end if(sel_list) */

    /* skip over SCOPE element */
    if (cur_elem->next) {
	cur_elem = cur_elem->next;
    }
    else {
	sctx->err_msg = apr_pstrdup(r->pool, "Closing SCOPE");
	return HTTP_BAD_REQUEST;
    }

    /* get depth */
    depth = dav_xml_get_cdata(cur_elem, r->pool, 1);

    /* Delete last slash */
    dav_repos_no_trail(search_result->m_uri);

    if ((result = dbms_get_resourcetype(search_result)) != 0) {
	if (result == 100) {	/* resource not found */
	    sctx->err_msg = apr_pstrdup(r->pool, "Resource not found");
	    return HTTP_NOT_FOUND;
	}
	else {			/* must be -1: internal server error */
	    sctx->err_msg = apr_pstrdup(r->pool, "BAB DB");
	    return HTTP_INTERNAL_SERVER_ERROR;
	}
    }

    /** FIXME : */
    /*
       search_result->resourcetype = 1;
     */
    /* add "AND" to continue where we left off */
    /*if (sel_list || where_list) {
       sctx->query = apr_pstrcat(r->pool, sctx->query, " AND", NULL);
       }
     */
    sctx->query = apr_pstrcat(r->pool, sctx->query, " AND", NULL);

    /** FIXME : What about the full URL ?? */
    if ((apr_strnatcmp(depth, "0") == 0) ||
	(search_result->m_resource_type == 0)) {
	sctx->query = apr_pstrcat(r->pool, sctx->query, " uri = '",
				  search_result->m_uri, "'", NULL);
    }
    else {
	/* this will be the same whether depth is 1 or infinity */
	sctx->query =
	    apr_pstrcat(r->pool, sctx->query, " uri like '",
			search_result->m_uri, "%'", NULL);

	if (apr_strnatcmp(depth, "1") == 0) {
	    search_result->m_depth = ap_count_dirs(search_result->m_uri);
	    depth = apr_psprintf(r->pool, "%d", search_result->m_depth + 1);
	    sctx->query = apr_pstrcat(r->pool, sctx->query,
				      " AND depth = '", depth, "'", NULL);
	}
    }

    return HTTP_OK;
}


/**
 * Uses a recursive, depth first, in-order approach to construct the
 * WHERE clause of the SQL query. The resulting query is completely
 * parenthesized in order to maintain operator precedence.
 * @param r The method request record
 * @param query The SQL query being constructed
 * @param cur_elem The current XML element of WHERE clause being processed
 * @return The HTTP response code 
 */
int parse_where(request_rec * r, search_ctx * sctx, apr_xml_elem * cur_elem,
		dead_prop_list ** dead_props)
{
    char *op = NULL;
    dead_prop_list *tmp = NULL;

    TRACE();

    op = apr_pstrdup(r->pool, cur_elem->name);
    if (sctx->where_cond) {
	sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond, "(", NULL);
    }
    else {
	sctx->where_cond = apr_pstrdup(r->pool, "(");
    }

    /* To do:
       (1) Add error checking for correct number of arguments. Return HTTP_BAD_REQUEST if
       incorrect...
       (2) Handle case sensitive searches...
     */
    if (apr_strnatcmp(op, "and") == 0) {
	if (cur_elem->first_child && cur_elem->first_child->next) {
	    parse_where(r, sctx, cur_elem->first_child, dead_props);
	    sctx->where_cond =
		apr_pstrcat(r->pool, sctx->where_cond, " AND ", NULL);
	    parse_where(r, sctx, cur_elem->first_child->next, dead_props);
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "or") == 0) {
	if (cur_elem->first_child && cur_elem->first_child->next) {
	    parse_where(r, sctx, cur_elem->first_child, dead_props);
	    sctx->where_cond =
		apr_pstrcat(r->pool, sctx->where_cond, " OR ", NULL);
	    parse_where(r, sctx, cur_elem->first_child->next, dead_props);
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "not") == 0) {
	if (cur_elem->first_child) {
	    sctx->where_cond =
		apr_pstrcat(r->pool, sctx->where_cond, "NOT ", NULL);
	    parse_where(r, sctx, cur_elem->first_child, dead_props);
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "eq") == 0) {
	if (cur_elem->first_child &&
	    cur_elem->first_child->first_child &&
	    cur_elem->first_child->first_child->name &&
	    cur_elem->first_child->next &&
	    cur_elem->first_child->next->first_cdata.first->text) {
	    if ((is_dead_prop(cur_elem->first_child->first_child->name) ==
		 23)) {
		tmp = (dead_prop_list *) apr_pcalloc(r->pool, sizeof(*tmp));
		tmp->next = *dead_props;
		tmp->prop = apr_pstrdup(r->pool,
					cur_elem->first_child->first_child->
					name);
		*dead_props = tmp;
		sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					       tmp->prop, "_t.name = '",
					       tmp->prop, "' AND ", tmp->prop,
					       "_t.value = '",
					       cur_elem->first_child->next->
					       first_cdata.first->text, "' ",
					       NULL);
	    }
	    else {
		sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					       cur_elem->first_child->
					       first_child->name, " = '",
					       cur_elem->first_child->next->
					       first_cdata.first->text, "' ",
					       NULL);
	    }
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "lt") == 0) {
	if (cur_elem->first_child &&
	    cur_elem->first_child->first_child &&
	    cur_elem->first_child->first_child->name &&
	    cur_elem->first_child->next &&
	    cur_elem->first_child->next->first_cdata.first->text) {
	    if ((is_dead_prop(cur_elem->first_child->first_child->name) ==
		 23)) {
		tmp = (dead_prop_list *) apr_pcalloc(r->pool, sizeof(*tmp));
		tmp->next = *dead_props;
		tmp->prop = apr_pstrdup(r->pool,
					cur_elem->first_child->first_child->
					name);
		*dead_props = tmp;
		sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					       tmp->prop, "_t.name = '",
					       tmp->prop, "' AND ", tmp->prop,
					       "_t.value < '",
					       cur_elem->first_child->next->
					       first_cdata.first->text, "' ",
					       NULL);
	    }
	    else {
		sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					       cur_elem->first_child->
					       first_child->name, " < '",
					       cur_elem->first_child->next->
					       first_cdata.first->text, "' ",
					       NULL);
	    }
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "gt") == 0) {
	if (cur_elem->first_child &&
	    cur_elem->first_child->first_child &&
	    cur_elem->first_child->first_child->name &&
	    cur_elem->first_child->next &&
	    cur_elem->first_child->next->first_cdata.first->text) {
	    if ((is_dead_prop(cur_elem->first_child->first_child->name) ==
		 23)) {
		tmp = (dead_prop_list *) apr_pcalloc(r->pool, sizeof(*tmp));
		tmp->next = *dead_props;
		tmp->prop = apr_pstrdup(r->pool,
					cur_elem->first_child->first_child->
					name);
		*dead_props = tmp;
		sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					       tmp->prop, "_t.name = '",
					       tmp->prop, "' AND ", tmp->prop,
					       "_t.value > '",
					       cur_elem->first_child->next->
					       first_cdata.first->text, "' ",
					       NULL);
	    }
	    else {
		sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					       cur_elem->first_child->
					       first_child->name, " > '",
					       cur_elem->first_child->next->
					       first_cdata.first->text, "' ",
					       NULL);
	    }
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "lte") == 0) {
	if (cur_elem->first_child &&
	    cur_elem->first_child->first_child &&
	    cur_elem->first_child->first_child->name &&
	    cur_elem->first_child->next &&
	    cur_elem->first_child->next->first_cdata.first->text) {
	    if ((is_dead_prop((char *) cur_elem->first_child->
			      first_child->name) == 23)) {
		tmp = (dead_prop_list *) apr_pcalloc(r->pool, sizeof(*tmp));
		tmp->next = *dead_props;
		tmp->prop = apr_pstrdup(r->pool,
					cur_elem->first_child->first_child->
					name);
		*dead_props = tmp;
	    }
	    sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					   cur_elem->first_child->
					   first_child->name, " <= '",
					   cur_elem->first_child->next->
					   first_cdata.first->text, "' ",
					   NULL);
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "gte") == 0) {
	if (cur_elem->first_child &&
	    cur_elem->first_child->first_child &&
	    cur_elem->first_child->first_child->name &&
	    cur_elem->first_child->next &&
	    cur_elem->first_child->next->first_cdata.first->text) {
	    if ((is_dead_prop(cur_elem->first_child->first_child->name) ==
		 23)) {
		tmp = (dead_prop_list *) apr_pcalloc(r->pool, sizeof(*tmp));
		tmp->next = *dead_props;
		tmp->prop = apr_pstrdup(r->pool,
					cur_elem->first_child->first_child->
					name);
		*dead_props = tmp;
	    }
	    sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					   cur_elem->first_child->
					   first_child->name, " >= '",
					   cur_elem->first_child->next->
					   first_cdata.first->text, "' ",
					   NULL);
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
    }
    else if (apr_strnatcmp(op, "like") == 0) {
	if (cur_elem->first_child &&
	    cur_elem->first_child->first_child &&
	    cur_elem->first_child->first_child->name &&
	    cur_elem->first_child->next &&
	    cur_elem->first_child->next->first_cdata.first->text) {
	    if ((is_dead_prop(cur_elem->first_child->first_child->name) ==
		 23)) {
		tmp = (dead_prop_list *) apr_pcalloc(r->pool, sizeof(*tmp));
		tmp->next = *dead_props;
		tmp->prop = apr_pstrdup(r->pool,
					cur_elem->first_child->first_child->
					name);
		*dead_props = tmp;
	    }
	    sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond,
					   cur_elem->first_child->
					   first_child->name, " like '",
					   cur_elem->first_child->next->
					   first_cdata.first->text, "'",
					   NULL);
	}
	else {
	    return HTTP_BAD_REQUEST;
	}
	/* According to the spec, the isdefined operator is optional. We'll
	   need to implement contains, however...

	   } else if (apr_strnatcmp(op, "isdefined") == 0) {

	   } else if (apr_strnatcmp(op, "contains") == 0) {
	 */
    }
    sctx->where_cond = apr_pstrcat(r->pool, sctx->where_cond, ")", NULL);

    return HTTP_OK;
}


/**
 * Processes the ORDER BY portion of the XML query
 * @param r The method request record
 * @param query The SQL query being constructed
 * @param cur_elem The current XML element of WHERE clause being processed
 * @return The HTTP response code
 */
int parse_orderby(request_rec * r, search_ctx * sctx,
		  apr_xml_elem * orderby_elem)
{
    apr_xml_elem *cur_elem = NULL;

    TRACE();
    sctx->orderby = apr_pstrdup(r->pool, " ORDER BY ");
    /* iterate through <D:order> elements */
    for (cur_elem = orderby_elem->first_child;
	 cur_elem; cur_elem = cur_elem->next) {
	if (!cur_elem->first_child || !cur_elem->first_child->first_child) {
	    return HTTP_BAD_REQUEST;
	}
	sctx->orderby = apr_pstrcat(r->pool, sctx->orderby,
				    cur_elem->first_child->first_child->
				    name, NULL);

	if (cur_elem->first_child->next) {
	    if (!cur_elem->first_child->next->name) {
		return HTTP_BAD_REQUEST;
	    }

	    if (apr_strnatcmp(cur_elem->first_child->next->name,
			      "descending") == 0) {
		sctx->orderby =
		    apr_pstrcat(r->pool, sctx->orderby, " DESC ", NULL);
	    }
	    else {
		sctx->orderby =
		    apr_pstrcat(r->pool, sctx->orderby, " ASC ", NULL);
	    }
	}
	else {			/* order not specified, use default ordering... */
	    sctx->orderby =
		apr_pstrcat(r->pool, sctx->orderby, " ASC ", NULL);
	}
    }				/* end for */

    return HTTP_OK;
}


/**
 * Tests whether a property is live or dead.
 * @param prop The property to test
 * @return A positive result indicates the property is dead
 */
static const char *dead_prop_names[] = {
    "creationdate",
    "displayname",
    "getcontentlanguage",
    "getcontenttype",
    "getcontentlength",
    "getetag",
    "getlastmodified",
    "lockdiscovery",
    "resourcetype",
    "source",
    "supportedlock",
    "filename",
    "uri",
    "textcontent",
    "istext",
    NULL
};


int is_dead_prop(const char *prop)
{
    int i;

    for (i = 0; dead_prop_names[i]; i++) {
	if (!apr_strnatcmp(prop, dead_prop_names[i])) {
	    return -23;
	}
    }

    return 23;
}


/**
 * Builds the XML body of the response from the SQL query results
 * @param r The method request record
 * @param search_results The result(s) of the SQL query
 */
int build_xml_response(dav_repos_resource * query_results,
		       dav_response ** res)
{
    dav_repos_resource *tmp_resource;
    dav_response *newres = NULL;

    TRACE();

    *res = NULL;
    /*
       ** loop through the dbms_result
       ** and build results
     */

    /* Build name space id hash
     * It will build query_results->ns_id_hash
     */
    dbms_build_ns_id_hash(query_results);

    /* FIXME : First data is root, so We need to skip this
     */
    for (tmp_resource = query_results->next; tmp_resource;
	 tmp_resource = tmp_resource->next) {
	/* need to set pool */
	tmp_resource->p = query_results->p;

	/* Need to set ns_id  */
	tmp_resource->ns_id_hash = query_results->ns_id_hash;

	/* Build live property hash */
	dav_repos_build_lpr_hash(tmp_resource);

	newres = dav_repos_mkresponse(tmp_resource);
	newres->next = *res;
	*res = newres;
    }

    return HTTP_OK;
}

/*
** search hook functions
*/
const dav_hooks_search dav_repos_hooks_search = {
    dav_repos_set_option_head,
    dav_repos_search_resource
};
