#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <cx/string.h>
#include <cx/buffer.h>
#include <cx/utils.h>
#include <cx/printf.h>
#include <libxml/tree.h>
#include <curl/curl.h>
#ifdef _WIN32
#include <conio.h>
#define getpasswordchar() getch()
#define IS_PATH_SEPARATOR(c) (c ==
'/' || c ==
'\\')
#define PATH_SEPARATOR '\\'
#else
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>
#include <termios.h>
#define getpasswordchar() getchar()
#define IS_PATH_SEPARATOR(c) (c ==
'/')
#define PATH_SEPARATOR '/'
#endif
#include "webdav.h"
#include "utils.h"
#include "crypto.h"
#include "session.h"
static size_t extractval(cxstring str,
char *result,
char delim) {
size_t n =
0;
for(
size_t i =
0; i < str.length ; i++) {
if(isdigit(str.ptr[i])) {
result[n++] = str.ptr[i];
}
else if(str.ptr[i] != delim) {
return 0;
}
}
result[n] =
'\0';
return n;
}
static time_t parse_iso8601(
char *iso8601str) {
if(!iso8601str) {
return 0;
}
struct tm tparts;
memset(&tparts,
0,
sizeof(
struct tm));
long val;
char conv[
16];
cxstring date = cx_strtrim(cx_str(iso8601str));
cxstring time = cx_strchr(date,
'T');
if(time.length ==
0) {
return 0;
}
date.length = time.ptr - date.ptr;
time.ptr++; time.length--;
cxstring tzinfo;
if((tzinfo = cx_strchr(time,
'Z')).length >
0 ||
(tzinfo = cx_strchr(time,
'+')).length >
0 ||
(tzinfo = cx_strchr(time,
'-')).length >
0) {
time.length = tzinfo.ptr - time.ptr;
}
if((date.length !=
8 && date.length !=
10)
|| extractval(date, conv ,
'-') !=
8) {
return 0;
}
val = atol(conv);
if(val <
19000000L) {
return 0;
}
tparts.tm_mday = val %
100;
tparts.tm_mon = (val %
10000) /
100 -
1;
tparts.tm_year = val /
10000 -
1900;
cxstring frac;
if((frac = cx_strchr(time,
'.')).length >
0 ||
(frac = cx_strchr(time,
',')).length >
0) {
time.length = frac.ptr - time.ptr;
}
if((time.length !=
6 && time.length !=
8)
|| extractval(time, conv ,
':') !=
6) {
return 0;
}
val = atol(conv);
tparts.tm_sec = val %
100;
tparts.tm_min = (val %
10000) /
100;
tparts.tm_hour = val /
10000;
if(tzinfo.length ==
0) {
tparts.tm_isdst = -
1;
return mktime(&tparts);
}
else if(!cx_strcmp(tzinfo, cx_str(
"Z"))) {
#if defined(__FreeBSD__)
return timegm(&tparts);
#elif defined(
_WIN32)
return _mkgmtime(&tparts);
#else
return mktime(&tparts) - timezone;
#endif
}
else if(tzinfo.ptr[
0] ==
'+' || tzinfo.ptr[
0] ==
'-') {
int sign = (tzinfo.ptr[
0] ==
'+') ? -
1 :
1;
if(tzinfo.length >
6) {
return 0;
}
else {
tzinfo.ptr++; tzinfo.length--;
extractval(tzinfo, conv,
':');
val = atol(conv);
val =
60 * (val /
100) + (val %
100);
#if defined(__FreeBSD__)
return timegm(&tparts) + (
time_t) (
60 * val * sign);
#elif defined(
_WIN32)
return _mkgmtime(&tparts) + (
time_t)(
60 * val * sign);
#else
return mktime(&tparts) - timezone + (
time_t) (
60 * val * sign);
#endif
}
}
else {
return 0;
}
}
time_t util_parse_creationdate(
char *str) {
if(!str) {
return 0;
}
return parse_iso8601(str);
}
time_t util_parse_lastmodified(
char *str) {
if(!str) {
return 0;
}
else {
time_t result = curl_getdate(str,
NULL);
if(result == -
1) {
return util_parse_creationdate(str);
}
else {
return result;
}
}
}
int util_getboolean(
const char *v) {
if(v[
0] ==
'T' || v[
0] ==
't') {
return 1;
}
return 0;
}
int util_strtouint(
const char *str,
uint64_t *value) {
if (str ==
NULL || *str ==
'\0')
return 0;
char *end;
errno =
0;
uint64_t val = strtoull(str, &end,
0);
if(errno ==
0 && *end ==
'\0') {
*value = val;
return 1;
}
else {
return 0;
}
}
int util_strtoint(
const char *str,
int64_t *value) {
if (str ==
NULL || *str ==
'\0')
return 0;
char *end;
errno =
0;
int64_t val = strtoll(str, &end,
0);
if(errno ==
0 && *end ==
'\0') {
*value = val;
return 1;
}
else {
return 0;
}
}
int util_szstrtouint(
const char *str,
uint64_t *value) {
if (str ==
NULL || *str ==
'\0')
return 0;
char *end;
errno =
0;
size_t len = strlen(str);
uint64_t val = strtoull(str, &end,
0);
if(errno !=
0) {
return 0;
}
if(end == str+len) {
*value = val;
return 1;
}
else if(end == str+len-
1) {
uint64_t mul =
1;
switch(end[
0]) {
case 'k':
case 'K': mul =
1024;
break;
case 'm':
case 'M': mul =
1024*
1024;
break;
case 'g':
case 'G': mul =
1024*
1024*
1024;
break;
default:
return 0;
}
uint64_t result =
0;
if(util_uint_mul(val, mul, &result)) {
return 0;
}
*value = result;
return 1;
}
return 0;
}
int util_uint_mul(
uint64_t a,
uint64_t b,
uint64_t *result) {
if(a ==
0 || b ==
0) {
*result =
0;
return 0;
}
uint64_t r = a * b;
if(r / b == a) {
*result = r;
return 0;
}
else {
*result =
0;
return 1;
}
}
cxstring util_url_base_s(cxstring url) {
size_t i =
0;
if(url.length >
0) {
int slmax;
if(cx_strprefix(url, cx_str(
"http://"))) {
slmax =
3;
}
else if(cx_strprefix(url, cx_str(
"https://"))) {
slmax =
3;
}
else {
slmax =
1;
}
int slashcount =
0;
for(i=
0;i<url.length;i++) {
if(url.ptr[i] ==
'/') {
slashcount++;
if(slashcount == slmax) {
i++;
break;
}
}
}
}
return cx_strsubsl(url,
0, i);
}
char* util_url_base(
const char *url) {
return cx_strdup(util_url_base_s(cx_str(url))).ptr;
}
#ifdef _WIN32
#define strncasecmp _strnicmp
#endif
const char* util_url_path(
const char *url) {
return util_url_path_s(cx_str(url)).ptr;
}
cxstring util_url_path_s(cxstring url) {
cxstring path = {
"",
0 };
int slashcount =
0;
int slmax;
if(url.length >
7 && !strncasecmp(url.ptr,
"http://",
7)) {
slmax =
3;
}
else if(url.length >
8 && !strncasecmp(url.ptr,
"https://",
8)) {
slmax =
3;
}
else {
slmax =
1;
}
char c;
for(
int i=
0;i<url.length;i++) {
c = url.ptr[i];
if(c ==
'/') {
slashcount++;
if(slashcount == slmax) {
path = cx_strsubs(url, i);
break;
}
}
}
return path;
}
char* util_url_decode(DavSession *sn,
const char *url) {
char *unesc = curl_easy_unescape(sn->handle, url, strlen(url),
NULL);
char *ret = strdup(unesc);
curl_free(unesc);
return ret;
}
static size_t util_header_callback(
char *buffer,
size_t size,
size_t nitems,
void *data) {
cxstring sbuffer = cx_strn(buffer, size*nitems);
CxMap *map = (CxMap*) data;
if(cx_strprefix(sbuffer, cx_str(
"HTTP/"))) {
cxMapClear(map);
return size*nitems;
}
if(!cx_strcmp(sbuffer, cx_str(
"\r\n"))) {
return 2;
}
cxstring key = sbuffer;
cxstring value = cx_strchr(sbuffer,
':');
if(value.length ==
0) {
return 0;
}
key.length = value.ptr - key.ptr;
value.ptr++; value.length--;
cxmutstr key_cp = cx_strdup(cx_strtrim(key));
cx_strlower(key_cp);
cxmutstr value_cp = cx_strdup(cx_strtrim(value));
cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr);
free(key_cp.ptr);
return sbuffer.length;
}
int util_path_isrelated(
const char *path1,
const char *path2) {
cxstring p1 = cx_str(path1);
cxstring p2 = cx_str(path2);
if(
IS_PATH_SEPARATOR(p1.ptr[p1.length-
1])) {
p1.length--;
}
if(
IS_PATH_SEPARATOR(p2.ptr[p2.length-
1])) {
p2.length--;
}
if(p2.length < p1.length) {
return 0;
}
if(!cx_strcmp(p1, p2)) {
return 1;
}
if(cx_strprefix(p2, p1)) {
if(
IS_PATH_SEPARATOR(p2.ptr[p1.length])) {
return 1;
}
}
return 0;
}
#ifdef _WIN32
int util_path_isabsolut(
const char *path) {
if(strlen(path) <
3) {
return 0;
}
char c = path[
0];
if(!((c >=
65 && c <=
90) || (c >=
97 && c <=
122))) {
return 0;
}
if(path[
1] ==
':' && path[
2] ==
'\\') {
return 1;
}
return 0;
}
#else
int util_path_isabsolut(
const char *path) {
return path[
0] ==
'/';
}
#endif
char* util_path_normalize(
const char *path) {
size_t len = strlen(path);
CxBuffer buf;
cxBufferInit(&buf,
NULL, len+
1, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
if(path[
0] ==
'/') {
cxBufferPut(&buf,
'/');
}
int add_separator =
0;
int seg_start =
0;
for(
int i=
0;i<=len;i++) {
char c = path[i];
if(
IS_PATH_SEPARATOR(c) || c ==
'\0') {
const char *seg_ptr = path+seg_start;
int seg_len = i - seg_start;
if(
IS_PATH_SEPARATOR(seg_ptr[
0])) {
seg_ptr++;
seg_len--;
}
if(seg_len >
0) {
cxstring seg = cx_strn(seg_ptr, seg_len);
if(!cx_strcmp(seg,
CX_STR(
".."))) {
for(
int j=buf.pos;j>=
0;j--) {
char t = j < buf.pos ? buf.space[j] :
0;
if(
IS_PATH_SEPARATOR(t) || j ==
0) {
buf.pos = j;
buf.size = j;
buf.space[j] =
0;
add_separator =
IS_PATH_SEPARATOR(t) ?
1 :
0;
break;
}
}
}
else if(!cx_strcmp(seg,
CX_STR(
"."))) {
}
else {
if(add_separator) {
cxBufferPut(&buf,
PATH_SEPARATOR);
}
cxBufferWrite(seg_ptr,
1, seg_len, &buf);
add_separator =
1;
}
}
seg_start = i;
}
}
cxBufferPut(&buf,
0);
return buf.space;
}
static char* create_relative_path(
const char *abspath,
const char *base) {
size_t path_len = strlen(abspath);
size_t base_len = strlen(base);
if(
IS_PATH_SEPARATOR(abspath[path_len-
1])) {
path_len--;
}
if(
IS_PATH_SEPARATOR(base[base_len-
1])) {
base_len--;
}
for(
int i=base_len-
1;i>=
0;i--) {
if(
IS_PATH_SEPARATOR(base[i])) {
base_len = i+
1;
break;
}
}
size_t max = path_len > base_len ? base_len : path_len;
size_t last_dir =
0;
for(
size_t i=
0;i<max;i++) {
char c = abspath[i];
if(c != base[i]) {
break;
}
else if(
IS_PATH_SEPARATOR(c)) {
last_dir = i;
}
}
char *ret =
NULL;
CxBuffer out;
if(last_dir+
1 < base_len) {
size_t dircount =
0;
for(
size_t i=last_dir+
1;i<base_len;i++) {
if(
IS_PATH_SEPARATOR(base[i])) {
dircount++;
}
}
cxBufferInit(&out,
NULL, dircount*
3+path_len-last_dir, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
for(
size_t i=
0;i<dircount;i++) {
cxBufferPutString(&out,
"../");
}
}
else {
cxBufferInit(&out,
NULL, path_len - last_dir, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
}
cxBufferPutString(&out, abspath + last_dir +
1);
cxBufferPut(&out,
0);
return out.space;
}
#ifdef _WIN32
char* util_create_relative_path(
const char *abspath,
const char *base) {
char *abspath_converted = strdup(abspath);
char *base_converted = strdup(base);
size_t abs_len = strlen(abspath_converted);
size_t base_len = strlen(base_converted);
for(
int i=
0;i<abs_len;i++) {
if(abspath_converted[i] ==
'\\') {
abspath_converted[i] =
'/';
}
}
for(
int i=
0;i<base_len;i++) {
if(base_converted[i] ==
'\\') {
base_converted[i] =
'/';
}
}
char *ret = create_relative_path(abspath_converted, base_converted);
free(abspath_converted);
free(base_converted);
return ret;
}
#else
char* util_create_relative_path(
const char *abspath,
const char *base) {
return create_relative_path(abspath, base);
}
#endif
void util_capture_header(
CURL *handle, CxMap* map) {
if(map) {
curl_easy_setopt(handle,
CURLOPT_HEADERFUNCTION, util_header_callback);
curl_easy_setopt(handle,
CURLOPT_HEADERDATA, map);
}
else {
curl_easy_setopt(handle,
CURLOPT_HEADERFUNCTION,
NULL);
curl_easy_setopt(handle,
CURLOPT_HEADERDATA,
NULL);
}
}
const char* util_resource_name(
const char *url) {
cxstring urlstr = cx_str(url);
if(urlstr.ptr[urlstr.length-
1] ==
'/') {
urlstr.length--;
}
cxstring resname = cx_strrchr(urlstr,
'/');
if(resname.length >
1) {
return resname.ptr+
1;
}
else {
return url;
}
}
const char* util_resource_name_c(
const char *url,
char pathseparator) {
cxstring urlstr = cx_str(url);
if(urlstr.ptr[urlstr.length-
1] == pathseparator) {
urlstr.length--;
}
cxstring resname = cx_strrchr(urlstr, pathseparator);
if(resname.length >
1) {
return resname.ptr+
1;
}
else {
return url;
}
}
const char* util_path_file_name(
const char *url) {
#ifdef _WIN32
return util_resource_name_c(url,
'\\');
#else
return util_resource_name_c(url,
'/');
#endif
}
int util_mkdir(
char *path,
mode_t mode) {
#ifdef _WIN32
return mkdir(path);
#else
return mkdir(path, mode);
#endif
}
char* util_concat_path(
const char *url_base,
const char *p) {
cxstring base = cx_str(url_base);
cxstring path;
if(p) {
path = cx_str((
char*)p);
}
else {
path =
CX_STR(
"");
}
return util_concat_path_s(base, path).ptr;
}
cxmutstr util_concat_path_s(cxstring base, cxstring path) {
if(!path.ptr) {
path =
CX_STR(
"");
}
int add_separator =
0;
if(base.length !=
0 && base.ptr[base.length-
1] ==
'/') {
if(path.ptr[
0] ==
'/') {
base.length--;
}
}
else {
if(path.length ==
0 || path.ptr[
0] !=
'/') {
add_separator =
1;
}
}
cxmutstr url;
if(add_separator) {
url = cx_strcat(
3, base,
CX_STR(
"/"), path);
}
else {
url = cx_strcat(
2, base, path);
}
return url;
}
cxmutstr util_concat_path_ext(cxstring base, cxstring path,
char separator) {
if(!path.ptr) {
path =
CX_STR(
"");
}
int add_separator =
0;
if(base.length !=
0 && base.ptr[base.length-
1] == separator) {
if(path.ptr[
0] == separator) {
base.length--;
}
}
else {
if(path.length ==
0 || path.ptr[
0] != separator) {
add_separator =
1;
}
}
cxmutstr url;
if(add_separator) {
url = cx_strcat(
3, base, cx_strn(&separator,
1), path);
}
else {
url = cx_strcat(
2, base, path);
}
return url;
}
cxmutstr util_concat_sys_path(cxstring base, cxstring path) {
#ifdef _WIN32
return util_concat_path_ext(base, path,
'\\');
#else
return util_concat_path_ext(base, path,
'/');
#endif
}
char* util_get_url(DavSession *sn,
const char *href) {
cxstring base = cx_str(sn->base_url);
cxstring href_str = cx_str(href);
const char *base_path = util_url_path(sn->base_url);
base.length -= strlen(base_path);
cxmutstr url = cx_strcat(
2, base, href_str);
return url.ptr;
}
void util_set_url(DavSession *sn,
const char *href) {
char *url = util_get_url(sn, href);
curl_easy_setopt(sn->handle,
CURLOPT_URL, url);
free(url);
}
char* util_path_to_url(DavSession *sn,
const char *path) {
size_t pathlen = path ? strlen(path) :
0;
if(pathlen ==
0) {
return strdup(sn->base_url);
}
CxBuffer url;
cxBufferInit(&url,
NULL,
256, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
cxBufferWrite(sn->base_url,
1, strlen(sn->base_url), &url);
cxBufferSeek(&url, -
1,
SEEK_CUR);
cxstring p = cx_strn(path, pathlen);
CxStrtokCtx tkctx = cx_strtok(p,
CX_STR(
"/"),
INT_MAX);
cxstring node;
while(cx_strtok_next(&tkctx, &node)) {
if(node.length >
0) {
char *esc = curl_easy_escape(sn->handle, node.ptr, node.length);
cxBufferPut(&url,
'/');
cxBufferWrite(esc,
1, strlen(esc), &url);
curl_free(esc);
}
}
if(path[p.length-
1] ==
'/') {
cxBufferPut(&url,
'/');
}
cxBufferPut(&url,
0);
return url.space;
}
char* util_parent_path(
const char *path) {
const char *name = util_resource_name(path);
size_t namelen = strlen(name);
size_t pathlen = strlen(path);
size_t parentlen = pathlen - namelen;
char *parent = malloc(parentlen +
1);
memcpy(parent, path, parentlen);
parent[parentlen] =
'\0';
return parent;
}
char* util_sys_parent_path(
const char *path) {
const char *name = util_path_file_name(path);
size_t namelen = strlen(name);
size_t pathlen = strlen(path);
size_t parentlen = pathlen - namelen;
char *parent = malloc(parentlen +
1);
memcpy(parent, path, parentlen);
parent[parentlen] =
'\0';
return parent;
}
char* util_size_str(DavBool iscollection,
uint64_t contentlength) {
return util_size_str2(iscollection, contentlength, contentlength,
1);
}
char* util_size_str2(DavBool iscollection,
uint64_t contentlength,
uint64_t dimension,
int precision) {
char *str = malloc(
16);
uint64_t size = contentlength;
if(iscollection) {
str[
0] =
'\0';
}
else if(dimension < 0x400) {
snprintf(str,
16,
"%" PRIu64
" bytes", size);
}
else if(dimension < 0x100000) {
float s = (
float)size/0x400;
int diff = (s*
100 - (
int)s*
100);
if(diff >
90) {
diff =
0;
s +=
0.10f;
}
if(dimension < 0x2800 && diff !=
0) {
snprintf(str,
16,
"%.*f KiB", precision, s);
}
else {
snprintf(str,
16,
"%.0f KiB", s);
}
}
else if(dimension < 0x40000000) {
float s = (
float)size/0x100000;
int diff = (s*
100 - (
int)s*
100);
if(diff >
90) {
diff =
0;
s +=
0.10f;
}
if(dimension < 0xa00000 && diff !=
0) {
snprintf(str,
16,
"%.*f MiB", precision, s);
}
else {
size /= 0x100000;
snprintf(str,
16,
"%.0f MiB", s);
}
}
else if(dimension < 0x1000000000ULL) {
float s = (
float)size/0x40000000;
int diff = (s*
100 - (
int)s*
100);
if(diff >
90) {
diff =
0;
s +=
0.10f;
}
if(dimension < 0x280000000 && diff !=
0) {
snprintf(str,
16,
"%.*f GiB", precision, s);
}
else {
size /= 0x40000000;
snprintf(str,
16,
"%.0f GiB", s);
}
}
else {
size /=
1024;
float s = (
float)size/0x40000000;
int diff = (s*
100 - (
int)s*
100);
if(diff >
90) {
diff =
0;
s +=
0.10f;
}
if(dimension < 0x280000000 && diff !=
0) {
snprintf(str,
16,
"%.*f TiB", precision, s);
}
else {
size /= 0x40000000;
snprintf(str,
16,
"%.0f TiB", s);
}
}
return str;
}
char* util_date_str(
time_t tm) {
struct tm t;
struct tm n;
time_t now = time(
NULL);
#ifdef _WIN32
memcpy(&t, localtime(&tm),
sizeof(
struct tm));
memcpy(&n, localtime(&now),
sizeof(
struct tm));
#else
localtime_r(&tm, &t);
localtime_r(&now, &n);
#endif
char *str = malloc(
16);
if(t.tm_year == n.tm_year) {
strftime(str,
16,
"%b %d %H:%M", &t);
}
else {
strftime(str,
16,
"%b %d %Y", &t);
}
return str;
}
char* util_xml_get_text(
const xmlNode *elm) {
xmlNode *node = elm->children;
while(node) {
if(node->type ==
XML_TEXT_NODE) {
return (
char*)node->content;
}
node = node->next;
}
return NULL;
}
char* util_base64decode(
const char *in) {
int len =
0;
return util_base64decode_len(in, &len);
}
#define WHITESPACE 64
#define EQUALS 65
#define INVALID 66
static char b64dectable[] = {
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
66,
66,
66,
66,
66,
66
};
char* util_base64decode_len(
const char* in,
int *outlen) {
if(!in) {
*outlen =
0;
return NULL;
}
size_t inlen = strlen(in);
size_t bufsize = (inlen*
3) /
4;
char *outbuf = malloc(bufsize+
1);
*outlen = -
1;
unsigned char *out = (
unsigned char*)outbuf;
const char *end = in + inlen;
char iter =
0;
uint32_t buf =
0;
size_t len =
0;
while (in < end) {
unsigned char c = b64dectable[*in++];
switch (c) {
case WHITESPACE:
continue;
case INVALID: {
outbuf[
0] =
0;
return outbuf;
}
case EQUALS: {
in = end;
continue;
}
default: {
buf = buf <<
6 | c;
iter++;
if (iter ==
4) {
if ((len +=
3) > bufsize) {
outbuf[
0] =
0;
return outbuf;
}
*(out++) = (buf >>
16) &
255;
*(out++) = (buf >>
8) &
255;
*(out++) = buf &
255;
buf =
0; iter =
0;
}
}
}
}
if (iter ==
3) {
if ((len +=
2) > bufsize) {
outbuf[
0] =
0;
return outbuf;
}
*(out++) = (buf >>
10) &
255;
*(out++) = (buf >>
2) &
255;
}
else if (iter ==
2) {
if (++len > bufsize) {
outbuf[
0] =
0;
return outbuf;
}
*(out++) = (buf >>
4) &
255;
}
*outlen = len;
outbuf[len] =
0;
return outbuf;
}
static char* b64enctable =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char* util_base64encode(
const char *in,
size_t len) {
size_t outlen =
4 * ((len +
2) /
3);
int pad = len %
3;
char *out = malloc(outlen +
1);
out[outlen] =
0;
size_t pos =
0;
size_t i;
size_t blockend = len - pad;
for(i=
0;i<blockend;i++) {
unsigned char b1 = in[i++];
unsigned char b2 = in[i++];
unsigned char b3 = in[i];
uint32_t inb = b1 <<
16 | (b2 <<
8) | b3;
out[pos++] = b64enctable[(inb >>
18) &
63];
out[pos++] = b64enctable[(inb >>
12) &
63];
out[pos++] = b64enctable[(inb >>
6) &
63];
out[pos++] = b64enctable[(inb) &
63];
}
if(pad >
0) {
char p[
3] = {
0,
0,
0};
for(
int j=
0;i<len;i++) {
p[j++] = in[i];
}
unsigned char b1 = p[
0];
unsigned char b2 = p[
1];
unsigned char b3 = p[
2];
uint32_t inb = (b1 <<
16) | (b2 <<
8) | b3;
out[pos++] = b64enctable[(inb >>
18) &
63];
out[pos++] = b64enctable[(inb >>
12) &
63];
out[pos++] = b64enctable[(inb >>
6) &
63];
out[pos++] = b64enctable[(inb) &
63];
for(
int k=outlen-
1;k>=outlen-(
3-pad);k--) {
out[k] =
'=';
}
}
return out;
}
char* util_encrypt_str(DavSession *sn,
const char *str,
const char *key) {
DavKey *k = dav_context_get_key(sn->context, key);
if(!k) {
sn->error =
DAV_ERROR;
cxmutstr err = cx_asprintf(
"Key %s not found", key);
dav_session_set_errstr(sn, err.ptr);
free(err.ptr);
return NULL;
}
return util_encrypt_str_k(sn, str, k);
}
char* util_encrypt_str_k(DavSession *sn,
const char *str, DavKey *key) {
char *enc_str = aes_encrypt(str, strlen(str), key);
char *ret_str = dav_session_strdup(sn, enc_str);
free(enc_str);
return ret_str;
}
char* util_decrypt_str(DavSession *sn,
const char *str,
const char *key) {
DavKey *k = dav_context_get_key(sn->context, key);
if(!k) {
sn->error =
DAV_ERROR;
cxmutstr err = cx_asprintf(
"Key %s not found", key);
dav_session_set_errstr(sn, err.ptr);
free(err.ptr);
return NULL;
}
return util_decrypt_str_k(sn, str, k);
}
char* util_decrypt_str_k(DavSession *sn,
const char *str, DavKey *key) {
size_t len =
0;
char *dec_str = aes_decrypt(str, &len, key);
char *ret_str = dav_session_strdup(sn, dec_str);
free(dec_str);
return ret_str;
}
char* util_random_str() {
unsigned char *str = malloc(
25);
str[
24] =
'\0';
cxstring t =
CX_STR(
"01234567890"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
const unsigned char *table = (
const unsigned char*)t.ptr;
#ifdef DAV_USE_OPENSSL
RAND_bytes(str,
24);
#else
dav_rand_bytes(str,
24);
#endif
for(
int i=
0;i<
24;i++) {
int c = str[i] % t.length;
str[i] = table[c];
}
return (
char*)str;
}
cxmutstr util_readline(
FILE *stream) {
CxBuffer buf;
cxBufferInit(&buf,
NULL,
128, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
int c;
while((c = fgetc(stream)) !=
EOF) {
if(c ==
'\n') {
break;
}
cxBufferPut(&buf, c);
}
cxmutstr str = cx_strdup(cx_strtrim(cx_strn(buf.space, buf.size)));
cxBufferDestroy(&buf);
return str;
}
char* util_password_input(
char *prompt) {
fprintf(stderr,
"%s", prompt);
fflush(stderr);
#ifndef _WIN32
struct termios oflags, nflags;
if(isatty(fileno(stdin))) {
tcgetattr(fileno(stdin), &oflags);
nflags = oflags;
nflags.c_lflag &= ~
ECHO;
nflags.c_lflag |=
ECHONL;
if (tcsetattr(fileno(stdin),
TCSANOW, &nflags) !=
0) {
perror(
"tcsetattr");
}
}
#endif
CxBuffer buf;
cxBufferInit(&buf,
NULL,
128, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
int c =
0;
while((c = getpasswordchar()) !=
EOF) {
if(c ==
'\n' || c ==
'\r') {
break;
}
cxBufferPut(&buf, c);
}
cxBufferPut(&buf,
0);
fflush(stdin);
#ifndef _WIN32
if (isatty(fileno(stdin)) && tcsetattr(fileno(stdin),
TCSANOW, &oflags) !=
0) {
perror(
"tcsetattr");
}
#endif
return buf.space;
}
int util_exec_command(
char *command, CxBuffer *outbuf) {
#ifdef _WIN32
fprintf(stderr,
"util_exec_command unsupported\n");
return 1;
#else
int pout[
2];
if(pipe(pout)) {
perror(
"pipe");
return 1;
}
int ret =
0;
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init(&actions);
posix_spawn_file_actions_addclose(&actions,
0);
posix_spawn_file_actions_adddup2(&actions, pout[
1],
1);
posix_spawn_file_actions_addclose(&actions,
2);
char *args[
4];
args[
0] =
"sh";
args[
1] =
"-c";
args[
2] = command;
args[
3] =
NULL;
pid_t pid;
ret = posix_spawn(&pid,
"/bin/sh", &actions,
NULL, args,
NULL);
close(pout[
1]);
if(!ret) {
ssize_t r;
char buf[
1024];
while((r = read(pout[
0], buf,
1024)) >
0) {
cxBufferWrite(buf,
1, r, outbuf);
}
}
ret =
1;
waitpid(pid, &ret,
0);
posix_spawn_file_actions_destroy(&actions);
close(pout[
0]);
return ret;
#endif
}
char* util_hexstr(
const unsigned char *data,
size_t len) {
size_t buflen =
2*len +
4;
CxBuffer buf;
cxBufferInit(&buf,
NULL, buflen +
1, cxDefaultAllocator,
CX_BUFFER_FREE_CONTENTS|
CX_BUFFER_AUTO_EXTEND);
for(
int i=
0;i<len;i++) {
cx_bprintf(&buf,
"%02x", data[i]);
}
cxBufferPut(&buf,
0);
return buf.space;
}
void util_remove_trailing_pathseparator(
char *path) {
size_t len = strlen(path);
if(len <
2) {
return;
}
if(path[len-
1] ==
'/') {
path[len-
1] =
'\0';
}
}
char* util_file_hash(
const char *path) {
FILE *in = fopen(path,
"r");
if(!in) {
return NULL;
}
DAV_SHA_CTX *sha = dav_hash_init();
char *buf = malloc(
16384);
size_t r;
while((r = fread(buf,
1,
16384, in)) >
0) {
dav_hash_update(sha, buf, r);
}
unsigned char hash[
DAV_SHA256_DIGEST_LENGTH];
dav_hash_final(sha, hash);
free(buf);
fclose(in);
return util_hexstr(hash,
DAV_SHA256_DIGEST_LENGTH);
}