libidav/webdav.c

changeset 1
b5bb7b3cd597
child 18
af411868ab9b
equal deleted inserted replaced
0:2483f517c562 1:b5bb7b3cd597
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2018 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <libxml/tree.h>
33
34 #include "utils.h"
35 #include "webdav.h"
36 #include "session.h"
37 #include "methods.h"
38 #include <cx/buffer.h>
39 #include <cx/utils.h>
40 #include <cx/linked_list.h>
41 #include <cx/hash_map.h>
42 #include <cx/compare.h>
43 #include "davqlparser.h"
44 #include "davqlexec.h"
45
46
47 DavContext* dav_context_new(void) {
48 // initialize
49 DavContext *context = calloc(1, sizeof(DavContext));
50 if(!context) {
51 return NULL;
52 }
53 context->sessions = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_intptr, CX_STORE_POINTERS);
54 context->sessions->destructor_data = dav_session_destructor;
55 context->http_proxy = calloc(1, sizeof(DavProxy));
56 if(!context->http_proxy) {
57 dav_context_destroy(context);
58 return NULL;
59 }
60 context->https_proxy = calloc(1, sizeof(DavProxy));
61 if(!context->https_proxy) {
62 dav_context_destroy(context);
63 return NULL;
64 }
65 context->namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
66 if(!context->namespaces) {
67 dav_context_destroy(context);
68 return NULL;
69 }
70 context->namespaceinfo = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
71 if(!context->namespaceinfo) {
72 dav_context_destroy(context);
73 }
74 context->keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
75 if(!context->keys) {
76 dav_context_destroy(context);
77 return NULL;
78 }
79
80 // add DAV: namespace
81 if(dav_add_namespace(context, "D", "DAV:")) {
82 dav_context_destroy(context);
83 return NULL;
84 }
85
86
87 // add idav namespace
88 if(dav_add_namespace(context, "idav", DAV_NS)) {
89 dav_context_destroy(context);
90 return NULL;
91 }
92
93 // add idavprops namespace
94 if(dav_add_namespace(context, "idavprops", DAV_PROPS_NS)) {
95 dav_context_destroy(context);
96 return NULL;
97 }
98
99 return context;
100 }
101
102 void dav_context_destroy(DavContext *ctx) {
103 // destroy all sessions assoziated with this context
104 // ctx->sessions destructor must be dav_session_destructor
105 cxListDestroy(ctx->sessions);
106
107 if(ctx->http_proxy) {
108 free(ctx->http_proxy);
109 }
110 if(ctx->https_proxy) {
111 free(ctx->https_proxy);
112 }
113
114 if(ctx->namespaces) {
115 CxIterator i = cxMapIteratorValues(ctx->namespaces);
116 cx_foreach(DavNamespace*, ns, i) {
117 if(!ns) continue;
118 if(ns->prefix) {
119 free(ns->prefix);
120 }
121 if(ns->name) {
122 free(ns->name);
123 }
124 free(ns);
125 }
126 cxMapDestroy(ctx->namespaces);
127 }
128 if(ctx->namespaceinfo) {
129 // TODO: implement
130 }
131 if(ctx->keys) {
132 CxIterator i = cxMapIteratorValues(ctx->keys);
133 cx_foreach(DavKey*, key, i) {
134 if(!key) continue;
135 if(key->name) {
136 free(key->name);
137 }
138 if(key->data) {
139 free(key->data);
140 }
141 free(key);
142 }
143 cxMapDestroy(ctx->keys);
144 }
145
146 free(ctx);
147 }
148
149 void dav_context_add_key(DavContext *context, DavKey *key) {
150 cxMapPut(context->keys, cx_hash_key_str(key->name), key);
151 }
152
153 DavKey* dav_context_get_key(DavContext *context, const char *name) {
154 if(name) {
155 return cxMapGet(context->keys, cx_hash_key_str(name));
156 }
157 return NULL;
158 }
159
160 int dav_add_namespace(DavContext *context, const char *prefix, const char *name) {
161 DavNamespace *namespace = malloc(sizeof(DavNamespace));
162 if(!namespace) {
163 return 1;
164 }
165
166 char *p = strdup(prefix);
167 char *n = strdup(name);
168
169 int err = 0;
170 if(p && n) {
171 namespace->prefix = p;
172 namespace->name = n;
173 err = cxMapPut(context->namespaces, cx_hash_key_str(prefix), namespace);
174 }
175
176 if(err) {
177 free(namespace);
178 if(p) free(p);
179 if(n) free(n);
180 }
181
182 return err;
183 }
184
185 DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) {
186 return cxMapGet(context->namespaces, cx_hash_key_str(prefix));
187 }
188
189 DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) {
190 return cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length));
191 }
192
193 int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt) {
194 CxHashKey hkey = cx_hash_key_str(ns);
195 DavNSInfo *info = cxMapGet(context->namespaceinfo, hkey);
196 if(!info) {
197 info = calloc(1, sizeof(DavNSInfo));
198 info->encrypt = encrypt;
199 cxMapPut(context->namespaceinfo, hkey, info);
200 } else {
201 info->encrypt = encrypt;
202 }
203 return 0;
204 }
205
206 int dav_namespace_is_encrypted(DavContext *context, const char *ns) {
207 DavNSInfo *info = cxMapGet(context->namespaceinfo, cx_hash_key_str(ns));
208 if(info) {
209 return info->encrypt;
210 }
211 return 0;
212 }
213
214 void dav_get_property_namespace_str(
215 DavContext *ctx,
216 char *prefixed_name,
217 char **ns,
218 char **name)
219 {
220 // TODO: rewrite using dav_get_property_ns
221
222 char *pname = strchr(prefixed_name, ':');
223 char *pns = "DAV:";
224 if(pname) {
225 DavNamespace *ns = dav_get_namespace_s(
226 ctx,
227 cx_strn(prefixed_name, pname-prefixed_name));
228 if(ns) {
229 pns = ns->name;
230 pname++;
231 } else {
232 pns = NULL;
233 pname = NULL;
234 }
235 } else {
236 pname = prefixed_name;
237 }
238 *ns = pns;
239 *name = pname;
240 }
241
242 DavNamespace* dav_get_property_namespace(
243 DavContext *ctx,
244 char *prefixed_name,
245 char **name)
246 {
247 char *pname = strchr(prefixed_name, ':');
248 if(pname) {
249 DavNamespace *ns = dav_get_namespace_s(
250 ctx,
251 cx_strn(prefixed_name, pname-prefixed_name));
252 if(ns) {
253 *name = pname +1;
254 return ns;
255 } else {
256 *name = NULL;
257 return NULL;
258 }
259 } else {
260 *name = prefixed_name;
261 return dav_get_namespace_s(ctx, cx_str("D"));
262 }
263 }
264
265 // TODO: add sstr_t version of dav_get_property_ns
266
267 void dav_set_effective_href(DavSession *sn, DavResource *resource) {
268 char *eff_url;
269 curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &eff_url);
270 if(eff_url) {
271 const char *href = util_url_path(eff_url);
272 if(strcmp(href, resource->href)) {
273 dav_session_free(sn, resource->href);
274 resource->href = dav_session_strdup(sn, href);
275 }
276 }
277 }
278
279 DavResource* dav_get(DavSession *sn, char *path, const char *properties) {
280 CURL *handle = sn->handle;
281 DavResource *resource = dav_resource_new(sn, path);
282 util_set_url(sn, dav_resource_get_href(resource));
283
284 CxList *proplist = NULL;
285 if(properties) {
286 proplist = parse_properties_string(sn->context, cx_str(properties));
287 }
288 CxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0);
289 CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
290
291 //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
292 //printf("\n");
293
294 CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
295 long status = 0;
296 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
297 if(ret == CURLE_OK && status == 207) {
298 dav_set_effective_href(sn, resource);
299
300 //printf("response\n%s\n", rpbuf->space);
301 // TODO: use PropfindParser
302 resource = parse_propfind_response(sn, resource, rpbuf);
303 resource->exists = 1;
304 sn->error = DAV_OK;
305 } else {
306 dav_session_set_error(sn, ret, status);
307 dav_resource_free(resource);
308 resource = NULL;
309 }
310
311 cxBufferFree(rqbuf);
312 cxBufferFree(rpbuf);
313
314 if(proplist) {
315 CxIterator i = cxListIterator(proplist);
316 cx_foreach(DavProperty*, p, i) {
317 free(p->name);
318 }
319 cxListDestroy(proplist);
320 }
321
322 return resource;
323 }
324
325
326 int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) {
327 // clean resource properties
328 DavResourceData *data = root->data;
329 cxMapClear(data->properties); // TODO: free existing content
330
331 CURL *handle = sn->handle;
332 util_set_url(sn, dav_resource_get_href(root));
333
334 CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
335 DavResource *resource = root;
336 CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
337 long status = 0;
338 long error = 0;
339 curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
340 if(ret == CURLE_OK && status == 207) {
341 //printf("response\n%s\n", rpbuf->space);
342 dav_set_effective_href(sn, resource);
343 // TODO: use PropfindParser
344 resource = parse_propfind_response(sn, resource, rpbuf);
345 sn->error = DAV_OK;
346 root->exists = 1;
347 } else {
348 dav_session_set_error(sn, ret, status);
349 error = 1;
350 }
351 cxBufferFree(rpbuf);
352 return error;
353 }
354
355 CxList* parse_properties_string(DavContext *context, cxstring str) {
356 CxList *proplist = cxLinkedListCreateSimple(sizeof(DavProperty));
357
358 CxStrtokCtx tok = cx_strtok(str, cx_str(","), INT_MAX);
359 cxstring s;
360 while(cx_strtok_next(&tok, &s)) {
361 cxstring nsname = cx_strchr(s, ':');
362 if(nsname.length > 0) {
363 cxstring nspre = cx_strsubsl(s, 0, nsname.ptr - s.ptr);
364 nsname.ptr++;
365 nsname.length--;
366
367 DavProperty dp;
368 cxstring pre = cx_strtrim(nspre);
369 dp.ns = dav_get_namespace_s(context, pre);
370 dp.name = cx_strdup(nsname).ptr;
371 dp.value = NULL;
372 if(dp.ns && dp.name) {
373 cxListAdd(proplist, &dp);
374 } else {
375 free(dp.name);
376 }
377 }
378 }
379
380 return proplist;
381 }
382
383 DavResource* dav_query(DavSession *sn, char *query, ...) {
384 DavQLStatement *stmt = dav_parse_statement(cx_str(query));
385 if(!stmt) {
386 sn->error = DAV_ERROR;
387 return NULL;
388 }
389 if(stmt->errorcode != 0) {
390 sn->error = DAV_QL_ERROR;
391 dav_free_statement(stmt);
392 return NULL;
393 }
394
395 va_list ap;
396 va_start(ap, query);
397 DavResult result = dav_statement_execv(sn, stmt, ap);
398 va_end(ap);
399
400 dav_free_statement(stmt);
401
402 if(result.status == -1) {
403 if(result.result) {
404 dav_resource_free(result.result);
405 result.result = NULL;
406 }
407 }
408
409 return result.result;
410 }
411
412
413
414
415 void dav_verbose_log(
416 DavSession *sn,
417 const char *method,
418 const char *url,
419 const char *request_body,
420 size_t request_bodylen,
421 int status,
422 const char *response_body,
423 size_t response_bodylen)
424 {
425 fprintf(stderr, "# method: %s url: %s status: %d\n", method, url, status);
426 fprintf(stderr, "# request len: %d\n%.*s\n", (int)request_bodylen, (int)request_bodylen, request_body);
427 fprintf(stderr, "# response len: %d\n%.*s\n", (int)response_bodylen, (int)response_bodylen, response_body);
428 }

mercurial