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