#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"
#include "methods.h"
#include "crypto.h"
#include "session.h"
#include "xml.h"
#include <cx/utils.h>
#include <cx/printf.h>
#include <cx/hash_map.h>
#define xstreq(a,b) xmlStrEqual(
BAD_CAST a,
BAD_CAST b)
int dav_buffer_seek(CxBuffer *b,
curl_off_t offset,
int origin) {
return cxBufferSeek(b, offset, origin) ==
0 ?
0:
CURL_SEEKFUNC_CANTSEEK;
}
CURLcode do_propfind_request(
DavSession *sn,
CxBuffer *request,
CxBuffer *response)
{
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"PROPFIND");
int depth =
1;
int maxretry =
2;
struct curl_slist *headers =
NULL;
CURLcode ret =
0;
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
1);
curl_easy_setopt(handle,
CURLOPT_READFUNCTION, cxBufferRead);
curl_easy_setopt(handle,
CURLOPT_SEEKFUNCTION, cxBufferSeek);
curl_easy_setopt(handle,
CURLOPT_READDATA, request);
curl_easy_setopt(handle,
CURLOPT_SEEKDATA, request);
curl_easy_setopt(handle,
CURLOPT_INFILESIZE, request->size);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, cxBufferWrite);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA, response);
CxMap *respheaders = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
32);
cxDefineDestructor(respheaders, free);
util_capture_header(handle, respheaders);
for(
int i=
0;i<maxretry;i++) {
if (depth ==
1) {
headers = curl_slist_append(headers,
"Depth: 1");
}
else if (depth == -
1) {
headers = curl_slist_append(headers,
"Depth: infinity");
}
else {
headers = curl_slist_append(headers,
"Depth: 0");
}
headers = curl_slist_append(headers,
"Content-Type: text/xml");
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
request->pos =
0;
response->size = response->pos =
0;
ret = dav_session_curl_perform_buf(sn, request, response,
NULL);
curl_slist_free_all(headers);
headers =
NULL;
char *msdavexterror;
msdavexterror = cxMapGet(respheaders, cx_hash_key_str(
"x-msdavext_error"));
int iishack = depth ==
1 &&
msdavexterror && !strncmp(msdavexterror,
"589831;",
7);
if(iishack) {
depth =
0;
}
else {
break;
}
}
util_capture_header(handle,
NULL);
cxMapDestroy(respheaders);
return ret;
}
CxBuffer* create_allprop_propfind_request(
void) {
CxBuffer *buf = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxstring s;
s =
CX_STR(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:propfind xmlns:D=\"DAV:\">\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:allprop/></D:propfind>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
return buf;
}
CxBuffer* create_cryptoprop_propfind_request(
void) {
CxBuffer *buf = cxBufferCreate(
NULL,
256, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxstring s;
s =
CX_STR(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
return buf;
}
CxBuffer* create_propfind_request(DavSession *sn, CxList *properties,
char *rootelm, DavBool nocrypt) {
CxBuffer *buf = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxstring s;
int add_crypto_name =
1;
int add_crypto_key =
1;
int add_crypto_hash =
1;
char *crypto_ns =
"idav";
CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
if(properties) {
CxIterator i = cxListIterator(properties);
cx_foreach(DavProperty*, p, i) {
if(strcmp(p->ns->name,
"DAV:")) {
cxMapPut(namespaces, cx_hash_key_str(p->ns->prefix), p->ns);
}
if(!strcmp(p->ns->name,
DAV_NS)) {
if(!strcmp(p->name,
"crypto-name")) {
add_crypto_name =
0;
crypto_ns = p->ns->prefix;
}
else if(!strcmp(p->name,
"crypto-key")) {
add_crypto_key =
0;
crypto_ns = p->ns->prefix;
}
else if(!strcmp(p->name,
"crypto-hash")) {
add_crypto_hash =
0;
crypto_ns = p->ns->prefix;
}
}
}
}
DavNamespace idav_ns;
if(add_crypto_name && add_crypto_key &&
DAV_CRYPTO(sn) && !nocrypt) {
idav_ns.prefix =
"idav";
idav_ns.name =
DAV_NS;
cxMapPut(namespaces, cx_hash_key_str(
"idav"), &idav_ns);
}
s =
CX_STR(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
cx_bprintf(buf,
"<D:%s xmlns:D=\"DAV:\"", rootelm);
CxIterator mapi = cxMapIteratorValues(namespaces);
cx_foreach(DavNamespace*, ns, mapi) {
s =
CX_STR(
" xmlns:");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(ns->prefix);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"=\"");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(ns->name);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"\"");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
s =
CX_STR(
">\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:prop>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:creationdate />\n<D:getlastmodified />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:getcontentlength />\n<D:getcontenttype />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:resourcetype />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
if(
DAV_CRYPTO(sn) && !nocrypt) {
if(add_crypto_name) {
cxBufferPut(buf,
'<');
cxBufferPutString(buf, crypto_ns);
s =
CX_STR(
":crypto-name />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
if(add_crypto_key) {
cxBufferPut(buf,
'<');
cxBufferPutString(buf, crypto_ns);
s =
CX_STR(
":crypto-key />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
if(add_crypto_hash) {
cxBufferPut(buf,
'<');
cxBufferPutString(buf, crypto_ns);
s =
CX_STR(
":crypto-hash />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
}
if(properties) {
CxIterator i = cxListIterator(properties);
cx_foreach(DavProperty*, prop, i) {
s =
CX_STR(
"<");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(prop->ns->prefix);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
":");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(prop->name);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
" />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
}
cx_bprintf(buf,
"</D:prop>\n</D:%s>\n", rootelm);
cxMapDestroy(namespaces);
return buf;
}
CxBuffer* create_basic_propfind_request(
void) {
CxBuffer *buf = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxstring s;
s =
CX_STR(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
DAV_NS);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"\" >\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:prop>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:resourcetype />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<i:crypto-key />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<i:crypto-name />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<i:crypto-hash />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"</D:prop>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"</D:propfind>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
return buf;
}
PropfindParser* create_propfind_parser(CxBuffer *response,
char *url) {
PropfindParser *parser = malloc(
sizeof(PropfindParser));
if(!parser) {
return NULL;
}
parser->document = xmlReadMemory(response->space, response->pos, url,
NULL,
0);
parser->current =
NULL;
if(parser->document) {
xmlNode *xml_root = xmlDocGetRootElement(parser->document);
if(xml_root) {
xmlNode *node = xml_root->children;
while(node) {
if(node->type ==
XML_ELEMENT_NODE) {
if(xstreq(node->name,
"response")) {
parser->current = node;
break;
}
}
node = node->next;
}
return parser;
}
else {
xmlFreeDoc(parser->document);
}
}
free(parser);
return NULL;
}
void destroy_propfind_parser(PropfindParser *parser) {
if(parser->document) {
xmlFreeDoc(parser->document);
}
free(parser);
}
int get_propfind_response(PropfindParser *parser, ResponseTag *result) {
if(parser->current ==
NULL) {
return 0;
}
char *href =
NULL;
int iscollection =
0;
char *crypto_name =
NULL;
char *crypto_key =
NULL;
result->properties = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
xmlNode *node = parser->current->children;
while(node) {
if(node->type ==
XML_ELEMENT_NODE) {
if(xstreq(node->name,
"href")) {
xmlNode *href_node = node->children;
if(href_node->type !=
XML_TEXT_NODE) {
return -
1;
}
href = (
char*)href_node->content;
}
else if(xstreq(node->name,
"propstat")) {
xmlNode *n = node->children;
xmlNode *prop_node =
NULL;
int ok =
0;
while(n) {
if(n->type ==
XML_ELEMENT_NODE) {
if(xstreq(n->name,
"prop")) {
prop_node = n;
}
else if(xstreq(n->name,
"status")) {
xmlNode *status_node = n->children;
if(status_node->type !=
XML_TEXT_NODE) {
return -
1;
}
cxstring status_str = cx_str((
char*)status_node->content);
if(status_str.length <
13) {
return -
1;
}
status_str = cx_strsubsl(status_str,
9,
3);
if(!cx_strcmp(status_str,
CX_STR(
"200"))) {
ok =
1;
}
}
}
n = n->next;
}
if(ok) {
n = prop_node->children;
while(n) {
if(n->type ==
XML_ELEMENT_NODE) {
cxListAdd(result->properties, n);
if(xstreq(n->name,
"resourcetype")) {
if(parse_resource_type(n)) {
iscollection =
TRUE;
}
}
else if(xstreq(n->ns->href,
DAV_NS)) {
if(xstreq(n->name,
"crypto-name")) {
crypto_name = util_xml_get_text(n);
}
else if(xstreq(n->name,
"crypto-key")) {
crypto_key = util_xml_get_text(n);
}
}
}
n = n->next;
}
}
}
}
node = node->next;
}
result->href = util_url_path(href);
result->iscollection = iscollection;
result->crypto_name = crypto_name;
result->crypto_key = crypto_key;
xmlNode *next = parser->current->next;
while(next) {
if(next->type ==
XML_ELEMENT_NODE) {
if(xstreq(next->name,
"response")) {
break;
}
}
next = next->next;
}
parser->current = next;
return 1;
}
void cleanup_response(ResponseTag *result) {
if(result) {
cxListDestroy(result->properties);
}
}
int hrefeq(DavSession *sn,
const char *href1,
const char *href2) {
cxmutstr href_s = cx_mutstr(util_url_decode(sn, href1));
cxmutstr href_r = cx_mutstr(util_url_decode(sn, href2));
int ret =
0;
if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) {
ret =
1;
}
else if(href_s.length == href_r.length +
1) {
if(href_s.ptr[href_s.length-
1] ==
'/') {
href_s.length--;
if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) {
ret =
1;
}
}
}
else if(href_r.length == href_s.length +
1) {
if(href_r.ptr[href_r.length-
1] ==
'/') {
href_r.length--;
if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) {
ret =
1;
}
}
}
free(href_s.ptr);
free(href_r.ptr);
return ret;
}
DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response) {
char *url =
NULL;
curl_easy_getinfo(sn->handle,
CURLINFO_EFFECTIVE_URL, &url);
if(!root) {
printf(
"methods.c: TODO: remove\n");
root = dav_resource_new_href(sn, util_url_path(url));
}
xmlDoc *doc = xmlReadMemory(response->space, response->size, url,
NULL,
0);
if(!doc) {
sn->error =
DAV_ERROR;
return NULL;
}
xmlNode *xml_root = xmlDocGetRootElement(doc);
xmlNode *node = xml_root->children;
while(node) {
if(node->type ==
XML_ELEMENT_NODE) {
if(xstreq(node->name,
"response")) {
parse_response_tag(root, node);
}
}
node = node->next;
}
xmlFreeDoc(doc);
return root;
}
DavResource* response2resource(DavSession *sn, ResponseTag *response,
char *parent_path) {
char *name =
NULL;
DavKey *key =
NULL;
if(
DAV_DECRYPT_NAME(sn) && response->crypto_name && (key = dav_context_get_key(sn->context, response->crypto_key))) {
if(!response->crypto_key) {
sn->error =
DAV_ERROR;
dav_session_set_errstr(sn,
"Missing crypto-key property");
return NULL;
}
name = util_decrypt_str_k(sn, response->crypto_name, key);
if(!name) {
sn->error =
DAV_ERROR;
dav_session_set_errstr(sn,
"Cannot decrypt resource name");
return NULL;
}
}
else {
cxstring resname = cx_str(util_resource_name(response->href));
int nlen =
0;
char *uname = curl_easy_unescape(
sn->handle,
resname.ptr,
resname.length,
&nlen);
name = dav_session_strdup(sn, uname);
curl_free(uname);
}
char *href = dav_session_strdup(sn, response->href);
DavResource *res =
NULL;
if(parent_path) {
res = dav_resource_new_full(sn, parent_path, name, href);
}
else {
res = dav_resource_new_href(sn, href);
}
dav_session_free(sn, name);
add_properties(res, response);
return res;
}
void add_properties(DavResource *res, ResponseTag *response) {
res->iscollection = response->iscollection;
int decrypt_props =
DAV_ENCRYPT_PROPERTIES(res->session);
xmlNode *crypto_prop =
NULL;
char *crypto_key =
NULL;
if(response->properties) {
CxIterator i = cxListIterator(response->properties);
cx_foreach(xmlNode*, prop, i) {
resource_add_property(res, (
char*)prop->ns->href, (
char*)prop->name, prop->children);
if (decrypt_props &&
prop->children &&
prop->children->type ==
XML_TEXT_NODE &&
xstreq(prop->ns->href,
DAV_NS))
{
if(xstreq(prop->name,
"crypto-prop")) {
crypto_prop = prop;
}
else if(xstreq(prop->name,
"crypto-key")) {
crypto_key = util_xml_get_text(prop);
}
}
}
}
if(crypto_prop && crypto_key) {
char *crypto_prop_content = util_xml_get_text(crypto_prop);
DavKey *key = dav_context_get_key(res->session->context, crypto_key);
if(crypto_prop_content) {
CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
resource_set_crypto_properties(res, cprops);
}
}
set_davprops(res);
}
int parse_response_tag(DavResource *resource, xmlNode *node) {
DavSession *sn = resource->session;
DavResource *res =
NULL;
const char *href =
NULL;
CxList *properties = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
char *crypto_name =
NULL;
char *crypto_key =
NULL;
int iscollection =
0;
node = node->children;
while(node) {
if(node->type ==
XML_ELEMENT_NODE) {
if(xstreq(node->name,
"href")) {
xmlNode *href_node = node->children;
if(href_node->type !=
XML_TEXT_NODE) {
sn->error =
DAV_ERROR;
return 1;
}
href = util_url_path((
const char*)href_node->content);
char *href_s = util_url_decode(resource->session, href);
char *href_r = util_url_decode(resource->session, resource->href);
if(hrefeq(sn, href_s, href_r)) {
res = resource;
}
free(href_s);
free(href_r);
}
else if(xstreq(node->name,
"propstat")) {
xmlNode *n = node->children;
xmlNode *prop_node =
NULL;
int ok =
0;
while(n) {
if(n->type ==
XML_ELEMENT_NODE) {
if(xstreq(n->name,
"prop")) {
prop_node = n;
}
else if(xstreq(n->name,
"status")) {
xmlNode *status_node = n->children;
if(status_node->type !=
XML_TEXT_NODE) {
sn->error =
DAV_ERROR;
return 1;
}
cxstring status_str = cx_str((
char*)status_node->content);
if(status_str.length <
13) {
sn->error =
DAV_ERROR;
return 1;
}
status_str = cx_strsubsl(status_str,
9,
3);
if(!cx_strcmp(status_str,
CX_STR(
"200"))) {
ok =
1;
}
}
}
n = n->next;
}
if(ok) {
n = prop_node->children;
while(n) {
if(n->type ==
XML_ELEMENT_NODE) {
cxListAdd(properties, n);
if(xstreq(n->name,
"resourcetype")) {
if(parse_resource_type(n)) {
iscollection =
TRUE;
}
}
else if(n->ns && xstreq(n->ns->href,
DAV_NS)) {
if(xstreq(n->name,
"crypto-name")) {
crypto_name = util_xml_get_text(n);
}
else if(xstreq(n->name,
"crypto-key")) {
crypto_key = util_xml_get_text(n);
}
}
}
n = n->next;
}
}
}
}
node = node->next;
}
if(!res) {
char *name =
NULL;
if(
DAV_DECRYPT_NAME(sn) && crypto_name) {
if(!crypto_key) {
sn->error =
DAV_ERROR;
dav_session_set_errstr(sn,
"Missing crypto-key property");
return -
1;
}
name = util_decrypt_str(sn, crypto_name, crypto_key);
if(!name) {
sn->error =
DAV_ERROR;
dav_session_set_errstr(sn,
"Cannot decrypt resource name");
return -
1;
}
}
else {
cxstring resname = cx_str(util_resource_name(href));
int nlen =
0;
char *uname = curl_easy_unescape(
sn->handle,
resname.ptr,
resname.length,
&nlen);
name = dav_session_strdup(sn, uname);
curl_free(uname);
}
char *href_cp = dav_session_strdup(sn, href);
res = dav_resource_new_full(sn, resource->path, name, href_cp);
dav_session_free(sn, name);
}
res->iscollection = iscollection;
int decrypt_props =
DAV_ENCRYPT_PROPERTIES(res->session);
xmlNode *crypto_prop =
NULL;
CxIterator i = cxListIterator(properties);
cx_foreach(xmlNode*, prop, i) {
if(!prop->ns) {
continue;
}
resource_add_property(res, (
char*)prop->ns->href, (
char*)prop->name, prop->children);
if (decrypt_props &&
prop->children &&
prop->children->type ==
XML_TEXT_NODE &&
xstreq(prop->ns->href,
DAV_NS))
{
if(xstreq(prop->name,
"crypto-prop")) {
crypto_prop = prop;
}
}
}
cxListDestroy(properties);
if(crypto_prop && crypto_key) {
char *crypto_prop_content = util_xml_get_text(crypto_prop);
DavKey *key = dav_context_get_key(res->session->context, crypto_key);
if(crypto_prop_content && key) {
CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
resource_set_crypto_properties(res, cprops);
}
}
set_davprops(res);
if(res != resource) {
resource_add_child(resource, res);
}
return 0;
}
void set_davprops(DavResource *res) {
char *cl = dav_get_string_property_ns(res,
"DAV:",
"getcontentlength");
char *ct = dav_get_string_property_ns(res,
"DAV:",
"getcontenttype");
char *cd = dav_get_string_property_ns(res,
"DAV:",
"creationdate");
char *lm = dav_get_string_property_ns(res,
"DAV:",
"getlastmodified");
res->contenttype = ct;
if(cl) {
char *end =
NULL;
res->contentlength = strtoull(cl, &end,
0);
}
res->creationdate = util_parse_creationdate(cd);
res->lastmodified = util_parse_lastmodified(lm);
}
int parse_resource_type(xmlNode *node) {
int collection =
FALSE;
xmlNode *c = node->children;
while(c) {
if(c->type ==
XML_ELEMENT_NODE) {
if(xstreq(c->ns->href,
"DAV:") && xstreq(c->name,
"collection")) {
collection =
TRUE;
break;
}
}
c = c->next;
}
return collection;
}
CURLcode do_proppatch_request(
DavSession *sn,
char *lock,
CxBuffer *request,
CxBuffer *response)
{
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"PROPPATCH");
struct curl_slist *headers =
NULL;
headers = curl_slist_append(headers,
"Content-Type: text/xml");
if(lock) {
char *url =
NULL;
curl_easy_getinfo(handle,
CURLINFO_EFFECTIVE_URL, &url);
char *ltheader = cx_asprintf(
"If: <%s> (<%s>)", url, lock).ptr;
headers = curl_slist_append(headers, ltheader);
free(ltheader);
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
}
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
1);
curl_easy_setopt(handle,
CURLOPT_READFUNCTION, cxBufferRead);
curl_easy_setopt(handle,
CURLOPT_SEEKFUNCTION, cxBufferSeek);
curl_easy_setopt(handle,
CURLOPT_READDATA, request);
curl_easy_setopt(handle,
CURLOPT_SEEKDATA, request);
curl_easy_setopt(handle,
CURLOPT_INFILESIZE, request->size);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, cxBufferWrite);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA, response);
cxBufferSeek(request,
0,
SEEK_SET);
CURLcode ret = dav_session_curl_perform_buf(sn, request, response,
NULL);
curl_slist_free_all(headers);
return ret;
}
CxBuffer* create_proppatch_request(DavResourceData *data) {
CxBuffer *buf = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxstring s;
CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator,
CX_STORE_POINTERS,
8);
cxDefineDestructor(namespaces, free);
{
char prefix[
8];
int pfxnum =
0;
if (data->set && cxListSize(data->set) >
0) {
CxIterator i = cxListIterator(data->set);
cx_foreach(DavProperty*, p, i) {
if (strcmp(p->ns->name,
"DAV:")) {
snprintf(prefix,
8,
"x%d", pfxnum++);
cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
}
}
}
if (data->remove && cxListSize(data->remove) >
0) {
CxIterator i = cxListIterator(data->remove);
cx_foreach(DavProperty*, p, i) {
if (strcmp(p->ns->name,
"DAV:")) {
snprintf(prefix,
8,
"x%d", pfxnum++);
cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
}
}
}
}
s =
CX_STR(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:propertyupdate xmlns:D=\"DAV:\"");
cxBufferWrite(s.ptr,
1, s.length, buf);
CxIterator mapi = cxMapIterator(namespaces);
cx_foreach(CxMapEntry*, entry, mapi) {
s =
CX_STR(
" xmlns:");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(entry->value);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"=\"");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_strn(entry->key->data, entry->key->len);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"\"");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
s =
CX_STR(
">\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
if(data->set) {
s =
CX_STR(
"<D:set>\n<D:prop>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
CxIterator i = cxListIterator(data->set);
cx_foreach(DavProperty*, property, i) {
char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
if(!prefix) {
prefix =
"D";
}
s =
CX_STR(
"<");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(prefix);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
":");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(property->name);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
">");
cxBufferWrite(s.ptr,
1, s.length, buf);
DavXmlNode *content = property->value;
if(content->type ==
DAV_XML_TEXT && !content->next) {
cxBufferWrite(content->content,
1, content->contentlength, buf);
}
else {
dav_print_node(buf, (cx_write_func)cxBufferWrite, namespaces, content);
}
s =
CX_STR(
"</");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(prefix);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
":");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(property->name);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
">\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
s =
CX_STR(
"</D:prop>\n</D:set>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
if(data->remove) {
s =
CX_STR(
"<D:remove>\n<D:prop>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
CxIterator i = cxListIterator(data->remove);
cx_foreach(DavProperty*, property, i) {
char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
s =
CX_STR(
"<");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(prefix);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
":");
cxBufferWrite(s.ptr,
1, s.length, buf);
s = cx_str(property->name);
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
" />\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
s =
CX_STR(
"</D:prop>\n</D:remove>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
s =
CX_STR(
"</D:propertyupdate>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
cxMapDestroy(namespaces);
return buf;
}
CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key,
const char *name,
const char *hash) {
CxBuffer *buf = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxstring s;
s =
CX_STR(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:set>\n<D:prop>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
if(
DAV_ENCRYPT_NAME(sn)) {
s =
CX_STR(
"<idav:crypto-name>");
cxBufferWrite(s.ptr,
1, s.length, buf);
char *crname = aes_encrypt(name, strlen(name), key);
cxBufferPutString(buf, crname);
free(crname);
s =
CX_STR(
"</idav:crypto-name>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
s =
CX_STR(
"<idav:crypto-key>");
cxBufferWrite(s.ptr,
1, s.length, buf);
cxBufferPutString(buf, key->name);
s =
CX_STR(
"</idav:crypto-key>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
if(hash) {
s =
CX_STR(
"<idav:crypto-hash>");
cxBufferWrite(s.ptr,
1, s.length, buf);
cxBufferPutString(buf, hash);
s =
CX_STR(
"</idav:crypto-hash>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
}
s =
CX_STR(
"</D:prop>\n</D:set>\n</D:propertyupdate>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
return buf;
}
static size_t dummy_write(
void *buf,
size_t s,
size_t n,
void *data) {
return s*n;
}
CURLcode do_put_request(DavSession *sn,
char *lock, DavBool create,
void *data, dav_read_func read_func, dav_seek_func seek_func,
size_t length) {
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
NULL);
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
1L);
struct curl_slist *headers =
NULL;
if(lock) {
char *url =
NULL;
curl_easy_getinfo(handle,
CURLINFO_EFFECTIVE_URL, &url);
char *ltheader =
NULL;
if(create) {
url = util_parent_path(url);
ltheader = cx_asprintf(
"If: <%s> (<%s>)", url, lock).ptr;
free(url);
}
else {
ltheader = cx_asprintf(
"If: <%s> (<%s>)", url, lock).ptr;
}
headers = curl_slist_append(headers, ltheader);
free(ltheader);
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
}
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
CxBuffer *buf =
NULL;
if(!read_func) {
buf = cxBufferCreate(data, length, cxDefaultAllocator,
0);
buf->size = length;
data = buf;
read_func = (dav_read_func)cxBufferRead;
curl_easy_setopt(handle,
CURLOPT_INFILESIZE_LARGE, (
curl_off_t)length);
}
else if(length ==
0) {
headers = curl_slist_append(headers,
"Transfer-Encoding: chunked");
curl_easy_setopt(handle,
CURLOPT_INFILESIZE_LARGE, (
curl_off_t)-
1);
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
}
else {
curl_easy_setopt(handle,
CURLOPT_INFILESIZE_LARGE, (
curl_off_t)length);
}
curl_easy_setopt(handle,
CURLOPT_READFUNCTION, read_func);
curl_easy_setopt(handle,
CURLOPT_SEEKFUNCTION, seek_func);
curl_easy_setopt(handle,
CURLOPT_SEEKDATA, data);
curl_easy_setopt(handle,
CURLOPT_READDATA, data);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, dummy_write);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA,
NULL);
CURLcode ret = dav_session_curl_perform(sn,
NULL);
curl_slist_free_all(headers);
if(buf) {
cxBufferFree(buf);
}
return ret;
}
CURLcode do_delete_request(DavSession *sn,
char *lock, CxBuffer *response) {
CURL *handle = sn->handle;
struct curl_slist *headers =
NULL;
if(lock) {
char *url =
NULL;
curl_easy_getinfo(handle,
CURLINFO_EFFECTIVE_URL, &url);
char *ltheader = cx_asprintf(
"If: <%s> (<%s>)", url, lock).ptr;
headers = curl_slist_append(headers, ltheader);
free(ltheader);
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
}
else {
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER,
NULL);
}
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"DELETE");
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
0L);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, cxBufferWrite);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA, response);
CURLcode ret = dav_session_curl_perform(sn,
NULL);
curl_slist_free_all(headers);
return ret;
}
CURLcode do_mkcol_request(DavSession *sn,
char *lock) {
CURL *handle = sn->handle;
struct curl_slist *headers =
NULL;
if(lock) {
char *url =
NULL;
curl_easy_getinfo(handle,
CURLINFO_EFFECTIVE_URL, &url);
url = util_parent_path(url);
char *ltheader = cx_asprintf(
"If: <%s> (<%s>)", url, lock).ptr;
free(url);
headers = curl_slist_append(headers, ltheader);
free(ltheader);
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
}
else {
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER,
NULL);
}
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"MKCOL");
curl_easy_setopt(handle,
CURLOPT_PUT,
0L);
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
0L);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, dummy_write);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA,
NULL);
CURLcode ret = dav_session_curl_perform(sn,
NULL);
curl_slist_free_all(headers);
return ret;
}
CURLcode do_head_request(DavSession *sn) {
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"HEAD");
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
0L);
curl_easy_setopt(handle,
CURLOPT_NOBODY,
1L);
struct curl_slist *headers =
NULL;
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, dummy_write);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA,
NULL);
CURLcode ret = dav_session_curl_perform(sn,
NULL);
curl_easy_setopt(handle,
CURLOPT_NOBODY,
0L);
return ret;
}
CURLcode do_copy_move_request(DavSession *sn,
char *dest,
char *lock, DavBool copy, DavBool override) {
CURL *handle = sn->handle;
if(copy) {
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"COPY");
}
else {
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"MOVE");
}
curl_easy_setopt(handle,
CURLOPT_PUT,
0L);
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
0L);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, dummy_write);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA,
NULL);
struct curl_slist *headers =
NULL;
if(lock) {
char *url =
NULL;
curl_easy_getinfo(handle,
CURLINFO_EFFECTIVE_URL, &url);
char *ltheader = cx_asprintf(
"If: <%s> (<%s>)", url, lock).ptr;
headers = curl_slist_append(headers, ltheader);
free(ltheader);
}
cxmutstr deststr = cx_strcat(
2,
CX_STR(
"Destination: "), cx_str(dest));
headers = curl_slist_append(headers, deststr.ptr);
if(override) {
headers = curl_slist_append(headers,
"Overwrite: T");
}
else {
headers = curl_slist_append(headers,
"Overwrite: F");
}
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
CURLcode ret = dav_session_curl_perform(sn,
NULL);
free(deststr.ptr);
curl_slist_free_all(headers);
headers =
NULL;
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
return ret;
}
CxBuffer* create_lock_request(
void) {
CxBuffer *buf = cxBufferCreate(
NULL,
512, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxstring s;
s =
CX_STR(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"<D:lockinfo xmlns:D=\"DAV:\">\n"
"<D:lockscope><D:exclusive/></D:lockscope>\n"
"<D:locktype><D:write/></D:locktype>\n"
"<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
s =
CX_STR(
"</D:lockinfo>\n");
cxBufferWrite(s.ptr,
1, s.length, buf);
return buf;
}
int parse_lock_response(DavSession *sn, CxBuffer *response, LockDiscovery *lock) {
lock->locktoken =
NULL;
lock->timeout =
NULL;
xmlDoc *doc = xmlReadMemory(response->space, response->size,
NULL,
NULL,
0);
if(!doc) {
sn->error =
DAV_ERROR;
return -
1;
}
char *timeout =
NULL;
char *locktoken =
NULL;
int ret = -
1;
xmlNode *xml_root = xmlDocGetRootElement(doc);
DavBool lockdiscovery =
0;
if(xml_root) {
xmlNode *node = xml_root->children;
while(node) {
if(node->type ==
XML_ELEMENT_NODE) {
if(xstreq(node->name,
"lockdiscovery")) {
node = node->children;
lockdiscovery =
1;
continue;
}
if(xstreq(node->name,
"activelock")) {
node = node->children;
continue;
}
if(lockdiscovery) {
if(xstreq(node->name,
"timeout")) {
timeout = util_xml_get_text(node);
}
else if(xstreq(node->name,
"locktoken")) {
xmlNode *n = node->children;
while(n) {
if(xstreq(n->name,
"href")) {
locktoken = util_xml_get_text(n);
break;
}
n = n->next;
}
}
}
}
node = node->next;
}
}
if(timeout && locktoken) {
lock->timeout = strdup(timeout);
lock->locktoken = strdup(locktoken);
ret =
0;
}
xmlFreeDoc(doc);
return ret;
}
CURLcode do_lock_request(DavSession *sn, CxBuffer *request, CxBuffer *response,
time_t timeout) {
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"LOCK");
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
1L);
request->pos =
0;
struct curl_slist *headers =
NULL;
if(timeout !=
0) {
cxmutstr thdr;
if(timeout <
0) {
thdr = cx_asprintf(
"%s",
"Timeout: Infinite");
}
else {
thdr = cx_asprintf(
"Timeout: Second-%u", (
unsigned int)timeout);
}
headers = curl_slist_append(headers, thdr.ptr);
free(thdr.ptr);
}
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
1);
curl_easy_setopt(handle,
CURLOPT_READFUNCTION, cxBufferRead);
curl_easy_setopt(handle,
CURLOPT_SEEKFUNCTION, cxBufferSeek);
curl_easy_setopt(handle,
CURLOPT_READDATA, request);
curl_easy_setopt(handle,
CURLOPT_SEEKDATA, request);
curl_easy_setopt(handle,
CURLOPT_INFILESIZE, request->size);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, cxBufferWrite);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA, response);
CURLcode ret = dav_session_curl_perform_buf(sn, request, response,
NULL);
if(headers) {
curl_slist_free_all(headers);
}
return ret;
}
CURLcode do_unlock_request(DavSession *sn,
char *locktoken) {
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"UNLOCK");
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
0L);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, dummy_write);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA,
NULL);
cxmutstr ltheader = cx_asprintf(
"Lock-Token: <%s>", locktoken);
struct curl_slist *headers = curl_slist_append(
NULL, ltheader.ptr);
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
CURLcode ret = dav_session_curl_perform(sn,
NULL);
curl_slist_free_all(headers);
free(ltheader.ptr);
return ret;
}
CURLcode do_simple_request(DavSession *sn,
char *method,
char *locktoken) {
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST, method);
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
0L);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, dummy_write);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA,
NULL);
cxmutstr ltheader;
struct curl_slist *headers =
NULL;
if(locktoken) {
ltheader = cx_asprintf(
"Lock-Token: <%s>", locktoken);
headers = curl_slist_append(
NULL, ltheader.ptr);
}
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
CURLcode ret = dav_session_curl_perform(sn,
NULL);
if(locktoken) {
curl_slist_free_all(headers);
free(ltheader.ptr);
}
return ret;
}
CURLcode do_report_request(DavSession *sn, CxBuffer *request, CxBuffer *response) {
CURL *handle = sn->handle;
curl_easy_setopt(handle,
CURLOPT_CUSTOMREQUEST,
"REPORT");
curl_easy_setopt(handle,
CURLOPT_UPLOAD,
1);
curl_easy_setopt(handle,
CURLOPT_READFUNCTION, cxBufferRead);
curl_easy_setopt(handle,
CURLOPT_SEEKFUNCTION, cxBufferSeek);
curl_easy_setopt(handle,
CURLOPT_READDATA, request);
curl_easy_setopt(handle,
CURLOPT_SEEKDATA, request);
curl_easy_setopt(handle,
CURLOPT_INFILESIZE, request->size);
curl_easy_setopt(handle,
CURLOPT_WRITEFUNCTION, cxBufferWrite);
curl_easy_setopt(handle,
CURLOPT_WRITEDATA, response);
struct curl_slist *headers =
NULL;
headers = curl_slist_append(headers,
"Content-Type: text/xml");
curl_easy_setopt(handle,
CURLOPT_HTTPHEADER, headers);
request->pos =
0;
response->size = response->pos =
0;
CURLcode ret = dav_session_curl_perform_buf(sn, request, response,
NULL);
curl_slist_free_all(headers);
return ret;
}