UNIXworkcode

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_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 // 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 #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 // TODO: rewrite using dav_get_property_ns 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 // TODO: add sstr_t version of dav_get_property_ns 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 //fwrite(rqbuf->space, 1, rqbuf->size, stdout); 394 //printf("\n"); 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 //printf("response\n%s\n", rpbuf->space); 403 // TODO: use PropfindParser 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 // clean resource properties 430 DavResourceData *data = root->data; 431 cxMapClear(data->properties); // TODO: free existing content 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 //printf("response\n%s\n", rpbuf->space); 444 dav_set_effective_href(sn, resource); 445 // TODO: use PropfindParser 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