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
33 #include <cx/buffer.h>
34 #include <cx/utils.h>
35 #include <cx/basic_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 = cxBasicMempoolCreate(
DAV_SESSION_MEMPOOL_SIZE);
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
66 sn->locks =
NULL;
67
68
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
82 }
83 }
84 if(proxy->no_proxy) {
85 curl_easy_setopt(sn->handle,
CURLOPT_NOPROXY,
86 proxy->no_proxy);
87 }
88 }
89
90
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
97 dav_context_add_session(context, 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 DavSession* dav_session_clone(DavSession *sn) {
118 CURL *newhandle = curl_easy_duphandle(sn->handle);
119
120 DavSession *newsn = malloc(
sizeof(DavSession));
121 memset(newsn,
0,
sizeof(DavSession));
122 newsn->mp = cxBasicMempoolCreate(
DAV_SESSION_MEMPOOL_SIZE);
123 newsn->pathcache = cxHashMapCreate(sn->mp->allocator,
CX_STORE_POINTERS,
DAV_PATH_CACHE_SIZE);
124 newsn->key = sn->key;
125 newsn->errorstr =
NULL;
126 newsn->error =
DAV_OK;
127 newsn->flags =
0;
128
129 newsn->handle = newhandle;
130
131 newsn->base_url = cx_strdup_a(newsn->mp->allocator, cx_str(sn->base_url)).ptr;
132 newsn->auth_prompt = sn->auth_prompt;
133 newsn->authprompt_userdata = sn->authprompt_userdata;
134 newsn->logfunc = sn->logfunc;
135 newsn->get_progress = sn->get_progress;
136 newsn->put_progress = sn->put_progress;
137 newsn->progress_userdata = sn->progress_userdata;
138
139
140 dav_context_add_session(sn->context, newsn);
141 newsn->context = sn->context;
142
143 return newsn;
144 }
145
146 void dav_session_set_auth(DavSession *sn,
const char *user,
const char *password) {
147 if(user && password) {
148 dav_session_set_auth_s(sn, cx_str(user), cx_str(password));
149 }
150 }
151
152 void dav_session_set_auth_s(DavSession *sn, cxstring user, cxstring password) {
153 if(user.length >
0 && password.length >
0) {
154 size_t upwdlen = user.length + password.length +
2;
155 char *upwdbuf = malloc(upwdlen);
156 snprintf(upwdbuf, upwdlen,
"%.*s:%.*s", (
int)user.length, user.ptr, (
int)password.length, password.ptr);
157 curl_easy_setopt(sn->handle,
CURLOPT_USERPWD, upwdbuf);
158 free(upwdbuf);
159 }
160 }
161
162 void dav_session_set_baseurl(DavSession *sn,
char *base_url) {
163 const CxAllocator *a = sn->mp->allocator;
164 if(sn->base_url) {
165 cxFree(a, sn->base_url);
166 }
167
168 cxstring url = cx_str(base_url);
169 if(url.ptr[url.length -
1] ==
'/') {
170 cxmutstr url_m = cx_strdup_a(a, cx_str(base_url));
171 sn->base_url = url_m.ptr;
172 }
else {
173 char *url_str = cxMalloc(a, url.length +
2);
174 memcpy(url_str, base_url, url.length);
175 url_str[url.length] =
'/';
176 url_str[url.length +
1] =
'\0';
177 sn->base_url = url_str;
178 }
179 }
180
181 void dav_session_enable_encryption(DavSession *sn, DavKey *key,
int flags) {
182 sn->key = key;
183
184 if(flags !=
0) {
185 sn->flags |= flags;
186 }
else {
187 sn->flags |=
DAV_SESSION_ENCRYPT_CONTENT;
188 }
189 }
190
191 void dav_session_set_authcallback(DavSession *sn, dav_auth_func func,
void *userdata) {
192 sn->auth_prompt = func;
193 sn->authprompt_userdata = userdata;
194 }
195
196 void dav_session_set_progresscallback(DavSession *sn, dav_progress_func get, dav_progress_func put,
void *userdata) {
197 sn->get_progress = get;
198 sn->put_progress = put;
199 sn->progress_userdata = userdata;
200 }
201
202 CURLcode dav_session_curl_perform(DavSession *sn,
long *status) {
203 return dav_session_curl_perform_buf(sn,
NULL,
NULL, status);
204 }
205
206 CURLcode dav_session_curl_perform_buf(DavSession *sn, CxBuffer *request, CxBuffer *response,
long *status) {
207 CURLcode ret = curl_easy_perform(sn->handle);
208 long http_status;
209 curl_easy_getinfo(sn->handle,
CURLINFO_RESPONSE_CODE, &http_status);
210 if(ret ==
CURLE_OK) {
211 if(sn->logfunc) {
212 char *log_method;
213 char *log_url;
214 curl_easy_getinfo(sn->handle,
CURLINFO_EFFECTIVE_URL, &log_url);
215 curl_easy_getinfo(sn->handle,
CURLINFO_EFFECTIVE_METHOD , &log_method);
216 char *log_reqbody =
NULL;
217 size_t log_reqbodylen =
0;
218 char *log_rpbody =
NULL;
219 size_t log_rpbodylen =
0;
220 if(request) {
221 log_reqbody = request->space;
222 log_reqbodylen = request->size;
223 }
224 if(response) {
225 log_rpbody = response->space;
226 log_rpbodylen = response->size;
227 }
228 sn->logfunc(sn, log_method, log_url, log_reqbody, log_reqbodylen, http_status, log_rpbody, log_rpbodylen);
229 }
230
231 if(http_status ==
401 && sn->auth_prompt) {
232 if(!sn->auth_prompt(sn, sn->authprompt_userdata)) {
233 if(request) {
234 cxBufferSeek(request,
0,
SEEK_SET);
235 }
236 if(response) {
237 cxBufferSeek(response,
0,
SEEK_SET);
238 }
239 ret = curl_easy_perform(sn->handle);
240 curl_easy_getinfo(sn->handle,
CURLINFO_RESPONSE_CODE, &http_status);
241 }
242 }
243
244 }
245
246 if(status) {
247 *status = http_status;
248 }
249 return ret;
250 }
251
252 int dav_session_get_progress(
void *clientp,
curl_off_t dltotal,
curl_off_t dlnow,
curl_off_t ultotal,
curl_off_t ulnow) {
253 DavResource *res = clientp;
254 DavSession *sn = res->session;
255 if(sn->get_progress) {
256 sn->get_progress(res, (
int64_t)dltotal, (
int64_t)dlnow, sn->progress_userdata);
257 }
258 return 0;
259 }
260
261 int dav_session_put_progress(
void *clientp,
curl_off_t dltotal,
curl_off_t dlnow,
curl_off_t ultotal,
curl_off_t ulnow) {
262 DavResource *res = clientp;
263 DavSession *sn = res->session;
264 if(sn->put_progress) {
265 sn->put_progress(res, (
int64_t)ultotal, (
int64_t)ulnow, sn->progress_userdata);
266 }
267 return 0;
268 }
269
270 void dav_session_set_error(DavSession *sn, C
Lcode c,
int status) {
271 if(status >
0) {
272 switch(status) {
273 default: {
274 switch(c) {
275 default: sn->error =
DAV_ERROR;
276 }
277 break;
278 }
279 case 401: sn->error =
DAV_UNAUTHORIZED;
break;
280 case 403: sn->error =
DAV_FORBIDDEN;
break;
281 case 404: sn->error =
DAV_NOT_FOUND;
break;
282 case 405: sn->error =
DAV_METHOD_NOT_ALLOWED;
break;
283 case 407: sn->error =
DAV_PROXY_AUTH_REQUIRED;
break;
284 case 409: sn->error =
DAV_CONFLICT;
break;
285 case 412: sn->error =
DAV_PRECONDITION_FAILED;
break;
286 case 413: sn->error =
DAV_REQUEST_ENTITY_TOO_LARGE;
break;
287 case 414: sn->error =
DAV_REQUEST_URL_TOO_LONG;
break;
288 case 423: sn->error =
DAV_LOCKED;
break;
289 case 511: sn->error =
DAV_NET_AUTH_REQUIRED;
break;
290 }
291 }
else {
292 switch(c) {
293 case CURLE_UNSUPPORTED_PROTOCOL: sn->error =
DAV_UNSUPPORTED_PROTOCOL;
break;
294 case CURLE_COULDNT_RESOLVE_PROXY: sn->error =
DAV_COULDNT_RESOLVE_PROXY;
break;
295 case CURLE_COULDNT_RESOLVE_HOST: sn->error =
DAV_COULDNT_RESOLVE_HOST;
break;
296 case CURLE_COULDNT_CONNECT: sn->error =
DAV_COULDNT_CONNECT;
break;
297 case CURLE_OPERATION_TIMEDOUT: sn->error =
DAV_TIMEOUT;
break;
298 case CURLE_SSL_CONNECT_ERROR:
299 case CURLE_PEER_FAILED_VERIFICATION:
300 case CURLE_SSL_ENGINE_NOTFOUND:
301 case CURLE_SSL_ENGINE_SETFAILED:
302 case CURLE_SSL_CERTPROBLEM:
303 case CURLE_SSL_CIPHER:
304
305
306
307 case CURLE_SSL_CACERT_BADFILE:
308 case CURLE_SSL_SHUTDOWN_FAILED:
309 case CURLE_SSL_CRL_BADFILE:
310 case CURLE_SSL_ISSUER_ERROR: sn->error =
DAV_SSL_ERROR;
break;
311 default: sn->error =
DAV_ERROR;
break;
312 }
313 }
314 if(c !=
CURLE_OK) {
315 dav_session_set_errstr(sn, curl_easy_strerror(c));
316 }
else {
317 dav_session_set_errstr(sn,
NULL);
318 }
319 }
320
321 void dav_session_set_errstr(DavSession *sn,
const char *str) {
322 if(sn->errorstr) {
323 dav_session_free(sn, sn->errorstr);
324 }
325 char *errstr =
NULL;
326 if(str) {
327 errstr = dav_session_strdup(sn, str);
328 }
329 sn->errorstr = errstr;
330 }
331
332 void dav_session_destroy(DavSession *sn) {
333
334 if (dav_context_remove_session(sn->context, sn)) {
335 fprintf(stderr,
"Error: session not found in ctx->sessions\n");
336 dav_session_destructor(sn);
337 }
338 }
339
340 void dav_session_destructor(DavSession *sn) {
341 cxMempoolDestroy(sn->mp);
342 curl_easy_cleanup(sn->handle);
343 free(sn);
344 }
345
346
347 void* dav_session_malloc(DavSession *sn,
size_t size) {
348 return cxMalloc(sn->mp->allocator, size);
349 }
350
351 void* dav_session_calloc(DavSession *sn,
size_t nelm,
size_t size) {
352 return cxCalloc(sn->mp->allocator, nelm, size);
353 }
354
355 void* dav_session_realloc(DavSession *sn,
void *ptr,
size_t size) {
356 return cxRealloc(sn->mp->allocator, ptr, size);
357 }
358
359 void dav_session_free(DavSession *sn,
void *ptr) {
360 cxFree(sn->mp->allocator, ptr);
361 }
362
363 char* dav_session_strdup(DavSession *sn,
const char *str) {
364 return cx_strdup_a(sn->mp->allocator, cx_str((
char*)str)).ptr;
365 }
366
367
368 char* dav_session_create_plain_href(DavSession *sn,
const char *path) {
369 if(!
DAV_ENCRYPT_NAME(sn) && !
DAV_DECRYPT_NAME(sn)) {
370
371 char *url = util_path_to_url(sn, path);
372 char *href = dav_session_strdup(sn, util_url_path(url));
373 free(url);
374 return href;
375 }
else {
376 return NULL;
377 }
378 }
379
380 char* dav_session_get_href(DavSession *sn,
const char *path) {
381 if(
DAV_DECRYPT_NAME(sn) ||
DAV_ENCRYPT_NAME(sn)) {
382 cxstring p = cx_str(path);
383 CxBuffer href;
384 CxBuffer pbuf;
385 cxBufferInit(&href,
NULL,
256, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
386 cxBufferInit(&pbuf,
NULL,
256, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
387
388 int start =
0;
389 int begin =
0;
390
391
392 char *cp = strdup(path);
393
394 while(strlen(cp) >
1) {
395 char *cached = cxMapGet(sn->pathcache, cx_hash_key_str(cp));
396 if(cached) {
397 start = strlen(cp);
398 begin = start;
399 cxBufferPutString(&href, cached);
400 break;
401 }
else {
402
403 char *f = cp;
404 cp = util_parent_path(cp);
405 free(f);
406 }
407 }
408 free(cp);
409 if(href.pos ==
0) {
410
411
412 cxBufferPutString(&href, util_url_path(sn->base_url));
413 }
414
415
416 cxmutstr rp = cx_strdup(cx_strn(path, start));
417 DavResource *root = dav_resource_new(sn, rp.ptr);
418 free(rp.ptr);
419 resource_set_href(root, cx_strn(href.space, href.pos));
420
421
422 CxBuffer *rqbuf = create_basic_propfind_request();
423
424 cxstring remaining = cx_strsubs(p, start);
425 CxStrtokCtx elms = cx_strtok(remaining,
CX_STR(
"/"),
INT_MAX);
426 DavResource *res = root;
427 cxBufferPutString(&pbuf, res->path);
428
429 cxstring elm;
430 while(cx_strtok_next(&elms, &elm)) {
431 if(elm.length >
0) {
432
433 DavResource *child = dav_find_child(sn, res, rqbuf, elm.ptr);
434
435
436 if(pbuf.space[pbuf.pos-
1] !=
'/') {
437 if(href.space[href.pos-
1] !=
'/') {
438 cxBufferPut(&href,
'/');
439 }
440 cxBufferPut(&pbuf,
'/');
441 }
442
443 cxstring pp = cx_strn(pbuf.space, pbuf.size);
444 cxstring hh = cx_strn(href.space, href.size);
445 dav_session_cache_path(sn, pp, hh);
446
447 cxBufferWrite(elm.ptr,
1, elm.length, &pbuf);
448 if(child) {
449
450 cxBufferPutString(&href, util_resource_name(child->href));
451 res = child;
452 }
else if(
DAV_ENCRYPT_NAME(sn)) {
453 char *random_name = util_random_str();
454 cxBufferPutString(&href, random_name);
455 free(random_name);
456 }
else {
457
458 cxstring resname = cx_str(util_resource_name((
const char*)path));
459
460
461 if(resname.ptr[resname.length-
1] ==
'/') {
462 char *esc = curl_easy_escape(sn->handle,
463 resname.ptr, resname.length-
1);
464 cxBufferWrite(esc,
1, strlen(esc), &href);
465 cxBufferPut(&href,
'/');
466 curl_free(esc);
467 }
else {
468 char *esc = curl_easy_escape(sn->handle,
469 resname.ptr, resname.length);
470 cxBufferWrite(esc,
1, strlen(esc), &href);
471 curl_free(esc);
472 }
473 }
474 }
475 }
476
477
478 if(p.ptr[p.length-
1] ==
'/') {
479 if(href.space[href.pos-
1] !=
'/') {
480 cxBufferPut(&href,
'/');
481 }
482 cxBufferPut(&pbuf,
'/');
483 }
484
485 cxstring pp = cx_strn(pbuf.space, pbuf.size);
486 cxstring hh = cx_strn(href.space, href.size);
487 dav_session_cache_path(sn, pp, hh);
488
489 cxmutstr href_str = cx_strdup_a(
490 sn->mp->allocator,
491 cx_strn(href.space, href.size));
492
493
494 dav_resource_free_all(root);
495 cxBufferFree(rqbuf);
496
497 cxBufferDestroy(&pbuf);
498 cxBufferDestroy(&href);
499
500 return href_str.ptr;
501 }
else {
502 return dav_session_create_plain_href(sn, path);
503 }
504 }
505
506 DavResource* dav_find_child(DavSession *sn, DavResource *res, CxBuffer *rqbuf,
const char *name) {
507 if(res && !dav_propfind(sn, res, rqbuf)) {
508 DavResource *child = res->children;
509 while(child) {
510 if(!strcmp(child->name, name)) {
511 return child;
512 }
513 child = child->next;
514 }
515 }
516 return NULL;
517 }
518 519
void dav_session_cache_path(DavSession *sn, cxstring path, cxstring href) {
520 CxHashKey path_key = cx_hash_key(path.ptr, path.length);
521 char *elm = cxMapGet(sn->pathcache, path_key);
522 if(!elm) {
523 cxmutstr href_s = cx_strdup_a(sn->mp->allocator, href);
524 cxMapPut(sn->pathcache, path_key, href_s.ptr);
525 }
526 }
527
528
529 DavLock* dav_create_lock(DavSession *sn,
const char *token,
char *timeout) {
530 DavLock *lock = dav_session_malloc(sn,
sizeof(DavLock));
531 lock->path =
NULL;
532 lock->token = dav_session_strdup(sn, token);
533
534
535
536 return lock;
537 }
538
539 void dav_destroy_lock(DavSession *sn, DavLock *lock) {
540 dav_session_free(sn, lock->token);
541 if(lock->path) {
542 dav_session_free(sn, lock->path);
543 }
544 dav_session_free(sn, lock);
545 }
546
547
548 static int dav_lock_cmp(
void const *left,
void const *right) {
549 const DavLock *l = left;
550 const DavLock *r = right;
551 return strcmp(l->path, r->path);
552 }
553
554 static int create_lock_manager(DavSession *sn) {
555
556 DavLockManager *locks = cxMalloc(sn->mp->allocator,
sizeof(DavLockManager));
557 locks->resource_locks = cxHashMapCreate(sn->mp->allocator,
CX_STORE_POINTERS,
16);
558 locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, dav_lock_cmp,
CX_STORE_POINTERS);
559 sn->locks = locks;
560 return 0;
561 }
562
563 static DavLockManager* get_lock_manager(DavSession *sn) {
564 DavLockManager *locks = sn->locks;
565 if(!locks) {
566 if(create_lock_manager(sn)) {
567 return NULL;
568 }
569 locks = sn->locks;
570 }
571 return locks;
572 }
573
574 int dav_add_resource_lock(DavSession *sn,
const char *path, DavLock *lock) {
575 DavLockManager *locks = get_lock_manager(sn);
576 if(!locks) {
577 return -
1;
578 }
579
580 CxHashKey path_key = cx_hash_key_str(path);
581 if(cxMapGet(locks->resource_locks, path_key)) {
582 return -
1;
583 }
584
585 cxMapPut(locks->resource_locks, path_key, lock);
586 return 0;
587 }
588
589 int dav_add_collection_lock(DavSession *sn,
const char *path, DavLock *lock) {
590 DavLockManager *locks = get_lock_manager(sn);
591 if(!locks) {
592 return -
1;
593 }
594
595 lock->path = dav_session_strdup(sn, path);
596 cxListAdd(locks->collection_locks, lock);
597 cxListSort(locks->collection_locks);
598
599 return 0;
600 }
601
602 DavLock* dav_get_lock(DavSession *sn,
const char *path) {
603 DavLockManager *locks = get_lock_manager(sn);
604 if(!locks) {
605 return NULL;
606 }
607
608 cxstring p = cx_str(path);
609
610 DavLock *lock = cxMapGet(locks->resource_locks, cx_hash_key(p.ptr, p.length));
611 if(lock) {
612 return lock;
613 }
614
615 CxIterator i = cxListIterator(locks->collection_locks);
616 cx_foreach(DavLock*, col_lock, i) {
617 int cmd = strcmp(path, col_lock->path);
618 if(cmd ==
0) {
619 return col_lock;
620 }
else if(cx_strprefix(p, cx_str(col_lock->path))) {
621 return col_lock;
622 }
else if(cmd >
0) {
623 break;
624 }
625 }
626
627 return NULL;
628 }
629
630 void dav_remove_lock(DavSession *sn,
const char *path, DavLock *lock) {
631 DavLockManager *locks = get_lock_manager(sn);
632 if(!locks) {
633 return;
634 }
635
636 if(cxMapRemoveAndGet(locks->resource_locks, cx_hash_key_str(path))) {
637 return;
638 }
639
640 CxMutIterator i = cxListMutIterator(locks->collection_locks);
641 int rm =
0;
642 cx_foreach(DavLock* , cl, i) {
643 if(rm) {
644 break;
645 }
646 if(cl == lock) {
647 cxIteratorFlagRemoval(i);
648 rm =
1;
649 }
650 }
651 }
652