UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2019 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 <time.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <errno.h> 34 #include <ctype.h> 35 #include <cx/string.h> 36 #include <cx/buffer.h> 37 #include <cx/utils.h> 38 #include <cx/printf.h> 39 #include <libxml/tree.h> 40 #include <curl/curl.h> 41 42 #ifdef _WIN32 43 #include <conio.h> 44 #define getpasswordchar() getch() 45 #define IS_PATH_SEPARATOR(c) (c == '/' || c == '\\') 46 #define PATH_SEPARATOR '\\' 47 #else 48 #include <unistd.h> 49 #include <spawn.h> 50 #include <sys/wait.h> 51 #include <termios.h> 52 #define getpasswordchar() getchar() 53 #define IS_PATH_SEPARATOR(c) (c == '/') 54 #define PATH_SEPARATOR '/' 55 #endif 56 57 #include "webdav.h" 58 #include "utils.h" 59 #include "crypto.h" 60 #include "session.h" 61 62 /* 63 #include <openssl/hmac.h> 64 #include <openssl/evp.h> 65 #include <openssl/bio.h> 66 #include <openssl/buffer.h> 67 #include <openssl/rand.h> 68 */ 69 70 static size_t extractval(cxstring str, char *result, char delim) { 71 size_t n = 0; 72 for(size_t i = 0; i < str.length ; i++) { 73 if(isdigit(str.ptr[i])) { 74 result[n++] = str.ptr[i]; 75 } else if(str.ptr[i] != delim) { 76 return 0; 77 } 78 } 79 result[n] = '\0'; 80 return n; 81 } 82 83 static time_t parse_iso8601(char *iso8601str) { 84 85 // safety 86 if(!iso8601str) { 87 return 0; 88 } 89 90 // local vars 91 struct tm tparts; 92 memset(&tparts, 0, sizeof(struct tm)); 93 long val; 94 char conv[16]; 95 96 // work on the trimmed string 97 cxstring date = cx_strtrim(cx_str(iso8601str)); 98 99 cxstring time = cx_strchr(date, 'T'); 100 if(time.length == 0) { 101 return 0; 102 } 103 date.length = time.ptr - date.ptr; 104 time.ptr++; time.length--; 105 106 cxstring tzinfo; 107 if((tzinfo = cx_strchr(time, 'Z')).length > 0 || 108 (tzinfo = cx_strchr(time, '+')).length > 0 || 109 (tzinfo = cx_strchr(time, '-')).length > 0) { 110 111 time.length = tzinfo.ptr - time.ptr; 112 } 113 114 // parse date 115 if((date.length != 8 && date.length != 10) 116 || extractval(date, conv , '-') != 8) { 117 return 0; 118 } 119 val = atol(conv); 120 if(val < 19000000L) { 121 return 0; 122 } 123 tparts.tm_mday = val % 100; 124 tparts.tm_mon = (val % 10000) / 100 - 1; 125 tparts.tm_year = val / 10000 - 1900; 126 127 // parse time and skip possible fractional seconds 128 cxstring frac; 129 if((frac = cx_strchr(time, '.')).length > 0 || 130 (frac = cx_strchr(time, ',')).length > 0) { 131 time.length = frac.ptr - time.ptr; 132 } 133 if((time.length != 6 && time.length != 8) 134 || extractval(time, conv , ':') != 6) { 135 return 0; 136 } 137 val = atol(conv); 138 tparts.tm_sec = val % 100; 139 tparts.tm_min = (val % 10000) / 100; 140 tparts.tm_hour = val / 10000; 141 142 143 // parse time zone (if any) 144 if(tzinfo.length == 0) { 145 // local time 146 tparts.tm_isdst = -1; 147 return mktime(&tparts); 148 } else if(!cx_strcmp(tzinfo, cx_str("Z"))) { 149 #if defined(__FreeBSD__) 150 return timegm(&tparts); 151 #elif defined(_WIN32) 152 return _mkgmtime(&tparts); 153 #else 154 return mktime(&tparts) - timezone; 155 #endif 156 } else if(tzinfo.ptr[0] == '+' || tzinfo.ptr[0] == '-') { 157 int sign = (tzinfo.ptr[0] == '+') ? -1 : 1; 158 159 if(tzinfo.length > 6) { 160 return 0; 161 } else { 162 tzinfo.ptr++; tzinfo.length--; 163 extractval(tzinfo, conv, ':'); 164 val = atol(conv); 165 val = 60 * (val / 100) + (val % 100); 166 #if defined(__FreeBSD__) 167 return timegm(&tparts) + (time_t) (60 * val * sign); 168 #elif defined(_WIN32) 169 return _mkgmtime(&tparts) + (time_t)(60 * val * sign); 170 #else 171 return mktime(&tparts) - timezone + (time_t) (60 * val * sign); 172 #endif 173 } 174 } else { 175 return 0; 176 } 177 } 178 179 180 time_t util_parse_creationdate(char *str) { 181 // parse a ISO-8601 date (rfc-3339) 182 // example: 2012-11-29T21:35:35Z 183 if(!str) { 184 return 0; 185 } 186 187 return parse_iso8601(str); 188 } 189 190 time_t util_parse_lastmodified(char *str) { 191 // parse a rfc-1123 date 192 // example: Thu, 29 Nov 2012 21:35:35 GMT 193 if(!str) { 194 return 0; 195 } else { 196 time_t result = curl_getdate(str, NULL); 197 if(result == -1) { 198 // fall back to the ISO-8601 format (e.g. Microsoft Sharepoint 199 // illegally uses this format for lastmodified, but also some 200 // users might want to give an ISO-8601 date) 201 return util_parse_creationdate(str); 202 } else { 203 return result; 204 } 205 } 206 } 207 208 int util_getboolean(const char *v) { 209 if(v[0] == 'T' || v[0] == 't') { 210 return 1; 211 } 212 return 0; 213 } 214 215 int util_strtouint(const char *str, uint64_t *value) { 216 if (str == NULL || *str == '\0') return 0; 217 char *end; 218 errno = 0; 219 uint64_t val = strtoull(str, &end, 0); 220 if(errno == 0 && *end == '\0') { 221 *value = val; 222 return 1; 223 } else { 224 return 0; 225 } 226 } 227 228 int util_strtoint(const char *str, int64_t *value) { 229 if (str == NULL || *str == '\0') return 0; 230 char *end; 231 errno = 0; 232 int64_t val = strtoll(str, &end, 0); 233 if(errno == 0 && *end == '\0') { 234 *value = val; 235 return 1; 236 } else { 237 return 0; 238 } 239 } 240 241 int util_szstrtouint(const char *str, uint64_t *value) { 242 if (str == NULL || *str == '\0') return 0; 243 char *end; 244 errno = 0; 245 size_t len = strlen(str); 246 uint64_t val = strtoull(str, &end, 0); 247 if(errno != 0) { 248 return 0; 249 } if(end == str+len) { 250 *value = val; 251 return 1; 252 } else if(end == str+len-1) { 253 uint64_t mul = 1; 254 switch(end[0]) { 255 case 'k': 256 case 'K': mul = 1024; break; 257 case 'm': 258 case 'M': mul = 1024*1024; break; 259 case 'g': 260 case 'G': mul = 1024*1024*1024; break; 261 default: return 0; 262 } 263 264 uint64_t result = 0; 265 if(util_uint_mul(val, mul, &result)) { 266 return 0; 267 } 268 *value = result; 269 return 1; 270 } 271 return 0; 272 } 273 274 int util_uint_mul(uint64_t a, uint64_t b, uint64_t *result) { 275 if(a == 0 || b == 0) { 276 *result = 0; 277 return 0; 278 } 279 uint64_t r = a * b; 280 if(r / b == a) { 281 *result = r; 282 return 0; 283 } else { 284 *result = 0; 285 return 1; 286 } 287 } 288 289 cxstring util_url_base_s(cxstring url) { 290 size_t i = 0; 291 if(url.length > 0) { 292 int slmax; 293 if(cx_strprefix(url, cx_str("http://"))) { 294 slmax = 3; 295 } else if(cx_strprefix(url, cx_str("https://"))) { 296 slmax = 3; 297 } else { 298 slmax = 1; 299 } 300 int slashcount = 0; 301 for(i=0;i<url.length;i++) { 302 if(url.ptr[i] == '/') { 303 slashcount++; 304 if(slashcount == slmax) { 305 i++; 306 break; 307 } 308 } 309 } 310 } 311 return cx_strsubsl(url, 0, i); 312 } 313 314 char* util_url_base(const char *url) { 315 return cx_strdup(util_url_base_s(cx_str(url))).ptr; 316 } 317 318 #ifdef _WIN32 319 #define strncasecmp _strnicmp 320 #endif 321 322 const char* util_url_path(const char *url) { 323 return util_url_path_s(cx_str(url)).ptr; 324 } 325 326 cxstring util_url_path_s(cxstring url) { 327 cxstring path = { "", 0 }; 328 int slashcount = 0; 329 int slmax; 330 if(url.length > 7 && !strncasecmp(url.ptr, "http://", 7)) { 331 slmax = 3; 332 } else if(url.length > 8 && !strncasecmp(url.ptr, "https://", 8)) { 333 slmax = 3; 334 } else { 335 slmax = 1; 336 } 337 char c; 338 for(int i=0;i<url.length;i++) { 339 c = url.ptr[i]; 340 if(c == '/') { 341 slashcount++; 342 if(slashcount == slmax) { 343 path = cx_strsubs(url, i); 344 break; 345 } 346 } 347 } 348 return path; 349 } 350 351 char* util_url_decode(DavSession *sn, const char *url) { 352 char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL); 353 char *ret = strdup(unesc); 354 curl_free(unesc); 355 return ret; 356 } 357 358 static size_t util_header_callback(char *buffer, size_t size, 359 size_t nitems, void *data) { 360 361 cxstring sbuffer = cx_strn(buffer, size*nitems); 362 363 CxMap *map = (CxMap*) data; 364 365 // if we get a status line, clear the map and exit 366 if(cx_strprefix(sbuffer, cx_str("HTTP/"))) { 367 // TODO: use new map destructor ucx_map_free_content(map, free); 368 cxMapClear(map); 369 return size*nitems; 370 } 371 372 // if we get the terminating CRLF, just exit 373 if(!cx_strcmp(sbuffer, cx_str("\r\n"))) { 374 return 2; 375 } 376 377 cxstring key = sbuffer; 378 cxstring value = cx_strchr(sbuffer, ':'); 379 380 if(value.length == 0) { 381 return 0; // invalid header line 382 } 383 384 key.length = value.ptr - key.ptr; 385 value.ptr++; value.length--; 386 387 cxmutstr key_cp = cx_strdup(cx_strtrim(key)); 388 cx_strlower(key_cp); 389 cxmutstr value_cp = cx_strdup(cx_strtrim(value)); 390 391 cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr); 392 393 free(key_cp.ptr); 394 395 return sbuffer.length; 396 } 397 398 int util_path_isrelated(const char *path1, const char *path2) { 399 cxstring p1 = cx_str(path1); 400 cxstring p2 = cx_str(path2); 401 402 if(IS_PATH_SEPARATOR(p1.ptr[p1.length-1])) { 403 p1.length--; 404 } 405 if(IS_PATH_SEPARATOR(p2.ptr[p2.length-1])) { 406 p2.length--; 407 } 408 409 if(p2.length < p1.length) { 410 return 0; 411 } 412 413 if(!cx_strcmp(p1, p2)) { 414 return 1; 415 } 416 417 if(cx_strprefix(p2, p1)) { 418 if(IS_PATH_SEPARATOR(p2.ptr[p1.length])) { 419 return 1; 420 } 421 } 422 423 return 0; 424 } 425 426 #ifdef _WIN32 427 int util_path_isabsolut(const char *path) { 428 if(strlen(path) < 3) { 429 return 0; 430 } 431 432 // check if first char is A-Z or a-z 433 char c = path[0]; 434 if(!((c >= 65 && c <= 90) || (c >= 97 && c <= 122))) { 435 return 0; 436 } 437 438 if(path[1] == ':' && path[2] == '\\') { 439 return 1; 440 } 441 return 0; 442 } 443 #else 444 int util_path_isabsolut(const char *path) { 445 return path[0] == '/'; 446 } 447 #endif 448 449 char* util_path_normalize(const char *path) { 450 size_t len = strlen(path); 451 CxBuffer buf; 452 cxBufferInit(&buf, NULL, len+1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 453 454 if(path[0] == '/') { 455 cxBufferPut(&buf, '/'); 456 } 457 458 int add_separator = 0; 459 int seg_start = 0; 460 for(int i=0;i<=len;i++) { 461 char c = path[i]; 462 if(IS_PATH_SEPARATOR(c) || c == '\0') { 463 const char *seg_ptr = path+seg_start; 464 int seg_len = i - seg_start; 465 if(IS_PATH_SEPARATOR(seg_ptr[0])) { 466 seg_ptr++; 467 seg_len--; 468 } 469 470 if(seg_len > 0) { 471 cxstring seg = cx_strn(seg_ptr, seg_len); 472 if(!cx_strcmp(seg, CX_STR(".."))) { 473 for(int j=buf.pos;j>=0;j--) { 474 char t = j < buf.pos ? buf.space[j] : 0; 475 if(IS_PATH_SEPARATOR(t) || j == 0) { 476 buf.pos = j; 477 buf.size = j; 478 buf.space[j] = 0; 479 add_separator = IS_PATH_SEPARATOR(t) ? 1 : 0; 480 break; 481 } 482 } 483 } else if(!cx_strcmp(seg, CX_STR("."))) { 484 // ignore 485 } else { 486 if(add_separator) { 487 cxBufferPut(&buf, PATH_SEPARATOR); 488 } 489 cxBufferWrite(seg_ptr, 1, seg_len, &buf); 490 add_separator = 1; 491 } 492 } 493 494 seg_start = i; 495 } 496 } 497 498 cxBufferPut(&buf, 0); 499 500 return buf.space; 501 } 502 503 static char* create_relative_path(const char *abspath, const char *base) { 504 size_t path_len = strlen(abspath); 505 size_t base_len = strlen(base); 506 507 if(IS_PATH_SEPARATOR(abspath[path_len-1])) { 508 path_len--; 509 } 510 if(IS_PATH_SEPARATOR(base[base_len-1])) { 511 base_len--; 512 } 513 // get base parent 514 for(int i=base_len-1;i>=0;i--) { 515 if(IS_PATH_SEPARATOR(base[i])) { 516 base_len = i+1; 517 break; 518 } 519 } 520 521 size_t max = path_len > base_len ? base_len : path_len; 522 523 // get prefix of abspath and base 524 // this dir is the root of the link 525 size_t last_dir = 0; 526 for(size_t i=0;i<max;i++) { 527 char c = abspath[i]; 528 if(c != base[i]) { 529 break; 530 } else if(IS_PATH_SEPARATOR(c)) { 531 last_dir = i; 532 } 533 } 534 535 char *ret = NULL; 536 CxBuffer out; 537 if(last_dir+1 < base_len) { 538 // base is deeper than the link root, we have to go backwards 539 size_t dircount = 0; 540 for(size_t i=last_dir+1;i<base_len;i++) { 541 if(IS_PATH_SEPARATOR(base[i])) { 542 dircount++; 543 } 544 } 545 546 cxBufferInit(&out, NULL, dircount*3+path_len-last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 547 548 for(size_t i=0;i<dircount;i++) { 549 cxBufferPutString(&out, "../"); 550 } 551 } else { 552 cxBufferInit(&out, NULL, path_len - last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 553 } 554 555 cxBufferPutString(&out, abspath + last_dir + 1); 556 cxBufferPut(&out, 0); 557 558 return out.space; 559 } 560 561 #ifdef _WIN32 562 char* util_create_relative_path(const char *abspath, const char *base) { 563 char *abspath_converted = strdup(abspath); 564 char *base_converted = strdup(base); 565 size_t abs_len = strlen(abspath_converted); 566 size_t base_len = strlen(base_converted); 567 568 for(int i=0;i<abs_len;i++) { 569 if(abspath_converted[i] == '\\') { 570 abspath_converted[i] = '/'; 571 } 572 } 573 for(int i=0;i<base_len;i++) { 574 if(base_converted[i] == '\\') { 575 base_converted[i] = '/'; 576 } 577 } 578 579 char *ret = create_relative_path(abspath_converted, base_converted); 580 free(abspath_converted); 581 free(base_converted); 582 return ret; 583 } 584 #else 585 char* util_create_relative_path(const char *abspath, const char *base) { 586 return create_relative_path(abspath, base); 587 } 588 #endif 589 590 591 void util_capture_header(CURL *handle, CxMap* map) { 592 if(map) { 593 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, util_header_callback); 594 curl_easy_setopt(handle, CURLOPT_HEADERDATA, map); 595 } else { 596 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, NULL); 597 curl_easy_setopt(handle, CURLOPT_HEADERDATA, NULL); 598 } 599 } 600 601 const char* util_resource_name(const char *url) { 602 cxstring urlstr = cx_str(url); 603 if(urlstr.ptr[urlstr.length-1] == '/') { 604 urlstr.length--; 605 } 606 cxstring resname = cx_strrchr(urlstr, '/'); 607 if(resname.length > 1) { 608 return resname.ptr+1; 609 } else { 610 return url; 611 } 612 } 613 614 const char* util_resource_name_c(const char *url, char pathseparator) { 615 cxstring urlstr = cx_str(url); 616 if(urlstr.ptr[urlstr.length-1] == pathseparator) { 617 urlstr.length--; 618 } 619 cxstring resname = cx_strrchr(urlstr, pathseparator); 620 if(resname.length > 1) { 621 return resname.ptr+1; 622 } else { 623 return url; 624 } 625 } 626 627 const char* util_path_file_name(const char *url) { 628 #ifdef _WIN32 629 return util_resource_name_c(url, '\\'); 630 #else 631 return util_resource_name_c(url, '/'); 632 #endif 633 } 634 635 636 int util_mkdir(char *path, mode_t mode) { 637 #ifdef _WIN32 638 return mkdir(path); 639 #else 640 return mkdir(path, mode); 641 #endif 642 } 643 644 char* util_concat_path(const char *url_base, const char *p) { 645 cxstring base = cx_str(url_base); 646 cxstring path; 647 if(p) { 648 path = cx_str((char*)p); 649 } else { 650 path = CX_STR(""); 651 } 652 653 return util_concat_path_s(base, path).ptr; 654 } 655 656 cxmutstr util_concat_path_s(cxstring base, cxstring path) { 657 if(!path.ptr) { 658 path = CX_STR(""); 659 } 660 661 int add_separator = 0; 662 if(base.length != 0 && base.ptr[base.length-1] == '/') { 663 if(path.ptr[0] == '/') { 664 base.length--; 665 } 666 } else { 667 if(path.length == 0 || path.ptr[0] != '/') { 668 add_separator = 1; 669 } 670 } 671 672 cxmutstr url; 673 if(add_separator) { 674 url = cx_strcat(3, base, CX_STR("/"), path); 675 } else { 676 url = cx_strcat(2, base, path); 677 } 678 679 return url; 680 } 681 682 cxmutstr util_concat_path_ext(cxstring base, cxstring path, char separator) { 683 if(!path.ptr) { 684 path = CX_STR(""); 685 } 686 687 int add_separator = 0; 688 if(base.length != 0 && base.ptr[base.length-1] == separator) { 689 if(path.ptr[0] == separator) { 690 base.length--; 691 } 692 } else { 693 if(path.length == 0 || path.ptr[0] != separator) { 694 add_separator = 1; 695 } 696 } 697 698 cxmutstr url; 699 if(add_separator) { 700 url = cx_strcat(3, base, cx_strn(&separator, 1), path); 701 } else { 702 url = cx_strcat(2, base, path); 703 } 704 705 return url; 706 } 707 708 cxmutstr util_concat_sys_path(cxstring base, cxstring path) { 709 #ifdef _WIN32 710 return util_concat_path_ext(base, path, '\\'); 711 #else 712 return util_concat_path_ext(base, path, '/'); 713 #endif 714 } 715 716 char* util_get_url(DavSession *sn, const char *href) { 717 cxstring base = cx_str(sn->base_url); 718 cxstring href_str = cx_str(href); 719 720 const char *base_path = util_url_path(sn->base_url); 721 base.length -= strlen(base_path); 722 723 cxmutstr url = cx_strcat(2, base, href_str); 724 return url.ptr; 725 } 726 727 void util_set_url(DavSession *sn, const char *href) { 728 char *url = util_get_url(sn, href); 729 curl_easy_setopt(sn->handle, CURLOPT_URL, url); 730 free(url); 731 } 732 733 char* util_path_to_url(DavSession *sn, const char *path) { 734 size_t pathlen = path ? strlen(path) : 0; 735 if(pathlen == 0) { 736 return strdup(sn->base_url); 737 } 738 739 CxBuffer url; 740 cxBufferInit(&url, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 741 742 // add base url 743 cxBufferWrite(sn->base_url, 1, strlen(sn->base_url), &url); 744 // remove trailing slash 745 cxBufferSeek(&url, -1, SEEK_CUR); 746 747 cxstring p = cx_strn(path, pathlen); 748 749 CxStrtokCtx tkctx = cx_strtok(p, CX_STR("/"), INT_MAX); 750 cxstring node; 751 while(cx_strtok_next(&tkctx, &node)) { 752 if(node.length > 0) { 753 char *esc = curl_easy_escape(sn->handle, node.ptr, node.length); 754 cxBufferPut(&url, '/'); 755 cxBufferWrite(esc, 1, strlen(esc), &url); 756 curl_free(esc); 757 } 758 } 759 760 if(path[p.length-1] == '/') { 761 cxBufferPut(&url, '/'); 762 } 763 cxBufferPut(&url, 0); 764 765 return url.space; 766 } 767 768 char* util_parent_path(const char *path) { 769 const char *name = util_resource_name(path); 770 size_t namelen = strlen(name); 771 size_t pathlen = strlen(path); 772 size_t parentlen = pathlen - namelen; 773 char *parent = malloc(parentlen + 1); 774 memcpy(parent, path, parentlen); 775 parent[parentlen] = '\0'; 776 return parent; 777 } 778 779 char* util_sys_parent_path(const char *path) { 780 const char *name = util_path_file_name(path); 781 size_t namelen = strlen(name); 782 size_t pathlen = strlen(path); 783 size_t parentlen = pathlen - namelen; 784 char *parent = malloc(parentlen + 1); 785 memcpy(parent, path, parentlen); 786 parent[parentlen] = '\0'; 787 return parent; 788 } 789 790 char* util_size_str(DavBool iscollection, uint64_t contentlength) { 791 return util_size_str2(iscollection, contentlength, contentlength, 1); 792 } 793 794 char* util_size_str2(DavBool iscollection, uint64_t contentlength, uint64_t dimension, int precision) { 795 char *str = malloc(16); 796 uint64_t size = contentlength; 797 798 if(iscollection) { 799 str[0] = '\0'; // currently no information for collections 800 } else if(dimension < 0x400) { 801 snprintf(str, 16, "%" PRIu64 " bytes", size); 802 } else if(dimension < 0x100000) { 803 float s = (float)size/0x400; 804 int diff = (s*100 - (int)s*100); 805 if(diff > 90) { 806 diff = 0; 807 s += 0.10f; 808 } 809 if(dimension < 0x2800 && diff != 0) { 810 // size < 10 KiB 811 snprintf(str, 16, "%.*f KiB", precision, s); 812 } else { 813 snprintf(str, 16, "%.0f KiB", s); 814 } 815 } else if(dimension < 0x40000000) { 816 float s = (float)size/0x100000; 817 int diff = (s*100 - (int)s*100); 818 if(diff > 90) { 819 diff = 0; 820 s += 0.10f; 821 } 822 if(dimension < 0xa00000 && diff != 0) { 823 // size < 10 MiB 824 snprintf(str, 16, "%.*f MiB", precision, s); 825 } else { 826 size /= 0x100000; 827 snprintf(str, 16, "%.0f MiB", s); 828 } 829 } else if(dimension < 0x1000000000ULL) { 830 float s = (float)size/0x40000000; 831 int diff = (s*100 - (int)s*100); 832 if(diff > 90) { 833 diff = 0; 834 s += 0.10f; 835 } 836 if(dimension < 0x280000000 && diff != 0) { 837 // size < 10 GiB 838 snprintf(str, 16, "%.*f GiB", precision, s); 839 } else { 840 size /= 0x40000000; 841 snprintf(str, 16, "%.0f GiB", s); 842 } 843 } else { 844 size /= 1024; 845 float s = (float)size/0x40000000; 846 int diff = (s*100 - (int)s*100); 847 if(diff > 90) { 848 diff = 0; 849 s += 0.10f; 850 } 851 if(dimension < 0x280000000 && diff != 0) { 852 // size < 10 TiB 853 snprintf(str, 16, "%.*f TiB", precision, s); 854 } else { 855 size /= 0x40000000; 856 snprintf(str, 16, "%.0f TiB", s); 857 } 858 } 859 return str; 860 } 861 862 char* util_date_str(time_t tm) { 863 struct tm t; 864 struct tm n; 865 time_t now = time(NULL); 866 #ifdef _WIN32 867 memcpy(&t, localtime(&tm), sizeof(struct tm)); 868 memcpy(&n, localtime(&now), sizeof(struct tm)); 869 #else 870 localtime_r(&tm, &t); 871 localtime_r(&now, &n); 872 #endif /* _WIN32 */ 873 char *str = malloc(16); 874 if(t.tm_year == n.tm_year) { 875 strftime(str, 16, "%b %d %H:%M", &t); 876 } else { 877 strftime(str, 16, "%b %d %Y", &t); 878 } 879 return str; 880 } 881 882 883 char* util_xml_get_text(const xmlNode *elm) { 884 xmlNode *node = elm->children; 885 while(node) { 886 if(node->type == XML_TEXT_NODE) { 887 return (char*)node->content; 888 } 889 node = node->next; 890 } 891 return NULL; 892 } 893 894 895 char* util_base64decode(const char *in) { 896 int len = 0; 897 return util_base64decode_len(in, &len); 898 } 899 900 #define WHITESPACE 64 901 #define EQUALS 65 902 #define INVALID 66 903 static char b64dectable[] = { 904 66,66,66,66,66,66,66,66,66,66,64,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 905 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,62,66,66,66,63,52,53, 906 54,55,56,57,58,59,60,61,66,66,66,65,66,66,66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 907 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,66,66,66,66,66,66,26,27,28, 908 29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,66,66, 909 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 910 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 911 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 912 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 913 66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66,66, 914 66,66,66,66,66,66 915 }; 916 char* util_base64decode_len(const char* in, int *outlen) { 917 /* code is mostly from wikibooks */ 918 919 if(!in) { 920 *outlen = 0; 921 return NULL; 922 } 923 924 size_t inlen = strlen(in); 925 size_t bufsize = (inlen*3) / 4; 926 char *outbuf = malloc(bufsize+1); 927 *outlen = -1; 928 929 unsigned char *out = (unsigned char*)outbuf; 930 931 const char *end = in + inlen; 932 char iter = 0; 933 uint32_t buf = 0; 934 size_t len = 0; 935 936 while (in < end) { 937 unsigned char c = b64dectable[*in++]; 938 939 switch (c) { 940 case WHITESPACE: continue; /* skip whitespace */ 941 case INVALID: { 942 /* invalid input */ 943 outbuf[0] = 0; 944 return outbuf; 945 } 946 case EQUALS: { 947 /* pad character, end of data */ 948 in = end; 949 continue; 950 } 951 default: { 952 buf = buf << 6 | c; 953 iter++; // increment the number of iteration 954 /* If the buffer is full, split it into bytes */ 955 if (iter == 4) { 956 if ((len += 3) > bufsize) { 957 /* buffer overflow */ 958 outbuf[0] = 0; 959 return outbuf; 960 } 961 *(out++) = (buf >> 16) & 255; 962 *(out++) = (buf >> 8) & 255; 963 *(out++) = buf & 255; 964 buf = 0; iter = 0; 965 966 } 967 } 968 } 969 } 970 971 if (iter == 3) { 972 if ((len += 2) > bufsize) { 973 /* buffer overflow */ 974 outbuf[0] = 0; 975 return outbuf; 976 } 977 *(out++) = (buf >> 10) & 255; 978 *(out++) = (buf >> 2) & 255; 979 } 980 else if (iter == 2) { 981 if (++len > bufsize) { 982 /* buffer overflow */ 983 outbuf[0] = 0; 984 return outbuf; 985 } 986 *(out++) = (buf >> 4) & 255; 987 } 988 989 *outlen = len; /* modify to reflect the actual output size */ 990 outbuf[len] = 0; 991 return outbuf; 992 } 993 994 995 static char* b64enctable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 996 char* util_base64encode(const char *in, size_t len) { 997 // calculate length of base64 output and create buffer 998 size_t outlen = 4 * ((len + 2) / 3); 999 int pad = len % 3; 1000 1001 char *out = malloc(outlen + 1); 1002 out[outlen] = 0; 1003 size_t pos = 0; 1004 1005 // encode blocks of 3 bytes 1006 size_t i; 1007 size_t blockend = len - pad; 1008 for(i=0;i<blockend;i++) { 1009 unsigned char b1 = in[i++]; 1010 unsigned char b2 = in[i++]; 1011 unsigned char b3 = in[i]; 1012 uint32_t inb = b1 << 16 | (b2 << 8) | b3; 1013 out[pos++] = b64enctable[(inb >> 18) & 63]; 1014 out[pos++] = b64enctable[(inb >> 12) & 63]; 1015 out[pos++] = b64enctable[(inb >> 6) & 63]; 1016 out[pos++] = b64enctable[(inb) & 63]; 1017 } 1018 1019 // encode last bytes 1020 if(pad > 0) { 1021 char p[3] = {0, 0, 0}; 1022 for(int j=0;i<len;i++) { 1023 p[j++] = in[i]; 1024 } 1025 unsigned char b1 = p[0]; 1026 unsigned char b2 = p[1]; 1027 unsigned char b3 = p[2]; 1028 uint32_t inb = (b1 << 16) | (b2 << 8) | b3; 1029 out[pos++] = b64enctable[(inb >> 18) & 63]; 1030 out[pos++] = b64enctable[(inb >> 12) & 63]; 1031 out[pos++] = b64enctable[(inb >> 6) & 63]; 1032 out[pos++] = b64enctable[(inb) & 63]; 1033 for(int k=outlen-1;k>=outlen-(3-pad);k--) { 1034 out[k] = '='; 1035 } 1036 } 1037 1038 return out; 1039 } 1040 1041 char* util_encrypt_str(DavSession *sn, const char *str, const char *key) { 1042 DavKey *k = dav_context_get_key(sn->context, key); 1043 if(!k) { 1044 sn->error = DAV_ERROR; 1045 cxmutstr err = cx_asprintf("Key %s not found", key); 1046 dav_session_set_errstr(sn, err.ptr); 1047 free(err.ptr); 1048 return NULL; 1049 } 1050 1051 return util_encrypt_str_k(sn, str, k); 1052 } 1053 1054 char* util_encrypt_str_k(DavSession *sn, const char *str, DavKey *key) { 1055 char *enc_str = aes_encrypt(str, strlen(str), key); 1056 char *ret_str = dav_session_strdup(sn, enc_str); 1057 free(enc_str); 1058 return ret_str; 1059 } 1060 1061 char* util_decrypt_str(DavSession *sn, const char *str, const char *key) { 1062 DavKey *k = dav_context_get_key(sn->context, key); 1063 if(!k) { 1064 sn->error = DAV_ERROR; 1065 cxmutstr err = cx_asprintf("Key %s not found", key); 1066 dav_session_set_errstr(sn, err.ptr); 1067 free(err.ptr); 1068 return NULL; 1069 } 1070 1071 return util_decrypt_str_k(sn, str, k); 1072 } 1073 1074 char* util_decrypt_str_k(DavSession *sn, const char *str, DavKey *key) { 1075 size_t len = 0; 1076 char *dec_str = aes_decrypt(str, &len, key); 1077 char *ret_str = dav_session_strdup(sn, dec_str); 1078 free(dec_str); 1079 return ret_str; 1080 } 1081 1082 char* util_random_str() { 1083 unsigned char *str = malloc(25); 1084 str[24] = '\0'; 1085 1086 cxstring t = CX_STR( 1087 "01234567890" 1088 "abcdefghijklmnopqrstuvwxyz" 1089 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 1090 const unsigned char *table = (const unsigned char*)t.ptr; 1091 1092 #ifdef DAV_USE_OPENSSL 1093 RAND_bytes(str, 24); 1094 #else 1095 dav_rand_bytes(str, 24); 1096 #endif 1097 for(int i=0;i<24;i++) { 1098 int c = str[i] % t.length; 1099 str[i] = table[c]; 1100 } 1101 1102 return (char*)str; 1103 } 1104 1105 /* 1106 * gets a substring from 0 to the appearance of the token 1107 * tokens are separated by space 1108 * sets sub to the substring and returns the remaining string 1109 */ 1110 // TODO: remove if it isn't used 1111 /* 1112 sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { 1113 int i; 1114 int token_start = -1; 1115 int token_end = -1; 1116 for(i=0;i<=str.length;i++) { 1117 int c; 1118 if(i == str.length) { 1119 c = ' '; 1120 } else { 1121 c = str.ptr[i]; 1122 } 1123 if(c < 33) { 1124 if(token_start != -1) { 1125 token_end = i; 1126 size_t len = token_end - token_start; 1127 sstr_t tk = sstrsubsl(str, token_start, len); 1128 //printf("token: {%.*s}\n", token.length, token.ptr); 1129 if(!sstrcmp(tk, token)) { 1130 *sub = sstrtrim(sstrsubsl(str, 0, token_start)); 1131 break; 1132 } 1133 token_start = -1; 1134 token_end = -1; 1135 } 1136 } else { 1137 if(token_start == -1) { 1138 token_start = i; 1139 } 1140 } 1141 } 1142 1143 if(i < str.length) { 1144 return sstrtrim(sstrsubs(str, i)); 1145 } else { 1146 str.ptr = NULL; 1147 str.length = 0; 1148 return str; 1149 } 1150 } 1151 */ 1152 1153 cxmutstr util_readline(FILE *stream) { 1154 CxBuffer buf; 1155 cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1156 1157 int c; 1158 while((c = fgetc(stream)) != EOF) { 1159 if(c == '\n') { 1160 break; 1161 } 1162 cxBufferPut(&buf, c); 1163 } 1164 1165 cxmutstr str = cx_strdup(cx_strtrim(cx_strn(buf.space, buf.size))); 1166 cxBufferDestroy(&buf); 1167 return str; 1168 } 1169 1170 char* util_password_input(char *prompt) { 1171 fprintf(stderr, "%s", prompt); 1172 fflush(stderr); 1173 1174 #ifndef _WIN32 1175 // hide terminal input 1176 struct termios oflags, nflags; 1177 if(isatty(fileno(stdin))) { 1178 tcgetattr(fileno(stdin), &oflags); 1179 nflags = oflags; 1180 nflags.c_lflag &= ~ECHO; 1181 nflags.c_lflag |= ECHONL; 1182 if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) { 1183 perror("tcsetattr"); 1184 } 1185 } 1186 1187 #endif 1188 1189 // read password input 1190 CxBuffer buf; 1191 cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1192 int c = 0; 1193 while((c = getpasswordchar()) != EOF) { 1194 if(c == '\n' || c == '\r') { 1195 break; 1196 } 1197 cxBufferPut(&buf, c); 1198 } 1199 cxBufferPut(&buf, 0); 1200 fflush(stdin); 1201 1202 #ifndef _WIN32 1203 // restore terminal settings 1204 if (isatty(fileno(stdin)) && tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) { 1205 perror("tcsetattr"); 1206 } 1207 #endif 1208 1209 return buf.space; 1210 } 1211 1212 int util_exec_command(char *command, CxBuffer *outbuf) { 1213 #ifdef _WIN32 1214 fprintf(stderr, "util_exec_command unsupported\n"); 1215 return 1; 1216 #else 1217 1218 int pout[2]; 1219 if(pipe(pout)) { 1220 perror("pipe"); 1221 return 1; 1222 } 1223 1224 int ret = 0; 1225 1226 // close stdin and stderr, use pipe for stdout 1227 posix_spawn_file_actions_t actions; 1228 posix_spawn_file_actions_init(&actions); 1229 posix_spawn_file_actions_addclose(&actions, 0); 1230 posix_spawn_file_actions_adddup2(&actions, pout[1], 1); 1231 posix_spawn_file_actions_addclose(&actions, 2); 1232 1233 char *args[4]; 1234 args[0] = "sh"; 1235 args[1] = "-c"; 1236 args[2] = command; 1237 args[3] = NULL; 1238 1239 pid_t pid; // child pid 1240 ret = posix_spawn(&pid, "/bin/sh", &actions, NULL, args, NULL); 1241 1242 close(pout[1]); 1243 1244 if(!ret) { 1245 ssize_t r; 1246 char buf[1024]; 1247 while((r = read(pout[0], buf, 1024)) > 0) { 1248 cxBufferWrite(buf, 1, r, outbuf); 1249 } 1250 } 1251 1252 // wait for child process 1253 ret = 1; 1254 waitpid(pid, &ret, 0); 1255 1256 posix_spawn_file_actions_destroy(&actions); 1257 close(pout[0]); 1258 1259 return ret; 1260 #endif 1261 } 1262 1263 char* util_hexstr(const unsigned char *data, size_t len) { 1264 size_t buflen = 2*len + 4; 1265 CxBuffer buf; 1266 cxBufferInit(&buf, NULL, buflen + 1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1267 for(int i=0;i<len;i++) { 1268 cx_bprintf(&buf, "%02x", data[i]); 1269 } 1270 cxBufferPut(&buf, 0); 1271 return buf.space; 1272 } 1273 1274 void util_remove_trailing_pathseparator(char *path) { 1275 size_t len = strlen(path); 1276 if(len < 2) { 1277 return; 1278 } 1279 1280 if(path[len-1] == '/') { 1281 path[len-1] = '\0'; 1282 } 1283 } 1284 1285 char* util_file_hash(const char *path) { 1286 FILE *in = fopen(path, "r"); 1287 if(!in) { 1288 return NULL; 1289 } 1290 1291 DAV_SHA_CTX *sha = dav_hash_init(); 1292 char *buf = malloc(16384); 1293 1294 size_t r; 1295 while((r = fread(buf, 1, 16384, in)) > 0) { 1296 dav_hash_update(sha, buf, r); 1297 } 1298 1299 unsigned char hash[DAV_SHA256_DIGEST_LENGTH]; 1300 dav_hash_final(sha, hash); 1301 free(buf); 1302 fclose(in); 1303 1304 return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH); 1305 } 1306 1307