1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
49 DavContext *context = calloc(
1,
sizeof(DavContext));
50 if(!context) {
51 return NULL;
52 }
53 context->sessions = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr,
CX_STORE_POINTERS);
54 cxDefineDestructor(context->sessions, 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
81 if(dav_add_namespace(context,
"D",
"DAV:")) {
82 dav_context_destroy(context);
83 return NULL;
84 }
85
86
87
88 if(dav_add_namespace(context,
"idav",
DAV_NS)) {
89 dav_context_destroy(context);
90 return NULL;
91 }
92
93
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
104
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
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 #ifndef _WIN32
150
151 void dav_context_set_mtsafe(DavContext *ctx, DavBool enable) {
152 if (enable) {
153 pthread_mutex_init(&ctx->mutex,
NULL);
154 }
else {
155 pthread_mutex_destroy(&ctx->mutex);
156 }
157 ctx->mtsafe = enable;
158 }
159
160 void dav_context_lock(DavContext *ctx) {
161 if (ctx->mtsafe) {
162 pthread_mutex_lock(&ctx->mutex);
163 }
164 }
165
166 void dav_context_unlock(DavContext *ctx) {
167 if (ctx->mtsafe) {
168 pthread_mutex_unlock(&ctx->mutex);
169 }
170 }
171
172 #else
173
174 void dav_context_set_mtsafe(DavContext *ctx, DavBool enable) {
175 if (enable) {
176 ctx->mutex = CreateMutex(
NULL,
FALSE,
NULL);
177 }
else {
178 CloseHandle(ctx->mutex);
179 }
180 ctx->mtsafe = enable;
181 }
182
183 void dav_context_lock(DavContext *ctx) {
184 if (ctx->mtsafe) {
185 WaitForSingleObject(ctx->mutex,
INFINITE);
186 }
187 }
188
189 void dav_context_unlock(DavContext *ctx) {
190 if (ctx->mtsafe) {
191 ReleaseMutex(ctx->mutex);
192 }
193 }
194
195 #endif
196
197 void dav_context_add_key(DavContext *context, DavKey *key) {
198 dav_context_lock(context);
199 cxMapPut(context->keys, cx_hash_key_str(key->name), key);
200 dav_context_unlock(context);
201 }
202
203 DavKey* dav_context_get_key(DavContext *context,
const char *name) {
204 DavKey *key =
NULL;
205 dav_context_lock(context);
206 if(name) {
207 key = cxMapGet(context->keys, cx_hash_key_str(name));
208 }
209 dav_context_unlock(context);
210 return key;
211 }
212
213 int dav_add_namespace(DavContext *context,
const char *prefix,
const char *name) {
214 DavNamespace *namespace = malloc(
sizeof(DavNamespace));
215 if(!namespace) {
216 return 1;
217 }
218
219 char *p = strdup(prefix);
220 if (!p) {
221 free(namespace);
222 return 1;
223 }
224 char *n = strdup(name);
225 if (!n) {
226 free(namespace);
227 free(p);
228 return 1;
229 }
230
231 dav_context_lock(context);
232
233 int err =
0;
234 if(p && n) {
235 namespace->prefix = p;
236 namespace->name = n;
237 err = cxMapPut(context->namespaces, cx_hash_key_str(prefix), namespace);
238 }
239
240 if(err) {
241 free(namespace);
242 if(p) free(p);
243 if(n) free(n);
244 }
245
246 dav_context_unlock(context);
247
248 return err;
249 }
250
251 DavNamespace* dav_get_namespace(DavContext *context,
const char *prefix) {
252 dav_context_lock(context);
253 DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key_str(prefix));
254 dav_context_unlock(context);
255 return ns;
256 }
257
258 DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) {
259 dav_context_lock(context);
260 DavNamespace *ns = cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length));
261 dav_context_unlock(context);
262 return ns;
263 }
264
265 int dav_enable_namespace_encryption(DavContext *context,
const char *ns, DavBool encrypt) {
266 dav_context_lock(context);
267
268 CxHashKey hkey = cx_hash_key_str(ns);
269 DavNSInfo *info = cxMapGet(context->namespaceinfo, hkey);
270 if(!info) {
271 info = calloc(
1,
sizeof(DavNSInfo));
272 info->encrypt = encrypt;
273 cxMapPut(context->namespaceinfo, hkey, info);
274 }
else {
275 info->encrypt = encrypt;
276 }
277
278 dav_context_unlock(context);
279 return 0;
280 }
281
282 int dav_namespace_is_encrypted(DavContext *context,
const char *ns) {
283 int ret =
0;
284 dav_context_lock(context);
285
286 DavNSInfo *info = cxMapGet(context->namespaceinfo, cx_hash_key_str(ns));
287 if(info) {
288 ret = info->encrypt;
289 }
290 dav_context_unlock(context);
291 return ret;
292 }
293
294 void dav_get_property_namespace_str(
295 DavContext *ctx,
296 char *prefixed_name,
297 char **ns,
298 char **name)
299 {
300
301
302 char *pname = strchr(prefixed_name,
':');
303 char *pns =
"DAV:";
304 if(pname) {
305 DavNamespace *davns = dav_get_namespace_s(
306 ctx,
307 cx_strn(prefixed_name, pname-prefixed_name));
308 if(davns) {
309 pns = davns->name;
310 pname++;
311 }
else {
312 pns =
NULL;
313 pname =
NULL;
314 }
315 }
else {
316 pname = prefixed_name;
317 }
318 *ns = pns;
319 *name = pname;
320 }
321
322 DavNamespace* dav_get_property_namespace(
323 DavContext *ctx,
324 char *prefixed_name,
325 char **name)
326 {
327 char *pname = strchr(prefixed_name,
':');
328 if(pname) {
329 DavNamespace *ns = dav_get_namespace_s(
330 ctx,
331 cx_strn(prefixed_name, pname-prefixed_name));
332 if(ns) {
333 *name = pname +
1;
334 return ns;
335 }
else {
336 *name =
NULL;
337 return NULL;
338 }
339 }
else {
340 *name = prefixed_name;
341 return dav_get_namespace_s(ctx, cx_str(
"D"));
342 }
343 }
344
345 int dav_context_add_session(DavContext *context, DavSession *sn) {
346 dav_context_lock(context);
347 int ret = cxListAdd(context->sessions, sn);
348 dav_context_unlock(context);
349 return ret;
350 }
351
352 int dav_context_remove_session(DavContext *context, DavSession *sn) {
353 int ret =
0;
354 dav_context_lock(context);
355 CxList *sessions = context->sessions;
356 ssize_t i = cxListFind(sessions, sn);
357 if(i >=
0) {
358 cxListRemove(sessions, i);
359 }
else {
360 ret =
1;
361 }
362 dav_context_unlock(context);
363 return ret;
364 }
365
366
367
368
369 void dav_set_effective_href(DavSession *sn, DavResource *resource) {
370 char *eff_url;
371 curl_easy_getinfo(sn->handle,
CURLINFO_EFFECTIVE_URL, &eff_url);
372 if(eff_url) {
373 const char *href = util_url_path(eff_url);
374 if(strcmp(href, resource->href)) {
375 dav_session_free(sn, resource->href);
376 resource->href = dav_session_strdup(sn, href);
377 }
378 }
379 }
380
381 DavResource* dav_get(DavSession *sn,
char *path,
const char *properties) {
382 CURL *handle = sn->handle;
383 DavResource *resource = dav_resource_new(sn, path);
384 util_set_url(sn, dav_resource_get_href(resource));
385
386 CxList *proplist =
NULL;
387 if(properties) {
388 proplist = parse_properties_string(sn->context, cx_str(properties));
389 }
390 CxBuffer *rqbuf = create_propfind_request(sn, proplist,
"propfind",
0);
391 CxBuffer *rpbuf = cxBufferCreate(
NULL,
4096, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
392
393
394
395
396 CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
397 long status =
0;
398 curl_easy_getinfo (handle,
CURLINFO_RESPONSE_CODE, &status);
399 if(ret ==
CURLE_OK && status ==
207) {
400 dav_set_effective_href(sn, resource);
401
402
403
404 resource = parse_propfind_response(sn, resource, rpbuf);
405 resource->exists =
1;
406 sn->error =
DAV_OK;
407 }
else {
408 dav_session_set_error(sn, ret, status);
409 dav_resource_free(resource);
410 resource =
NULL;
411 }
412
413 cxBufferFree(rqbuf);
414 cxBufferFree(rpbuf);
415
416 if(proplist) {
417 CxIterator i = cxListIterator(proplist);
418 cx_foreach(DavProperty*, p, i) {
419 free(p->name);
420 }
421 cxListDestroy(proplist);
422 }
423
424 return resource;
425 }
426
427
428 int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) {
429
430 DavResourceData *data = root->data;
431 cxMapClear(data->properties);
432
433 CURL *handle = sn->handle;
434 util_set_url(sn, dav_resource_get_href(root));
435
436 CxBuffer *rpbuf = cxBufferCreate(
NULL,
4096, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
437 DavResource *resource = root;
438 CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
439 long status =
0;
440 long error =
0;
441 curl_easy_getinfo (handle,
CURLINFO_RESPONSE_CODE, &status);
442 if(ret ==
CURLE_OK && status ==
207) {
443
444 dav_set_effective_href(sn, resource);
445
446 resource = parse_propfind_response(sn, resource, rpbuf);
447 sn->error =
DAV_OK;
448 root->exists =
1;
449 }
else {
450 dav_session_set_error(sn, ret, status);
451 error =
1;
452 }
453 cxBufferFree(rpbuf);
454 return error;
455 }
456
457 CxList* parse_properties_string(DavContext *context, cxstring str) {
458 CxList *proplist = cxLinkedListCreateSimple(
sizeof(DavProperty));
459
460 CxStrtokCtx tok = cx_strtok(str, cx_str(
","),
INT_MAX);
461 cxstring s;
462 while(cx_strtok_next(&tok, &s)) {
463 cxstring nsname = cx_strchr(s,
':');
464 if(nsname.length >
0) {
465 cxstring nspre = cx_strsubsl(s,
0, nsname.ptr - s.ptr);
466 nsname.ptr++;
467 nsname.length--;
468
469 DavProperty dp;
470 cxstring pre = cx_strtrim(nspre);
471 dp.ns = dav_get_namespace_s(context, pre);
472 dp.name = cx_strdup(nsname).ptr;
473 dp.value =
NULL;
474 if(dp.ns && dp.name) {
475 cxListAdd(proplist, &dp);
476 }
else {
477 free(dp.name);
478 }
479 }
480 }
481
482 return proplist;
483 }
484
485 DavResource* dav_query(DavSession *sn,
char *query, ...) {
486 DavQLStatement *stmt = dav_parse_statement(cx_str(query));
487 if(!stmt) {
488 sn->error =
DAV_ERROR;
489 return NULL;
490 }
491 if(stmt->errorcode !=
0) {
492 sn->error =
DAV_QL_ERROR;
493 dav_free_statement(stmt);
494 return NULL;
495 }
496
497 va_list ap;
498 va_start(ap, query);
499 DavResult result = dav_statement_execv(sn, stmt, ap);
500 va_end(ap);
501
502 dav_free_statement(stmt);
503
504 if(result.status == -
1) {
505 if(result.result) {
506 dav_resource_free(result.result);
507 result.result =
NULL;
508 }
509 }
510
511 return result.result;
512 }
513
514
515
516
517 void dav_verbose_log(
518 DavSession *sn,
519 const char *method,
520 const char *url,
521 const char *request_body,
522 size_t request_bodylen,
523 int status,
524 const char *response_body,
525 size_t response_bodylen)
526 {
527 fprintf(stderr,
"# method: %s url: %s status: %d\n", method, url, status);
528 fprintf(stderr,
"# request len: %d\n%.*s\n", (
int)request_bodylen, (
int)request_bodylen, request_body);
529 fprintf(stderr,
"# response len: %d\n%.*s\n", (
int)response_bodylen, (
int)response_bodylen, response_body);
530 }
531