507 lines
13 KiB
C
507 lines
13 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: props.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.
|
|
|
|
****************************************************************************/
|
|
|
|
/* TODO
|
|
1. remove etags filed
|
|
2. change collection to int
|
|
3. change all time field to long
|
|
*/
|
|
|
|
#include <httpd.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 <apr_dbm.h>
|
|
#include <apr_file_io.h>
|
|
|
|
#include "dav_repos.h"
|
|
#include "dbms.h"
|
|
#include "util.h"
|
|
|
|
|
|
struct dav_namespace_map
|
|
{
|
|
int *ns_map;
|
|
};
|
|
|
|
|
|
struct dav_db
|
|
{
|
|
apr_pool_t *pool;
|
|
dav_repos_resource *mdb_r;
|
|
|
|
/* when used as a property database: */
|
|
int version; /* *minor* version of this db */
|
|
|
|
dav_buffer ns_table; /* table of namespace URIs */
|
|
short ns_count; /* number of entries in table */
|
|
int ns_table_dirty; /* ns_table was modified */
|
|
apr_hash_t *uri_index;
|
|
|
|
dav_buffer wb_key; /* work buffer for dav_key */
|
|
|
|
apr_datum_t iter; /* iteration key */
|
|
};
|
|
|
|
static dav_error *dav_repos_db_open(apr_pool_t * p,
|
|
const dav_resource * resource, int ro,
|
|
dav_db ** pdb)
|
|
{
|
|
DBG0("dav_repos_db_open: ");
|
|
// 2013.08.22 dadamin
|
|
// 디렉토리 경우 유일한 값을 추출할 수 없어서 속성 변경 불가능 하도록함
|
|
if(resource && resource->info)
|
|
{
|
|
dav_resource_private *ctx = resource->info;
|
|
if(ctx->db_r)
|
|
{
|
|
if(ctx->db_r->m_resource_type)
|
|
{
|
|
*pdb = NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
dav_db *db = apr_pcalloc(p, sizeof(*db));
|
|
|
|
TRACE();
|
|
/* Set some values for DB */
|
|
db->pool = p;
|
|
db->mdb_r = (dav_repos_resource *) resource->info->db_r;
|
|
*pdb = db;
|
|
|
|
db->uri_index = apr_hash_make(p);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void dav_repos_db_close(dav_db * db)
|
|
{
|
|
TRACE();
|
|
}
|
|
|
|
static dav_error *dav_repos_db_define_namespaces(dav_db * db,
|
|
dav_xmlns_info * xi)
|
|
{
|
|
const char *ns, *xmlns;
|
|
dav_repos_property *dead_prop;
|
|
dav_repos_resource *dbr = db->mdb_r;
|
|
|
|
DBG0("dav_repos_db_define_namespaces: ");
|
|
TRACE();
|
|
|
|
/* Null lock */
|
|
if (dbr == NULL)
|
|
return NULL;
|
|
|
|
// 2013.08.22 dadamin
|
|
/* Check all used name space in this resource */
|
|
for (dead_prop = dbr->pr; dead_prop; dead_prop = dead_prop->next)
|
|
{
|
|
ns = dbms_get_ns(dbr, dead_prop->m_ns_id);
|
|
if (ns && strlen(ns))
|
|
{ /* not nullns */
|
|
xmlns = apr_psprintf(dbr->p, "ns%ld", dead_prop->m_ns_id);
|
|
dav_xmlns_add(xi, xmlns, ns);
|
|
}
|
|
}
|
|
|
|
/* ### we don't have any other possible namespaces right now. */
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static dav_error *dav_repos_db_output_value(dav_db * db,
|
|
const dav_prop_name * name,
|
|
dav_xmlns_info * xi,
|
|
apr_text_header * phdr,
|
|
int *found)
|
|
{
|
|
const char *s = NULL;
|
|
const char *val;
|
|
|
|
/* Get resource records */
|
|
dav_repos_resource *dbr = db->mdb_r;
|
|
apr_pool_t *pool = db->pool;
|
|
|
|
DBG1("dav_repos_db_output_value: name:%s", name->name);
|
|
|
|
TRACE();
|
|
|
|
if (dbr == NULL) {
|
|
*found = 0;
|
|
return NULL;
|
|
}
|
|
|
|
DBG2("OUT %s [] %s\n", name->ns, name->name);
|
|
|
|
if (!strcmp(name->ns, "DAV:")) {
|
|
val = apr_hash_get(dbr->lpr_hash, name->name, APR_HASH_KEY_STRING);
|
|
if (val) {
|
|
s = apr_psprintf(pool, "<D:%s>%s</D:%s>" DEBUG_CR,
|
|
name->name, val, name->name);
|
|
}
|
|
// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
|
|
else if (!strcmp(name->name, "lockdiscovery") ) {
|
|
s = apr_psprintf(pool, "<D:%s></D:%s>" DEBUG_CR,
|
|
name->name, name->name);
|
|
}
|
|
else if (!strcmp(name->name, "supportedlock")) {
|
|
|
|
}
|
|
}
|
|
|
|
DBG2("OUT2 %s [] %s\n", name->ns, name->name);
|
|
|
|
if (s == NULL) { /* Dead props or Not found */
|
|
/* Get the key */
|
|
const char *key =
|
|
dav_repos_build_ns_name_key(name->ns, name->name, pool);
|
|
|
|
/* Get the dead prop */
|
|
dav_repos_property *dead_prop =
|
|
apr_hash_get(dbr->pr_hash, key, APR_HASH_KEY_STRING);
|
|
|
|
if (dead_prop != NULL) {
|
|
/* Null ns */
|
|
if (strlen(name->ns) == 0 || !strcmp(name->ns, " ")) {
|
|
s = apr_psprintf(pool, "<%s xmlns=\"\">%s</%s>" DEBUG_CR,
|
|
name->name, dead_prop->m_propvalue, name->name);
|
|
} else { /* Not null ns */
|
|
s = apr_psprintf(pool, "<ns%ld:%s>%s</ns%ld:%s>" DEBUG_CR,
|
|
dead_prop->m_ns_id, name->name,
|
|
dead_prop->m_propvalue, dead_prop->m_ns_id,
|
|
name->name);
|
|
}
|
|
} else {
|
|
// 2013.08.27 dadamin
|
|
// SolDirve PROPFIND 시 네임스페이스 사용하기 때문에
|
|
// 해당 파일(디렉토리 경우 소속된 모든 정보)에 대해서
|
|
// RCDB 네임스페이스 관련 조회 발생으로 응답 성능 저하가
|
|
// 발생함으로 opendav 지정된(DAV:)와 기본 속성이 외에 값에
|
|
// 대해서 오퍼레이션되지 않게함
|
|
#ifdef __USE_NAMESPACE__
|
|
// 2013.08.22 dadamin
|
|
// t_dav_resource 없는 property에 대해서 조회
|
|
dav_repos_property *pr = apr_palloc(db->pool, sizeof(*pr));
|
|
|
|
// Let's fill fill pr
|
|
pr->m_ns_id = dbms_get_ns_id(db->mdb_r, name->ns);
|
|
pr->m_propname = name->name;
|
|
|
|
if(dbms_get_dead_property(dbr, pr) == 0)
|
|
{
|
|
dbr->pr = pr;
|
|
s = apr_psprintf(pool, "<ns%ld:%s>%s</ns%ld:%s>" DEBUG_CR,
|
|
pr->m_ns_id, pr->m_propname,
|
|
pr->m_propvalue, pr->m_ns_id,
|
|
pr->m_propname);
|
|
|
|
// set namespace
|
|
// 해당 네임스페이스 존재하지 않을 시 PROPFIND에서
|
|
// 올바른 네임스페이스로 리턴되지 않는 것을 방지하기 위해서
|
|
// 관련 네임스페이스를 헤쉬테이블에 저장함
|
|
dbms_set_namespace(dbr, pr, name->ns);
|
|
}
|
|
else
|
|
#endif // __USE_NAMESPACE__
|
|
{
|
|
DBG1("NOFPUND [%s]\n", key);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s) {
|
|
*found = 1;
|
|
apr_text_append(pool, phdr, s);
|
|
}
|
|
else {
|
|
*found = 0;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static dav_error *dav_repos_db_map_namespaces(dav_db * db,
|
|
const apr_array_header_t *
|
|
namespaces,
|
|
dav_namespace_map ** mapping)
|
|
{
|
|
|
|
dav_namespace_map *m = apr_palloc(db->pool, sizeof(*m));
|
|
int i;
|
|
int *pmap;
|
|
const char **puri;
|
|
|
|
TRACE();
|
|
|
|
|
|
/*
|
|
** Iterate over the provided namespaces. If a namespace already appears
|
|
** in our internal map of URI -> ns_id, then store that in the map. If
|
|
** we don't know the namespace yet, then add it to the map and to our
|
|
** table of known namespaces.
|
|
*/
|
|
pmap = apr_palloc(db->pool, namespaces->nelts * sizeof(*pmap));
|
|
for (i = namespaces->nelts, puri = (const char **) namespaces->elts;
|
|
i-- > 0; ++puri, ++pmap) {
|
|
|
|
const char *uri = *puri;
|
|
apr_size_t uri_len = strlen(uri);
|
|
long ns_id = (long) apr_hash_get(db->uri_index, uri, uri_len);
|
|
|
|
if (ns_id == 0) {
|
|
dav_check_bufsize(db->pool, &db->ns_table, uri_len + 1);
|
|
memcpy(db->ns_table.buf + db->ns_table.cur_len, uri, uri_len + 1);
|
|
db->ns_table.cur_len += uri_len + 1;
|
|
|
|
/* copy the uri in case the passed-in namespaces changes in
|
|
some way. */
|
|
apr_hash_set(db->uri_index, apr_pstrdup(db->pool, uri), uri_len,
|
|
(void *) ((long)(db->ns_count + 1)));
|
|
|
|
db->ns_table_dirty = 1;
|
|
|
|
*pmap = db->ns_count++;
|
|
}
|
|
else {
|
|
*pmap = ns_id - 1;
|
|
}
|
|
}
|
|
|
|
m->ns_map = pmap;
|
|
*mapping = m;
|
|
return NULL;
|
|
}
|
|
|
|
static dav_error *dav_repos_db_store(dav_db * db, const dav_prop_name * name,
|
|
const apr_xml_elem * elem,
|
|
dav_namespace_map * mapping)
|
|
{
|
|
size_t l_val;
|
|
#ifdef __OPENDAV_DEBUG__
|
|
char *buf;
|
|
#endif // __OPENDAV_DEBUG__
|
|
char *val;
|
|
dav_repos_property *pr = apr_palloc(db->pool, sizeof(*pr));
|
|
|
|
TRACE();
|
|
|
|
// 2013.08.27 dadamin
|
|
// SolDirve PROPFIND 시 네임스페이스 사용하기 때문에
|
|
// 해당 파일(디렉토리 경우 소속된 모든 정보)에 대해서
|
|
// RCDB 네임스페이스 관련 조회 발생으로 응답 성능 저하가
|
|
// 발생함으로 opendav 지정된(DAV:)와 기본 속성이 외에 값에
|
|
// 대해서 오퍼레이션되지 않게함
|
|
#ifndef __USE_NAMESPACE__
|
|
return solbox_dav_new_error(db->pool, HTTP_BAD_REQUEST, 0,
|
|
"Invalid namespace declaration.");
|
|
#endif // !__USE_NAMESPACE__
|
|
|
|
/* Litmus test <aa xmlns=""> case */
|
|
/*
|
|
if(name->ns==NULL || strlen(name->ns)==0) {
|
|
return solbox_dav_new_error(db->pool, HTTP_BAD_REQUEST, 0,
|
|
"Invalid namespace declaration.");
|
|
}
|
|
*/
|
|
/* quote all the values in the element */
|
|
/* ### be nice to do this without affecting the element itself */
|
|
/* ### of course, the cast indicates Badness is occurring here */
|
|
apr_xml_quote_elem(db->pool, (apr_xml_elem *) elem);
|
|
|
|
/* generate a text blob for the xml:lang plus the contents */
|
|
apr_xml_to_text(db->pool, elem, APR_XML_X2T_LANG_INNER, NULL,
|
|
mapping->ns_map, (const char **) &val, &l_val);
|
|
/* Warning : val[0] value is NULL, so you should use 'val+1' */
|
|
|
|
/* Let's fill fill pr */
|
|
pr->m_ns_id = dbms_get_ns_id(db->mdb_r, name->ns);
|
|
pr->m_propname = name->name;
|
|
pr->m_propvalue = val + 1;
|
|
|
|
if (dbms_set_dead_property(db->mdb_r, pr) != 0) {
|
|
return solbox_dav_new_error(db->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
|
|
"An error occurred while getting a dead property.");
|
|
}
|
|
|
|
#ifdef __OPENDAV_DEBUG__
|
|
buf = apr_psprintf(db->pool, "I want to save [%ld:%s] [%s]",
|
|
pr->m_ns_id, pr->m_propname, pr->m_propvalue);
|
|
|
|
DBG0(buf);
|
|
#endif // __OPENDAV_DEBUG__
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static dav_error *dav_repos_db_remove(dav_db * db, const dav_prop_name * name)
|
|
{
|
|
// 2013.08.27 dadamin
|
|
// SolDirve PROPFIND 시 네임스페이스 사용하기 때문에
|
|
// 해당 파일(디렉토리 경우 소속된 모든 정보)에 대해서
|
|
// RCDB 네임스페이스 관련 조회 발생으로 응답 성능 저하가
|
|
// 발생함으로 opendav 지정된(DAV:)와 기본 속성이 외에 값에
|
|
// 대해서 오퍼레이션되지 않게함
|
|
#ifndef __USE_NAMESPACE__
|
|
return solbox_dav_new_error(db->pool, HTTP_BAD_REQUEST, 0,
|
|
"Invalid namespace declaration.");
|
|
#endif // !__USE_NAMESPACE__
|
|
|
|
#ifdef __OPENDAV_DEBUG__
|
|
char *buf;
|
|
#endif // __OPENDAV_DEBUG__
|
|
dav_repos_property *pr = apr_palloc(db->pool, sizeof(*pr));
|
|
|
|
pr->m_ns_id = dbms_get_ns_id(db->mdb_r, name->ns);
|
|
pr->m_propname = name->name;
|
|
|
|
if (dbms_del_dead_property(db->mdb_r, pr) != 0) {
|
|
return solbox_dav_new_error(db->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
|
|
"An error occurred while deleting a dead property.");
|
|
}
|
|
|
|
TRACE();
|
|
#ifdef __OPENDAV_DEBUG__
|
|
buf = apr_psprintf(db->pool, "I want to delete [%s] ", pr->m_propname);
|
|
DBG0(buf);
|
|
#endif // __OPENDAV_DEBUG__
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int dav_repos_db_exists(dav_db * db, const dav_prop_name * name)
|
|
{
|
|
/* Get resource records */
|
|
dav_repos_resource *dbr = db->mdb_r;
|
|
|
|
/* Build the key */
|
|
const char *key;
|
|
|
|
TRACE();
|
|
|
|
if (dbr == NULL)
|
|
return 0;
|
|
|
|
key = dav_repos_build_ns_name_key(name->ns, name->name, dbr->p);
|
|
|
|
/* Get the the prop */
|
|
if (apr_hash_get(dbr->pr_hash, key, APR_HASH_KEY_STRING))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static dav_error *dav_repos_db_first_name(dav_db * db, dav_prop_name * pname)
|
|
{
|
|
const char *key = NULL;
|
|
apr_ssize_t klen;
|
|
dav_repos_property *pr;
|
|
|
|
/* Get resource records */
|
|
dav_repos_resource *dbr = db->mdb_r;
|
|
|
|
TRACE();
|
|
|
|
if (dbr == NULL) {
|
|
pname->ns = NULL;
|
|
pname->name = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
dbr->pr_hash_index = apr_hash_first(db->pool, dbr->pr_hash);
|
|
if (dbr->pr_hash_index == NULL) {
|
|
pname->ns = NULL;
|
|
pname->name = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
apr_hash_this(dbr->pr_hash_index, (void *) &key, &klen, (void *) &pr);
|
|
|
|
/* Get ns and name from the key */
|
|
dav_repos_get_ns_name_key(key, pname, db->pool);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static dav_error *dav_repos_db_next_name(dav_db * db, dav_prop_name * pname)
|
|
{
|
|
const char *key = NULL;
|
|
apr_ssize_t klen;
|
|
dav_repos_property *pr;
|
|
|
|
/* Get resource records */
|
|
dav_repos_resource *dbr = db->mdb_r;
|
|
|
|
TRACE();
|
|
pname->ns = NULL;
|
|
pname->name = NULL;
|
|
|
|
if (dbr == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Run first_name first */
|
|
if (dbr->pr_hash_index == NULL)
|
|
return NULL;
|
|
|
|
dbr->pr_hash_index = apr_hash_next(dbr->pr_hash_index);
|
|
|
|
/* End of hash */
|
|
if (dbr->pr_hash_index == NULL)
|
|
return NULL;
|
|
|
|
apr_hash_this(dbr->pr_hash_index, (void *) &key, &klen, (void *) &pr);
|
|
|
|
/* Get ns and name from the key */
|
|
dav_repos_get_ns_name_key(key, pname, db->pool);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static dav_error *dav_repos_db_get_rollback(dav_db * db,
|
|
const dav_prop_name * name,
|
|
dav_deadprop_rollback **
|
|
prollback)
|
|
{
|
|
TRACE();
|
|
return NULL;
|
|
}
|
|
|
|
static dav_error *dav_repos_db_apply_rollback(dav_db * db,
|
|
dav_deadprop_rollback *
|
|
rollback)
|
|
{
|
|
TRACE();
|
|
return NULL;
|
|
}
|
|
|
|
const dav_hooks_propdb dav_repos_hooks_propdb = {
|
|
dav_repos_db_open,
|
|
dav_repos_db_close,
|
|
dav_repos_db_define_namespaces,
|
|
dav_repos_db_output_value,
|
|
dav_repos_db_map_namespaces,
|
|
dav_repos_db_store,
|
|
dav_repos_db_remove,
|
|
dav_repos_db_exists,
|
|
dav_repos_db_first_name,
|
|
dav_repos_db_next_name,
|
|
dav_repos_db_get_rollback,
|
|
dav_repos_db_apply_rollback,
|
|
};
|