kore

Kore is a web application platform for writing scalable, concurrent web based processes in C or Python.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

utils.c (14050B)



      1 /*
      2  * Copyright (c) 2013-2022 Joris Vink <joris@coders.se>
      3  *
      4  * Permission to use, copy, modify, and distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <sys/types.h>
     18 #include <sys/time.h>
     19 
     20 #include <ctype.h>
     21 #include <stdio.h>
     22 #include <stdarg.h>
     23 #include <string.h>
     24 #include <stdlib.h>
     25 #include <time.h>
     26 #include <limits.h>
     27 
     28 #include "kore.h"
     29 
     30 static struct {
     31 	char		*name;
     32 	int		value;
     33 } month_names[] = {
     34 	{ "Jan",	0 },
     35 	{ "Feb",	1 },
     36 	{ "Mar",	2 },
     37 	{ "Apr",	3 },
     38 	{ "May",	4 },
     39 	{ "Jun",	5 },
     40 	{ "Jul",	6 },
     41 	{ "Aug",	7 },
     42 	{ "Sep",	8 },
     43 	{ "Oct",	9 },
     44 	{ "Nov",	10 },
     45 	{ "Dec",	11 },
     46 	{ NULL,		0 },
     47 };
     48 
     49 static void	fatal_log(const char *, va_list);
     50 static int	utils_base64_encode(const void *, size_t, char **,
     51 		    const char *, int);
     52 static int	utils_base64_decode(const char *, u_int8_t **,
     53 		    size_t *, const char *, int);
     54 static int	utils_x509name_tobuf(void *, int, int, const char *,
     55 		    const void *, size_t, int);
     56 
     57 static char b64_table[] = 	\
     58     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
     59 
     60 static char b64url_table[] = 	\
     61     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
     62 
     63 /* b64_table and b64url_table are the same size. */
     64 #define B64_TABLE_LEN		(sizeof(b64_table))
     65 
     66 size_t
     67 kore_strlcpy(char *dst, const char *src, const size_t len)
     68 {
     69 	char		*d = dst;
     70 	const char	*s = src;
     71 	const char	*end = dst + len - 1;
     72 
     73 	if (len == 0)
     74 		fatal("kore_strlcpy: len == 0");
     75 
     76 	while ((*d = *s) != '\0') {
     77 		if (d == end) {
     78 			*d = '\0';
     79 			break;
     80 		}
     81 
     82 		d++;
     83 		s++;
     84 	}
     85 
     86 	while (*s != '\0')
     87 		s++;
     88 
     89 	return (s - src);
     90 }
     91 
     92 int
     93 kore_snprintf(char *str, size_t size, int *len, const char *fmt, ...)
     94 {
     95 	int		l;
     96 	va_list		args;
     97 
     98 	va_start(args, fmt);
     99 	l = vsnprintf(str, size, fmt, args);
    100 	va_end(args);
    101 
    102 	if (l == -1 || (size_t)l >= size)
    103 		return (KORE_RESULT_ERROR);
    104 
    105 	if (len != NULL)
    106 		*len = l;
    107 
    108 	return (KORE_RESULT_OK);
    109 }
    110 
    111 long long
    112 kore_strtonum(const char *str, int base, long long min, long long max, int *err)
    113 {
    114 	long long	l;
    115 	char		*ep;
    116 
    117 	if (min > max) {
    118 		*err = KORE_RESULT_ERROR;
    119 		return (0);
    120 	}
    121 
    122 	errno = 0;
    123 	l = strtoll(str, &ep, base);
    124 	if (errno != 0 || str == ep || *ep != '\0') {
    125 		*err = KORE_RESULT_ERROR;
    126 		return (0);
    127 	}
    128 
    129 	if (l < min) {
    130 		*err = KORE_RESULT_ERROR;
    131 		return (0);
    132 	}
    133 
    134 	if (l > max) {
    135 		*err = KORE_RESULT_ERROR;
    136 		return (0);
    137 	}
    138 
    139 	*err = KORE_RESULT_OK;
    140 	return (l);
    141 }
    142 
    143 u_int64_t
    144 kore_strtonum64(const char *str, int sign, int *err)
    145 {
    146 	u_int64_t	l;
    147 	long long	ll;
    148 	char		*ep;
    149 	int		check;
    150 
    151 	l = 0;
    152 	check = 1;
    153 
    154 	ll = strtoll(str, &ep, 10);
    155 	if ((errno == EINVAL || errno == ERANGE) &&
    156 	    (ll == LLONG_MIN || ll == LLONG_MAX)) {
    157 		if (sign) {
    158 			*err = KORE_RESULT_ERROR;
    159 			return (0);
    160 		}
    161 
    162 		check = 0;
    163 	}
    164 
    165 	if (!sign) {
    166 		l = strtoull(str, &ep, 10);
    167 		if ((errno == EINVAL || errno == ERANGE) && l == ULONG_MAX) {
    168 			*err = KORE_RESULT_ERROR;
    169 			return (0);
    170 		}
    171 
    172 		if (check && ll < 0) {
    173 			*err = KORE_RESULT_ERROR;
    174 			return (0);
    175 		}
    176 	}
    177 
    178 	if (str == ep || *ep != '\0') {
    179 		*err = KORE_RESULT_ERROR;
    180 		return (0);
    181 	}
    182 
    183 	*err = KORE_RESULT_OK;
    184 	return ((sign) ? (u_int64_t)ll : l);
    185 }
    186 
    187 double
    188 kore_strtodouble(const char *str, long double min, long double max, int *err)
    189 {
    190 	double		d;
    191 	char		*ep;
    192 
    193 	if (min > max) {
    194 		*err = KORE_RESULT_ERROR;
    195 		return (0);
    196 	}
    197 
    198 	errno = 0;
    199 	d = strtod(str, &ep);
    200 	if (errno == ERANGE || str == ep || *ep != '\0') {
    201 		*err = KORE_RESULT_ERROR;
    202 		return (0);
    203 	}
    204 
    205 	if (d < min) {
    206 		*err = KORE_RESULT_ERROR;
    207 		return (0);
    208 	}
    209 
    210 	if (d > max) {
    211 		*err = KORE_RESULT_ERROR;
    212 		return (0);
    213 	}
    214 
    215 	*err = KORE_RESULT_OK;
    216 	return (d);
    217 }
    218 
    219 int
    220 kore_split_string(char *input, const char *delim, char **out, size_t ele)
    221 {
    222 	int		count;
    223 	char		**ap;
    224 
    225 	if (ele == 0)
    226 		return (0);
    227 
    228 	count = 0;
    229 	for (ap = out; ap < &out[ele - 1] &&
    230 	    (*ap = strsep(&input, delim)) != NULL;) {
    231 		if (**ap != '\0') {
    232 			ap++;
    233 			count++;
    234 		}
    235 	}
    236 
    237 	*ap = NULL;
    238 	return (count);
    239 }
    240 
    241 void
    242 kore_strip_chars(const char *in, const char strip, char **out)
    243 {
    244 	char		*p;
    245 	const char	*s;
    246 	u_int32_t	len;
    247 
    248 	len = strlen(in);
    249 	*out = kore_malloc(len + 1);
    250 	p = *out;
    251 
    252 	for (s = in; s < (in + len); s++) {
    253 		if (*s == strip)
    254 			continue;
    255 
    256 		*p++ = *s;
    257 	}
    258 
    259 	*p = '\0';
    260 }
    261 
    262 time_t
    263 kore_date_to_time(const char *http_date)
    264 {
    265 	time_t			t;
    266 	int			err, i;
    267 	struct tm		tm, *ltm;
    268 	char			*args[7], *tbuf[5], *sdup;
    269 
    270 	time(&t);
    271 	ltm = localtime(&t);
    272 	sdup = kore_strdup(http_date);
    273 
    274 	t = KORE_RESULT_ERROR;
    275 
    276 	if (kore_split_string(sdup, " ", args, 7) != 6) {
    277 		kore_log(LOG_WARNING, "misformed http-date: '%s'", http_date);
    278 		goto out;
    279 	}
    280 
    281 	memset(&tm, 0, sizeof(tm));
    282 
    283 	tm.tm_year = kore_strtonum(args[3], 10, 1900, 2068, &err) - 1900;
    284 	if (err == KORE_RESULT_ERROR) {
    285 		kore_log(LOG_WARNING, "misformed year in http-date: '%s'",
    286 		    http_date);
    287 		goto out;
    288 	}
    289 
    290 	for (i = 0; month_names[i].name != NULL; i++) {
    291 		if (!strcmp(month_names[i].name, args[2])) {
    292 			tm.tm_mon = month_names[i].value;
    293 			break;
    294 		}
    295 	}
    296 
    297 	if (month_names[i].name == NULL) {
    298 		kore_log(LOG_WARNING, "misformed month in http-date: '%s'",
    299 		    http_date);
    300 		goto out;
    301 	}
    302 
    303 	tm.tm_mday = kore_strtonum(args[1], 10, 1, 31, &err);
    304 	if (err == KORE_RESULT_ERROR) {
    305 		kore_log(LOG_WARNING, "misformed mday in http-date: '%s'",
    306 		    http_date);
    307 		goto out;
    308 	}
    309 
    310 	if (kore_split_string(args[4], ":", tbuf, 5) != 3) {
    311 		kore_log(LOG_WARNING, "misformed HH:MM:SS in http-date: '%s'",
    312 		    http_date);
    313 		goto out;
    314 	}
    315 
    316 	tm.tm_hour = kore_strtonum(tbuf[0], 10, 0, 23, &err);
    317 	if (err == KORE_RESULT_ERROR) {
    318 		kore_log(LOG_WARNING, "misformed hour in http-date: '%s'",
    319 		    http_date);
    320 		goto out;
    321 	}
    322 
    323 	tm.tm_min = kore_strtonum(tbuf[1], 10, 0, 59, &err);
    324 	if (err == KORE_RESULT_ERROR) {
    325 		kore_log(LOG_WARNING, "misformed minutes in http-date: '%s'",
    326 		    http_date);
    327 		goto out;
    328 	}
    329 
    330 	tm.tm_sec = kore_strtonum(tbuf[2], 10, 0, 60, &err);
    331 	if (err == KORE_RESULT_ERROR) {
    332 		kore_log(LOG_WARNING, "misformed seconds in http-date: '%s'",
    333 		    http_date);
    334 		goto out;
    335 	}
    336 
    337 	tm.tm_isdst = ltm->tm_isdst;
    338 	t = mktime(&tm) + ltm->tm_gmtoff;
    339 	if (t == -1) {
    340 		t = 0;
    341 		kore_log(LOG_WARNING, "mktime() on '%s' failed", http_date);
    342 	}
    343 
    344 out:
    345 	kore_free(sdup);
    346 	return (t);
    347 }
    348 
    349 char *
    350 kore_time_to_date(time_t now)
    351 {
    352 	struct tm		*tm;
    353 	static time_t		last = 0;
    354 	static char		tbuf[32];
    355 
    356 	if (now != last) {
    357 		last = now;
    358 
    359 		tm = gmtime(&now);
    360 		if (!strftime(tbuf, sizeof(tbuf), "%a, %d %b %Y %T GMT", tm))
    361 			return (NULL);
    362 	}
    363 
    364 	return (tbuf);
    365 }
    366 
    367 u_int64_t
    368 kore_time_ms(void)
    369 {
    370 	struct timespec		ts;
    371 
    372 	(void)clock_gettime(CLOCK_MONOTONIC, &ts);
    373 
    374 	return ((u_int64_t)(ts.tv_sec * 1000 + (ts.tv_nsec / 1000000)));
    375 }
    376 
    377 int
    378 kore_base64url_encode(const void *data, size_t len, char **out, int flags)
    379 {
    380 	return (utils_base64_encode(data, len, out, b64url_table, flags));
    381 }
    382 
    383 int
    384 kore_base64_encode(const void *data, size_t len, char **out)
    385 {
    386 	return (utils_base64_encode(data, len, out, b64_table, 0));
    387 }
    388 
    389 int
    390 kore_base64url_decode(const char *in, u_int8_t **out, size_t *olen, int flags)
    391 {
    392 	return (utils_base64_decode(in, out, olen, b64url_table, flags));
    393 }
    394 
    395 int
    396 kore_base64_decode(const char *in, u_int8_t **out, size_t *olen)
    397 {
    398 	return (utils_base64_decode(in, out, olen, b64_table, 0));
    399 }
    400 
    401 void *
    402 kore_mem_find(void *src, size_t slen, const void *needle, size_t len)
    403 {
    404 	size_t		pos;
    405 
    406 	for (pos = 0; pos < slen; pos++) {
    407 		if ( *((u_int8_t *)src + pos) != *(const u_int8_t *)needle)
    408 			continue;
    409 
    410 		if ((slen - pos) < len)
    411 			return (NULL);
    412 
    413 		if (!memcmp((u_int8_t *)src + pos, needle, len))
    414 			return ((u_int8_t *)src + pos);
    415 	}
    416 
    417 	return (NULL);
    418 }
    419 
    420 char *
    421 kore_text_trim(char *string, size_t len)
    422 {
    423 	char		*end;
    424 
    425 	if (len == 0)
    426 		return (string);
    427 
    428 	end = (string + len) - 1;
    429 	while (isspace(*(unsigned char *)string) && string < end)
    430 		string++;
    431 
    432 	while (isspace(*(unsigned char *)end) && end > string)
    433 		*(end)-- = '\0';
    434 
    435 	return (string);
    436 }
    437 
    438 char *
    439 kore_read_line(FILE *fp, char *in, size_t len)
    440 {
    441 	char	*p, *t;
    442 
    443 	if (fgets(in, len, fp) == NULL)
    444 		return (NULL);
    445 
    446 	p = in;
    447 	in[strcspn(in, "\n")] = '\0';
    448 
    449 	while (isspace(*(unsigned char *)p))
    450 		p++;
    451 
    452 	if (p[0] == '#' || p[0] == '\0') {
    453 		p[0] = '\0';
    454 		return (p);
    455 	}
    456 
    457 	for (t = p; *t != '\0'; t++) {
    458 		if (*t == '\t')
    459 			*t = ' ';
    460 	}
    461 
    462 	return (p);
    463 }
    464 
    465 const char *
    466 kore_worker_name(int id)
    467 {
    468 	static char	buf[64];
    469 
    470 	switch (id) {
    471 	case KORE_WORKER_KEYMGR:
    472 		(void)snprintf(buf, sizeof(buf), "[keymgr]");
    473 		break;
    474 	case KORE_WORKER_ACME:
    475 		(void)snprintf(buf, sizeof(buf), "[acme]");
    476 		break;
    477 	default:
    478 		(void)snprintf(buf, sizeof(buf), "[wrk %d]", id);
    479 		break;
    480 	}
    481 
    482 	return (buf);
    483 }
    484 
    485 int
    486 kore_x509_issuer_name(struct connection *c, char **out, int flags)
    487 {
    488 	struct kore_buf		buf;
    489 	KORE_X509_NAMES		*name;
    490 
    491 	if ((name = kore_tls_x509_issuer_name(c)) == NULL)
    492 		return (KORE_RESULT_ERROR);
    493 
    494 	kore_buf_init(&buf, 1024);
    495 
    496 	if (!kore_tls_x509name_foreach(name, flags, &buf,
    497 	    utils_x509name_tobuf)) {
    498 		kore_buf_cleanup(&buf);
    499 		return (KORE_RESULT_ERROR);
    500 	}
    501 
    502 	*out = kore_buf_stringify(&buf, NULL);
    503 
    504 	buf.offset = 0;
    505 	buf.data = NULL;
    506 
    507 	return (KORE_RESULT_OK);
    508 }
    509 
    510 int
    511 kore_x509_subject_name(struct connection *c, char **out, int flags)
    512 {
    513 	struct kore_buf		buf;
    514 	KORE_X509_NAMES		*name;
    515 
    516 	if ((name = kore_tls_x509_subject_name(c)) == NULL)
    517 		return (KORE_RESULT_ERROR);
    518 
    519 	kore_buf_init(&buf, 1024);
    520 
    521 	if (!kore_tls_x509name_foreach(name, flags, &buf,
    522 	    utils_x509name_tobuf)) {
    523 		kore_buf_cleanup(&buf);
    524 		return (KORE_RESULT_ERROR);
    525 	}
    526 
    527 	*out = kore_buf_stringify(&buf, NULL);
    528 
    529 	buf.offset = 0;
    530 	buf.data = NULL;
    531 
    532 	return (KORE_RESULT_OK);
    533 }
    534 
    535 void
    536 fatal(const char *fmt, ...)
    537 {
    538 	va_list		args;
    539 
    540 	va_start(args, fmt);
    541 	fatal_log(fmt, args);
    542 	va_end(args);
    543 
    544 	exit(1);
    545 }
    546 
    547 void
    548 fatalx(const char *fmt, ...)
    549 {
    550 	va_list		args;
    551 
    552 	/* In case people call fatalx() from the parent context. */
    553 	if (worker != NULL)
    554 		kore_msg_send(KORE_MSG_PARENT, KORE_MSG_FATALX, NULL, 0);
    555 
    556 	va_start(args, fmt);
    557 	fatal_log(fmt, args);
    558 	va_end(args);
    559 
    560 	exit(1);
    561 }
    562 
    563 static void
    564 fatal_log(const char *fmt, va_list args)
    565 {
    566 	char			buf[2048];
    567 
    568 	(void)vsnprintf(buf, sizeof(buf), fmt, args);
    569 	kore_log(LOG_ERR, "fatal: %s", buf);
    570 
    571 	if (worker != NULL && worker->id == KORE_WORKER_KEYMGR)
    572 		kore_keymgr_cleanup(1);
    573 }
    574 
    575 static int
    576 utils_x509name_tobuf(void *udata, int islast, int nid, const char *field,
    577     const void *data, size_t len, int flags)
    578 {
    579 	struct kore_buf		*buf = udata;
    580 
    581 	if (flags & KORE_X509_COMMON_NAME_ONLY) {
    582 		if (nid == KORE_X509_NAME_COMMON_NAME)
    583 			kore_buf_append(buf, data, len);
    584 	} else {
    585 		kore_buf_appendf(buf, "%s=", field);
    586 		kore_buf_append(buf, data, len);
    587 		if (!islast)
    588 			kore_buf_appendf(buf, " ");
    589 	}
    590 
    591 	return (KORE_RESULT_OK);
    592 }
    593 
    594 static int
    595 utils_base64_encode(const void *data, size_t len, char **out,
    596     const char *table, int flags)
    597 {
    598 	u_int8_t		n;
    599 	size_t			nb;
    600 	const u_int8_t		*ptr;
    601 	u_int32_t		bytes;
    602 	struct kore_buf		result;
    603 
    604 	nb = 0;
    605 	ptr = data;
    606 	kore_buf_init(&result, (len / 3) * 4);
    607 
    608 	while (len > 0) {
    609 		if (len > 2) {
    610 			nb = 3;
    611 			bytes = *ptr++ << 16;
    612 			bytes |= *ptr++ << 8;
    613 			bytes |= *ptr++;
    614 		} else if (len > 1) {
    615 			nb = 2;
    616 			bytes = *ptr++ << 16;
    617 			bytes |= *ptr++ << 8;
    618 		} else if (len == 1) {
    619 			nb = 1;
    620 			bytes = *ptr++ << 16;
    621 		} else {
    622 			kore_buf_cleanup(&result);
    623 			return (KORE_RESULT_ERROR);
    624 		}
    625 
    626 		n = (bytes >> 18) & 0x3f;
    627 		kore_buf_append(&result, &(table[n]), 1);
    628 		n = (bytes >> 12) & 0x3f;
    629 		kore_buf_append(&result, &(table[n]), 1);
    630 		if (nb > 1) {
    631 			n = (bytes >> 6) & 0x3f;
    632 			kore_buf_append(&result, &(table[n]), 1);
    633 			if (nb > 2) {
    634 				n = bytes & 0x3f;
    635 				kore_buf_append(&result, &(table[n]), 1);
    636 			}
    637 		}
    638 
    639 		len -= nb;
    640 	}
    641 
    642 	if (!(flags & KORE_BASE64_RAW)) {
    643 		switch (nb) {
    644 		case 1:
    645 			kore_buf_appendf(&result, "==");
    646 			break;
    647 		case 2:
    648 			kore_buf_appendf(&result, "=");
    649 			break;
    650 		case 3:
    651 			break;
    652 		default:
    653 			kore_buf_cleanup(&result);
    654 			return (KORE_RESULT_ERROR);
    655 		}
    656 	}
    657 
    658 	/* result.data gets taken over so no need to cleanup result. */
    659 	*out = kore_buf_stringify(&result, NULL);
    660 
    661 	return (KORE_RESULT_OK);
    662 }
    663 
    664 static int
    665 utils_base64_decode(const char *in, u_int8_t **out, size_t *olen,
    666     const char *table, int flags)
    667 {
    668 	int			i, c;
    669 	u_int8_t		d, n, o;
    670 	struct kore_buf		*res, buf;
    671 	const char		*ptr, *pad;
    672 	u_int32_t		b, len, plen, idx;
    673 
    674 	i = 4;
    675 	b = 0;
    676 	d = 0;
    677 	c = 0;
    678 	len = strlen(in);
    679 	memset(&buf, 0, sizeof(buf));
    680 
    681 	if (flags & KORE_BASE64_RAW) {
    682 		switch (len % 4) {
    683 		case 2:
    684 			plen = 2;
    685 			pad = "==";
    686 			break;
    687 		case 3:
    688 			plen = 1;
    689 			pad = "=";
    690 			break;
    691 		default:
    692 			return (KORE_RESULT_ERROR);
    693 		}
    694 
    695 		kore_buf_init(&buf, len + plen);
    696 		kore_buf_append(&buf, in, len);
    697 		kore_buf_append(&buf, pad, plen);
    698 
    699 		len = len + plen;
    700 		ptr = (const char *)buf.data;
    701 	} else {
    702 		ptr = in;
    703 	}
    704 
    705 	res = kore_buf_alloc(len);
    706 
    707 	for (idx = 0; idx < len; idx++) {
    708 		c = ptr[idx];
    709 		if (c == '=')
    710 			break;
    711 
    712 		for (o = 0; o < B64_TABLE_LEN; o++) {
    713 			if (table[o] == c) {
    714 				d = o;
    715 				break;
    716 			}
    717 		}
    718 
    719 		if (o == B64_TABLE_LEN) {
    720 			*out = NULL;
    721 			kore_buf_free(res);
    722 			kore_buf_cleanup(&buf);
    723 			return (KORE_RESULT_ERROR);
    724 		}
    725 
    726 		b |= (d & 0x3f) << ((i - 1) * 6);
    727 		i--;
    728 		if (i == 0) {
    729 			for (i = 2; i >= 0; i--) {
    730 				n = (b >> (8 * i));
    731 				kore_buf_append(res, &n, 1);
    732 			}
    733 
    734 			b = 0;
    735 			i = 4;
    736 		}
    737 	}
    738 
    739 	if (c == '=') {
    740 		if (i > 2) {
    741 			*out = NULL;
    742 			kore_buf_free(res);
    743 			kore_buf_cleanup(&buf);
    744 			return (KORE_RESULT_ERROR);
    745 		}
    746 
    747 		o = i;
    748 		for (i = 2; i >= o; i--) {
    749 			n = (b >> (8 * i));
    750 			kore_buf_append(res, &n, 1);
    751 		}
    752 	}
    753 
    754 	kore_buf_cleanup(&buf);
    755 	*out = kore_buf_release(res, olen);
    756 
    757 	return (KORE_RESULT_OK);
    758 }