libidav/utils.c

changeset 1
b5bb7b3cd597
child 2
fbdfaacc4182
equal deleted inserted replaced
0:2483f517c562 1:b5bb7b3cd597
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 char *end;
217 errno = 0;
218 uint64_t val = strtoull(str, &end, 0);
219 if(errno == 0) {
220 *value = val;
221 return 1;
222 } else {
223 return 0;
224 }
225 }
226
227 int util_strtoint(const char *str, int64_t *value) {
228 char *end;
229 errno = 0;
230 int64_t val = strtoll(str, &end, 0);
231 if(errno == 0) {
232 *value = val;
233 return 1;
234 } else {
235 return 0;
236 }
237 }
238
239 int util_szstrtouint(const char *str, uint64_t *value) {
240 char *end;
241 errno = 0;
242 size_t len = strlen(str);
243 uint64_t val = strtoull(str, &end, 0);
244 if(end == str+len) {
245 *value = val;
246 return 1;
247 } else if(end == str+len-1) {
248 uint64_t mul = 1;
249 switch(end[0]) {
250 case 'k':
251 case 'K': mul = 1024; break;
252 case 'm':
253 case 'M': mul = 1024*1024; break;
254 case 'g':
255 case 'G': mul = 1024*1024*1024; break;
256 default: return 0;
257 }
258
259 uint64_t result = 0;
260 if(util_uint_mul(val, mul, &result)) {
261 return 0;
262 }
263 *value = result;
264 return 1;
265 }
266 return 0;
267 }
268
269 int util_uint_mul(uint64_t a, uint64_t b, uint64_t *result) {
270 if(a == 0 || b == 0) {
271 *result = 0;
272 return 0;
273 }
274 uint64_t r = a * b;
275 if(r / b == a) {
276 *result = r;
277 return 0;
278 } else {
279 *result = 0;
280 return 1;
281 }
282 }
283
284 char* util_url_base_s(cxstring url) {
285 size_t i = 0;
286 if(url.length > 0) {
287 int slmax;
288 if(cx_strprefix(url, cx_str("http://"))) {
289 slmax = 3;
290 } else if(cx_strprefix(url, cx_str("https://"))) {
291 slmax = 3;
292 } else {
293 slmax = 1;
294 }
295 int slashcount = 0;
296 for(i=0;i<url.length;i++) {
297 if(url.ptr[i] == '/') {
298 slashcount++;
299 if(slashcount == slmax) {
300 i++;
301 break;
302 }
303 }
304 }
305 }
306 cxstring server = cx_strsubsl(url, 0, i);
307 return cx_strdup(server).ptr;
308 }
309
310 char* util_url_base(char *url) {
311 return util_url_base_s(cx_str(url));
312 }
313
314 #ifdef _WIN32
315 #define strncasecmp _strnicmp
316 #endif
317
318 const char* util_url_path(const char *url) {
319 const char *path = NULL;
320 size_t len = strlen(url);
321 int slashcount = 0;
322 int slmax;
323 if(len > 7 && !strncasecmp(url, "http://", 7)) {
324 slmax = 3;
325 } else if(len > 8 && !strncasecmp(url, "https://", 8)) {
326 slmax = 3;
327 } else {
328 slmax = 1;
329 }
330 char c;
331 for(int i=0;i<len;i++) {
332 c = url[i];
333 if(c == '/') {
334 slashcount++;
335 if(slashcount == slmax) {
336 path = url + i;
337 break;
338 }
339 }
340 }
341 if(!path) {
342 path = url + len; // empty string
343 }
344 return path;
345 }
346
347 char* util_url_decode(DavSession *sn, const char *url) {
348 char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL);
349 char *ret = strdup(unesc);
350 curl_free(unesc);
351 return ret;
352 }
353
354 static size_t util_header_callback(char *buffer, size_t size,
355 size_t nitems, void *data) {
356
357 cxstring sbuffer = cx_strn(buffer, size*nitems);
358
359 CxMap *map = (CxMap*) data;
360
361 // if we get a status line, clear the map and exit
362 if(cx_strprefix(sbuffer, cx_str("HTTP/"))) {
363 // TODO: use new map destructor ucx_map_free_content(map, free);
364 cxMapClear(map);
365 return size*nitems;
366 }
367
368 // if we get the terminating CRLF, just exit
369 if(!cx_strcmp(sbuffer, cx_str("\r\n"))) {
370 return 2;
371 }
372
373 cxstring key = sbuffer;
374 cxstring value = cx_strchr(sbuffer, ':');
375
376 if(value.length == 0) {
377 return 0; // invalid header line
378 }
379
380 key.length = value.ptr - key.ptr;
381 value.ptr++; value.length--;
382
383 cxmutstr key_cp = cx_strdup(cx_strtrim(key));
384 cx_strlower(key_cp);
385 cxmutstr value_cp = cx_strdup(cx_strtrim(value));
386
387 cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr);
388
389 free(key_cp.ptr);
390
391 return sbuffer.length;
392 }
393
394 int util_path_isrelated(const char *path1, const char *path2) {
395 cxstring p1 = cx_str(path1);
396 cxstring p2 = cx_str(path2);
397
398 if(IS_PATH_SEPARATOR(p1.ptr[p1.length-1])) {
399 p1.length--;
400 }
401 if(IS_PATH_SEPARATOR(p2.ptr[p2.length-1])) {
402 p2.length--;
403 }
404
405 if(p2.length < p1.length) {
406 return 0;
407 }
408
409 if(!cx_strcmp(p1, p2)) {
410 return 1;
411 }
412
413 if(cx_strprefix(p2, p1)) {
414 if(IS_PATH_SEPARATOR(p2.ptr[p1.length])) {
415 return 1;
416 }
417 }
418
419 return 0;
420 }
421
422 #ifdef _WIN32
423 int util_path_isabsolut(const char *path) {
424 if(strlen(path) < 3) {
425 return 0;
426 }
427
428 // check if first char is A-Z or a-z
429 char c = path[0];
430 if(!((c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
431 return 0;
432 }
433
434 if(path[1] == ':' && path[2] == '\\') {
435 return 1;
436 }
437 return 0;
438 }
439 #else
440 int util_path_isabsolut(const char *path) {
441 return path[0] == '/';
442 }
443 #endif
444
445 char* util_path_normalize(const char *path) {
446 size_t len = strlen(path);
447 CxBuffer buf;
448 cxBufferInit(&buf, NULL, len+1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
449
450 if(path[0] == '/') {
451 cxBufferPut(&buf, '/');
452 }
453
454 int add_separator = 0;
455 int seg_start = 0;
456 for(int i=0;i<=len;i++) {
457 char c = path[i];
458 if(IS_PATH_SEPARATOR(c) || c == '\0') {
459 const char *seg_ptr = path+seg_start;
460 int seg_len = i - seg_start;
461 if(IS_PATH_SEPARATOR(seg_ptr[0])) {
462 seg_ptr++;
463 seg_len--;
464 }
465
466 if(seg_len > 0) {
467 cxstring seg = cx_strn(seg_ptr, seg_len);
468 if(!cx_strcmp(seg, CX_STR(".."))) {
469 for(int j=buf.pos;j>=0;j--) {
470 char t = j < buf.pos ? buf.space[j] : 0;
471 if(IS_PATH_SEPARATOR(t) || j == 0) {
472 buf.pos = j;
473 buf.size = j;
474 buf.space[j] = 0;
475 add_separator = IS_PATH_SEPARATOR(t) ? 1 : 0;
476 break;
477 }
478 }
479 } else if(!cx_strcmp(seg, CX_STR("."))) {
480 // ignore
481 } else {
482 if(add_separator) {
483 cxBufferPut(&buf, PATH_SEPARATOR);
484 }
485 cxBufferWrite(seg_ptr, 1, seg_len, &buf);
486 add_separator = 1;
487 }
488 }
489
490 seg_start = i;
491 }
492 }
493
494 cxBufferPut(&buf, 0);
495
496 return buf.space;
497 }
498
499 static char* create_relative_path(const char *abspath, const char *base) {
500 size_t path_len = strlen(abspath);
501 size_t base_len = strlen(base);
502
503 if(IS_PATH_SEPARATOR(abspath[path_len-1])) {
504 path_len--;
505 }
506 if(IS_PATH_SEPARATOR(base[base_len-1])) {
507 base_len--;
508 }
509 // get base parent
510 for(int i=base_len-1;i>=0;i--) {
511 if(IS_PATH_SEPARATOR(base[i])) {
512 base_len = i+1;
513 break;
514 }
515 }
516
517 size_t max = path_len > base_len ? base_len : path_len;
518
519 // get prefix of abspath and base
520 // this dir is the root of the link
521 size_t i;
522 size_t last_dir = 0;
523 for(i=0;i<max;i++) {
524 char c = abspath[i];
525 if(c != base[i]) {
526 break;
527 } else if(IS_PATH_SEPARATOR(c)) {
528 last_dir = i;
529 }
530 }
531
532 char *ret = NULL;
533 CxBuffer out;
534 if(last_dir+1 < base_len) {
535 // base is deeper than the link root, we have to go backwards
536 int dircount = 0;
537 for(int i=last_dir+1;i<base_len;i++) {
538 if(IS_PATH_SEPARATOR(base[i])) {
539 dircount++;
540 }
541 }
542
543 cxBufferInit(&out, NULL, dircount*3+path_len-last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
544
545 for(int i=0;i<dircount;i++) {
546 cxBufferPutString(&out, "../");
547 }
548 } else {
549 cxBufferInit(&out, NULL, path_len - last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
550 }
551
552 cxBufferPutString(&out, abspath + last_dir + 1);
553 cxBufferPut(&out, 0);
554
555 return out.space;
556 }
557
558 #ifdef _WIN32
559 char* util_create_relative_path(const char *abspath, const char *base) {
560 char *abspath_converted = strdup(abspath);
561 char *base_converted = strdup(base);
562 size_t abs_len = strlen(abspath_converted);
563 size_t base_len = strlen(base_converted);
564
565 for(int i=0;i<abs_len;i++) {
566 if(abspath_converted[i] == '\\') {
567 abspath_converted[i] = '/';
568 }
569 }
570 for(int i=0;i<base_len;i++) {
571 if(base_converted[i] == '\\') {
572 base_converted[i] = '/';
573 }
574 }
575
576 char *ret = create_relative_path(abspath_converted, base_converted);
577 free(abspath_converted);
578 free(base_converted);
579 return ret;
580 }
581 #else
582 char* util_create_relative_path(const char *abspath, const char *base) {
583 return create_relative_path(abspath, base);
584 }
585 #endif
586
587
588 void util_capture_header(CURL *handle, CxMap* map) {
589 if(map) {
590 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, util_header_callback);
591 curl_easy_setopt(handle, CURLOPT_HEADERDATA, map);
592 } else {
593 curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, NULL);
594 curl_easy_setopt(handle, CURLOPT_HEADERDATA, NULL);
595 }
596 }
597
598 const char* util_resource_name(const char *url) {
599 cxstring urlstr = cx_str(url);
600 if(urlstr.ptr[urlstr.length-1] == '/') {
601 urlstr.length--;
602 }
603 cxstring resname = cx_strrchr(urlstr, '/');
604 if(resname.length > 1) {
605 return resname.ptr+1;
606 } else {
607 return url;
608 }
609 }
610
611 int util_mkdir(char *path, mode_t mode) {
612 #ifdef _WIN32
613 return mkdir(path);
614 #else
615 return mkdir(path, mode);
616 #endif
617 }
618
619 char* util_concat_path(const char *url_base, const char *p) {
620 cxstring base = cx_str((char*)url_base);
621 cxstring path;
622 if(p) {
623 path = cx_str((char*)p);
624 } else {
625 path = CX_STR("");
626 }
627
628 int add_separator = 0;
629 if(base.length != 0 && base.ptr[base.length-1] == '/') {
630 if(path.ptr[0] == '/') {
631 base.length--;
632 }
633 } else {
634 if(path.length == 0 || path.ptr[0] != '/') {
635 add_separator = 1;
636 }
637 }
638
639 cxmutstr url;
640 if(add_separator) {
641 url = cx_strcat(3, base, CX_STR("/"), path);
642 } else {
643 url = cx_strcat(2, base, path);
644 }
645
646 return url.ptr;
647 }
648
649 char* util_get_url(DavSession *sn, const char *href) {
650 cxstring base = cx_str(sn->base_url);
651 cxstring href_str = cx_str(href);
652
653 const char *base_path = util_url_path(sn->base_url);
654 base.length -= strlen(base_path);
655
656 cxmutstr url = cx_strcat(2, base, href_str);
657 return url.ptr;
658 }
659
660 void util_set_url(DavSession *sn, const char *href) {
661 char *url = util_get_url(sn, href);
662 curl_easy_setopt(sn->handle, CURLOPT_URL, url);
663 free(url);
664 }
665
666 char* util_path_to_url(DavSession *sn, const char *path) {
667 CxBuffer url;
668 cxBufferInit(&url, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
669
670 // add base url
671 cxBufferWrite(sn->base_url, 1, strlen(sn->base_url), &url);
672 // remove trailing slash
673 cxBufferSeek(&url, -1, SEEK_CUR);
674
675 cxstring p = cx_str(path);
676
677 CxStrtokCtx tkctx = cx_strtok(p, CX_STR("/"), INT_MAX);
678 cxstring node;
679 while(cx_strtok_next(&tkctx, &node)) {
680 if(node.length > 0) {
681 char *esc = curl_easy_escape(sn->handle, node.ptr, node.length);
682 cxBufferPut(&url, '/');
683 cxBufferWrite(esc, 1, strlen(esc), &url);
684 curl_free(esc);
685 }
686 }
687
688 if(path[p.length-1] == '/') {
689 cxBufferPut(&url, '/');
690 }
691 cxBufferPut(&url, 0);
692
693 return url.space;
694 }
695
696 char* util_parent_path(const char *path) {
697 const char *name = util_resource_name(path);
698 size_t namelen = strlen(name);
699 size_t pathlen = strlen(path);
700 size_t parentlen = pathlen - namelen;
701 char *parent = malloc(parentlen + 1);
702 memcpy(parent, path, parentlen);
703 parent[parentlen] = '\0';
704 return parent;
705 }
706
707 char* util_size_str(DavBool iscollection, uint64_t contentlength) {
708 char *str = malloc(16);
709 uint64_t size = contentlength;
710
711 if(iscollection) {
712 str[0] = '\0'; // currently no information for collections
713 } else if(size < 0x400) {
714 snprintf(str, 16, "%" PRIu64 " bytes", size);
715 } else if(size < 0x100000) {
716 float s = (float)size/0x400;
717 int diff = (s*100 - (int)s*100);
718 if(diff > 90) {
719 diff = 0;
720 s += 0.10f;
721 }
722 if(size < 0x2800 && diff != 0) {
723 // size < 10 KiB
724 snprintf(str, 16, "%.1f KiB", s);
725 } else {
726 snprintf(str, 16, "%.0f KiB", s);
727 }
728 } else if(size < 0x40000000) {
729 float s = (float)size/0x100000;
730 int diff = (s*100 - (int)s*100);
731 if(diff > 90) {
732 diff = 0;
733 s += 0.10f;
734 }
735 if(size < 0xa00000 && diff != 0) {
736 // size < 10 MiB
737 snprintf(str, 16, "%.1f MiB", s);
738 } else {
739 size /= 0x100000;
740 snprintf(str, 16, "%.0f MiB", s);
741 }
742 } else if(size < 0x1000000000ULL) {
743 float s = (float)size/0x40000000;
744 int diff = (s*100 - (int)s*100);
745 if(diff > 90) {
746 diff = 0;
747 s += 0.10f;
748 }
749 if(size < 0x280000000 && diff != 0) {
750 // size < 10 GiB
751 snprintf(str, 16, "%.1f GiB", s);
752 } else {
753 size /= 0x40000000;
754 snprintf(str, 16, "%.0f GiB", s);
755 }
756 } else {
757 size /= 1024;
758 float s = (float)size/0x40000000;
759 int diff = (s*100 - (int)s*100);
760 if(diff > 90) {
761 diff = 0;
762 s += 0.10f;
763 }
764 if(size < 0x280000000 && diff != 0) {
765 // size < 10 TiB
766 snprintf(str, 16, "%.1f TiB", s);
767 } else {
768 size /= 0x40000000;
769 snprintf(str, 16, "%.0f TiB", s);
770 }
771 }
772 return str;
773 }
774
775 char* util_date_str(time_t tm) {
776 struct tm t;
777 struct tm n;
778 time_t now = time(NULL);
779 #ifdef _WIN32
780 memcpy(&t, localtime(&tm), sizeof(struct tm));
781 memcpy(&n, localtime(&now), sizeof(struct tm));
782 #else
783 localtime_r(&tm, &t);
784 localtime_r(&now, &n);
785 #endif /* _WIN32 */
786 char *str = malloc(16);
787 if(t.tm_year == n.tm_year) {
788 strftime(str, 16, "%b %d %H:%M", &t);
789 } else {
790 strftime(str, 16, "%b %d %Y", &t);
791 }
792 return str;
793 }
794
795
796 char* util_xml_get_text(const xmlNode *elm) {
797 xmlNode *node = elm->children;
798 while(node) {
799 if(node->type == XML_TEXT_NODE) {
800 return (char*)node->content;
801 }
802 node = node->next;
803 }
804 return NULL;
805 }
806
807
808 char* util_base64decode(const char *in) {
809 int len = 0;
810 return util_base64decode_len(in, &len);
811 }
812
813 #define WHITESPACE 64
814 #define EQUALS 65
815 #define INVALID 66
816 static char b64dectable[] = {
817 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,
818 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,
819 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,
820 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,
821 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,
822 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,
823 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,
824 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,
825 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,
826 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,
827 66,66,66,66,66,66
828 };
829 char* util_base64decode_len(const char* in, int *outlen) {
830 /* code is mostly from wikibooks */
831
832 if(!in) {
833 *outlen = 0;
834 return NULL;
835 }
836
837 size_t inlen = strlen(in);
838 size_t bufsize = (inlen*3) / 4;
839 char *outbuf = malloc(bufsize+1);
840 *outlen = -1;
841
842 unsigned char *out = (unsigned char*)outbuf;
843
844 const char *end = in + inlen;
845 char iter = 0;
846 uint32_t buf = 0;
847 size_t len = 0;
848
849 while (in < end) {
850 unsigned char c = b64dectable[*in++];
851
852 switch (c) {
853 case WHITESPACE: continue; /* skip whitespace */
854 case INVALID: {
855 /* invalid input */
856 outbuf[0] = 0;
857 return outbuf;
858 }
859 case EQUALS: {
860 /* pad character, end of data */
861 in = end;
862 continue;
863 }
864 default: {
865 buf = buf << 6 | c;
866 iter++; // increment the number of iteration
867 /* If the buffer is full, split it into bytes */
868 if (iter == 4) {
869 if ((len += 3) > bufsize) {
870 /* buffer overflow */
871 outbuf[0] = 0;
872 return outbuf;
873 }
874 *(out++) = (buf >> 16) & 255;
875 *(out++) = (buf >> 8) & 255;
876 *(out++) = buf & 255;
877 buf = 0; iter = 0;
878
879 }
880 }
881 }
882 }
883
884 if (iter == 3) {
885 if ((len += 2) > bufsize) {
886 /* buffer overflow */
887 outbuf[0] = 0;
888 return outbuf;
889 }
890 *(out++) = (buf >> 10) & 255;
891 *(out++) = (buf >> 2) & 255;
892 }
893 else if (iter == 2) {
894 if (++len > bufsize) {
895 /* buffer overflow */
896 outbuf[0] = 0;
897 return outbuf;
898 }
899 *(out++) = (buf >> 4) & 255;
900 }
901
902 *outlen = len; /* modify to reflect the actual output size */
903 outbuf[len] = 0;
904 return outbuf;
905 }
906
907
908 static char* b64enctable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
909 char* util_base64encode(const char *in, size_t len) {
910 // calculate length of base64 output and create buffer
911 size_t outlen = 4 * ((len + 2) / 3);
912 int pad = len % 3;
913
914 char *out = malloc(outlen + 1);
915 out[outlen] = 0;
916 size_t pos = 0;
917
918 // encode blocks of 3 bytes
919 size_t i;
920 size_t blockend = len - pad;
921 for(i=0;i<blockend;i++) {
922 unsigned char b1 = in[i++];
923 unsigned char b2 = in[i++];
924 unsigned char b3 = in[i];
925 uint32_t inb = b1 << 16 | (b2 << 8) | b3;
926 out[pos++] = b64enctable[(inb >> 18) & 63];
927 out[pos++] = b64enctable[(inb >> 12) & 63];
928 out[pos++] = b64enctable[(inb >> 6) & 63];
929 out[pos++] = b64enctable[(inb) & 63];
930 }
931
932 // encode last bytes
933 if(pad > 0) {
934 char p[3] = {0, 0, 0};
935 for(int j=0;i<len;i++) {
936 p[j++] = in[i];
937 }
938 unsigned char b1 = p[0];
939 unsigned char b2 = p[1];
940 unsigned char b3 = p[2];
941 uint32_t inb = (b1 << 16) | (b2 << 8) | b3;
942 out[pos++] = b64enctable[(inb >> 18) & 63];
943 out[pos++] = b64enctable[(inb >> 12) & 63];
944 out[pos++] = b64enctable[(inb >> 6) & 63];
945 out[pos++] = b64enctable[(inb) & 63];
946 for(int k=outlen-1;k>=outlen-(3-pad);k--) {
947 out[k] = '=';
948 }
949 }
950
951 return out;
952 }
953
954 char* util_encrypt_str(DavSession *sn, const char *str, const char *key) {
955 DavKey *k = dav_context_get_key(sn->context, key);
956 if(!k) {
957 sn->error = DAV_ERROR;
958 cxmutstr err = cx_asprintf("Key %s not found", key);
959 dav_session_set_errstr(sn, err.ptr);
960 free(err.ptr);
961 return NULL;
962 }
963
964 return util_encrypt_str_k(sn, str, k);
965 }
966
967 char* util_encrypt_str_k(DavSession *sn, const char *str, DavKey *key) {
968 char *enc_str = aes_encrypt(str, strlen(str), key);
969 char *ret_str = dav_session_strdup(sn, enc_str);
970 free(enc_str);
971 return ret_str;
972 }
973
974 char* util_decrypt_str(DavSession *sn, const char *str, const char *key) {
975 DavKey *k = dav_context_get_key(sn->context, key);
976 if(!k) {
977 sn->error = DAV_ERROR;
978 cxmutstr err = cx_asprintf("Key %s not found", key);
979 dav_session_set_errstr(sn, err.ptr);
980 free(err.ptr);
981 return NULL;
982 }
983
984 return util_decrypt_str_k(sn, str, k);
985 }
986
987 char* util_decrypt_str_k(DavSession *sn, const char *str, DavKey *key) {
988 size_t len = 0;
989 char *dec_str = aes_decrypt(str, &len, key);
990 char *ret_str = dav_session_strdup(sn, dec_str);
991 free(dec_str);
992 return ret_str;
993 }
994
995 char* util_random_str() {
996 unsigned char *str = malloc(25);
997 str[24] = '\0';
998
999 cxstring t = CX_STR(
1000 "01234567890"
1001 "abcdefghijklmnopqrstuvwxyz"
1002 "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
1003 const unsigned char *table = (const unsigned char*)t.ptr;
1004
1005 #ifdef DAV_USE_OPENSSL
1006 RAND_bytes(str, 24);
1007 #else
1008 dav_rand_bytes(str, 24);
1009 #endif
1010 for(int i=0;i<24;i++) {
1011 int c = str[i] % t.length;
1012 str[i] = table[c];
1013 }
1014
1015 return (char*)str;
1016 }
1017
1018 /*
1019 * gets a substring from 0 to the appearance of the token
1020 * tokens are separated by space
1021 * sets sub to the substring and returns the remaining string
1022 */
1023 // TODO: remove if it isn't used
1024 /*
1025 sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) {
1026 int i;
1027 int token_start = -1;
1028 int token_end = -1;
1029 for(i=0;i<=str.length;i++) {
1030 int c;
1031 if(i == str.length) {
1032 c = ' ';
1033 } else {
1034 c = str.ptr[i];
1035 }
1036 if(c < 33) {
1037 if(token_start != -1) {
1038 token_end = i;
1039 size_t len = token_end - token_start;
1040 sstr_t tk = sstrsubsl(str, token_start, len);
1041 //printf("token: {%.*s}\n", token.length, token.ptr);
1042 if(!sstrcmp(tk, token)) {
1043 *sub = sstrtrim(sstrsubsl(str, 0, token_start));
1044 break;
1045 }
1046 token_start = -1;
1047 token_end = -1;
1048 }
1049 } else {
1050 if(token_start == -1) {
1051 token_start = i;
1052 }
1053 }
1054 }
1055
1056 if(i < str.length) {
1057 return sstrtrim(sstrsubs(str, i));
1058 } else {
1059 str.ptr = NULL;
1060 str.length = 0;
1061 return str;
1062 }
1063 }
1064 */
1065
1066 cxmutstr util_readline(FILE *stream) {
1067 CxBuffer buf;
1068 cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
1069
1070 int c;
1071 while((c = fgetc(stream)) != EOF) {
1072 if(c == '\n') {
1073 break;
1074 }
1075 cxBufferPut(&buf, c);
1076 }
1077
1078 cxmutstr str = cx_strdup(cx_strtrim(cx_strn(buf.space, buf.size)));
1079 cxBufferDestroy(&buf);
1080 return str;
1081 }
1082
1083 char* util_password_input(char *prompt) {
1084 fprintf(stderr, "%s", prompt);
1085 fflush(stderr);
1086
1087 #ifndef _WIN32
1088 // hide terminal input
1089 struct termios oflags, nflags;
1090 if(isatty(fileno(stdin))) {
1091 tcgetattr(fileno(stdin), &oflags);
1092 nflags = oflags;
1093 nflags.c_lflag &= ~ECHO;
1094 nflags.c_lflag |= ECHONL;
1095 if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) {
1096 perror("tcsetattr");
1097 }
1098 }
1099
1100 #endif
1101
1102 // read password input
1103 CxBuffer buf;
1104 cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
1105 int c = 0;
1106 while((c = getpasswordchar()) != EOF) {
1107 if(c == '\n' || c == '\r') {
1108 break;
1109 }
1110 cxBufferPut(&buf, c);
1111 }
1112 cxBufferPut(&buf, 0);
1113 fflush(stdin);
1114
1115 #ifndef _WIN32
1116 // restore terminal settings
1117 if (isatty(fileno(stdin)) && tcsetattr(fileno(stdin), TCSANOW, &oflags) != 0) {
1118 perror("tcsetattr");
1119 }
1120 #endif
1121
1122 return buf.space;
1123 }
1124
1125 int util_exec_command(char *command, CxBuffer *outbuf) {
1126 #ifdef _WIN32
1127 fprintf(stderr, "util_exec_command unsupported\n");
1128 return 1;
1129 #else
1130
1131 int pout[2];
1132 if(pipe(pout)) {
1133 perror("pipe");
1134 return 1;
1135 }
1136
1137 int ret = 0;
1138
1139 // close stdin and stderr, use pipe for stdout
1140 posix_spawn_file_actions_t actions;
1141 posix_spawn_file_actions_init(&actions);
1142 posix_spawn_file_actions_addclose(&actions, 0);
1143 posix_spawn_file_actions_adddup2(&actions, pout[1], 1);
1144 posix_spawn_file_actions_addclose(&actions, 2);
1145
1146 char *args[4];
1147 args[0] = "sh";
1148 args[1] = "-c";
1149 args[2] = command;
1150 args[3] = NULL;
1151
1152 pid_t pid; // child pid
1153 ret = posix_spawn(&pid, "/bin/sh", &actions, NULL, args, NULL);
1154
1155 close(pout[1]);
1156
1157 if(!ret) {
1158 ssize_t r;
1159 char buf[1024];
1160 while((r = read(pout[0], buf, 1024)) > 0) {
1161 cxBufferWrite(buf, 1, r, outbuf);
1162 }
1163 }
1164
1165 // wait for child process
1166 ret = 1;
1167 waitpid(pid, &ret, 0);
1168
1169 posix_spawn_file_actions_destroy(&actions);
1170 close(pout[0]);
1171
1172 return ret;
1173 #endif
1174 }
1175
1176 char* util_hexstr(const unsigned char *data, size_t len) {
1177 size_t buflen = 2*len + 4;
1178 CxBuffer buf;
1179 cxBufferInit(&buf, NULL, buflen + 1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
1180 for(int i=0;i<len;i++) {
1181 cx_bprintf(&buf, "%02x", data[i]);
1182 }
1183 cxBufferPut(&buf, 0);
1184 return buf.space;
1185 }
1186
1187 void util_remove_trailing_pathseparator(char *path) {
1188 size_t len = strlen(path);
1189 if(len < 2) {
1190 return;
1191 }
1192
1193 if(path[len-1] == '/') {
1194 path[len-1] = '\0';
1195 }
1196 }
1197
1198 char* util_file_hash(const char *path) {
1199 FILE *in = fopen(path, "r");
1200 if(!in) {
1201 return NULL;
1202 }
1203
1204 DAV_SHA_CTX *sha = dav_hash_init();
1205 char *buf = malloc(16384);
1206
1207 size_t r;
1208 while((r = fread(buf, 1, 16384, in)) > 0) {
1209 dav_hash_update(sha, buf, r);
1210 }
1211
1212 unsigned char hash[DAV_SHA256_DIGEST_LENGTH];
1213 dav_hash_final(sha, hash);
1214 free(buf);
1215 fclose(in);
1216
1217 return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH);
1218 }
1219

mercurial