#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cx/string.h>
#include <cx/utils.h>
#include <cx/map.h>
#include <cx/hash_map.h>
#include "requestparser.h"
#include "webdav.h"
#include "../util/pool.h"
#define xstreq(a, b) !strcmp((
const char*)a, (
const char*)b)
void proplist_free(
pool_handle_t *pool, WebdavPList *list) {
while(list) {
WebdavPList *next = list->next;
pool_free(pool, list);
list = next;
}
}
WebdavProperty* prop_create(
pool_handle_t *pool,
WSNamespace *ns,
const char *name)
{
WebdavProperty *prop = pool_malloc(pool,
sizeof(WebdavProperty));
memset(prop,
0,
sizeof(WebdavProperty));
prop->lang =
NULL;
prop->name = (
char*)name;
prop->namespace = ns;
return prop;
}
static int parse_prop(
Session *sn,
xmlNode *node,
CxMap *propmap,
WebdavPList **plist_begin,
WebdavPList **plist_end,
size_t *propcount,
int proppatch,
int *error)
{
xmlNode *pnode = node->children;
for(;pnode;pnode=pnode->next) {
if(pnode->type !=
XML_ELEMENT_NODE) {
continue;
}
const char* ns = (
const char*)pnode->ns->href;
const char* name = (
const char*)pnode->name;
CxHashKey k = webdav_property_key((
const char*)ns, (
const char*)name);
if(!k.data) {
*error = proppatch ?
PROPPATCH_PARSER_OOM :
PROPFIND_PARSER_OOM;
return 1;
}
void *c = cxMapGet(propmap, k);
if(!c) {
if(cxMapPut(propmap, k, (
void*)
1)) {
*error = proppatch ?
PROPPATCH_PARSER_OOM :
PROPFIND_PARSER_OOM;
}
WebdavProperty *prop = prop_create(sn->pool, pnode->ns, name);
if(proppatch) {
prop->value.node = pnode->children;
prop->vtype =
WS_VALUE_XML_NODE;
}
if(prop) {
if(webdav_plist_add(sn->pool, plist_begin, plist_end, prop)) {
*error = proppatch ?
PROPPATCH_PARSER_OOM :
PROPFIND_PARSER_OOM;
}
(*propcount)++;
}
else {
*error = proppatch ?
PROPPATCH_PARSER_OOM :
PROPFIND_PARSER_OOM;
}
}
else if(proppatch) {
*error =
PROPPATCH_PARSER_DUPLICATE;
}
free((
void*)k.data);
if(*error) {
return 1;
}
}
return 0;
}
WebdavPropfindRequest* propfind_parse(
Session *sn,
Request *rq,
const char *buf,
size_t buflen,
int *error)
{
xmlDoc *doc = xmlReadMemory(buf, buflen,
NULL,
NULL,
0);
if(!doc) {
*error =
PROPFIND_PARSER_INVALID_REQUEST;
return NULL;
}
*error =
0;
WSBool allprop =
FALSE;
WSBool propname =
FALSE;
WebdavPList *plist_begin =
NULL;
WebdavPList *plist_end =
NULL;
size_t propcount =
0;
int depth = webdav_getdepth(rq);
xmlNode *root = xmlDocGetRootElement(doc);
xmlNode *node = root->children;
if(
!(root->ns
&& xstreq(root->ns->href,
"DAV:")
&& xstreq(root->name,
"propfind")))
{
*error =
PROPFIND_PARSER_NO_PROPFIND;
xmlFreeDoc(doc);
return NULL;
}
CxAllocator *a = pool_allocator(sn->pool);
CxMap *propmap = cxHashMapCreate(a,
CX_STORE_POINTERS,
32);
if(!propmap) {
*error =
PROPFIND_PARSER_OOM;
xmlFreeDoc(doc);
return NULL;
}
int ret =
0;
while(node && !ret) {
if(node->type ==
XML_ELEMENT_NODE) {
if(xstreq(node->ns->href,
"DAV:") && !allprop && !propname) {
if(xstreq(node->name,
"prop")) {
ret = parse_prop(
sn,
node,
propmap,
&plist_begin,
&plist_end,
&propcount,
0,
error);
}
else if(xstreq(node->name,
"allprop")) {
allprop =
TRUE;
}
else if(xstreq(node->name,
"propname")) {
propname =
TRUE;
}
}
}
node = node->next;
}
cxMapDestroy(propmap);
if(ret) {
xmlFreeDoc(doc);
return NULL;
}
if(!allprop && !propname && propcount ==
0) {
*error =
PROPFIND_PARSER_NO_PROPERTIES;
xmlFreeDoc(doc);
return NULL;
}
WebdavPropfindRequest *request = pool_malloc(
sn->pool,
sizeof(WebdavPropfindRequest));
if(!request) {
*error =
PROPFIND_PARSER_OOM;
xmlFreeDoc(doc);
return NULL;
}
request->sn = sn;
request->rq = rq;
request->properties =
NULL;
request->propcount =
0;
request->depth = depth;
request->doc = doc;
if(allprop) {
request->allprop =
TRUE;
request->propname =
FALSE;
}
else if(propname) {
request->allprop =
FALSE;
request->propname =
TRUE;
}
else {
request->allprop =
FALSE;
request->propname =
FALSE;
request->properties = plist_begin;
request->propcount = propcount;
}
if(!request->properties && plist_begin) {
proplist_free(sn->pool, plist_begin);
}
return request;
}
WebdavProppatchRequest* proppatch_parse(
Session *sn,
Request *rq,
const char *buf,
size_t buflen,
int *error)
{
return webdav_parse_set(
sn, rq, buf, buflen,
"DAV:",
"propertyupdate",
TRUE, error);
}
static xmlNode* find_child(xmlNode *node,
const char *name) {
xmlNode *c = node->children;
while(c) {
if(c->ns) {
if(xstreq(c->ns->href,
"DAV:") && xstreq(c->name, name)) {
return c;
}
}
c = c->next;
}
return NULL;
}
WebdavProppatchRequest* webdav_parse_set(
Session *sn,
Request *rq,
const char *buf,
size_t buflen,
const char *rootns,
const char *rootname,
WSBool allowremove,
int *error)
{
xmlDoc *doc = xmlReadMemory(buf, buflen,
NULL,
NULL,
0);
if(!doc) {
*error =
PROPPATCH_PARSER_INVALID_REQUEST;
return NULL;
}
xmlNode *root = xmlDocGetRootElement(doc);
xmlNode *node = root->children;
if(
!(root->ns
&& xstreq(root->ns->href, rootns)
&& xstreq(root->name, rootname)))
{
*error =
PROPPATCH_PARSER_NO_PROPERTYUPDATE;
xmlFreeDoc(doc);
return NULL;
}
*error =
0;
CxAllocator *a = pool_allocator(sn->pool);
CxMap *propmap = cxHashMapCreate(a,
CX_STORE_POINTERS,
32);
if(!propmap) {
*error =
PROPPATCH_PARSER_OOM;
xmlFreeDoc(doc);
return NULL;
}
WebdavPList *set_begin =
NULL;
WebdavPList *set_end =
NULL;
WebdavPList *remove_begin =
NULL;
WebdavPList *remove_end =
NULL;
size_t set_count =
0;
size_t remove_count =
0;
int ret =
0;
while(node && !ret) {
if(node->type ==
XML_ELEMENT_NODE) {
if(node->ns && xstreq(node->ns->href,
"DAV:")) {
if(xstreq(node->name,
"set")) {
xmlNode *prop = find_child(node,
"prop");
ret = parse_prop(
sn,
prop,
propmap,
&set_begin,
&set_end,
&set_count,
TRUE,
error);
}
else if(xstreq(node->name,
"remove")) {
if(!allowremove) {
*error =
PROPPATCH_PARSER_INVALID_REQUEST;
ret =
1;
break;
}
xmlNode *prop = find_child(node,
"prop");
ret = parse_prop(
sn,
prop,
propmap,
&remove_begin,
&remove_end,
&remove_count,
TRUE,
error);
}
}
}
node = node->next;
}
cxMapDestroy(propmap);
if(set_count + remove_count ==
0) {
*error =
PROPPATCH_PARSER_NO_PROPERTIES;
ret =
1;
}
if(ret) {
xmlFreeDoc(doc);
return NULL;
}
WebdavProppatchRequest *request = pool_malloc(
sn->pool,
sizeof(WebdavProppatchRequest));
if(!request) {
*error =
PROPPATCH_PARSER_OOM;
xmlFreeDoc(doc);
return NULL;
}
request->sn = sn;
request->rq = rq;
request->doc = doc;
request->set = set_begin;
request->setcount = set_count;
request->remove = remove_begin;
request->removecount = remove_count;
return request;
}
WebdavLockRequest* lock_parse(
Session *sn,
Request *rq,
const char *buf,
size_t buflen,
int *error)
{
xmlDoc *doc = xmlReadMemory(buf, buflen,
NULL,
NULL,
0);
if(!doc) {
*error =
LOCK_PARSER_INVALID_REQUEST;
return NULL;
}
xmlNode *root = xmlDocGetRootElement(doc);
xmlNode *node = root->children;
if(
!(root->ns
&& xstreq(root->ns->href,
"DAV:")
&& xstreq(root->name,
"lockinfo")))
{
*error =
LOCK_PARSER_NO_LOCKINFO;
xmlFreeDoc(doc);
return NULL;
}
WebdavLockScope lockscope =
WEBDAV_LOCK_SCOPE_UNKNOWN;
WebdavLockType locktype =
WEBDAV_LOCK_TYPE_UNKNOWN;
WSXmlNode *owner =
NULL;
int ret =
0;
while(node && !ret) {
if(
node->type ==
XML_ELEMENT_NODE
&& node->ns
&& xstreq(node->ns->href,
"DAV:"))
{
char *name = (
char*)node->name;
if(xstreq(name,
"lockscope")) {
xmlNode *s = node->children;
while(s) {
if(
s->type ==
XML_ELEMENT_NODE
&& s->ns
&& xstreq(s->ns->href,
"DAV:"))
{
if(xstreq(s->name,
"exclusive")) {
lockscope =
WEBDAV_LOCK_EXCLUSIVE;
}
else if(xstreq(s->name,
"shared")) {
lockscope =
WEBDAV_LOCK_SHARED;
}
else {
*error =
LOCK_PARSER_UNKNOWN_ELEMENT;
ret =
1;
break;
}
}
s = s->next;
}
}
else if(xstreq(name,
"locktype")) {
xmlNode *t = node->children;
while(t) {
if(
t->type ==
XML_ELEMENT_NODE
&& t->ns
&& xstreq(t->ns->href,
"DAV:"))
{
if(xstreq(t->name,
"write")) {
locktype =
WEBDAV_LOCK_WRITE;
}
else {
*error =
LOCK_PARSER_UNKNOWN_ELEMENT;
ret =
1;
break;
}
}
t = t->next;
}
}
else if(xstreq(name,
"owner")) {
owner = node->children;
}
}
node = node->next;
}
if(ret) {
xmlFreeDoc(doc);
return NULL;
}
WebdavLockRequest *request = pool_malloc(
sn->pool,
sizeof(WebdavLockRequest));
if(!request) {
*error =
LOCK_PARSER_OOM;
xmlFreeDoc(doc);
return NULL;
}
request->sn = sn;
request->rq = rq;
request->doc = doc;
if(locktype ==
WEBDAV_LOCK_TYPE_UNKNOWN) {
locktype =
WEBDAV_LOCK_WRITE;
}
if(lockscope ==
WEBDAV_LOCK_SCOPE_UNKNOWN) {
lockscope =
WEBDAV_LOCK_EXCLUSIVE;
}
request->scope = lockscope;
request->type = locktype;
request->owner = owner;
return request;
}