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