921 lines
26 KiB
C
921 lines
26 KiB
C
/****************************************************************************
|
|
|
|
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
|
|
};
|