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 } 3 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 rminating 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 i; 526 size_t last_dir = 0; 527 for(i=0;i<max;i++) { 528 char c = abspath[i]; 529 if(c != base[i]) { 530 break; 531 } else if(IS_PATH_SEPARATOR(c)) { 532 last_dir = i; 533 } 534 } 535 536 char *ret = NULL; 537 CxBuffer out; 538 if(last_dir+1 < base_len) { 539 // base is deeper than the link root, we have to go backwards 540 int dircount = 0; 541 for(int i=last_dir+1;i<base_len;i++) { 542 if(IS_PATH_SEPARATOR(base[i])) { 543 dircount++; 544 } 545 } 546 547 cxBufferInit(&out, NULL, dircount*3+path_len-last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 548 549 for(int i=0;i<dircount;i++) { 550 cxBufferPutString(&out, "../"); 551 } 552 } else { 553 cxBufferInit(&out, NULL, path_len - last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 554 } 555 556 cxBufferPutString(&out, abspath + last_dir + 1); 557 cxBufferPut(&out, 0); 558 559 return out.space; 560 } 561 562 #ifdef _WIN32 563 char* util_create_relative_path(const char *abspath, const char *base) { 564 char *abspath_converted = strdup(abspath); 565 char *base_converted = strdup(base); 566 size_t abs_len = strlen(abspath_converted); 567 size_t base_len = strlen(base_converted); 568 569 for(int i=0;i<abs_len;i++) { 570 if(abspath_converted[i] == '\\') { 571 abspath_converted[i] = '/'; 572 } 573 } 574 for(int i=0;i<base_len;i++) { 575 if(base_converted[i] == '\\') { 576 base_converted[i] = '/'; 577 } 578 } 579 580 char *ret = create_relative_path(abspath_converted, base_converted); 581 free(abspath_converted); 582 free(base_converted); 583 return ret; 584 } 585 #else 586 char* util_create_relative_path(const char *abspath, const char *base) { 587 return create_relative_path(abspath, base); 588 } 589 #endif 590 591 592 void util_capture_header(CURL *handle, CxMap* map) { 593 if(map) { 594 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, util_header_callback); 595 curl_easy_setopt(handle, CURLOPT_HEADERDATA, map); 596 } else { 597 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, NULL); 598 curl_easy_setopt(handle, CURLOPT_HEADERDATA, NULL); 599 } 600 } 601 602 const char* util_resource_name(const char *url) { 603 cxstring urlstr = cx_str(url); 604 if(urlstr.ptr[urlstr.length-1] == '/') { 605 urlstr.length--; 606 } 607 cxstring resname = cx_strrchr(urlstr, '/'); 608 if(resname.length > 1) { 609 return resname.ptr+1; 610 } else { 611 return url; 612 } 613 } 614 615 const char* util_resource_name_c(const char *url, char pathseparator) { 616 cxstring urlstr = cx_str(url); 617 if(urlstr.ptr[urlstr.length-1] == pathseparator) { 618 urlstr.length--; 619 } 620 cxstring resname = cx_strrchr(urlstr, pathseparator); 621 if(resname.length > 1) { 622 return resname.ptr+1; 623 } else { 624 return url; 625 } 626 } 627 628 const char* util_path_file_name(const char *url) { 629 #ifdef _WIN32 630 return util_resource_name_c(url, '\\'); 631 #else 632 return util_resource_name_c(url, '/'); 633 #endif 634 } 635 636 637 int util_mkdir(char *path, mode_t mode) { 638 #ifdef _WIN32 639 return mkdir(path); 640 #else 641 return mkdir(path, mode); 642 #endif 643 } 644 645 char* util_concat_path(const char *url_base, const char *p) { 646 cxstring base = cx_str(url_base); 647 cxstring path; 648 if(p) { 649 path = cx_str((char*)p); 650 } else { 651 path = CX_STR(""); 652 } 653 654 return util_concat_path_s(base, path).ptr; 655 } 656 657 cxmutstr util_concat_path_s(cxstring base, cxstring path) { 658 if(!path.ptr) { 659 path = CX_STR(""); 660 } 661 662 int add_separator = 0; 663 if(base.length != 0 && base.ptr[base.length-1] == '/') { 664 if(path.ptr[0] == '/') { 665 base.length--; 666 } 667 } else { 668 if(path.length == 0 || path.ptr[0] != '/') { 669 add_separator = 1; 670 } 671 } 672 673 cxmutstr url; 674 if(add_separator) { 675 url = cx_strcat(3, base, CX_STR("/"), path); 676 } else { 677 url = cx_strcat(2, base, path); 678 } 679 680 return url; 681 } 682 683 cxmutstr util_concat_path_ext(cxstring base, cxstring path, char separator) { 684 if(!path.ptr) { 685 path = CX_STR(""); 686 } 687 688 int add_separator = 0; 689 if(base.length != 0 && base.ptr[base.length-1] == separator) { 690 if(path.ptr[0] == separator) { 691 base.length--; 692 } 693 } else { 694 if(path.length == 0 || path.ptr[0] != separator) { 695 add_separator = 1; 696 } 697 } 698 699 cxmutstr url; 700 if(add_separator) { 701 url = cx_strcat(3, base, cx_strn(&separator, 1), path); 702 } else { 703 url = cx_strcat(2, base, path); 704 } 705 706 return url; 707 } 708 709 cxmutstr util_concat_sys_path(cxstring base, cxstring path) { 710 #ifdef _WIN32 711 return util_concat_path_ext(base, path, '\\'); 712 #else 713 return util_concat_path_ext(base, path, '/'); 714 #endif 715 } 716 717 char* util_get_url(DavSession *sn, const char *href) { 718 cxstring base = cx_str(sn->base_url); 719 cxstring href_str = cx_str(href); 720 721 const char *base_path = util_url_path(sn->base_url); 722 base.length -= strlen(base_path); 723 724 cxmutstr url = cx_strcat(2, base, href_str); 725 return url.ptr; 726 } 727 728 void util_set_url(DavSession *sn, const char *href) { 729 char *url = util_get_url(sn, href); 730 curl_easy_setopt(sn->handle, CURLOPT_URL, url); 731 free(url); 732 } 733 734 char* util_path_to_url(DavSession *sn, const char *path) { 735 CxBuffer url; 736 cxBufferInit(&url, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 737 738 // add base url 739 cxBufferWrite(sn->base_url, 1, strlen(sn->base_url), &url); 740 // remove trailing slash 741 cxBufferSeek(&url, -1, SEEK_CUR); 742 743 cxstring p = cx_str(path); 744 745 CxStrtokCtx tkctx = cx_strtok(p, CX_STR("/"), INT_MAX); 746 cxstring node; 747 while(cx_strtok_next(&tkctx, &node)) { 748 if(node.length > 0) { 749 char *esc = curl_easy_escape(sn->handle, node.ptr, node.length); 750 cxBufferPut(&url, '/'); 751 cxBufferWrite(esc, 1, strlen(esc), &url); 752 curl_free(esc); 753 } 754 } 755 756 if(path[p.length-1] == '/') { 757 cxBufferPut(&url, '/'); 758 } 759 cxBufferPut(&url, 0); 760 761 return url.space; 762 } 763 764 char* util_parent_path(const char *path) { 765 const char *name = util_resource_name(path); 766 size_t namelen = strlen(name); 767 size_t pathlen = strlen(path); 768 size_t parentlen = pathlen - namelen; 769 char *parent = malloc(parentlen + 1); 770 memcpy(parent, path, parentlen); 771 parent[parentlen] = '\0'; 772 return parent; 773 } 774 775 char* util_sys_parent_path(const char *path) { 776 const char *name = util_path_file_name(path); 777 size_t namelen = strlen(name); 778 size_t pathlen = strlen(path); 779 size_t parentlen = pathlen - namelen; 780 char *parent = malloc(parentlen + 1); 781 memcpy(parent, path, parentlen); 782 parent[parentlen] = '\0'; 783 return parent; 784 } 785 786 char* util_size_str(DavBool iscollection, uint64_t contentlength) { 787 return util_size_str2(iscollection, contentlength, contentlength, 1); 788 } 789 790 char* util_size_str2(DavBool iscollection, uint64_t contentlength, uint64_t dimension, int precision) { 791 char *str = malloc(16); 792 uint64_t size = contentlength; 793 794 if(iscollection) { 795 str[0] = '\0'; // currently no information for collections 796 } else if(dimension < 0x400) { 797 snprintf(str, 16, "%" PRIu64 " bytes", size); 798 } else if(dimension < 0x100000) { 799 float s = (float)size/0x400; 800 int diff = (s*100 - (int)s*100); 801 if(diff > 90) { 802 diff = 0; 803 s += 0.10f; 804 } 805 if(dimension < 0x2800 && diff != 0) { 806 // size < 10 KiB 807 snprintf(str, 16, "%.*f KiB", precision, s); 808 } else { 809 snprintf(str, 16, "%.0f KiB", s); 810 } 811 } else if(dimension < 0x40000000) { 812 float s = (float)size/0x100000; 813 int diff = (s*100 - (int)s*100); 814 if(diff > 90) { 815 diff = 0; 816 s += 0.10f; 817 } 818 if(dimension < 0xa00000 && diff != 0) { 819 // size < 10 MiB 820 snprintf(str, 16, "%.*f MiB", precision, s); 821 } else { 822 size /= 0x100000; 823 snprintf(str, 16, "%.0f MiB", s); 824 } 825 } else if(dimension < 0x1000000000ULL) { 826 float s = (float)size/0x40000000; 827 int diff = (s*100 - (int)s*100); 828 if(diff > 90) { 829 diff = 0; 830 s += 0.10f; 831 } 832 if(dimension < 0x280000000 && diff != 0) { 833 // size < 10 GiB 834 snprintf(str, 16, "%.*f GiB", precision, s); 835 } else { 836 size /= 0x40000000; 837 snprintf(str, 16, "%.0f GiB", s); 838 } 839 } else { 840 size /= 1024; 841 float s = (float)size/0x40000000; 842 int diff = (s*100 - (int)s*100); 843 if(diff > 90) { 844 diff = 0; 845 s += 0.10f; 846 } 847 if(dimension < 0x280000000 && diff != 0) { 848 // size < 10 TiB 849 snprintf(str, 16, "%.*f TiB", precision, s); 850 } else { 851 size /= 0x40000000; 852 snprintf(str, 16, "%.0f TiB", s); 853 } 854 } 855 return str; 856 } 857 858 char* util_date_str(time_t tm) { 859 struct tm t; 860 struct tm n; 861 time_t now = time(NULL); 862 #ifdef _WIN32 863 memcpy(&t, localtime(&tm), sizeof(struct tm)); 864 memcpy(&n, localtime(&now), sizeof(struct tm)); 865 #else 866 localtime_r(&tm, &t); 867 localtime_r(&now, &n); 868 #endif /* _WIN32 */ 869 char *str = malloc(16); 870 if(t.tm_year == n.tm_year) { 871 strftime(str, 16, "%b %d %H:%M", &t); 872 } else { 873 strftime(str, 16, "%b %d %Y", &t); 874 } 875 return str; 876 } 877 878 879 char* util_xml_get_text(const xmlNode *elm) { 880 xmlNode *node = elm->children; 881 while(node) { 882 if(node->type == XML_TEXT_NODE) { 883 return (char*)node->content; 884 } 885 node = node->next; 886 } 887 return NULL; 888 } 889 890 891 char* util_base64decode(const char *in) { 892 int len = 0; 893 return util_base64decode_len(in, &len); 894 } 895 896 #define WHITESPACE 64 897 #define EQUALS 65 898 #define INVALID 66 899 static char b64dectable[] = { 900 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, 901 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, 902 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, 903 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, 904 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, 905 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, 906 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, 907 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, 908 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, 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 911 }; 912 char* util_base64decode_len(const char* in, int *outlen) { 913 /* code is mostly from wikibooks */ 914 915 if(!in) { 916 *outlen = 0; 917 return NULL; 918 } 919 920 size_t inlen = strlen(in); 921 size_t bufsize = (inlen*3) / 4; 922 char *outbuf = malloc(bufsize+1); 923 *outlen = -1; 924 925 unsigned char *out = (unsigned char*)outbuf; 926 927 const char *end = in + inlen; 928 char iter = 0; 929 uint32_t buf = 0; 930 size_t len = 0; 931 932 while (in < end) { 933 unsigned char c = b64dectable[*in++]; 934 935 switch (c) { 936 case WHITESPACE: continue; /* skip whitespace */ 937 case INVALID: { 938 /* invalid input */ 939 outbuf[0] = 0; 940 return outbuf; 941 } 942 case EQUALS: { 943 /* pad character, end of data */ 944 in = end; 945 continue; 946 } 947 default: { 948 buf = buf << 6 | c; 949 iter++; // increment the number of iteration 950 /* If the buffer is full, split it into bytes */ 951 if (iter == 4) { 952 if ((len += 3) > bufsize) { 953 /* buffer overflow */ 954 outbuf[0] = 0; 955 return outbuf; 956 } 957 *(out++) = (buf >> 16) & 255; 958 *(out++) = (buf >> 8) & 255; 959 *(out++) = buf & 255; 960 buf = 0; iter = 0; 961 962 } 963 } 964 } 965 } 966 967 if (iter == 3) { 968 if ((len += 2) > bufsize) { 969 /* buffer overflow */ 970 outbuf[0] = 0; 971 return outbuf; 972 } 973 *(out++) = (buf >> 10) & 255; 974 *(out++) = (buf >> 2) & 255; 975 } 976 else if (iter == 2) { 977 if (++len > bufsize) { 978 /* buffer overflow */ 979 outbuf[0] = 0; 980 return outbuf; 981 } 982 *(out++) = (buf >> 4) & 255; 983 } 984 985 *outlen = len; /* modify to reflect the actual output size */ 986 outbuf[len] = 0; 987 return outbuf; 988 } 989 990 991 static char* b64enctable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 992 char* util_base64encode(const char *in, size_t len) { 993 // calculate length of base64 output and create buffer 994 size_t outlen = 4 * ((len + 2) / 3); 995 int pad = len % 3; 996 997 char *out = malloc(outlen + 1); 998 out[outlen] = 0; 999 size_t pos = 0; 1000 1001 // encode blocks of 3 bytes 1002 size_t i; 1003 size_t blockend = len - pad; 1004 for(i=0;i<blockend;i++) { 1005 unsigned char b1 = in[i++]; 1006 unsigned char b2 = in[i++]; 1007 unsigned char b3 = in[i]; 1008 uint32_t inb = b1 << 16 | (b2 << 8) | b3; 1009 out[pos++] = b64enctable[(inb >> 18) & 63]; 1010 out[pos++] = b64enctable[(inb >> 12) & 63]; 1011 out[pos++] = b64enctable[(inb >> 6) & 63]; 1012 out[pos++] = b64enctable[(inb) & 63]; 1013 } 1014 1015 // encode last bytes 1016 if(pad > 0) { 1017 char p[3] = {0, 0, 0}; 1018 for(int j=0;i<len;i++) { 1019 p[j++] = in[i]; 1020 } 1021 unsigned char b1 = p[0]; 1022 unsigned char b2 = p[1]; 1023 unsigned char b3 = p[2]; 1024 uint32_t inb = (b1 << 16) | (b2 << 8) | b3; 1025 out[pos++] = b64enctable[(inb >> 18) & 63]; 1026 out[pos++] = b64enctable[(inb >> 12) & 63]; 1027 out[pos++] = b64enctable[(inb >> 6) & 63]; 1028 out[pos++] = b64enctable[(inb) & 63]; 1029 for(int k=outlen-1;k>=outlen-(3-pad);k--) { 1030 out[k] = '='; 1031 } 1032 } 1033 1034 return out; 1035 } 1036 1037 char* util_encrypt_str(DavSession *sn, const char *str, const char *key) { 1038 DavKey *k = dav_context_get_key(sn->context, key); 1039 if(!k) { 1040 sn->error = DAV_ERROR; 1041 cxmutstr err = cx_asprintf("Key %s not found", key); 1042 dav_session_set_errstr(sn, err.ptr); 1043 free(err.ptr); 1044 return NULL; 1045 } 1046 1047 return util_encrypt_str_k(sn, str, k); 1048 } 1049 1050 char* util_encrypt_str_k(DavSession *sn, const char *str, DavKey *key) { 1051 char *enc_str = aes_encrypt(str, strlen(str), key); 1052 char *ret_str = dav_session_strdup(sn, enc_str); 1053 free(enc_str); 1054 return ret_str; 1055 } 1056 1057 char* util_decrypt_str(DavSession *sn, const char *str, const char *key) { 1058 DavKey *k = dav_context_get_key(sn->context, key); 1059 if(!k) { 1060 sn->error = DAV_ERROR; 1061 cxmutstr err = cx_asprintf("Key %s not found", key); 1062 dav_session_set_errstr(sn, err.ptr); 1063 free(err.ptr); 1064 return NULL; 1065 } 1066 1067 return util_decrypt_str_k(sn, str, k); 1068 } 1069 1070 char* util_decrypt_str_k(DavSession *sn, const char *str, DavKey *key) { 1071 size_t len = 0; 1072 char *dec_str = aes_decrypt(str, &len, key); 1073 char *ret_str = dav_session_strdup(sn, dec_str); 1074 free(dec_str); 1075 return ret_str; 1076 } 1077 1078 char* util_random_str() { 1079 unsigned char *str = malloc(25); 1080 str[24] = '\0'; 1081 1082 cxstring t = CX_STR( 1083 "01234567890" 1084 "abcdefghijklmnopqrstuvwxyz" 1085 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 1086 const unsigned char *table = (const unsigned char*)t.ptr; 1087 1088 #ifdef DAV_USE_OPENSSL 1089 RAND_bytes(str, 24); 1090 #else 1091 dav_rand_bytes(str, 24); 1092 #endif 1093 for(int i=0;i<24;i++) { 1094 int c = str[i] % t.length; 1095 str[i] = table[c]; 1096 } 1097 1098 return (char*)str; 1099 } 1100 1101 /* 1102 * gets a substring from 0 to the appearance of the token 1103 * tokens are separated by space 1104 * sets sub to the substring and returns the remaining string 1105 */ 1106 // TODO: remove if it isn't used 1107 /* 1108 sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) { 1109 int i; 1110 int token_start = -1; 1111 int token_end = -1; 1112 for(i=0;i<=str.length;i++) { 1113 int c; 1114 if(i == str.length) { 1115 c = ' '; 1116 } else { 1117 c = str.ptr[i]; 1118 } 1119 if(c < 33) { 1120 if(token_start != -1) { 1121 token_end = i; 1122 size_t len = token_end - token_start; 1123 sstr_t tk = sstrsubsl(str, token_start, len); 1124 //printf("token: {%.*s}\n", token.length, token.ptr); 1125 if(!sstrcmp(tk, token)) { 1126 *sub = sstrtrim(sstrsubsl(str, 0, token_start)); 1127 break; 1128 } 1129 token_start = -1; 1130 token_end = -1; 1131 } 1132 } else { 1133 if(token_start == -1) { 1134 token_start = i; 1135 } 1136 } 1137 } 1138 1139 if(i < str.length) { 1140 return sstrtrim(sstrsubs(str, i)); 1141 } else { 1142 str.ptr = NULL; 1143 str.length = 0; 1144 return str; 1145 } 1146 } 1147 */ 1148 1149 cxmutstr util_readline(FILE *stream) { 1150 CxBuffer buf; 1151 cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1152 1153 int c; 1154 while((c = fgetc(stream)) != EOF) { 1155 if(c == '\n') { 1156 break; 1157 } 1158 cxBufferPut(&buf, c); 1159 } 1160 1161 cxmutstr str = cx_strdup(cx_strtrim(cx_strn(buf.space, buf.size))); 1162 cxBufferDestroy(&buf); 1163 return str; 1164 } 1165 1166 char* util_password_input(char *prompt) { 1167 fprintf(stderr, "%s", prompt); 1168 fflush(stderr); 1169 1170 #ifndef _WIN32 1171 // hide terminal input 1172 struct termios oflags, nflags; 1173 if(isatty(fileno(stdin))) { 1174 tcgetattr(fileno(stdin), &oflags); 1175 nflags = oflags; 1176 nflags.c_lflag &= ~ECHO; 1177 nflags.c_lflag |= ECHONL; 1178 if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) { 1179 perror("tcsetattr"); 1180 } 1181 } 1182 1183 #endif 1184 1185 // read password input 1186 CxBuffer buf; 1187 cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1188 int c = 0; 1189 while((c = getpasswordchar()) != EOF) { 1190 if(c == '\n' || c == '\r') { 1191 break; 1192 } 1193 cxBufferPut(&buf, c); 1194 } 1195 cxBufferPut(&buf, 0); 1196 fflush(stdin); 1197 1198 #ifndef _WIN32 1199 // restore terminal settings 1200 if (isatty(fileno(stdin)) && tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) { 1201 perror("tcsetattr"); 1202 } 1203 #endif 1204 1205 return buf.space; 1206 } 1207 1208 int util_exec_command(char *command, CxBuffer *outbuf) { 1209 #ifdef _WIN32 1210 fprintf(stderr, "util_exec_command unsupported\n"); 1211 return 1; 1212 #else 1213 1214 int pout[2]; 1215 if(pipe(pout)) { 1216 perror("pipe"); 1217 return 1; 1218 } 1219 1220 int ret = 0; 1221 1222 // close stdin and stderr, use pipe for stdout 1223 posix_spawn_file_actions_t actions; 1224 posix_spawn_file_actions_init(&actions); 1225 posix_spawn_file_actions_addclose(&actions, 0); 1226 posix_spawn_file_actions_adddup2(&actions, pout[1], 1); 1227 posix_spawn_file_actions_addclose(&actions, 2); 1228 1229 char *args[4]; 1230 args[0] = "sh"; 1231 args[1] = "-c"; 1232 args[2] = command; 1233 args[3] = NULL; 1234 1235 pid_t pid; // child pid 1236 ret = posix_spawn(&pid, "/bin/sh", &actions, NULL, args, NULL); 1237 1238 close(pout[1]); 1239 1240 if(!ret) { 1241 ssize_t r; 1242 char buf[1024]; 1243 while((r = read(pout[0], buf, 1024)) > 0) { 1244 cxBufferWrite(buf, 1, r, outbuf); 1245 } 1246 } 1247 1248 // wait for child process 1249 ret = 1; 1250 waitpid(pid, &ret, 0); 1251 1252 posix_spawn_file_actions_destroy(&actions); 1253 close(pout[0]); 1254 1255 return ret; 1256 #endif 1257 } 1258 1259 char* util_hexstr(const unsigned char *data, size_t len) { 1260 size_t buflen = 2*len + 4; 1261 CxBuffer buf; 1262 cxBufferInit(&buf, NULL, buflen + 1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 1263 for(int i=0;i<len;i++) { 1264 cx_bprintf(&buf, "%02x", data[i]); 1265 } 1266 cxBufferPut(&buf, 0); 1267 return buf.space; 1268 } 1269 1270 void util_remove_trailing_pathseparator(char *path) { 1271 size_t len = strlen(path); 1272 if(len < 2) { 1273 return; 1274 } 1275 1276 if(path[len-1] == '/') { 1277 path[len-1] = '\0'; 1278 } 1279 } 1280 1281 char* util_file_hash(const char *path) { 1282 FILE *in = fopen(path, "r"); 1283 if(!in) { 1284 return NULL; 1285 } 1286 1287 DAV_SHA_CTX *sha = dav_hash_init(); 1288 char *buf = malloc(16384); 1289 1290 size_t r; 1291 while((r = fread(buf, 1, 16384, in)) > 0) { 1292 dav_hash_update(sha, buf, r); 1293 } 1294 1295 unsigned char hash[DAV_SHA256_DIGEST_LENGTH]; 1296 dav_hash_final(sha, hash); 1297 free(buf); 1298 fclose(in); 1299 1300 return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH); 1301 } 1302 1303