1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <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
64
65
66
67
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
86 if(!iso8601str) {
87 return 0;
88 }
89
90
91 struct tm tparts;
92 memset(&tparts,
0,
sizeof(
struct tm));
93 long val;
94 char conv[
16];
95
96
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
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
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
144 if(tzinfo.length ==
0) {
145
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
182
183 if(!str) {
184 return 0;
185 }
186
187 return parse_iso8601(str);
188 }
189
190 time_t util_parse_lastmodified(
char *str) {
191
192
193 if(!str) {
194 return 0;
195 }
else {
196 time_t result = curl_getdate(str,
NULL);
197 if(result == -
1) {
198
199
200
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
366 if(cx_strprefix(sbuffer, cx_str(
"HTTP/"))) {
367
368 cxMapClear(map);
369 return size*nitems;
370 }
371
372
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;
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
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
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
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
524
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
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
739 cxBufferWrite(sn->base_url,
1, strlen(sn->base_url), &url);
740
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';
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
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
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
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
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
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
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;
937 case INVALID: {
938
939 outbuf[
0] =
0;
940 return outbuf;
941 }
942 case EQUALS: {
943
944 in = end;
945 continue;
946 }
947 default: {
948 buf = buf <<
6 | c;
949 iter++;
950
951 if (iter ==
4) {
952 if ((len +=
3) > bufsize) {
953
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
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
979 outbuf[
0] =
0;
980 return outbuf;
981 }
982 *(out++) = (buf >>
4) &
255;
983 }
984
985 *outlen = len;
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
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
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
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
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
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
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
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
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
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;
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
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