#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cx/buffer.h>
#include <cx/list.h>
#include <cx/linked_list.h>
#include <cx/hash_map.h>
#include <cx/printf.h>
#include "webdav.h"
#include "search.h"
#include "versioning.h"
#include "multistatus.h"
#include "requestparser.h"
#include "operation.h"
#include "xattrbackend.h"
#include "../util/pblock.h"
#include "../util/util.h"
#include "../daemon/session.h"
#include "../daemon/http.h"
#include "../daemon/protocol.h"
#include "../daemon/vfs.h"
static CxMap *method_handler_map;
static CxMap *webdav_type_map;
static WebdavBackend default_backend;
static WSNamespace dav_namespace;
static WebdavProperty dav_resourcetype_empty;
static WebdavProperty dav_resourcetype_collection;
static WSXmlData dav_resourcetype_collection_value;
#define WEBDAV_RESOURCE_TYPE_COLLECTION "<D:collection/>"
static WebdavBackend* default_backend_create(Session *sn, Request *rq, pblock *pb,
void *initData) {
WebdavBackend *dav = pool_malloc(sn->pool,
sizeof(WebdavBackend));
if(!dav) {
return NULL;
}
*dav = default_backend;
return dav;
}
static int init_default_backend(
void) {
memset(&default_backend,
0,
sizeof(WebdavBackend));
default_backend.propfind_init = default_propfind_init;
default_backend.propfind_do = default_propfind_do;
default_backend.propfind_finish = default_propfind_finish;
default_backend.proppatch_do = default_proppatch_do;
default_backend.proppatch_finish = default_proppatch_finish;
default_backend.opt_mkcol =
NULL;
default_backend.opt_delete =
NULL;
default_backend.settings =
WS_WEBDAV_PROPFIND_USE_VFS;
default_backend.instance =
NULL;
return webdav_register_backend(
"default",
NULL, default_backend_create);
}
int webdav_register_backend(
const char *name, webdav_init_func webdavInit, webdav_create_func webdavCreate) {
WebdavType *webdavType = malloc(
sizeof(WebdavType));
webdavType->init = webdavInit;
webdavType->create = webdavCreate;
return cxMapPut(webdav_type_map, cx_hash_key_str(name), webdavType);
}
WebdavType* webdav_get_type(cxstring dav_class) {
return cxMapGet(webdav_type_map, cx_hash_key_bytes((
unsigned const char *)dav_class.ptr, dav_class.length));
}
void* webdav_init_backend(ServerConfiguration *cfg,
pool_handle_t *pool, WebdavType *dav_class, WSConfigNode *config,
int *error) {
*error =
0;
if(dav_class->init) {
void *initData = dav_class->init(cfg, pool, config);
if(!initData) {
*error =
1;
}
return initData;
}
else {
return NULL;
}
}
WebdavBackend* webdav_create(Session *sn, Request *rq,
const char *dav_class, pblock *pb,
void *initData) {
WebdavType *webdavType = cxMapGet(webdav_type_map, cx_hash_key_str(dav_class));
if(!webdavType) {
log_ereport(
LOG_MISCONFIG,
"webdav_create: unkown dav type %s", dav_class);
return NULL;
}
return webdavType->create(sn, rq, pb, initData);
}
static WSBool webdav_is_initialized =
FALSE;
int webdav_init(pblock *pb, Session *sn, Request *rq) {
if(webdav_is_initialized) {
return REQ_NOACTION;
}
webdav_is_initialized =
TRUE;
webdav_type_map = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
if(!webdav_type_map) {
return REQ_ABORTED;
}
method_handler_map = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
64);
if(!method_handler_map) {
return REQ_ABORTED;
}
if(init_default_backend()) {
return REQ_ABORTED;
}
if(webdav_init_xattr_backend()) {
return REQ_ABORTED;
}
cxMapPut(method_handler_map, cx_hash_key_str(
"OPTIONS"), webdav_options);
cxMapPut(method_handler_map, cx_hash_key_str(
"PROPFIND"), webdav_propfind);
cxMapPut(method_handler_map, cx_hash_key_str(
"PROPPATCH"), webdav_proppatch);
cxMapPut(method_handler_map, cx_hash_key_str(
"MKCOL"), webdav_mkcol);
cxMapPut(method_handler_map, cx_hash_key_str(
"POST"), webdav_post);
cxMapPut(method_handler_map, cx_hash_key_str(
"DELETE"), webdav_delete);
cxMapPut(method_handler_map, cx_hash_key_str(
"PUT"), webdav_put);
cxMapPut(method_handler_map, cx_hash_key_str(
"COPY"), webdav_copy);
cxMapPut(method_handler_map, cx_hash_key_str(
"MOVE"), webdav_move);
cxMapPut(method_handler_map, cx_hash_key_str(
"LOCK"), webdav_lock);
cxMapPut(method_handler_map, cx_hash_key_str(
"UNLOCK"), webdav_unlock);
cxMapPut(method_handler_map, cx_hash_key_str(
"REPORT"), webdav_report);
cxMapPut(method_handler_map, cx_hash_key_str(
"ACL"), webdav_acl);
cxMapPut(method_handler_map, cx_hash_key_str(
"SEARCH"), webdav_search);
cxMapPut(method_handler_map, cx_hash_key_str(
"VERSION-CONTROL"), webdav_version_control);
cxMapPut(method_handler_map, cx_hash_key_str(
"CHECKOUT"), webdav_checkout);
cxMapPut(method_handler_map, cx_hash_key_str(
"CHECKIN"), webdav_checkin);
cxMapPut(method_handler_map, cx_hash_key_str(
"UNCHECKOUT"), webdav_uncheckout);
cxMapPut(method_handler_map, cx_hash_key_str(
"MKWORKSPACE"), webdav_mkworkspace);
cxMapPut(method_handler_map, cx_hash_key_str(
"UPDATE"), webdav_update);
cxMapPut(method_handler_map, cx_hash_key_str(
"LABEL"), webdav_label);
cxMapPut(method_handler_map, cx_hash_key_str(
"MERGE"), webdav_merge);
dav_namespace.href = (xmlChar*)
"DAV:";
dav_namespace.prefix = (xmlChar*)
"D";
dav_resourcetype_empty.namespace = &dav_namespace;
dav_resourcetype_empty.name =
"resourcetype";
dav_resourcetype_collection_value.data =
WEBDAV_RESOURCE_TYPE_COLLECTION;
dav_resourcetype_collection_value.length =
sizeof(
WEBDAV_RESOURCE_TYPE_COLLECTION)-
1;
dav_resourcetype_collection.namespace = &dav_namespace;
dav_resourcetype_collection.name =
"resourcetype";
dav_resourcetype_collection.value.data = dav_resourcetype_collection_value;
dav_resourcetype_collection.vtype =
WS_VALUE_XML_DATA;
return REQ_PROCEED;
}
int webdav_service(pblock *pb, Session *sn, Request *rq) {
if(!method_handler_map) {
log_ereport(
LOG_FAILURE,
"WebDAV module not initialized");
protocol_status(sn, rq,
500,
NULL);
return REQ_ABORTED;
}
char *method = pblock_findkeyval(pb_key_method, rq->reqpb);
FuncPtr saf = (FuncPtr)cxMapGet(method_handler_map, cx_hash_key_str(method));
if(!saf) {
return REQ_NOACTION;
}
return saf(pb, sn, rq);
}
int rqbody2buffer(Session *sn, Request *rq, CxBuffer *buf) {
if(!sn->inbuf) {
protocol_status(sn, rq,
400,
NULL);
return 1;
}
CxAllocator *a = pool_allocator(sn->pool);
if(cxBufferInit(buf,
NULL, sn->inbuf->maxsize, a,
CX_BUFFER_AUTO_EXTEND|
CX_BUFFER_FREE_CONTENTS)) {
protocol_status(sn, rq,
500,
NULL);
return 1;
}
char in[
2048];
int r;
while((r = netbuf_getbytes(sn->inbuf, in,
2048)) >
0) {
if(cxBufferWrite(in,
1, r, buf) != r) {
protocol_status(sn, rq,
500,
NULL);
cxBufferDestroy(buf);
return 1;
}
}
return 0;
}
int webdav_options(pblock *pb, Session *sn, Request *rq) {
return REQ_ABORTED;
}
static const char* propfind_error2str(
int error) {
switch(error) {
case PROPFIND_PARSER_OK:
return "ok";
case PROPFIND_PARSER_NO_PROPFIND:
return "invalid xml root element";
case PROPFIND_PARSER_NO_PROPERTIES:
return "no properties specified";
case PROPFIND_PARSER_INVALID_REQUEST:
return "invalid propfind request";
case PROPFIND_PARSER_OOM:
return "OOM";
case PROPFIND_PARSER_ERROR:
return "error";
}
return "";
}
int webdav_propfind(pblock *pb, Session *sn, Request *rq) {
char *expect = pblock_findkeyval(pb_key_expect, rq->headers);
if(expect) {
if(!strcasecmp(expect,
"100-continue")) {
if(http_send_continue(sn)) {
return REQ_ABORTED;
}
}
}
CxBuffer reqbody;
if(rqbody2buffer(sn, rq, &reqbody)) {
return REQ_ABORTED;
}
int error =
0;
WebdavPropfindRequest *propfind = propfind_parse(
sn,
rq,
reqbody.space,
reqbody.size,
&error);
cxBufferDestroy(&reqbody);
if(!propfind) {
log_ereport(
LOG_FAILURE,
"webdav-propfind: %s", propfind_error2str(error));
return REQ_ABORTED;
}
WebdavBackend *dav = rq->davCollection ?
rq->davCollection : &default_backend;
char *path = pblock_findkeyval(pb_key_path, rq->vars);
char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
Multistatus *ms = multistatus_response(sn, rq);
if(!ms) {
return REQ_ABORTED;
}
int ret = webdav_propfind_do(dav, propfind, (WebdavResponse*)ms,
NULL, path, uri);
if(ret ==
REQ_PROCEED) {
if(multistatus_send(ms, sn->csd)) {
ret =
REQ_ABORTED;
}
}
else if(rq->status_num <
400 || rq->status_num >=
500) {
log_ereport(
LOG_FAILURE,
"webdav-propfind: operation failed");
}
if(propfind->doc) {
xmlFreeDoc(propfind->doc);
}
return ret;
}
int webdav_propfind_init(
WebdavBackend *dav,
WebdavPropfindRequest *propfind,
const char *path,
const char *uri,
WebdavPropfindRequestList **out_req)
{
pool_handle_t *pool = propfind->sn->pool;
CxAllocator *a = pool_allocator(pool);
WebdavPropfindRequestList *requestObjectsBegin =
NULL;
WebdavPropfindRequestList *requestObjectsEnd =
NULL;
WebdavPList *newProp = webdav_plist_clone(pool, propfind->properties);
size_t newPropCount = propfind->propcount;
WebdavBackend *davList = dav;
while(davList) {
WebdavPropfindRequest *pReq = pool_malloc(
pool,
sizeof(WebdavPropfindRequest));
memcpy(pReq, propfind,
sizeof(WebdavPropfindRequest));
pReq->properties = newProp;
pReq->propcount = newPropCount;
pReq->dav = davList;
WebdavPropfindRequestList *reqListElm = pool_malloc(pool,
sizeof(WebdavPropfindRequestList));
if(!reqListElm) {
return REQ_ABORTED;
}
reqListElm->propfind = pReq;
reqListElm->next =
NULL;
cx_linked_list_add(
(
void**)&requestObjectsBegin,
(
void**)&requestObjectsEnd,
-
1,
offsetof(WebdavPropfindRequestList, next),
reqListElm);
newProp = webdav_plist_clone(pool, newProp);
if(davList->propfind_init(pReq, path, uri, &newProp)) {
return REQ_ABORTED;
}
newPropCount = webdav_plist_size(newProp);
davList = davList->next;
}
*out_req = requestObjectsBegin;
return REQ_PROCEED;
}
int webdav_propfind_do(
WebdavBackend *dav,
WebdavPropfindRequest *propfind,
WebdavResponse *response,
VFSContext *vfs,
char *path,
char *uri)
{
Session *sn = propfind->sn;
Request *rq = propfind->rq;
uint32_t settings = dav->settings;
WebdavPropfindRequestList *requestObjects =
NULL;
if(webdav_propfind_init(dav, propfind, path, uri, &requestObjects)) {
return REQ_ABORTED;
}
WebdavOperation *op = webdav_create_propfind_operation(
sn,
rq,
dav,
propfind->properties,
requestObjects,
response);
WSBool usevfs = (settings &
WS_WEBDAV_PROPFIND_USE_VFS)
==
WS_WEBDAV_PROPFIND_USE_VFS;
struct stat s;
struct stat *statptr =
NULL;
if(usevfs && !vfs) {
vfs = vfs_request_context(sn, rq);
if(!vfs) {
return REQ_ABORTED;
}
if(vfs_stat(vfs, path, &s)) {
protocol_status(sn, rq, util_errno2status(vfs->vfs_errno),
NULL);
return REQ_ABORTED;
}
statptr = &s;
if(!
S_ISDIR(s.st_mode)) {
usevfs =
FALSE;
}
}
if(propfind->depth ==
0) {
usevfs =
FALSE;
}
int ret =
REQ_PROCEED;
if(!webdav_op_propfind_begin(op, uri,
NULL, statptr)) {
if(usevfs) {
if(webdav_op_propfind_children(op, vfs, uri, path)) {
ret =
REQ_ABORTED;
}
}
}
if(webdav_op_propfind_finish(op)) {
ret =
REQ_ABORTED;
}
return ret;
}
static const char* proppatch_error2str(
int error) {
switch(error) {
case PROPPATCH_PARSER_OK:
return "ok";
case PROPPATCH_PARSER_NO_PROPERTYUPDATE:
return "no property update";
case PROPPATCH_PARSER_NO_PROPERTIES:
return "no properties";
case PROPPATCH_PARSER_INVALID_REQUEST:
return "invalid proppatch request";
case PROPPATCH_PARSER_DUPLICATE:
return "proppatch property duplicate";
case PROPPATCH_PARSER_OOM:
return "OOM";
case PROPPATCH_PARSER_ERROR:
return "proppatch parser error";
}
return "error";
}
int webdav_proppatch(pblock *pb, Session *sn, Request *rq) {
char *expect = pblock_findkeyval(pb_key_expect, rq->headers);
if(expect) {
if(!strcasecmp(expect,
"100-continue")) {
if(http_send_continue(sn)) {
return REQ_ABORTED;
}
}
}
CxBuffer reqbody;
if(rqbody2buffer(sn, rq, &reqbody)) {
return REQ_ABORTED;
}
int error =
0;
WebdavProppatchRequest *proppatch = proppatch_parse(
sn,
rq,
reqbody.space,
reqbody.size,
&error);
cxBufferDestroy(&reqbody);
if(!proppatch) {
log_ereport(
LOG_FAILURE,
"webdav-proppatch: %s", proppatch_error2str(error));
return REQ_ABORTED;
}
WebdavBackend *dav = rq->davCollection ?
rq->davCollection : &default_backend;
char *path = pblock_findkeyval(pb_key_path, rq->vars);
char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
Multistatus *ms = multistatus_response(sn, rq);
if(!ms) {
return REQ_ABORTED;
}
ms->proppatch =
TRUE;
WebdavResponse *response = (WebdavResponse*)ms;
WebdavOperation *op = webdav_create_proppatch_operation(
sn,
rq,
dav,
proppatch,
response);
int ret =
REQ_PROCEED;
if(webdav_op_proppatch(op, uri, path)) {
log_ereport(
LOG_FAILURE,
"webdav-proppatch: proppatch operation failed");
ret =
REQ_ABORTED;
}
if(ret ==
REQ_PROCEED) {
if(multistatus_send(ms, sn->csd)) {
log_ereport(
LOG_FAILURE,
"webdav-proppatch: multistatus_send failed");
}
}
else {
protocol_status(sn, rq,
500,
NULL);
}
xmlFreeDoc(proppatch->doc);
return ret;
}
int webdav_mkcol(pblock *pb, Session *sn, Request *rq) {
WebdavVFSOperation *op = webdav_vfs_op(sn, rq, rq->davCollection,
TRUE);
if(!op) {
return REQ_ABORTED;
}
int ret =
REQ_ABORTED;
if(!webdav_vfs_op_do(op,
WEBDAV_VFS_MKDIR)) {
pblock_nvinsert(
"content-length",
"0", rq->srvhdrs);
protocol_status(sn, rq,
201,
NULL);
protocol_start_response(sn, rq);
ret =
REQ_PROCEED;
}
else if(rq->status_num <=
0) {
int status_code =
500;
if(op->vfs->vfs_errno ==
EEXIST) {
status_code =
405;
}
else if(op->vfs->vfs_errno ==
ENOENT) {
status_code =
409;
}
else {
log_ereport(
LOG_VERBOSE,
"webdav_mkcol: errno: %d", op->vfs->vfs_errno);
}
protocol_status(sn, rq, status_code,
NULL);
}
return ret;
}
int webdav_post(pblock *pb, Session *sn, Request *rq) {
return REQ_ABORTED;
}
typedef struct DeleteFile {
char *path;
struct stat s;
struct DeleteFile *prev;
struct DeleteFile *next;
} DeleteFile;
typedef struct DeleteLists {
CxAllocator *a;
DeleteFile *dirs_begin;
DeleteFile *dirs_end;
DeleteFile *files_begin;
DeleteFile *files_end;
} DeleteOp;
static int deletelist_add(
VFSContext *vfs,
const char *href,
const char *path,
VFSDir *parent,
struct stat *s,
void *userdata)
{
DeleteOp *op = userdata;
DeleteFile *file = cxMalloc(op->a,
sizeof(DeleteFile));
if(!file) {
return 1;
}
file->path = cx_strdup_a(op->a, cx_str((
char*)path)).ptr;
if(!file->path) {
return 1;
}
file->s = *s;
file->next =
NULL;
DeleteFile **begin;
DeleteFile **end;
if(
S_ISDIR(s->st_mode)) {
begin = &op->dirs_begin;
end = &op->dirs_end;
}
else {
begin = &op->files_begin;
end = &op->files_end;
}
cx_linked_list_add(
(
void**)begin, (
void**)end,
offsetof(DeleteFile, prev), offsetof(DeleteFile, next),
file);
return 0;
}
static int webdav_delete_collection(WebdavVFSOperation *op)
{
DeleteOp del;
ZERO(&del,
sizeof(DeleteOp));
del.a = pool_allocator(op->sn->pool);
if(webdav_op_iterate_children(op->vfs, -
1,
NULL, op->path,
deletelist_add, &del))
{
return 1;
}
DeleteFile root;
root.path = op->path;
root.s = *op->stat;
root.prev =
NULL;
root.next = del.dirs_begin;
if(del.dirs_begin) {
del.dirs_begin->prev = &root;
}
else {
del.dirs_end = &root;
}
del.dirs_begin = &root;
for(DeleteFile *file=del.files_begin;file;file=file->next) {
WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s);
if(webdav_vfs_op_do(&sub,
WEBDAV_VFS_DELETE)) {
return 1;
}
}
for(DeleteFile *file=del.dirs_end;file;file=file->prev) {
WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s);
if(webdav_vfs_op_do(&sub,
WEBDAV_VFS_DELETE)) {
return 1;
}
}
return 0;
}
int webdav_delete(pblock *pb, Session *sn, Request *rq) {
WebdavVFSOperation *op = webdav_vfs_op(sn, rq, rq->davCollection,
TRUE);
if(!op) {
return REQ_ABORTED;
}
struct stat s;
if(vfs_stat(op->vfs, op->path, &s)) {
sys_set_error_status(op->vfs);
return REQ_ABORTED;
}
op->stat = &s;
int ret;
if(
S_ISDIR(s.st_mode)) {
ret = webdav_delete_collection(op);
}
else {
ret = webdav_vfs_op_do(op,
WEBDAV_VFS_DELETE);
}
if(ret ==
REQ_PROCEED) {
pblock_nvinsert(
"content-length",
"0", rq->srvhdrs);
protocol_status(sn, rq,
204,
NULL);
protocol_start_response(sn, rq);
}
else {
protocol_status(sn, rq,
500,
NULL);
}
return ret;
}
int webdav_put(pblock *pb, Session *sn, Request *rq) {
char *path = pblock_findkeyval(pb_key_path, rq->vars);
VFSContext *vfs = vfs_request_context(sn, rq);
if(!vfs) {
protocol_status(sn, rq,
PROTOCOL_SERVER_ERROR,
NULL);
return REQ_ABORTED;
}
struct stat s;
int create_file =
0;
if(vfs_stat(vfs, path, &s)) {
if(vfs->vfs_errno ==
ENOENT) {
create_file =
O_CREAT;
}
else {
protocol_status(sn, rq, util_errno2status(vfs->vfs_errno),
NULL);
return REQ_ABORTED;
}
}
else if(
S_ISDIR(s.st_mode)) {
protocol_status(sn, rq,
PROTOCOL_METHOD_NOT_ALLOWED,
NULL);
return REQ_ABORTED;
}
vfs->error_response_set =
FALSE;
SYS_FILE fd = vfs_open(vfs, path,
O_WRONLY |
O_TRUNC | create_file);
if(!fd) {
return REQ_ABORTED;
}
char *expect = pblock_findkeyval(pb_key_expect, rq->headers);
if(expect) {
if(!strcasecmp(expect,
"100-continue")) {
if(http_send_continue(sn)) {
return REQ_ABORTED;
}
}
}
char in[
4096];
int r;
while((r = netbuf_getbytes(sn->inbuf, in,
2048)) >
0) {
int w =
0;
while(w < r) {
w += system_fwrite(fd, in, r);
}
}
system_fclose(fd);
int status = create_file ?
PROTOCOL_CREATED :
PROTOCOL_NO_CONTENT;
pblock_nvinsert(
"content-length",
"0", rq->srvhdrs);
protocol_status(sn, rq, status,
NULL);
protocol_start_response(sn, rq);
return REQ_PROCEED;
}
int webdav_copy(pblock *pb, Session *sn, Request *rq) {
char *path = pblock_findkeyval(pb_key_path, rq->vars);
char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
char *destination = pblock_findval(
"destination", rq->headers);
if(!destination) {
protocol_status(sn, rq,
PROTOCOL_BAD_REQUEST,
NULL);
return REQ_ABORTED;
}
VFSContext *vfs = vfs_request_context(sn, rq);
if(!vfs) {
protocol_status(sn, rq,
PROTOCOL_SERVER_ERROR,
NULL);
return REQ_ABORTED;
}
struct stat src_s;
if(vfs_stat(vfs, path, &src_s)) {
protocol_status(sn, rq, util_errno2status(vfs->vfs_errno),
NULL);
return REQ_ABORTED;
}
return REQ_ABORTED;
}
int webdav_move(pblock *pb, Session *sn, Request *rq) {
return REQ_ABORTED;
}
int webdav_lock(pblock *pb, Session *sn, Request *rq) {
return REQ_ABORTED;
}
int webdav_unlock(pblock *pb, Session *sn, Request *rq) {
return REQ_ABORTED;
}
int webdav_report(pblock *pb, Session *sn, Request *rq) {
return REQ_ABORTED;
}
int webdav_acl(pblock *pb, Session *sn, Request *rq) {
return REQ_ABORTED;
}
int default_propfind_init(
WebdavPropfindRequest *rq,
const char* path,
const char *href,
WebdavPList **outplist)
{
DefaultWebdavData *data = pool_malloc(
rq->sn->pool,
sizeof(DefaultWebdavData));
if(!data) {
return 1;
}
rq->userdata = data;
data->vfsproperties = webdav_vfs_properties(outplist,
TRUE, rq->allprop,
0);
return 0;
}
int default_propfind_do(
WebdavPropfindRequest *request,
WebdavResponse *response,
VFS_DIR parent,
WebdavResource *resource,
struct stat *s)
{
DefaultWebdavData *data = request->userdata;
if(!s) {
return 1;
}
if(webdav_add_vfs_properties(
resource,
request->sn->pool,
data->vfsproperties,
s))
{
return 1;
}
return 0;
}
int default_propfind_finish(WebdavPropfindRequest *rq) {
return 0;
}
int default_proppatch_do(
WebdavProppatchRequest *request,
WebdavResource *response,
VFSFile *file,
WebdavPList **setInOut,
WebdavPList **removeInOut)
{
return 0;
}
int default_proppatch_finish(
WebdavProppatchRequest *request,
WebdavResource *response,
VFSFile *file,
WSBool commit)
{
return 0;
}
CxHashKey webdav_property_key_a(
const CxAllocator *a,
const char *ns,
const char *name) {
CxHashKey key;
cxmutstr data = cx_asprintf(
"%s\n%s", name, ns);
if(data.ptr) {
key.data = data.ptr;
key.len = data.length;
cx_hash_murmur(&key);
}
else {
key.data =
NULL;
key.len =
0;
key.hash =
0;
}
return key;
}
CxHashKey webdav_property_key(
const char *ns,
const char *name) {
return webdav_property_key_a(cxDefaultAllocator, ns, name);
}
int webdav_getdepth(Request *rq) {
char *depth_str = pblock_findkeyval(pb_key_depth, rq->headers);
int depth =
0;
if(depth_str) {
size_t dlen = strlen(depth_str);
if(!memcmp(depth_str,
"infinity", dlen)) {
depth = -
1;
}
else if(dlen ==
1 && depth_str[
0] ==
'1') {
depth =
1;
}
}
return depth;
}
int webdav_plist_add(
pool_handle_t *pool,
WebdavPList **begin,
WebdavPList **end,
WebdavProperty *prop)
{
WebdavPList *elm = pool_malloc(pool,
sizeof(WebdavPList));
if(!elm) {
return 1;
}
elm->prev = *end;
elm->next =
NULL;
elm->property = prop;
if(!*begin) {
*begin = elm;
*end = elm;
return 0;
}
(*end)->next = elm;
*end = elm;
return 0;
}
WebdavPList* webdav_plist_clone(
pool_handle_t *pool, WebdavPList *list) {
return webdav_plist_clone_s(pool, list,
NULL);
}
WebdavPList* webdav_plist_clone_s(
pool_handle_t *pool,
WebdavPList *list,
size_t *newlen)
{
WebdavPList *new_list =
NULL;
WebdavPList *new_list_end =
NULL;
size_t len =
0;
WebdavPList *elm = list;
while(elm) {
WebdavPList *new_elm = pool_malloc(pool,
sizeof(WebdavPList));
if(!new_elm) {
if(newlen) *newlen =
0;
return NULL;
}
new_elm->property = elm->property;
new_elm->prev = new_list_end;
new_elm->next =
NULL;
if(new_list_end) {
new_list_end->next = new_elm;
}
else {
new_list = new_elm;
}
new_list_end = new_elm;
len++;
elm = elm->next;
}
if(newlen) *newlen = len;
return new_list;
}
size_t webdav_plist_size(WebdavPList *list) {
size_t count =
0;
WebdavPList *elm = list;
while(elm) {
count++;
elm = elm->next;
}
return count;
}
WebdavPListIterator webdav_plist_iterator(WebdavPList **list) {
WebdavPListIterator i;
i.list = list;
i.cur =
NULL;
i.next = *list;
i.index =
0;
return i;
}
int webdav_plist_iterator_next(WebdavPListIterator *i, WebdavPList **cur) {
if(i->cur) {
i->index++;
}
i->cur = i->next;
i->next = i->cur ? i->cur->next :
NULL;
*cur = i->cur;
return i->cur !=
NULL;
}
void webdav_plist_iterator_remove_current(WebdavPListIterator *i) {
WebdavPList *cur = i->cur;
if(cur->prev) {
cur->prev->next = cur->next;
if(cur->next) {
cur->next->prev = cur->prev;
}
}
else {
*i->list = cur->next;
if(cur->next) {
cur->next->prev =
NULL;
}
}
}
int webdav_nslist_add(
pool_handle_t *pool,
WebdavNSList **begin,
WebdavNSList **end,
WSNamespace *ns)
{
WebdavNSList *elm = pool_malloc(pool,
sizeof(WebdavNSList));
if(!elm) {
return 1;
}
elm->prev = *end;
elm->next =
NULL;
elm->namespace = ns;
if(!*begin) {
*begin = elm;
*end = elm;
return 0;
}
(*end)->next = elm;
*end = elm;
return 0;
}
WSNamespace* webdav_dav_namespace(
void) {
return &dav_namespace;
}
WebdavProperty* webdav_resourcetype_collection(
void) {
return &dav_resourcetype_collection;
}
WebdavProperty* webdav_resourcetype_empty(
void) {
return &dav_resourcetype_empty;
}
WebdavProperty* webdav_dav_property(
pool_handle_t *pool,
const char *name)
{
WebdavProperty *property = pool_malloc(pool,
sizeof(WebdavProperty));
if(!property) {
return NULL;
}
memset(property,
0,
sizeof(WebdavProperty));
property->namespace = &dav_namespace;
property->name = name;
return property;
}
int webdav_resource_add_dav_stringproperty(
WebdavResource *res,
pool_handle_t pool,
const char *name,
const char *str,
size_t len)
{
WebdavProperty *property = webdav_dav_property(pool, name);
if(!property) {
return 1;
}
property->name = pool_strdup(pool, name);
if(!property->name) {
return 1;
}
char *value = pool_malloc(pool, len+
1);
if(!value) {
return 1;
}
memcpy(value, str, len);
value[len] =
'\0';
property->value.text.str = value;
property->value.text.length = len;
property->vtype =
WS_VALUE_TEXT;
return res->addproperty(res, property,
200);
}
int webdav_resource_add_stringproperty(
WebdavResource *res,
pool_handle_t pool,
const char *xmlns_prefix,
const char *xmlns_href,
const char *name,
const char *str,
size_t len)
{
WebdavProperty *property = pool_malloc(pool,
sizeof(WebdavProperty));
if(!property) {
return 1;
}
memset(property,
0,
sizeof(WebdavProperty));
property->name = pool_strdup(pool, name);
if(!property->name) {
return 1;
}
xmlNs *ns = pool_malloc(pool,
sizeof(xmlNs));
if(!ns) {
return 1;
}
memset(ns,
0,
sizeof(xmlNs));
ns->prefix = (
const xmlChar*)pool_strdup(pool, xmlns_prefix);
ns->href = (
const xmlChar*)pool_strdup(pool, xmlns_href);
if(!ns->prefix || !ns->href) {
return 1;
}
char *value = pool_malloc(pool, len+
1);
if(!value) {
return 1;
}
memcpy(value, str, len);
value[len] =
'\0';
property->value.text.str = value;
property->value.text.length = len;
property->vtype =
WS_VALUE_TEXT;
property->value.text.str = value;
property->value.text.length = len;
property->vtype =
WS_VALUE_TEXT;
return res->addproperty(res, property,
200);
}
int webdav_property_set_value(
WebdavProperty *p,
pool_handle_t *pool,
char *value)
{
WSXmlNode *node = pool_malloc(pool,
sizeof(WSXmlNode));
if(!node) {
return 1;
}
ZERO(node,
sizeof(WSXmlNode));
node->content = (xmlChar*)value;
node->type =
XML_TEXT_NODE;
p->value.node = node;
p->vtype =
WS_VALUE_XML_NODE;
return 0;
}
WebdavVFSProperties webdav_vfs_properties(
WebdavPList **plistInOut,
WSBool removefromlist,
WSBool allprop,
uint32_t flags)
{
WebdavVFSProperties ret;
ZERO(&ret,
sizeof(WebdavVFSProperties));
WSBool etag =
1;
WSBool creationdate =
1;
WebdavPListIterator i = webdav_plist_iterator(plistInOut);
WebdavPList *cur;
while(webdav_plist_iterator_next(&i, &cur)) {
WSNamespace *ns = cur->property->namespace;
if(ns && !strcmp((
const char*)ns->href,
"DAV:")) {
const char *name = cur->property->name;
WSBool remove_prop = removefromlist;
if(!strcmp(name,
"getlastmodified")) {
ret.getlastmodified =
1;
}
else if(!strcmp(name,
"getcontentlength")) {
ret.getcontentlength =
1;
}
else if(!strcmp(name,
"resourcetype")) {
ret.getresourcetype =
1;
}
else if(etag && !strcmp(name,
"getetag")) {
ret.getetag =
1;
}
else if(creationdate && !strcmp(name,
"creationdate")) {
ret.creationdate =
1;
}
else {
remove_prop =
FALSE;
}
if(remove_prop) {
webdav_plist_iterator_remove_current(&i);
}
}
}
if(allprop) {
ret.creationdate =
1;
ret.getcontentlength =
1;
ret.getetag =
1;
ret.getlastmodified =
1;
ret.getresourcetype =
1;
}
return ret;
}
int webdav_add_vfs_properties(
WebdavResource *res,
pool_handle_t *pool,
WebdavVFSProperties properties,
struct stat *s)
{
if(properties.getresourcetype) {
if(
S_ISDIR(s->st_mode)) {
res->addproperty(res, &dav_resourcetype_collection,
200);
}
else {
res->addproperty(res, &dav_resourcetype_empty,
200);
}
}
if(properties.getcontentlength) {
char *buf = pool_malloc(pool,
64);
if(!buf) {
return 1;
}
uint64_t contentlength = s->st_size;
snprintf(buf,
64,
"%" PRIu64, contentlength);
if(webdav_resource_add_dav_stringproperty(res, pool,
"getcontentlength", buf, strlen(buf))) {
return 1;
}
}
if(properties.getlastmodified) {
char *buf = pool_malloc(pool,
HTTP_DATE_LEN+
1);
if(!buf) {
return 1;
}
buf[
HTTP_DATE_LEN] =
0;
struct tm mtms;
struct tm *mtm = system_gmtime(&s->st_mtime, &mtms);
if(mtm) {
strftime(buf,
HTTP_DATE_LEN,
HTTP_DATE_FMT, mtm);
if(webdav_resource_add_dav_stringproperty(res, pool,
"getlastmodified", buf, strlen(buf))) {
return 1;
}
}
else {
return 1;
}
}
if(properties.creationdate) {
}
if(properties.getetag) {
char *buf = pool_malloc(pool,
96);
if(!buf) {
return 1;
}
snprintf(buf,
96,
"\"%x-%x\"",
(
int)s->st_size,
(
int)s->st_mtime);
if(webdav_resource_add_dav_stringproperty(res, pool,
"getetag", buf, strlen(buf))) {
return 1;
}
}
return 0;
}