|
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 |
|
33 #include <cx/buffer.h> |
|
34 #include <cx/utils.h> |
|
35 #include <cx/mempool.h> |
|
36 #include <cx/hash_map.h> |
|
37 |
|
38 #include "utils.h" |
|
39 #include "session.h" |
|
40 #include "resource.h" |
|
41 #include "methods.h" |
|
42 |
|
43 DavSession* dav_session_new(DavContext *context, char *base_url) { |
|
44 if(!base_url) { |
|
45 return NULL; |
|
46 } |
|
47 cxstring url = cx_str(base_url); |
|
48 if(url.length == 0) { |
|
49 return NULL; |
|
50 } |
|
51 DavSession *sn = malloc(sizeof(DavSession)); |
|
52 memset(sn, 0, sizeof(DavSession)); |
|
53 sn->mp = cxMempoolCreate(DAV_SESSION_MEMPOOL_SIZE, NULL); |
|
54 sn->pathcache = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, DAV_PATH_CACHE_SIZE); |
|
55 sn->key = NULL; |
|
56 sn->errorstr = NULL; |
|
57 sn->error = DAV_OK; |
|
58 sn->flags = 0; |
|
59 |
|
60 dav_session_set_baseurl(sn, base_url); |
|
61 |
|
62 sn->handle = curl_easy_init(); |
|
63 curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L); |
|
64 |
|
65 // lock manager is created on-demand |
|
66 sn->locks = NULL; |
|
67 |
|
68 // set proxy |
|
69 DavProxy *proxy = cx_strprefix(url, CX_STR("https")) ? context->https_proxy |
|
70 : context->http_proxy; |
|
71 |
|
72 if (proxy->url) { |
|
73 curl_easy_setopt(sn->handle, CURLOPT_PROXY, proxy->url); |
|
74 if (proxy->username) { |
|
75 curl_easy_setopt(sn->handle, CURLOPT_PROXYUSERNAME, |
|
76 proxy->username); |
|
77 if (proxy->password) { |
|
78 curl_easy_setopt(sn->handle, CURLOPT_PROXYPASSWORD, |
|
79 proxy->password); |
|
80 } else { |
|
81 // TODO: prompt |
|
82 } |
|
83 } |
|
84 if(proxy->no_proxy) { |
|
85 curl_easy_setopt(sn->handle, CURLOPT_NOPROXY, |
|
86 proxy->no_proxy); |
|
87 } |
|
88 } |
|
89 |
|
90 // set url |
|
91 #if LIBCURL_VERSION_NUM >= 0x072D00 |
|
92 curl_easy_setopt(sn->handle, CURLOPT_DEFAULT_PROTOCOL, "http"); |
|
93 #endif |
|
94 curl_easy_setopt(sn->handle, CURLOPT_URL, base_url); |
|
95 |
|
96 // add to context |
|
97 cxListAdd(context->sessions, sn); |
|
98 sn->context = context; |
|
99 |
|
100 return sn; |
|
101 } |
|
102 |
|
103 DavSession* dav_session_new_auth( |
|
104 DavContext *context, |
|
105 char *base_url, |
|
106 char *user, |
|
107 char *password) |
|
108 { |
|
109 DavSession *sn = dav_session_new(context, base_url); |
|
110 if(!sn) { |
|
111 return NULL; |
|
112 } |
|
113 dav_session_set_auth(sn, user, password); |
|
114 return sn; |
|
115 } |
|
116 |
|
117 void dav_session_set_auth(DavSession *sn, char *user, char *password) { |
|
118 if(user && password) { |
|
119 size_t ulen = strlen(user); |
|
120 size_t plen = strlen(password); |
|
121 size_t upwdlen = ulen + plen + 2; |
|
122 char *upwdbuf = malloc(upwdlen); |
|
123 snprintf(upwdbuf, upwdlen, "%s:%s", user, password); |
|
124 curl_easy_setopt(sn->handle, CURLOPT_USERPWD, upwdbuf); |
|
125 free(upwdbuf); |
|
126 } |
|
127 } |
|
128 |
|
129 void dav_session_set_baseurl(DavSession *sn, char *base_url) { |
|
130 const CxAllocator *a = sn->mp->allocator; |
|
131 if(sn->base_url) { |
|
132 cxFree(a, sn->base_url); |
|
133 } |
|
134 |
|
135 cxstring url = cx_str(base_url); |
|
136 if(url.ptr[url.length - 1] == '/') { |
|
137 cxmutstr url_m = cx_strdup_a(a, cx_str(base_url)); |
|
138 sn->base_url = url_m.ptr; |
|
139 } else { |
|
140 char *url_str = cxMalloc(a, url.length + 2); |
|
141 memcpy(url_str, base_url, url.length); |
|
142 url_str[url.length] = '/'; |
|
143 url_str[url.length + 1] = '\0'; |
|
144 sn->base_url = url_str; |
|
145 } |
|
146 } |
|
147 |
|
148 void dav_session_enable_encryption(DavSession *sn, DavKey *key, int flags) { |
|
149 sn->key = key; |
|
150 // TODO: review sanity |
|
151 if(flags != 0) { |
|
152 sn->flags |= flags; |
|
153 } else { |
|
154 sn->flags |= DAV_SESSION_ENCRYPT_CONTENT; |
|
155 } |
|
156 } |
|
157 |
|
158 void dav_session_set_authcallback(DavSession *sn, dav_auth_func func, void *userdata) { |
|
159 sn->auth_prompt = func; |
|
160 sn->authprompt_userdata = userdata; |
|
161 } |
|
162 |
|
163 void dav_session_set_progresscallback(DavSession *sn, dav_progress_func get, dav_progress_func put, void *userdata) { |
|
164 sn->get_progress = get; |
|
165 sn->put_progress = put; |
|
166 sn->progress_userdata = userdata; |
|
167 } |
|
168 |
|
169 CURLcode dav_session_curl_perform(DavSession *sn, long *status) { |
|
170 return dav_session_curl_perform_buf(sn, NULL, NULL, status); |
|
171 } |
|
172 |
|
173 CURLcode dav_session_curl_perform_buf(DavSession *sn, CxBuffer *request, CxBuffer *response, long *status) { |
|
174 CURLcode ret = curl_easy_perform(sn->handle); |
|
175 long http_status; |
|
176 curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status); |
|
177 if(ret == CURLE_OK) { |
|
178 if(sn->logfunc) { |
|
179 char *log_method; |
|
180 char *log_url; |
|
181 curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &log_url); |
|
182 curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_METHOD , &log_method); |
|
183 char *log_reqbody = NULL; |
|
184 size_t log_reqbodylen = 0; |
|
185 char *log_rpbody = NULL; |
|
186 size_t log_rpbodylen = 0; |
|
187 if(request) { |
|
188 log_reqbody = request->space; |
|
189 log_reqbodylen = request->size; |
|
190 } |
|
191 if(response) { |
|
192 log_rpbody = response->space; |
|
193 log_rpbodylen = response->size; |
|
194 } |
|
195 sn->logfunc(sn, log_method, log_url, log_reqbody, log_reqbodylen, http_status, log_rpbody, log_rpbodylen); |
|
196 } |
|
197 |
|
198 if(http_status == 401 && sn->auth_prompt) { |
|
199 if(!sn->auth_prompt(sn, sn->authprompt_userdata)) { |
|
200 if(request) { |
|
201 cxBufferSeek(request, 0, SEEK_SET); |
|
202 } |
|
203 if(response) { |
|
204 cxBufferSeek(response, 0, SEEK_SET); |
|
205 } |
|
206 ret = curl_easy_perform(sn->handle); |
|
207 curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status); |
|
208 } |
|
209 } |
|
210 |
|
211 } |
|
212 |
|
213 if(status) { |
|
214 *status = http_status; |
|
215 } |
|
216 return ret; |
|
217 } |
|
218 |
|
219 int dav_session_get_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { |
|
220 DavResource *res = clientp; |
|
221 DavSession *sn = res->session; |
|
222 if(sn->get_progress) { |
|
223 sn->get_progress(res, (int64_t)dltotal, (int64_t)dlnow, sn->progress_userdata); |
|
224 } |
|
225 return 0; |
|
226 } |
|
227 |
|
228 int dav_session_put_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { |
|
229 DavResource *res = clientp; |
|
230 DavSession *sn = res->session; |
|
231 if(sn->put_progress) { |
|
232 sn->put_progress(res, (int64_t)ultotal, (int64_t)ulnow, sn->progress_userdata); |
|
233 } |
|
234 return 0; |
|
235 } |
|
236 |
|
237 void dav_session_set_error(DavSession *sn, CURLcode c, int status) { |
|
238 if(status > 0) { |
|
239 switch(status) { |
|
240 default: { |
|
241 switch(c) { |
|
242 default: sn->error = DAV_ERROR; |
|
243 } |
|
244 break; |
|
245 } |
|
246 case 401: sn->error = DAV_UNAUTHORIZED; break; |
|
247 case 403: sn->error = DAV_FORBIDDEN; break; |
|
248 case 404: sn->error = DAV_NOT_FOUND; break; |
|
249 case 405: sn->error = DAV_METHOD_NOT_ALLOWED; break; |
|
250 case 407: sn->error = DAV_PROXY_AUTH_REQUIRED; break; |
|
251 case 409: sn->error = DAV_CONFLICT; break; |
|
252 case 412: sn->error = DAV_PRECONDITION_FAILED; break; |
|
253 case 413: sn->error = DAV_REQUEST_ENTITY_TOO_LARGE; break; |
|
254 case 414: sn->error = DAV_REQUEST_URL_TOO_LONG; break; |
|
255 case 423: sn->error = DAV_LOCKED; break; |
|
256 case 511: sn->error = DAV_NET_AUTH_REQUIRED; break; |
|
257 } |
|
258 } else { |
|
259 switch(c) { |
|
260 case CURLE_UNSUPPORTED_PROTOCOL: sn->error = DAV_UNSUPPORTED_PROTOCOL; break; |
|
261 case CURLE_COULDNT_RESOLVE_PROXY: sn->error = DAV_COULDNT_RESOLVE_PROXY; break; |
|
262 case CURLE_COULDNT_RESOLVE_HOST: sn->error = DAV_COULDNT_RESOLVE_HOST; break; |
|
263 case CURLE_COULDNT_CONNECT: sn->error = DAV_COULDNT_CONNECT; break; |
|
264 case CURLE_OPERATION_TIMEDOUT: sn->error = DAV_TIMEOUT; break; |
|
265 case CURLE_SSL_CONNECT_ERROR: |
|
266 case CURLE_PEER_FAILED_VERIFICATION: |
|
267 case CURLE_SSL_ENGINE_NOTFOUND: |
|
268 case CURLE_SSL_ENGINE_SETFAILED: |
|
269 case CURLE_SSL_CERTPROBLEM: |
|
270 case CURLE_SSL_CIPHER: |
|
271 //#ifndef CURLE_SSL_CACERT |
|
272 // case CURLE_SSL_CACERT: |
|
273 //#endif |
|
274 case CURLE_SSL_CACERT_BADFILE: |
|
275 case CURLE_SSL_SHUTDOWN_FAILED: |
|
276 case CURLE_SSL_CRL_BADFILE: |
|
277 case CURLE_SSL_ISSUER_ERROR: sn->error = DAV_SSL_ERROR; break; |
|
278 default: sn->error = DAV_ERROR; break; |
|
279 } |
|
280 } |
|
281 if(c != CURLE_OK) { |
|
282 dav_session_set_errstr(sn, curl_easy_strerror(c)); |
|
283 } else { |
|
284 dav_session_set_errstr(sn, NULL); |
|
285 } |
|
286 } |
|
287 |
|
288 void dav_session_set_errstr(DavSession *sn, const char *str) { |
|
289 if(sn->errorstr) { |
|
290 dav_session_free(sn, sn->errorstr); |
|
291 } |
|
292 char *errstr = NULL; |
|
293 if(str) { |
|
294 errstr = dav_session_strdup(sn, str); |
|
295 } |
|
296 sn->errorstr = errstr; |
|
297 } |
|
298 |
|
299 void dav_session_destroy(DavSession *sn) { |
|
300 // remove session from context |
|
301 CxList *sessions = sn->context->sessions; |
|
302 ssize_t i = cxListFind(sessions, sn); |
|
303 if(i >= 0) { |
|
304 cxListRemove(sessions, i); |
|
305 } else { |
|
306 printf("Error: session not found in ctx->sessions\n"); |
|
307 dav_session_destructor(sn); |
|
308 } |
|
309 } |
|
310 |
|
311 void dav_session_destructor(DavSession *sn) { |
|
312 cxMempoolDestroy(sn->mp); |
|
313 curl_easy_cleanup(sn->handle); |
|
314 free(sn); |
|
315 } |
|
316 |
|
317 |
|
318 void* dav_session_malloc(DavSession *sn, size_t size) { |
|
319 return cxMalloc(sn->mp->allocator, size); |
|
320 } |
|
321 |
|
322 void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size) { |
|
323 return cxCalloc(sn->mp->allocator, nelm, size); |
|
324 } |
|
325 |
|
326 void* dav_session_realloc(DavSession *sn, void *ptr, size_t size) { |
|
327 return cxRealloc(sn->mp->allocator, ptr, size); |
|
328 } |
|
329 |
|
330 void dav_session_free(DavSession *sn, void *ptr) { |
|
331 cxFree(sn->mp->allocator, ptr); |
|
332 } |
|
333 |
|
334 char* dav_session_strdup(DavSession *sn, const char *str) { |
|
335 return cx_strdup_a(sn->mp->allocator, cx_str((char*)str)).ptr; |
|
336 } |
|
337 |
|
338 |
|
339 char* dav_session_create_plain_href(DavSession *sn, const char *path) { |
|
340 if(!DAV_ENCRYPT_NAME(sn) && !DAV_DECRYPT_NAME(sn)) { |
|
341 // non encrypted file names |
|
342 char *url = util_path_to_url(sn, path); |
|
343 char *href = dav_session_strdup(sn, util_url_path(url)); |
|
344 free(url); |
|
345 return href; |
|
346 } else { |
|
347 return NULL; |
|
348 } |
|
349 } |
|
350 |
|
351 char* dav_session_get_href(DavSession *sn, const char *path) { |
|
352 if(DAV_DECRYPT_NAME(sn) || DAV_ENCRYPT_NAME(sn)) { |
|
353 cxstring p = cx_str(path); |
|
354 CxBuffer href; |
|
355 CxBuffer pbuf; |
|
356 cxBufferInit(&href, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); |
|
357 cxBufferInit(&pbuf, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); |
|
358 |
|
359 int start = 0; |
|
360 int begin = 0; |
|
361 |
|
362 // check path cache |
|
363 char *cp = strdup(path); |
|
364 //printf("cp: %s\n", cp); |
|
365 while(strlen(cp) > 1) { |
|
366 char *cached = cxMapGet(sn->pathcache, cx_hash_key_str(cp)); |
|
367 if(cached) { |
|
368 start = strlen(cp); |
|
369 begin = start; |
|
370 cxBufferPutString(&href, cached); |
|
371 break; |
|
372 } else { |
|
373 // check, if the parent path is cached |
|
374 char *f = cp; |
|
375 cp = util_parent_path(cp); |
|
376 free(f); |
|
377 } |
|
378 } |
|
379 free(cp); |
|
380 if(href.pos == 0) { |
|
381 // if there are no cached elements we have to add the base url path |
|
382 // to the href buffer |
|
383 cxBufferPutString(&href, util_url_path(sn->base_url)); |
|
384 } |
|
385 |
|
386 // create resource for name lookup |
|
387 cxmutstr rp = cx_strdup(cx_strn(path, start)); |
|
388 DavResource *root = dav_resource_new(sn, rp.ptr); |
|
389 free(rp.ptr); |
|
390 resource_set_href(root, cx_strn(href.space, href.pos)); |
|
391 |
|
392 // create request buffer for propfind requests |
|
393 CxBuffer *rqbuf = create_basic_propfind_request(); |
|
394 |
|
395 cxstring remaining = cx_strsubs(p, start); |
|
396 CxStrtokCtx elms = cx_strtok(remaining, CX_STR("/"), INT_MAX); |
|
397 DavResource *res = root; |
|
398 cxBufferPutString(&pbuf, res->path); |
|
399 // iterate over all remaining path elements |
|
400 cxstring elm; |
|
401 while(cx_strtok_next(&elms, &elm)) { |
|
402 if(elm.length > 0) { |
|
403 //printf("elm: %.*s\n", elm.length, elm.ptr); |
|
404 DavResource *child = dav_find_child(sn, res, rqbuf, elm.ptr); |
|
405 |
|
406 // if necessary add a path separator |
|
407 if(pbuf.space[pbuf.pos-1] != '/') { |
|
408 if(href.space[href.pos-1] != '/') { |
|
409 cxBufferPut(&href, '/'); |
|
410 } |
|
411 cxBufferPut(&pbuf, '/'); |
|
412 } |
|
413 // add last path/href to the cache |
|
414 cxstring pp = cx_strn(pbuf.space, pbuf.size); |
|
415 cxstring hh = cx_strn(href.space, href.size); |
|
416 dav_session_cache_path(sn, pp, hh); |
|
417 |
|
418 cxBufferWrite(elm.ptr, 1, elm.length, &pbuf); |
|
419 if(child) { |
|
420 // href is already URL encoded, so don't encode again |
|
421 cxBufferPutString(&href, util_resource_name(child->href)); |
|
422 res = child; |
|
423 } else if(DAV_ENCRYPT_NAME(sn)) { |
|
424 char *random_name = util_random_str(); |
|
425 cxBufferPutString(&href, random_name); |
|
426 free(random_name); |
|
427 } else { |
|
428 // path is not URL encoded, so we have to do this here |
|
429 cxstring resname = cx_str(util_resource_name((const char*)path)); |
|
430 // the name of collections ends with |
|
431 // a trailing slash, which MUST NOT be encoded |
|
432 if(resname.ptr[resname.length-1] == '/') { |
|
433 char *esc = curl_easy_escape(sn->handle, |
|
434 resname.ptr, resname.length-1); |
|
435 cxBufferWrite(esc, 1, strlen(esc), &href); |
|
436 cxBufferPut(&href, '/'); |
|
437 curl_free(esc); |
|
438 } else { |
|
439 char *esc = curl_easy_escape(sn->handle, |
|
440 resname.ptr, resname.length); |
|
441 cxBufferWrite(esc, 1, strlen(esc), &href); |
|
442 curl_free(esc); |
|
443 } |
|
444 } |
|
445 } |
|
446 } |
|
447 |
|
448 // if necessary add a path separator |
|
449 if(p.ptr[p.length-1] == '/') { |
|
450 if(href.space[href.pos-1] != '/') { |
|
451 cxBufferPut(&href, '/'); |
|
452 } |
|
453 cxBufferPut(&pbuf, '/'); |
|
454 } |
|
455 // add the final path to the cache |
|
456 cxstring pp = cx_strn(pbuf.space, pbuf.size); |
|
457 cxstring hh = cx_strn(href.space, href.size); |
|
458 dav_session_cache_path(sn, pp, hh); |
|
459 |
|
460 cxmutstr href_str = cx_strdup_a( |
|
461 sn->mp->allocator, |
|
462 cx_strn(href.space, href.size)); |
|
463 |
|
464 // cleanup |
|
465 dav_resource_free_all(root); |
|
466 cxBufferFree(rqbuf); |
|
467 |
|
468 cxBufferDestroy(&pbuf); |
|
469 cxBufferDestroy(&href); |
|
470 |
|
471 return href_str.ptr; |
|
472 } else { |
|
473 return dav_session_create_plain_href(sn, path); |
|
474 } |
|
475 } |
|
476 |
|
477 DavResource* dav_find_child(DavSession *sn, DavResource *res, CxBuffer *rqbuf, const char *name) { |
|
478 if(res && !dav_propfind(sn, res, rqbuf)) { |
|
479 DavResource *child = res->children; |
|
480 while(child) { |
|
481 if(!strcmp(child->name, name)) { |
|
482 return child; |
|
483 } |
|
484 child = child->next; |
|
485 } |
|
486 } |
|
487 return NULL; |
|
488 } |
|
489 |
|
490 void dav_session_cache_path(DavSession *sn, cxstring path, cxstring href) { |
|
491 CxHashKey path_key = cx_hash_key(path.ptr, path.length); |
|
492 char *elm = cxMapGet(sn->pathcache, path_key); |
|
493 if(!elm) { |
|
494 cxmutstr href_s = cx_strdup_a(sn->mp->allocator, href); |
|
495 cxMapPut(sn->pathcache, path_key, href_s.ptr); |
|
496 } |
|
497 } |
|
498 |
|
499 |
|
500 DavLock* dav_create_lock(DavSession *sn, const char *token, char *timeout) { |
|
501 DavLock *lock = dav_session_malloc(sn, sizeof(DavLock)); |
|
502 lock->path = NULL; |
|
503 lock->token = dav_session_strdup(sn, token); |
|
504 |
|
505 // TODO: timeout |
|
506 |
|
507 return lock; |
|
508 } |
|
509 |
|
510 void dav_destroy_lock(DavSession *sn, DavLock *lock) { |
|
511 dav_session_free(sn, lock->token); |
|
512 if(lock->path) { |
|
513 dav_session_free(sn, lock->path); |
|
514 } |
|
515 dav_session_free(sn, lock); |
|
516 } |
|
517 |
|
518 |
|
519 static int dav_lock_cmp(void const *left, void const *right) { |
|
520 const DavLock *l = left; |
|
521 const DavLock *r = right; |
|
522 return strcmp(l->path, r->path); |
|
523 } |
|
524 |
|
525 static int create_lock_manager(DavSession *sn) { |
|
526 // create lock manager |
|
527 DavLockManager *locks = cxMalloc(sn->mp->allocator, sizeof(DavLockManager)); |
|
528 locks->resource_locks = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 16); |
|
529 locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, dav_lock_cmp, CX_STORE_POINTERS); |
|
530 sn->locks = locks; |
|
531 return 0; |
|
532 } |
|
533 |
|
534 static DavLockManager* get_lock_manager(DavSession *sn) { |
|
535 DavLockManager *locks = sn->locks; |
|
536 if(!locks) { |
|
537 if(create_lock_manager(sn)) { |
|
538 return NULL; |
|
539 } |
|
540 locks = sn->locks; |
|
541 } |
|
542 return locks; |
|
543 } |
|
544 |
|
545 int dav_add_resource_lock(DavSession *sn, const char *path, DavLock *lock) { |
|
546 DavLockManager *locks = get_lock_manager(sn); |
|
547 if(!locks) { |
|
548 return -1; |
|
549 } |
|
550 |
|
551 CxHashKey path_key = cx_hash_key_str(path); |
|
552 if(cxMapGet(locks->resource_locks, path_key)) { |
|
553 return -1; |
|
554 } |
|
555 |
|
556 cxMapPut(locks->resource_locks, path_key, lock); |
|
557 return 0; |
|
558 } |
|
559 |
|
560 int dav_add_collection_lock(DavSession *sn, const char *path, DavLock *lock) { |
|
561 DavLockManager *locks = get_lock_manager(sn); |
|
562 if(!locks) { |
|
563 return -1; |
|
564 } |
|
565 |
|
566 lock->path = dav_session_strdup(sn, path); |
|
567 cxListAdd(locks->collection_locks, lock); |
|
568 cxListSort(locks->collection_locks); |
|
569 |
|
570 return 0; |
|
571 } |
|
572 |
|
573 DavLock* dav_get_lock(DavSession *sn, const char *path) { |
|
574 DavLockManager *locks = get_lock_manager(sn); |
|
575 if(!locks) { |
|
576 return NULL; |
|
577 } |
|
578 |
|
579 cxstring p = cx_str(path); |
|
580 |
|
581 DavLock *lock = cxMapGet(locks->resource_locks, cx_hash_key(p.ptr, p.length)); |
|
582 if(lock) { |
|
583 return lock; |
|
584 } |
|
585 |
|
586 CxIterator i = cxListIterator(locks->collection_locks); |
|
587 cx_foreach(DavLock*, col_lock, i) { |
|
588 int cmd = strcmp(path, col_lock->path); |
|
589 if(cmd == 0) { |
|
590 return col_lock; |
|
591 } else if(cx_strprefix(p, cx_str(col_lock->path))) { |
|
592 return col_lock; |
|
593 } else if(cmd > 0) { |
|
594 break; |
|
595 } |
|
596 } |
|
597 |
|
598 return NULL; |
|
599 } |
|
600 |
|
601 void dav_remove_lock(DavSession *sn, const char *path, DavLock *lock) { |
|
602 DavLockManager *locks = get_lock_manager(sn); |
|
603 if(!locks) { |
|
604 return; |
|
605 } |
|
606 |
|
607 if(cxMapRemoveAndGet(locks->resource_locks, cx_hash_key_str(path))) { |
|
608 return; |
|
609 } |
|
610 |
|
611 CxMutIterator i = cxListMutIterator(locks->collection_locks); |
|
612 int rm = 0; |
|
613 cx_foreach(DavLock* , cl, i) { |
|
614 if(rm) { |
|
615 break; |
|
616 } |
|
617 if(cl == lock) { |
|
618 cxIteratorFlagRemoval(i); |
|
619 rm = 1; |
|
620 } |
|
621 } |
|
622 } |