#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/tree.h>
#include "utils.h"
#include "webdav.h"
#include "session.h"
#include "methods.h"
#include <cx/buffer.h>
#include <cx/utils.h>
#include <cx/linked_list.h>
#include <cx/hash_map.h>
#include <cx/compare.h>
#include "davqlparser.h"
#include "davqlexec.h"
DavContext* dav_context_new(
void) {
DavContext *context = calloc(
1,
sizeof(DavContext));
if(!context) {
return NULL;
}
context->sessions = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr,
CX_STORE_POINTERS);
cxDefineDestructor(context->sessions, dav_session_destructor);
context->http_proxy = calloc(
1,
sizeof(DavProxy));
if(!context->http_proxy) {
dav_context_destroy(context);
return NULL;
}
context->https_proxy = calloc(
1,
sizeof(DavProxy));
if(!context->https_proxy) {
dav_context_destroy(context);
return NULL;
}
context->namespaces = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
16);
if(!context->namespaces) {
dav_context_destroy(context);
return NULL;
}
context->namespaceinfo = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
16);
if(!context->namespaceinfo) {
dav_context_destroy(context);
}
context->keys = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
16);
if(!context->keys) {
dav_context_destroy(context);
return NULL;
}
if(dav_add_namespace(context,
"D",
"DAV:")) {
dav_context_destroy(context);
return NULL;
}
if(dav_add_namespace(context,
"idav",
DAV_NS)) {
dav_context_destroy(context);
return NULL;
}
if(dav_add_namespace(context,
"idavprops",
DAV_PROPS_NS)) {
dav_context_destroy(context);
return NULL;
}
return context;
}
void dav_context_destroy(DavContext *ctx) {
cxListDestroy(ctx->sessions);
if(ctx->http_proxy) {
free(ctx->http_proxy);
}
if(ctx->https_proxy) {
free(ctx->https_proxy);
}
if(ctx->namespaces) {
CxIterator i = cxMapIteratorValues(ctx->namespaces);
cx_foreach(DavNamespace*, ns, i) {
if(!ns)
continue;
if(ns->prefix) {
free(ns->prefix);
}
if(ns->name) {
free(ns->name);
}
free(ns);
}
cxMapDestroy(ctx->namespaces);
}
if(ctx->namespaceinfo) {
}
if(ctx->keys) {
CxIterator i = cxMapIteratorValues(ctx->keys);
cx_foreach(DavKey*, key, i) {
if(!key)
continue;
if(key->name) {
free(key->name);
}
if(key->data) {
free(key->data);
}
free(key);
}
cxMapDestroy(ctx->keys);
}
free(ctx);
}
#ifndef _WIN32
void dav_context_set_mtsafe(DavContext *ctx, DavBool enable) {
if (enable) {
pthread_mutex_init(&ctx->mutex,
NULL);
}
else {
pthread_mutex_destroy(&ctx->mutex);
}
ctx->mtsafe = enable;
}
void dav_context_lock(DavContext *ctx) {
if (ctx->mtsafe) {
pthread_mutex_lock(&ctx->mutex);
}
}
void dav_context_unlock(DavContext *ctx) {
if (ctx->mtsafe) {
pthread_mutex_unlock(&ctx->mutex);
}
}
#else
void dav_context_set_mtsafe(DavContext *ctx, DavBool enable) {
if (enable) {
ctx->mutex = CreateMutex(
NULL,
FALSE,
NULL);
}
else {
CloseHandle(ctx->mutex);
}
ctx->mtsafe = enable;
}
void dav_context_lock(DavContext *ctx) {
if (ctx->mtsafe) {
WaitForSingleObject(ctx->mutex,
INFINITE);
}
}
void dav_context_unlock(DavContext *ctx) {
if (ctx->mtsafe) {
ReleaseMutex(ctx->mutex);
}
}
#endif
void dav_context_add_key(DavContext *context, DavKey *key) {
dav_context_lock(context);
cxMapPut(context->keys, cx_hash_key_str(key->name), key);
dav_context_unlock(context);
}
DavKey* dav_context_get_key(DavContext *context,
const char *name) {
DavKey *key =
NULL;
dav_context_lock(context);
if(name) {
key = cxMapGet(context->keys, cx_hash_key_str(name));
}
dav_context_unlock(context);
return key;
}
int dav_add_namespace(DavContext *context,
const char *prefix,
const char *name) {
DavNamespace *namespace = malloc(
sizeof(DavNamespace));
if(!namespace) {
return 1;
}
char *p = strdup(prefix);
if (!p) {
free(namespace);
return 1;
}
char *n = strdup(name);
if (!n) {
free(namespace);
free(p);
return 1;
}
dav_context_lock(context);
int err =
0;
if(p && n) {
namespace->prefix = p;
namespace->name = n;
err = cxMapPut(context->namespaces, cx_hash_key_str(prefix), namespace);
}
if(err) {
free(namespace);
if(p) free(p);
if(n) free(n);
}
dav_context_unlock(context);
return err;
}
DavNamespace* dav_get_namespace(DavContext *context,
const char *prefix) {
dav_context_lock(context);
DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key_str(prefix));
dav_context_unlock(context);
return ns;
}
DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) {
dav_context_lock(context);
DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length));
dav_context_unlock(context);
return ns;
}
int dav_enable_namespace_encryption(DavContext *context,
const char *ns, DavBool encrypt) {
dav_context_lock(context);
CxHashKey hkey = cx_hash_key_str(ns);
DavNSInfo *info = cxMapGet(context->namespaceinfo, hkey);
if(!info) {
info = calloc(
1,
sizeof(DavNSInfo));
info->encrypt = encrypt;
cxMapPut(context->namespaceinfo, hkey, info);
}
else {
info->encrypt = encrypt;
}
dav_context_unlock(context);
return 0;
}
int dav_namespace_is_encrypted(DavContext *context,
const char *ns) {
int ret =
0;
dav_context_lock(context);
DavNSInfo *info = cxMapGet(context->namespaceinfo, cx_hash_key_str(ns));
if(info) {
ret = info->encrypt;
}
dav_context_unlock(context);
return ret;
}
void dav_get_property_namespace_str(
DavContext *ctx,
char *prefixed_name,
char **ns,
char **name)
{
char *pname = strchr(prefixed_name,
':');
char *pns =
"DAV:";
if(pname) {
DavNamespace *davns = dav_get_namespace_s(
ctx,
cx_strn(prefixed_name, pname-prefixed_name));
if(davns) {
pns = davns->name;
pname++;
}
else {
pns =
NULL;
pname =
NULL;
}
}
else {
pname = prefixed_name;
}
*ns = pns;
*name = pname;
}
DavNamespace* dav_get_property_namespace(
DavContext *ctx,
char *prefixed_name,
char **name)
{
char *pname = strchr(prefixed_name,
':');
if(pname) {
DavNamespace *ns = dav_get_namespace_s(
ctx,
cx_strn(prefixed_name, pname-prefixed_name));
if(ns) {
*name = pname +
1;
return ns;
}
else {
*name =
NULL;
return NULL;
}
}
else {
*name = prefixed_name;
return dav_get_namespace_s(ctx, cx_str(
"D"));
}
}
int dav_context_add_session(DavContext *context, DavSession *sn) {
dav_context_lock(context);
int ret = cxListAdd(context->sessions, sn);
dav_context_unlock(context);
return ret;
}
int dav_context_remove_session(DavContext *context, DavSession *sn) {
int ret =
0;
dav_context_lock(context);
CxList *sessions = context->sessions;
ssize_t i = cxListFind(sessions, sn);
if(i >=
0) {
cxListRemove(sessions, i);
}
else {
ret =
1;
}
dav_context_unlock(context);
return ret;
}
void dav_set_effective_href(DavSession *sn, DavResource *resource) {
char *eff_url;
curl_easy_getinfo(sn->handle,
CURLINFO_EFFECTIVE_URL, &eff_url);
if(eff_url) {
const char *href = util_url_path(eff_url);
if(strcmp(href, resource->href)) {
dav_session_free(sn, resource->href);
resource->href = dav_session_strdup(sn, href);
}
}
}
DavResource* dav_get(DavSession *sn,
char *path,
const char *properties) {
CURL *handle = sn->handle;
DavResource *resource = dav_resource_new(sn, path);
util_set_url(sn, dav_resource_get_href(resource));
CxList *proplist =
NULL;
if(properties) {
proplist = parse_properties_string(sn->context, cx_str(properties));
}
CxBuffer *rqbuf = create_propfind_request(sn, proplist,
"propfind",
0);
CxBuffer *rpbuf = cxBufferCreate(
NULL,
4096, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
long status =
0;
curl_easy_getinfo (handle,
CURLINFO_RESPONSE_CODE, &status);
if(ret ==
CURLE_OK && status ==
207) {
dav_set_effective_href(sn, resource);
resource = parse_propfind_response(sn, resource, rpbuf);
resource->exists =
1;
sn->error =
DAV_OK;
}
else {
dav_session_set_error(sn, ret, status);
dav_resource_free(resource);
resource =
NULL;
}
cxBufferFree(rqbuf);
cxBufferFree(rpbuf);
if(proplist) {
CxIterator i = cxListIterator(proplist);
cx_foreach(DavProperty*, p, i) {
free(p->name);
}
cxListDestroy(proplist);
}
return resource;
}
int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) {
DavResourceData *data = root->data;
cxMapClear(data->properties);
CURL *handle = sn->handle;
util_set_url(sn, dav_resource_get_href(root));
CxBuffer *rpbuf = cxBufferCreate(
NULL,
4096, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
DavResource *resource = root;
CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
long status =
0;
long error =
0;
curl_easy_getinfo (handle,
CURLINFO_RESPONSE_CODE, &status);
if(ret ==
CURLE_OK && status ==
207) {
dav_set_effective_href(sn, resource);
resource = parse_propfind_response(sn, resource, rpbuf);
sn->error =
DAV_OK;
root->exists =
1;
}
else {
dav_session_set_error(sn, ret, status);
error =
1;
}
cxBufferFree(rpbuf);
return error;
}
CxList* parse_properties_string(DavContext *context, cxstring str) {
CxList *proplist = cxLinkedListCreateSimple(
sizeof(DavProperty));
CxStrtokCtx tok = cx_strtok(str, cx_str(
","),
INT_MAX);
cxstring s;
while(cx_strtok_next(&tok, &s)) {
cxstring nsname = cx_strchr(s,
':');
if(nsname.length >
0) {
cxstring nspre = cx_strsubsl(s,
0, nsname.ptr - s.ptr);
nsname.ptr++;
nsname.length--;
DavProperty dp;
cxstring pre = cx_strtrim(nspre);
dp.ns = dav_get_namespace_s(context, pre);
dp.name = cx_strdup(nsname).ptr;
dp.value =
NULL;
if(dp.ns && dp.name) {
cxListAdd(proplist, &dp);
}
else {
free(dp.name);
}
}
}
return proplist;
}
DavResource* dav_query(DavSession *sn,
char *query, ...) {
DavQLStatement *stmt = dav_parse_statement(cx_str(query));
if(!stmt) {
sn->error =
DAV_ERROR;
return NULL;
}
if(stmt->errorcode !=
0) {
sn->error =
DAV_QL_ERROR;
dav_free_statement(stmt);
return NULL;
}
va_list ap;
va_start(ap, query);
DavResult result = dav_statement_execv(sn, stmt, ap);
va_end(ap);
dav_free_statement(stmt);
if(result.status == -
1) {
if(result.result) {
dav_resource_free(result.result);
result.result =
NULL;
}
}
return result.result;
}
void dav_verbose_log(
DavSession *sn,
const char *method,
const char *url,
const char *request_body,
size_t request_bodylen,
int status,
const char *response_body,
size_t response_bodylen)
{
fprintf(stderr,
"# method: %s url: %s status: %d\n", method, url, status);
fprintf(stderr,
"# request len: %d\n%.*s\n", (
int)request_bodylen, (
int)request_bodylen, request_body);
fprintf(stderr,
"# response len: %d\n%.*s\n", (
int)response_bodylen, (
int)response_bodylen, response_body);
}