subrepo: subdir: "deps/nats.c" merged: "66cec7f" upstream: origin: "https://github.com/nats-io/nats.c.git" branch: "v3.6.1" commit: "66cec7f" git-subrepo: version: "0.4.6" commit: "b8b46501e"
2285 lines
62 KiB
C
2285 lines
62 KiB
C
// Copyright 2015-2021 The NATS Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "natsp.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
|
|
#include "util.h"
|
|
#include "mem.h"
|
|
|
|
int jsonMaxNested = JSON_MAX_NEXTED;
|
|
|
|
// A long double memory size is larger (or equal to) u/int64_t, so use that
|
|
// as the maximum size of a num element in an array.
|
|
#define JSON_MAX_NUM_SIZE ((int) sizeof(long double))
|
|
|
|
// Forward declarations due to recursive calls
|
|
static natsStatus _jsonParse(nats_JSON **newJSON, int *parsedLen, const char *jsonStr, int jsonLen, int nested);
|
|
static natsStatus _jsonParseValue(char **str, nats_JSONField *field, int nested);
|
|
static void _jsonFreeArray(nats_JSONArray *arr, bool freeObj);
|
|
|
|
#define JSON_GET_AS(jt, t) \
|
|
natsStatus s = NATS_OK; \
|
|
nats_JSONField *field = NULL; \
|
|
s = nats_JSONGetField(json, fieldName, (jt), &field); \
|
|
if ((s == NATS_OK) && (field == NULL)) \
|
|
{ \
|
|
*value = 0; \
|
|
return NATS_OK; \
|
|
} \
|
|
else if (s == NATS_OK) \
|
|
{ \
|
|
switch (field->numTyp) \
|
|
{ \
|
|
case TYPE_INT: \
|
|
*value = (t)field->value.vint; break; \
|
|
case TYPE_UINT: \
|
|
*value = (t)field->value.vuint; break; \
|
|
default: \
|
|
*value = (t)field->value.vdec; \
|
|
} \
|
|
} \
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
|
|
#define JSON_ARRAY_AS(t) \
|
|
int i; \
|
|
t* values = (t*) NATS_CALLOC(arr->size, sizeof(t)); \
|
|
if (values == NULL) \
|
|
return nats_setDefaultError(NATS_NO_MEMORY); \
|
|
for (i=0; i<arr->size; i++) \
|
|
values[i] = ((t*) arr->values)[i]; \
|
|
*array = values; \
|
|
*arraySize = arr->size; \
|
|
return NATS_OK;
|
|
|
|
#define JSON_ARRAY_AS_NUM(t) \
|
|
int i; \
|
|
t* values = (t*) NATS_CALLOC(arr->size, sizeof(t)); \
|
|
if (values == NULL) \
|
|
return nats_setDefaultError(NATS_NO_MEMORY); \
|
|
for (i=0; i<arr->size; i++) \
|
|
{ \
|
|
void *ptr = NULL; \
|
|
ptr = (void*) ((char*)(arr->values)+(i*JSON_MAX_NUM_SIZE)); \
|
|
values[i] = *(t*) ptr; \
|
|
} \
|
|
*array = values; \
|
|
*arraySize = arr->size; \
|
|
return NATS_OK;
|
|
|
|
#define JSON_GET_ARRAY(t, f) \
|
|
natsStatus s = NATS_OK; \
|
|
nats_JSONField *field = NULL; \
|
|
s = nats_JSONGetArrayField(json, fieldName, (t), &field); \
|
|
if ((s == NATS_OK) && (field == NULL)) \
|
|
{ \
|
|
*array = NULL; \
|
|
*arraySize = 0; \
|
|
return NATS_OK; \
|
|
} \
|
|
else if (s == NATS_OK) \
|
|
s = (f)(field->value.varr, array, arraySize); \
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
|
|
|
|
#define ASCII_0 (48)
|
|
#define ASCII_9 (57)
|
|
|
|
static char base32DecodeMap[256];
|
|
|
|
static const char *base64EncodeURL= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
static const char *base64EncodeStd= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
static char base64Padding = '=';
|
|
|
|
static int base64Ints[] = {
|
|
62, -1, -1, -1, 63, 52, 53, 54, 55, 56,
|
|
57, 58, 59, 60, 61, -1, -1, -1, -1, -1,
|
|
-1, -1, 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, -1, -1,
|
|
-1, -1, -1, -1, 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};
|
|
|
|
|
|
// An implementation of crc16 according to CCITT standards for XMODEM.
|
|
static uint16_t crc16tab[256] = {
|
|
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
|
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
|
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
|
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
|
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
|
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
|
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
|
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
|
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
|
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
|
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
|
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
|
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
|
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
|
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
|
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
|
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
|
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
|
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
|
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
|
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
|
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
|
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
|
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
|
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
|
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
|
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
|
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
|
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
|
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
|
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
|
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
|
|
};
|
|
|
|
// parseInt64 expects decimal positive numbers. We
|
|
// return -1 to signal error
|
|
int64_t
|
|
nats_ParseInt64(const char *d, int dLen)
|
|
{
|
|
int i;
|
|
char dec;
|
|
int64_t pn = 0;
|
|
int64_t n = 0;
|
|
|
|
if (dLen == 0)
|
|
return -1;
|
|
|
|
for (i=0; i<dLen; i++)
|
|
{
|
|
dec = d[i];
|
|
if ((dec < ASCII_0) || (dec > ASCII_9))
|
|
return -1;
|
|
|
|
n = (n * 10) + (int64_t)(dec - ASCII_0);
|
|
|
|
// Check overflow..
|
|
if (n < pn)
|
|
return -1;
|
|
|
|
pn = n;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
natsStatus
|
|
nats_Trim(char **pres, const char *s)
|
|
{
|
|
int len = 0;
|
|
char *res = NULL;
|
|
char *ptr = (char*) s;
|
|
char *start = (char*) s;
|
|
|
|
while ((*ptr != '\0') && isspace((unsigned char) *ptr))
|
|
ptr++;
|
|
|
|
start = ptr;
|
|
ptr = (char*) (s + strlen(s) - 1);
|
|
while ((ptr != start) && isspace((unsigned char) *ptr))
|
|
ptr--;
|
|
|
|
// Compute len of trimmed string
|
|
len = (int) (ptr-start) + 1;
|
|
|
|
// Allocate for copy (add 1 for terminating 0)
|
|
res = NATS_MALLOC(len+1);
|
|
if (res == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
memcpy(res, start, (size_t) len);
|
|
res[len] = '\0';
|
|
*pres = res;
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
natsStatus
|
|
nats_ParseControl(natsControl *control, const char *line)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
char *tok = NULL;
|
|
int len = 0;
|
|
|
|
if ((line == NULL) || (line[0] == '\0'))
|
|
return nats_setDefaultError(NATS_PROTOCOL_ERROR);
|
|
|
|
tok = strchr(line, (int) ' ');
|
|
if (tok == NULL)
|
|
{
|
|
control->op = NATS_STRDUP(line);
|
|
if (control->op == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
len = (int) (tok - line);
|
|
control->op = NATS_MALLOC(len + 1);
|
|
if (control->op == NULL)
|
|
{
|
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
|
}
|
|
else
|
|
{
|
|
memcpy(control->op, line, len);
|
|
control->op[len] = '\0';
|
|
}
|
|
|
|
if (s == NATS_OK)
|
|
{
|
|
// Discard all spaces and the like in between the next token
|
|
while ((tok[0] != '\0')
|
|
&& ((tok[0] == ' ')
|
|
|| (tok[0] == '\r')
|
|
|| (tok[0] == '\n')
|
|
|| (tok[0] == '\t')))
|
|
{
|
|
tok++;
|
|
}
|
|
}
|
|
|
|
// If there is a token...
|
|
if (tok[0] != '\0')
|
|
{
|
|
char *tmp;
|
|
|
|
len = (int) strlen(tok);
|
|
tmp = &(tok[len - 1]);
|
|
|
|
// Remove trailing spaces and the like.
|
|
while ((tmp[0] != '\0')
|
|
&& ((tmp[0] == ' ')
|
|
|| (tmp[0] == '\r')
|
|
|| (tmp[0] == '\n')
|
|
|| (tmp[0] == '\t')))
|
|
{
|
|
tmp--;
|
|
len--;
|
|
}
|
|
|
|
// We are sure that len is > 0 because of the first while() loop.
|
|
|
|
control->args = NATS_MALLOC(len + 1);
|
|
if (control->args == NULL)
|
|
{
|
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
|
}
|
|
else
|
|
{
|
|
memcpy(control->args, tok, len);
|
|
control->args[len] = '\0';
|
|
}
|
|
}
|
|
|
|
if (s != NATS_OK)
|
|
{
|
|
NATS_FREE(control->op);
|
|
control->op = NULL;
|
|
|
|
NATS_FREE(control->args);
|
|
control->args = NULL;
|
|
}
|
|
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_CreateStringFromBuffer(char **newStr, natsBuffer *buf)
|
|
{
|
|
char *str = NULL;
|
|
int len = 0;
|
|
|
|
if ((buf == NULL) || ((len = natsBuf_Len(buf)) == 0))
|
|
return NATS_OK;
|
|
|
|
str = NATS_MALLOC(len + 1);
|
|
if (str == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
memcpy(str, natsBuf_Data(buf), len);
|
|
str[len] = '\0';
|
|
|
|
*newStr = str;
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
void
|
|
nats_Sleep(int64_t millisec)
|
|
{
|
|
#ifdef _WIN32
|
|
Sleep((DWORD) millisec);
|
|
#else
|
|
usleep(millisec * 1000);
|
|
#endif
|
|
}
|
|
|
|
const char*
|
|
nats_GetBoolStr(bool value)
|
|
{
|
|
if (value)
|
|
return "true";
|
|
|
|
return "false";
|
|
}
|
|
|
|
void
|
|
nats_NormalizeErr(char *error)
|
|
{
|
|
int start = 0;
|
|
int end = 0;
|
|
int len = (int) strlen(error);
|
|
int i;
|
|
|
|
if (strncmp(error, _ERR_OP_, _ERR_OP_LEN_) == 0)
|
|
start = _ERR_OP_LEN_;
|
|
|
|
for (i=start; i<len; i++)
|
|
{
|
|
if ((error[i] != ' ') && (error[i] != '\''))
|
|
break;
|
|
}
|
|
|
|
start = i;
|
|
if (start == len)
|
|
{
|
|
error[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
for (end=len-1; end>0; end--)
|
|
{
|
|
char c = error[end];
|
|
if ((c == '\r') || (c == '\n') || (c == '\'') || (c == ' '))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if (end <= start)
|
|
{
|
|
error[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
len = end - start + 1;
|
|
memmove(error, error + start, len);
|
|
error[len] = '\0';
|
|
}
|
|
|
|
static natsStatus
|
|
_jsonCreateField(nats_JSONField **newField, char *fieldName)
|
|
{
|
|
nats_JSONField *field = NULL;
|
|
|
|
field = (nats_JSONField*) NATS_CALLOC(1, sizeof(nats_JSONField));
|
|
if (field == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
field->name = fieldName;
|
|
field->typ = TYPE_NOT_SET;
|
|
|
|
*newField = field;
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
static void
|
|
_jsonFreeArray(nats_JSONArray *arr, bool freeObj)
|
|
{
|
|
if (arr == NULL)
|
|
return;
|
|
|
|
if ((arr->typ == TYPE_OBJECT) || (arr->typ == TYPE_ARRAY))
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<arr->size; i++)
|
|
{
|
|
if (arr->typ == TYPE_OBJECT)
|
|
{
|
|
nats_JSON *fjson = ((nats_JSON**)arr->values)[i];
|
|
nats_JSONDestroy(fjson);
|
|
}
|
|
else
|
|
{
|
|
nats_JSONArray *farr = ((nats_JSONArray**)arr->values)[i];
|
|
_jsonFreeArray(farr, true);
|
|
}
|
|
}
|
|
}
|
|
NATS_FREE(arr->values);
|
|
if (freeObj)
|
|
NATS_FREE(arr);
|
|
}
|
|
|
|
static void
|
|
_jsonFreeField(nats_JSONField *field)
|
|
{
|
|
if (field->typ == TYPE_ARRAY)
|
|
_jsonFreeArray(field->value.varr, true);
|
|
else if (field->typ == TYPE_OBJECT)
|
|
nats_JSONDestroy(field->value.vobj);
|
|
NATS_FREE(field);
|
|
}
|
|
|
|
static char*
|
|
_jsonTrimSpace(char *ptr)
|
|
{
|
|
while ((*ptr != '\0')
|
|
&& ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')))
|
|
{
|
|
ptr += 1;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static natsStatus
|
|
_decodeUni(char **iPtr, char *val)
|
|
{
|
|
int res = 0;
|
|
char *i = *iPtr;
|
|
int j;
|
|
|
|
if (strlen(i) < 5)
|
|
return NATS_ERR;
|
|
|
|
i++;
|
|
for (j=0; j<4; j++)
|
|
{
|
|
char c = i[j];
|
|
if ((c >= '0') && (c <= '9'))
|
|
c = c - '0';
|
|
else if ((c >= 'a') && (c <= 'f'))
|
|
c = c - 'a' + 10;
|
|
else if ((c >= 'A') && (c <= 'F'))
|
|
c = c - 'A' + 10;
|
|
else
|
|
return NATS_ERR;
|
|
|
|
res = (res << 4) + c;
|
|
}
|
|
*val = (char) res;
|
|
*iPtr += 5;
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
static natsStatus
|
|
_jsonGetStr(char **ptr, char **value)
|
|
{
|
|
char *p = *ptr;
|
|
char *o = *ptr;
|
|
|
|
while ((*p != '\0') && (*p != '"'))
|
|
{
|
|
if (*p != '\\')
|
|
{
|
|
if (o != p)
|
|
*o = *p;
|
|
o++;
|
|
p++;
|
|
continue;
|
|
}
|
|
p++;
|
|
// Escaped character here...
|
|
if (*p == '\0')
|
|
{
|
|
*o = '\0';
|
|
return nats_setError(NATS_ERR,
|
|
"error parsing string '%s': invalid control character at the end",
|
|
o);
|
|
}
|
|
// based on what http://www.json.org/ says a string should be
|
|
switch (*p)
|
|
{
|
|
case 'b': *o++ = '\b'; break;
|
|
case 'f': *o++ = '\f'; break;
|
|
case 'n': *o++ = '\n'; break;
|
|
case 'r': *o++ = '\r'; break;
|
|
case 't': *o++ = '\t'; break;
|
|
case '"':
|
|
case '\\':
|
|
case '/':
|
|
*o++ = *p;
|
|
break;
|
|
case 'u':
|
|
{
|
|
char val = 0;
|
|
natsStatus s = _decodeUni(&p, &val);
|
|
if (s != NATS_OK)
|
|
{
|
|
return nats_setError(NATS_ERR,
|
|
"error parsing string '%s': invalid unicode character",
|
|
p);
|
|
}
|
|
*o++ = val;
|
|
p--;
|
|
break;
|
|
}
|
|
default:
|
|
return nats_setError(NATS_ERR,
|
|
"error parsing string '%s': invalid control character",
|
|
p);
|
|
}
|
|
p++;
|
|
}
|
|
|
|
if (*p != '\0')
|
|
{
|
|
*o = '\0';
|
|
*value = *ptr;
|
|
*ptr = (char*) (p + 1);
|
|
return NATS_OK;
|
|
}
|
|
return nats_setError(NATS_ERR,
|
|
"error parsing string '%s': unexpected end of JSON input",
|
|
*ptr);
|
|
}
|
|
|
|
static natsStatus
|
|
_jsonGetNum(char **ptr, nats_JSONField *field)
|
|
{
|
|
char *p = *ptr;
|
|
bool expIsNegative = false;
|
|
uint64_t uintVal = 0;
|
|
uint64_t decVal = 0;
|
|
uint64_t decPower = 1;
|
|
long double sign = 1.0;
|
|
long double ePower = 1.0;
|
|
int decPCount = 0;
|
|
int numTyp = 0;
|
|
|
|
while (isspace((unsigned char) *p))
|
|
p++;
|
|
|
|
sign = (*p == '-' ? -1.0 : 1.0);
|
|
|
|
if ((*p == '-') || (*p == '+'))
|
|
p++;
|
|
|
|
while (isdigit((unsigned char) *p))
|
|
uintVal = uintVal * 10 + (*p++ - '0');
|
|
|
|
if (*p == '.')
|
|
{
|
|
p++;
|
|
numTyp = TYPE_DOUBLE;
|
|
}
|
|
|
|
while (isdigit((unsigned char) *p))
|
|
{
|
|
decVal = decVal * 10 + (*p++ - '0');
|
|
decPower *= 10;
|
|
decPCount++;
|
|
}
|
|
|
|
if ((*p == 'e') || (*p == 'E'))
|
|
{
|
|
int64_t eVal = 0;
|
|
|
|
numTyp = TYPE_DOUBLE;
|
|
|
|
p++;
|
|
|
|
expIsNegative = (*p == '-' ? true : false);
|
|
|
|
if ((*p == '-') || (*p == '+'))
|
|
p++;
|
|
|
|
while (isdigit((unsigned char) *p))
|
|
eVal = eVal * 10 + (*p++ - '0');
|
|
|
|
if (expIsNegative)
|
|
{
|
|
if (decPower > 0)
|
|
ePower = (long double) decPower;
|
|
}
|
|
else
|
|
{
|
|
if (decPCount > eVal)
|
|
{
|
|
eVal = decPCount - eVal;
|
|
expIsNegative = true;
|
|
}
|
|
else
|
|
{
|
|
eVal -= decPCount;
|
|
}
|
|
}
|
|
while (eVal != 0)
|
|
{
|
|
ePower *= 10;
|
|
eVal--;
|
|
}
|
|
}
|
|
|
|
// If we don't end with a ' ', ',', ']', or '}', this is syntax error.
|
|
if ((*p != ' ') && (*p != ',') && (*p != '}') && (*p != ']'))
|
|
return nats_setError(NATS_ERR,
|
|
"error parsing number '%s': missing separator or unexpected end of JSON input",
|
|
*ptr);
|
|
|
|
if (numTyp == TYPE_DOUBLE)
|
|
{
|
|
long double res = 0.0;
|
|
|
|
if (decVal > 0)
|
|
res = sign * (long double) (uintVal * decPower + decVal);
|
|
else
|
|
res = sign * (long double) uintVal;
|
|
|
|
if (ePower > 1)
|
|
{
|
|
if (expIsNegative)
|
|
res /= ePower;
|
|
else
|
|
res *= ePower;
|
|
}
|
|
else if (decVal > 0)
|
|
{
|
|
res /= decPower;
|
|
}
|
|
field->value.vdec = res;
|
|
}
|
|
else if (sign < 0)
|
|
{
|
|
numTyp = TYPE_INT;
|
|
field->value.vint = -((int64_t) uintVal);
|
|
}
|
|
else
|
|
{
|
|
numTyp = TYPE_UINT;
|
|
field->value.vuint = uintVal;
|
|
}
|
|
*ptr = p;
|
|
field->numTyp = numTyp;
|
|
return NATS_OK;
|
|
}
|
|
|
|
static natsStatus
|
|
_jsonGetBool(char **ptr, bool *val)
|
|
{
|
|
if (strncmp(*ptr, "true", 4) == 0)
|
|
{
|
|
*val = true;
|
|
*ptr += 4;
|
|
return NATS_OK;
|
|
}
|
|
else if (strncmp(*ptr, "false", 5) == 0)
|
|
{
|
|
*val = false;
|
|
*ptr += 5;
|
|
return NATS_OK;
|
|
}
|
|
return nats_setError(NATS_ERR,
|
|
"error parsing boolean, got: '%s'", *ptr);
|
|
}
|
|
|
|
static natsStatus
|
|
_jsonGetArray(char **ptr, nats_JSONArray **newArray, int nested)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
char *p = *ptr;
|
|
bool end = false;
|
|
int typ = TYPE_NOT_SET;
|
|
nats_JSONField field;
|
|
nats_JSONArray array;
|
|
|
|
if (nested >= jsonMaxNested)
|
|
return nats_setError(NATS_ERR, "json reached maximum nested arrays of %d", jsonMaxNested);
|
|
|
|
// Initialize our stack variable
|
|
memset(&array, 0, sizeof(nats_JSONArray));
|
|
|
|
while ((s == NATS_OK) && (*p != '\0'))
|
|
{
|
|
p = _jsonTrimSpace(p);
|
|
|
|
if ((typ == TYPE_NOT_SET) && (*p == ']'))
|
|
{
|
|
array.typ = TYPE_NULL;
|
|
end = true;
|
|
break;
|
|
}
|
|
|
|
// Initialize the field before parsing.
|
|
memset(&field, 0, sizeof(nats_JSONField));
|
|
|
|
s = _jsonParseValue(&p, &field, nested);
|
|
if (s == NATS_OK)
|
|
{
|
|
if (typ == TYPE_NOT_SET)
|
|
{
|
|
typ = field.typ;
|
|
array.typ = field.typ;
|
|
|
|
// Set the element size based on type.
|
|
switch (typ)
|
|
{
|
|
case TYPE_STR: array.eltSize = sizeof(char*); break;
|
|
case TYPE_BOOL: array.eltSize = sizeof(bool); break;
|
|
case TYPE_NUM: array.eltSize = JSON_MAX_NUM_SIZE; break;
|
|
case TYPE_OBJECT: array.eltSize = sizeof(nats_JSON*); break;
|
|
case TYPE_ARRAY: array.eltSize = sizeof(nats_JSONArray*); break;
|
|
default:
|
|
s = nats_setError(NATS_ERR,
|
|
"array of type %d not supported", typ);
|
|
}
|
|
}
|
|
else if (typ != field.typ)
|
|
{
|
|
s = nats_setError(NATS_ERR,
|
|
"array content of different types '%s'",
|
|
*ptr);
|
|
}
|
|
}
|
|
if (s != NATS_OK)
|
|
break;
|
|
|
|
if (array.size + 1 > array.cap)
|
|
{
|
|
char **newValues = NULL;
|
|
int newCap = 2 * array.cap;
|
|
|
|
if (newCap == 0)
|
|
newCap = 4;
|
|
|
|
newValues = (char**) NATS_REALLOC(array.values, newCap * array.eltSize);
|
|
if (newValues == NULL)
|
|
{
|
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
|
break;
|
|
}
|
|
array.values = (void**) newValues;
|
|
array.cap = newCap;
|
|
}
|
|
// Set value based on type
|
|
switch (typ)
|
|
{
|
|
case TYPE_STR:
|
|
((char**)array.values)[array.size++] = field.value.vstr;
|
|
break;
|
|
case TYPE_BOOL:
|
|
((bool*)array.values)[array.size++] = field.value.vbool;
|
|
break;
|
|
case TYPE_NUM:
|
|
{
|
|
void *numPtr = NULL;
|
|
size_t sz = 0;
|
|
|
|
switch (field.numTyp)
|
|
{
|
|
case TYPE_INT:
|
|
numPtr = &(field.value.vint);
|
|
sz = sizeof(int64_t);
|
|
break;
|
|
case TYPE_UINT:
|
|
numPtr = &(field.value.vuint);
|
|
sz = sizeof(uint64_t);
|
|
break;
|
|
default:
|
|
numPtr = &(field.value.vdec);
|
|
sz = sizeof(long double);
|
|
}
|
|
memcpy((void*)(((char *)array.values)+(array.size*array.eltSize)), numPtr, sz);
|
|
array.size++;
|
|
break;
|
|
}
|
|
case TYPE_OBJECT:
|
|
((nats_JSON**)array.values)[array.size++] = field.value.vobj;
|
|
break;
|
|
case TYPE_ARRAY:
|
|
((nats_JSONArray**)array.values)[array.size++] = field.value.varr;
|
|
break;
|
|
}
|
|
|
|
p = _jsonTrimSpace(p);
|
|
if (*p == '\0')
|
|
break;
|
|
|
|
if (*p == ']')
|
|
{
|
|
end = true;
|
|
break;
|
|
}
|
|
else if (*p == ',')
|
|
{
|
|
p += 1;
|
|
}
|
|
else
|
|
{
|
|
s = nats_setError(NATS_ERR, "expected ',' got '%s'", p);
|
|
}
|
|
}
|
|
if ((s == NATS_OK) && !end)
|
|
{
|
|
s = nats_setError(NATS_ERR,
|
|
"unexpected end of array: '%s'",
|
|
(*p != '\0' ? p : "NULL"));
|
|
}
|
|
if (s == NATS_OK)
|
|
{
|
|
*newArray = NATS_MALLOC(sizeof(nats_JSONArray));
|
|
if (*newArray == NULL)
|
|
{
|
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
|
}
|
|
else
|
|
{
|
|
memcpy(*newArray, &array, sizeof(nats_JSONArray));
|
|
*ptr = (char*) (p + 1);
|
|
}
|
|
}
|
|
if (s != NATS_OK)
|
|
_jsonFreeArray(&array, false);
|
|
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
#define JSON_STATE_START (0)
|
|
#define JSON_STATE_NO_FIELD_YET (1)
|
|
#define JSON_STATE_FIELD (2)
|
|
#define JSON_STATE_SEPARATOR (3)
|
|
#define JSON_STATE_VALUE (4)
|
|
#define JSON_STATE_NEXT_FIELD (5)
|
|
#define JSON_STATE_END (6)
|
|
|
|
static natsStatus
|
|
_jsonParseValue(char **str, nats_JSONField *field, int nested)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
char *ptr = *str;
|
|
|
|
// Parsing value here. Determine the type based on first character.
|
|
if (*ptr == '"')
|
|
{
|
|
ptr += 1;
|
|
field->typ = TYPE_STR;
|
|
s = _jsonGetStr(&ptr, &field->value.vstr);
|
|
}
|
|
else if ((*ptr == 't') || (*ptr == 'f'))
|
|
{
|
|
field->typ = TYPE_BOOL;
|
|
s = _jsonGetBool(&ptr, &field->value.vbool);
|
|
}
|
|
else if (isdigit((unsigned char) *ptr) || (*ptr == '-'))
|
|
{
|
|
field->typ = TYPE_NUM;
|
|
s = _jsonGetNum(&ptr, field);
|
|
}
|
|
else if (*ptr == '[')
|
|
{
|
|
ptr += 1;
|
|
field->typ = TYPE_ARRAY;
|
|
s = _jsonGetArray(&ptr, &field->value.varr, nested+1);
|
|
}
|
|
else if (*ptr == '{')
|
|
{
|
|
nats_JSON *object = NULL;
|
|
int objLen = 0;
|
|
|
|
ptr += 1;
|
|
field->typ = TYPE_OBJECT;
|
|
s = _jsonParse(&object, &objLen, ptr, -1, nested+1);
|
|
if (s == NATS_OK)
|
|
{
|
|
field->value.vobj = object;
|
|
ptr += objLen;
|
|
}
|
|
}
|
|
else if ((*ptr == 'n') && (strstr(ptr, "null") == ptr))
|
|
{
|
|
ptr += 4;
|
|
field->typ = TYPE_NULL;
|
|
}
|
|
else
|
|
{
|
|
s = nats_setError(NATS_ERR,
|
|
"looking for value, got: '%s'", ptr);
|
|
}
|
|
if (s == NATS_OK)
|
|
*str = ptr;
|
|
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
static natsStatus
|
|
_jsonParse(nats_JSON **newJSON, int *parsedLen, const char *jsonStr, int jsonLen, int nested)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
nats_JSON *json = NULL;
|
|
nats_JSONField *field = NULL;
|
|
void *oldField = NULL;
|
|
char *ptr;
|
|
char *fieldName = NULL;
|
|
int state;
|
|
char *copyStr = NULL;
|
|
bool breakLoop = false;
|
|
|
|
if (parsedLen != NULL)
|
|
*parsedLen = 0;
|
|
|
|
if (nested >= jsonMaxNested)
|
|
return nats_setError(NATS_ERR, "json reached maximum nested objects of %d", jsonMaxNested);
|
|
|
|
if (jsonLen < 0)
|
|
{
|
|
if (jsonStr == NULL)
|
|
return nats_setDefaultError(NATS_INVALID_ARG);
|
|
|
|
jsonLen = (int) strlen(jsonStr);
|
|
}
|
|
|
|
json = NATS_CALLOC(1, sizeof(nats_JSON));
|
|
if (json == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
s = natsStrHash_Create(&(json->fields), 4);
|
|
if (s == NATS_OK)
|
|
{
|
|
json->str = NATS_MALLOC(jsonLen + 1);
|
|
if (json->str == NULL)
|
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
if (s == NATS_OK)
|
|
{
|
|
memcpy(json->str, jsonStr, jsonLen);
|
|
json->str[jsonLen] = '\0';
|
|
}
|
|
}
|
|
if (s != NATS_OK)
|
|
{
|
|
nats_JSONDestroy(json);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
ptr = json->str;
|
|
copyStr = NATS_STRDUP(ptr);
|
|
if (copyStr == NULL)
|
|
{
|
|
nats_JSONDestroy(json);
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
}
|
|
state = (nested == 0 ? JSON_STATE_START : JSON_STATE_NO_FIELD_YET);
|
|
|
|
while ((s == NATS_OK) && (*ptr != '\0') && !breakLoop)
|
|
{
|
|
ptr = _jsonTrimSpace(ptr);
|
|
if (*ptr == '\0')
|
|
break;
|
|
switch (state)
|
|
{
|
|
case JSON_STATE_START:
|
|
{
|
|
// Should be the start of the JSON string
|
|
if (*ptr != '{')
|
|
{
|
|
s = nats_setError(NATS_ERR, "incorrect JSON string: '%s'", ptr);
|
|
break;
|
|
}
|
|
ptr += 1;
|
|
state = JSON_STATE_NO_FIELD_YET;
|
|
break;
|
|
}
|
|
case JSON_STATE_NO_FIELD_YET:
|
|
case JSON_STATE_FIELD:
|
|
{
|
|
// Check for end, which is valid only in state == JSON_STATE_NO_FIELD_YET
|
|
if (*ptr == '}')
|
|
{
|
|
if (state == JSON_STATE_NO_FIELD_YET)
|
|
{
|
|
ptr += 1;
|
|
state = JSON_STATE_END;
|
|
break;
|
|
}
|
|
s = nats_setError(NATS_ERR,
|
|
"expected beginning of field, got: '%s'",
|
|
ptr);
|
|
break;
|
|
}
|
|
// Check for
|
|
// Should be the first quote of a field name
|
|
if (*ptr != '"')
|
|
{
|
|
s = nats_setError(NATS_ERR, "missing quote: '%s'", ptr);
|
|
break;
|
|
}
|
|
ptr += 1;
|
|
s = _jsonGetStr(&ptr, &fieldName);
|
|
if (s != NATS_OK)
|
|
break;
|
|
s = _jsonCreateField(&field, fieldName);
|
|
if (s != NATS_OK)
|
|
{
|
|
NATS_UPDATE_ERR_STACK(s);
|
|
break;
|
|
}
|
|
s = natsStrHash_Set(json->fields, fieldName, false, (void*) field, &oldField);
|
|
if (s != NATS_OK)
|
|
{
|
|
NATS_UPDATE_ERR_STACK(s);
|
|
break;
|
|
}
|
|
if (oldField != NULL)
|
|
{
|
|
NATS_FREE(oldField);
|
|
oldField = NULL;
|
|
}
|
|
state = JSON_STATE_SEPARATOR;
|
|
break;
|
|
}
|
|
case JSON_STATE_SEPARATOR:
|
|
{
|
|
// Should be the separation between field name and value.
|
|
if (*ptr != ':')
|
|
{
|
|
s = nats_setError(NATS_ERR, "missing value for field '%s': '%s'", fieldName, ptr);
|
|
break;
|
|
}
|
|
ptr += 1;
|
|
state = JSON_STATE_VALUE;
|
|
break;
|
|
}
|
|
case JSON_STATE_VALUE:
|
|
{
|
|
s = _jsonParseValue(&ptr, field, nested);
|
|
if (s == NATS_OK)
|
|
state = JSON_STATE_NEXT_FIELD;
|
|
break;
|
|
}
|
|
case JSON_STATE_NEXT_FIELD:
|
|
{
|
|
// We should have a ',' separator or be at the end of the string
|
|
if ((*ptr != ',') && (*ptr != '}'))
|
|
{
|
|
s = nats_setError(NATS_ERR, "missing separator: '%s' (%s)", ptr, copyStr);
|
|
break;
|
|
}
|
|
if (*ptr == ',')
|
|
state = JSON_STATE_FIELD;
|
|
else
|
|
state = JSON_STATE_END;
|
|
ptr += 1;
|
|
break;
|
|
}
|
|
case JSON_STATE_END:
|
|
{
|
|
if (nested > 0)
|
|
{
|
|
breakLoop = true;
|
|
break;
|
|
}
|
|
// If we are here it means that there was a character after the '}'
|
|
// so that's considered a failure.
|
|
s = nats_setError(NATS_ERR,
|
|
"invalid characters after end of JSON: '%s'",
|
|
ptr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (s == NATS_OK)
|
|
{
|
|
if (state != JSON_STATE_END)
|
|
s = nats_setError(NATS_ERR, "%s", "JSON string not properly closed");
|
|
}
|
|
if (s == NATS_OK)
|
|
{
|
|
if (parsedLen != NULL)
|
|
*parsedLen = (int) (ptr - json->str);
|
|
*newJSON = json;
|
|
}
|
|
else
|
|
nats_JSONDestroy(json);
|
|
|
|
NATS_FREE(copyStr);
|
|
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONParse(nats_JSON **newJSON, const char *jsonStr, int jsonLen)
|
|
{
|
|
natsStatus s = _jsonParse(newJSON, NULL, jsonStr, jsonLen, 0);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetField(nats_JSON *json, const char *fieldName, int fieldType, nats_JSONField **retField)
|
|
{
|
|
nats_JSONField *field = NULL;
|
|
|
|
field = (nats_JSONField*) natsStrHash_Get(json->fields, (char*) fieldName);
|
|
if ((field == NULL) || (field->typ == TYPE_NULL))
|
|
{
|
|
*retField = NULL;
|
|
return NATS_OK;
|
|
}
|
|
|
|
// Check parsed type matches what is being asked.
|
|
switch (fieldType)
|
|
{
|
|
case TYPE_INT:
|
|
case TYPE_UINT:
|
|
case TYPE_DOUBLE:
|
|
if (field->typ != TYPE_NUM)
|
|
return nats_setError(NATS_INVALID_ARG,
|
|
"Asked for field '%s' as type %d, but got type %d when parsing",
|
|
field->name, fieldType, field->typ);
|
|
break;
|
|
case TYPE_BOOL:
|
|
case TYPE_STR:
|
|
case TYPE_OBJECT:
|
|
if (field->typ != fieldType)
|
|
return nats_setError(NATS_INVALID_ARG,
|
|
"Asked for field '%s' as type %d, but got type %d when parsing",
|
|
field->name, fieldType, field->typ);
|
|
break;
|
|
default:
|
|
return nats_setError(NATS_INVALID_ARG,
|
|
"Asked for field '%s' as type %d, but this type does not exist",
|
|
field->name, fieldType);
|
|
}
|
|
*retField = field;
|
|
return NATS_OK;
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetStr(nats_JSON *json, const char *fieldName, char **value)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
nats_JSONField *field = NULL;
|
|
|
|
s = nats_JSONGetField(json, fieldName, TYPE_STR, &field);
|
|
if (s == NATS_OK)
|
|
{
|
|
if ((field == NULL) || (field->value.vstr == NULL))
|
|
{
|
|
*value = NULL;
|
|
return NATS_OK;
|
|
}
|
|
else
|
|
{
|
|
char *tmp = NATS_STRDUP(field->value.vstr);
|
|
if (tmp == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
*value = tmp;
|
|
}
|
|
}
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetStrPtr(nats_JSON *json, const char *fieldName, const char **str)
|
|
{
|
|
natsStatus s;
|
|
nats_JSONField *field = NULL;
|
|
|
|
s = nats_JSONGetField(json, fieldName, TYPE_STR, &field);
|
|
if (s == NATS_OK)
|
|
{
|
|
if (field == NULL)
|
|
*str = NULL;
|
|
else
|
|
*str = field->value.vstr;
|
|
}
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetBytes(nats_JSON *json, const char *fieldName, unsigned char **value, int *len)
|
|
{
|
|
natsStatus s;
|
|
const char *str = NULL;
|
|
|
|
*value = NULL;
|
|
*len = 0;
|
|
|
|
s = nats_JSONGetStrPtr(json, fieldName, &str);
|
|
if ((s == NATS_OK) && (str != NULL))
|
|
s = nats_Base64_Decode(str, value, len);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetInt(nats_JSON *json, const char *fieldName, int *value)
|
|
{
|
|
JSON_GET_AS(TYPE_INT, int);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetInt32(nats_JSON *json, const char *fieldName, int32_t *value)
|
|
{
|
|
JSON_GET_AS(TYPE_INT, int32_t);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetUInt16(nats_JSON *json, const char *fieldName, uint16_t *value)
|
|
{
|
|
JSON_GET_AS(TYPE_UINT, uint16_t);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetBool(nats_JSON *json, const char *fieldName, bool *value)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
nats_JSONField *field = NULL;
|
|
|
|
s = nats_JSONGetField(json, fieldName, TYPE_BOOL, &field);
|
|
if (s == NATS_OK)
|
|
{
|
|
*value = (field == NULL ? false : field->value.vbool);
|
|
return NATS_OK;
|
|
}
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetLong(nats_JSON *json, const char *fieldName, int64_t *value)
|
|
{
|
|
JSON_GET_AS(TYPE_INT, int64_t);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetULong(nats_JSON *json, const char *fieldName, uint64_t *value)
|
|
{
|
|
JSON_GET_AS(TYPE_UINT, uint64_t);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetDouble(nats_JSON *json, const char *fieldName, long double *value)
|
|
{
|
|
JSON_GET_AS(TYPE_DOUBLE, long double);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetObject(nats_JSON *json, const char *fieldName, nats_JSON **value)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
nats_JSONField *field = NULL;
|
|
|
|
s = nats_JSONGetField(json, fieldName, TYPE_OBJECT, &field);
|
|
if (s == NATS_OK)
|
|
{
|
|
*value = (field == NULL ? NULL : field->value.vobj);
|
|
return NATS_OK;
|
|
}
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_parseTime(char *orgStr, int64_t *timeUTC)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
char *dotPos = NULL;
|
|
char utcOff[7] = {'\0'};
|
|
int64_t nanosecs = 0;
|
|
char *p = NULL;
|
|
char timeStr[42] = {'\0'};
|
|
char tmpStr[36] = {'\0'};
|
|
char *str = NULL;
|
|
char offSign = '+';
|
|
int offHours = 0;
|
|
int offMin = 0;
|
|
int i, l;
|
|
struct tm tp;
|
|
|
|
// Check for "0"
|
|
if (strcmp(orgStr, "0001-01-01T00:00:00Z") == 0)
|
|
{
|
|
*timeUTC = 0;
|
|
return NATS_OK;
|
|
}
|
|
|
|
l = (int) strlen(orgStr);
|
|
// The smallest date/time should be: "YYYY:MM:DDTHH:MM:SSZ", which is 20
|
|
// while the longest should be: "YYYY:MM:DDTHH:MM:SS.123456789-12:34" which is 35
|
|
if ((l < 20) || (l > (int) (sizeof(tmpStr) - 1)))
|
|
{
|
|
if (l < 20)
|
|
s = nats_setError(NATS_INVALID_ARG, "time '%s' too small", orgStr);
|
|
else
|
|
s = nats_setError(NATS_INVALID_ARG, "time '%s' too long", orgStr);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
// Copy the user provided string in our temporary buffer since we may alter
|
|
// the string as we parse.
|
|
snprintf(tmpStr, sizeof(tmpStr), "%s", orgStr);
|
|
str = (char*) tmpStr;
|
|
memset(&tp, 0, sizeof(struct tm));
|
|
|
|
// If ends with 'Z', the time is already UTC
|
|
if ((str[l-1] == 'Z') || (str[l-1] == 'z'))
|
|
{
|
|
// Set the timezone to "+00:00"
|
|
snprintf(utcOff, sizeof(utcOff), "%s", "+00:00");
|
|
str[l-1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
// Make sure the UTC offset comes as "+12:34" (or "-12:34").
|
|
p = str+l-6;
|
|
if ((strlen(p) != 6) || ((*p != '+') && (*p != '-')) || (*(p+3) != ':'))
|
|
{
|
|
s = nats_setError(NATS_INVALID_ARG, "time '%s' has invalid UTC offset", orgStr);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
snprintf(utcOff, sizeof(utcOff), "%s", p);
|
|
// Set end of 'str' to beginning of the offset.
|
|
*p = '\0';
|
|
}
|
|
|
|
// Check if there is below seconds precision
|
|
dotPos = strstr(str, ".");
|
|
if (dotPos != NULL)
|
|
{
|
|
int64_t val = 0;
|
|
|
|
p = (char*) (dotPos+1);
|
|
// Need to recompute the length, since it has changed.
|
|
l = (int) strlen(p);
|
|
|
|
val = nats_ParseInt64((const char*) p, l);
|
|
if (val == -1)
|
|
{
|
|
s = nats_setError(NATS_INVALID_ARG, "time '%s' is invalid", orgStr);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
for (i=0; i<9-l; i++)
|
|
val *= 10;
|
|
|
|
if (val > 999999999)
|
|
{
|
|
s = nats_setError(NATS_INVALID_ARG, "time '%s' second fraction too big", orgStr);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
nanosecs = val;
|
|
// Set end of string at the place of the '.'
|
|
*dotPos = '\0';
|
|
}
|
|
|
|
snprintf(timeStr, sizeof(timeStr), "%s%s", str, utcOff);
|
|
if (sscanf(timeStr, "%4d-%2d-%2dT%2d:%2d:%2d%c%2d:%2d",
|
|
&tp.tm_year, &tp.tm_mon, &tp.tm_mday, &tp.tm_hour, &tp.tm_min, &tp.tm_sec,
|
|
&offSign, &offHours, &offMin) == 9)
|
|
{
|
|
int64_t res = 0;
|
|
int64_t off = 0;
|
|
|
|
tp.tm_year -= 1900;
|
|
tp.tm_mon--;
|
|
tp.tm_isdst = 0;
|
|
#ifdef _WIN32
|
|
res = (int64_t) _mkgmtime64(&tp);
|
|
#else
|
|
res = (int64_t) timegm(&tp);
|
|
#endif
|
|
if (res == -1)
|
|
{
|
|
s = nats_setError(NATS_ERR, "error parsing time '%s'", orgStr);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
// Compute the offset
|
|
off = (int64_t) ((offHours * 60 * 60) + (offMin * 60));
|
|
// If UTC offset is positive, then we need to remove to get down to UTC time,
|
|
// where as if negative, we need to add the offset to get up to UTC time.
|
|
if (offSign == '+')
|
|
off *= (int64_t) -1;
|
|
|
|
res *= (int64_t) 1E9;
|
|
res += (off * (int64_t) 1E9);
|
|
res += nanosecs;
|
|
*timeUTC = res;
|
|
}
|
|
else
|
|
{
|
|
s = nats_setError(NATS_ERR, "error parsing time '%s'", orgStr);
|
|
}
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetTime(nats_JSON *json, const char *fieldName, int64_t *timeUTC)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
char *str = NULL;
|
|
|
|
s = nats_JSONGetStr(json, fieldName, &str);
|
|
if ((s == NATS_OK) && (str == NULL))
|
|
{
|
|
*timeUTC = 0;
|
|
return NATS_OK;
|
|
}
|
|
else if (s != NATS_OK)
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
|
|
s = nats_parseTime(str, timeUTC);
|
|
NATS_FREE(str);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayField(nats_JSON *json, const char *fieldName, int fieldType, nats_JSONField **retField)
|
|
{
|
|
nats_JSONField *field = NULL;
|
|
|
|
field = (nats_JSONField*) natsStrHash_Get(json->fields, (char*) fieldName);
|
|
if ((field == NULL) || (field->typ == TYPE_NULL))
|
|
{
|
|
*retField = NULL;
|
|
return NATS_OK;
|
|
}
|
|
|
|
// Check parsed type matches what is being asked.
|
|
if (field->typ != TYPE_ARRAY)
|
|
return nats_setError(NATS_INVALID_ARG,
|
|
"Field '%s' is not an array, it has type: %d",
|
|
field->name, field->typ);
|
|
// If empty array, return NULL/OK
|
|
if (field->value.varr->typ == TYPE_NULL)
|
|
{
|
|
*retField = NULL;
|
|
return NATS_OK;
|
|
}
|
|
if (fieldType != field->value.varr->typ)
|
|
return nats_setError(NATS_INVALID_ARG,
|
|
"Asked for field '%s' as an array of type: %d, but it is an array of type: %d",
|
|
field->name, fieldType, field->typ);
|
|
|
|
*retField = field;
|
|
return NATS_OK;
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsStrings(nats_JSONArray *arr, char ***array, int *arraySize)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
int i;
|
|
|
|
char **values = NATS_CALLOC(arr->size, arr->eltSize);
|
|
if (values == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
for (i=0; i<arr->size; i++)
|
|
{
|
|
values[i] = NATS_STRDUP((char*)(arr->values[i]));
|
|
if (values[i] == NULL)
|
|
{
|
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
|
break;
|
|
}
|
|
}
|
|
if (s != NATS_OK)
|
|
{
|
|
int j;
|
|
|
|
for (j=0; j<i; j++)
|
|
NATS_FREE(values[i]);
|
|
|
|
NATS_FREE(values);
|
|
}
|
|
else
|
|
{
|
|
*array = values;
|
|
*arraySize = arr->size;
|
|
}
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayStr(nats_JSON *json, const char *fieldName, char ***array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_STR, nats_JSONArrayAsStrings);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsBools(nats_JSONArray *arr, bool **array, int *arraySize)
|
|
{
|
|
JSON_ARRAY_AS(bool);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayBool(nats_JSON *json, const char *fieldName, bool **array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_BOOL, nats_JSONArrayAsBools);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsDoubles(nats_JSONArray *arr, long double **array, int *arraySize)
|
|
{
|
|
JSON_ARRAY_AS_NUM(long double);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayDouble(nats_JSON *json, const char *fieldName, long double **array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_NUM, nats_JSONArrayAsDoubles);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsInts(nats_JSONArray *arr, int **array, int *arraySize)
|
|
{
|
|
JSON_ARRAY_AS_NUM(int);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayInt(nats_JSON *json, const char *fieldName, int **array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_NUM, nats_JSONArrayAsInts);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsLongs(nats_JSONArray *arr, int64_t **array, int *arraySize)
|
|
{
|
|
JSON_ARRAY_AS_NUM(int64_t);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayLong(nats_JSON *json, const char *fieldName, int64_t **array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_NUM, nats_JSONArrayAsLongs);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsULongs(nats_JSONArray *arr, uint64_t **array, int *arraySize)
|
|
{
|
|
JSON_ARRAY_AS_NUM(uint64_t);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayULong(nats_JSON *json, const char *fieldName, uint64_t **array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_NUM, nats_JSONArrayAsULongs);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsObjects(nats_JSONArray *arr, nats_JSON ***array, int *arraySize)
|
|
{
|
|
JSON_ARRAY_AS(nats_JSON*);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayObject(nats_JSON *json, const char *fieldName, nats_JSON ***array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_OBJECT, nats_JSONArrayAsObjects);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONArrayAsArrays(nats_JSONArray *arr, nats_JSONArray ***array, int *arraySize)
|
|
{
|
|
JSON_ARRAY_AS(nats_JSONArray*);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONGetArrayArray(nats_JSON *json, const char *fieldName, nats_JSONArray ***array, int *arraySize)
|
|
{
|
|
JSON_GET_ARRAY(TYPE_ARRAY, nats_JSONArrayAsArrays);
|
|
}
|
|
|
|
natsStatus
|
|
nats_JSONRange(nats_JSON *json, int expectedType, int expectedNumType, jsonRangeCB cb, void *userInfo)
|
|
{
|
|
natsStrHashIter iter;
|
|
char *fname = NULL;
|
|
void *val = NULL;
|
|
natsStatus s = NATS_OK;
|
|
|
|
natsStrHashIter_Init(&iter, json->fields);
|
|
while ((s == NATS_OK) && natsStrHashIter_Next(&iter, &fname, &val))
|
|
{
|
|
nats_JSONField *f = (nats_JSONField*) val;
|
|
|
|
if (f->typ != expectedType)
|
|
s = nats_setError(NATS_ERR, "field '%s': expected value type of %d, got %d",
|
|
f->name, expectedType, f->typ);
|
|
else if ((f->typ == TYPE_NUM) && (f->numTyp != expectedNumType))
|
|
s = nats_setError(NATS_ERR, "field '%s': expected numeric type of %d, got %d",
|
|
f->name, expectedNumType, f->numTyp);
|
|
else
|
|
s = cb(userInfo, (const char*) f->name, f);
|
|
}
|
|
natsStrHashIter_Done(&iter);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
void
|
|
nats_JSONDestroy(nats_JSON *json)
|
|
{
|
|
natsStrHashIter iter;
|
|
void *field = NULL;
|
|
|
|
if (json == NULL)
|
|
return;
|
|
|
|
natsStrHashIter_Init(&iter, json->fields);
|
|
while (natsStrHashIter_Next(&iter, NULL, &field))
|
|
{
|
|
natsStrHashIter_RemoveCurrent(&iter);
|
|
_jsonFreeField((nats_JSONField*) field);
|
|
}
|
|
natsStrHash_Destroy(json->fields);
|
|
NATS_FREE(json->str);
|
|
NATS_FREE(json);
|
|
}
|
|
|
|
natsStatus
|
|
nats_EncodeTimeUTC(char *buf, size_t bufLen, int64_t timeUTC)
|
|
{
|
|
int64_t t = timeUTC / (int64_t) 1E9;
|
|
int64_t ns = timeUTC - ((int64_t) t * (int64_t) 1E9);
|
|
struct tm tp;
|
|
int n;
|
|
|
|
// We will encode at most: "YYYY:MM:DDTHH:MM:SS.123456789+12:34"
|
|
// so we need at least 35+1 characters.
|
|
if (bufLen < 36)
|
|
return nats_setError(NATS_INVALID_ARG,
|
|
"buffer to encode UTC time is too small (%d), needs 36",
|
|
(int) bufLen);
|
|
|
|
if (timeUTC == 0)
|
|
{
|
|
snprintf(buf, bufLen, "%s", "0001-01-01T00:00:00Z");
|
|
return NATS_OK;
|
|
}
|
|
|
|
memset(&tp, 0, sizeof(struct tm));
|
|
#ifdef _WIN32
|
|
_gmtime64_s(&tp, (const __time64_t*) &t);
|
|
#else
|
|
gmtime_r((const time_t*) &t, &tp);
|
|
#endif
|
|
n = (int) strftime(buf, bufLen, "%FT%T", &tp);
|
|
if (n == 0)
|
|
return nats_setDefaultError(NATS_ERR);
|
|
|
|
if (ns > 0)
|
|
{
|
|
char nsBuf[15];
|
|
int i, nd;
|
|
|
|
nd = snprintf(nsBuf, sizeof(nsBuf), ".%" PRId64, ns);
|
|
for (; (nd > 0) && (nsBuf[nd-1] == '0'); )
|
|
nd--;
|
|
|
|
for (i=0; i<nd; i++)
|
|
*(buf+n++) = nsBuf[i];
|
|
}
|
|
*(buf+n) = 'Z';
|
|
*(buf+n+1) = '\0';
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
void
|
|
nats_Base32_Init(void)
|
|
{
|
|
const char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
|
int alphaLen = (int) strlen(alphabet);
|
|
int i;
|
|
|
|
for (i=0; i<(int)sizeof(base32DecodeMap); i++)
|
|
base32DecodeMap[i] = (char) 0xFF;
|
|
|
|
for (i=0; i<alphaLen; i++)
|
|
base32DecodeMap[(int)alphabet[i]] = (char) i;
|
|
}
|
|
|
|
natsStatus
|
|
nats_Base32_DecodeString(const char *src, char *dst, int dstMax, int *dstLen)
|
|
{
|
|
char *ptr = (char*) src;
|
|
int n = 0;
|
|
bool done = false;
|
|
int srcLen = (int) strlen(src);
|
|
int remaining = srcLen;
|
|
|
|
*dstLen = 0;
|
|
|
|
while (remaining > 0)
|
|
{
|
|
char dbuf[8];
|
|
int dLen = 8;
|
|
int j;
|
|
int needs;
|
|
|
|
for (j=0; j<8; )
|
|
{
|
|
int in;
|
|
|
|
if (remaining == 0)
|
|
{
|
|
dLen = j;
|
|
done = true;
|
|
break;
|
|
}
|
|
|
|
in = (int) *ptr;
|
|
ptr++;
|
|
remaining--;
|
|
|
|
dbuf[j] = base32DecodeMap[in];
|
|
// If invalid character, report the position but as the number of character
|
|
// since beginning, not array index.
|
|
if (dbuf[j] == (char) 0xFF)
|
|
return nats_setError(NATS_ERR, "base32: invalid data at location %d", srcLen - remaining);
|
|
j++;
|
|
}
|
|
|
|
needs = 0;
|
|
switch (dLen)
|
|
{
|
|
case 8: needs = 5; break;
|
|
case 7: needs = 4; break;
|
|
case 5: needs = 3; break;
|
|
case 4: needs = 2; break;
|
|
case 2: needs = 1; break;
|
|
}
|
|
if (n+needs > dstMax)
|
|
return nats_setError(NATS_INSUFFICIENT_BUFFER, "based32: needs %d bytes, max is %d", n+needs, dstMax);
|
|
|
|
if (dLen == 8)
|
|
dst[4] = dbuf[6]<<5 | dbuf[7];
|
|
if (dLen >= 7)
|
|
dst[3] = dbuf[4]<<7 | dbuf[5]<<2 | dbuf[6]>>3;
|
|
if (dLen >= 5)
|
|
dst[2] = dbuf[3]<<4 | dbuf[4]>>1;
|
|
if (dLen >= 4)
|
|
dst[1] = dbuf[1]<<6 | dbuf[2]<<1 | dbuf[3]>>4;
|
|
if (dLen >= 2)
|
|
dst[0] = dbuf[0]<<3 | dbuf[1]>>2;
|
|
|
|
n += needs;
|
|
|
|
if (!done)
|
|
dst += 5;
|
|
}
|
|
|
|
*dstLen = n;
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
static natsStatus
|
|
_base64Encode(const char *map, bool padding, const unsigned char *src, int srcLen, char **pDest)
|
|
{
|
|
char *dst = NULL;
|
|
int dstLen = 0;
|
|
int n;
|
|
int di = 0;
|
|
int si = 0;
|
|
int remain = 0;
|
|
uint32_t val = 0;
|
|
|
|
*pDest = NULL;
|
|
|
|
if ((src == NULL) || (src[0] == '\0'))
|
|
return NATS_OK;
|
|
|
|
n = srcLen;
|
|
if (padding)
|
|
dstLen = (n + 2) / 3 * 4;
|
|
else
|
|
dstLen = (n * 8 + 5) / 6;
|
|
dst = NATS_CALLOC(1, dstLen + 1);
|
|
if (dst == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
n = ((srcLen / 3) * 3);
|
|
for (si = 0; si < n; )
|
|
{
|
|
// Convert 3x 8bit source bytes into 4 bytes
|
|
val = (uint32_t)(src[si+0])<<16 | (uint32_t)(src[si+1])<<8 | (uint32_t)(src[si+2]);
|
|
|
|
dst[di+0] = map[val >> 18 & 0x3F];
|
|
dst[di+1] = map[val >> 12 & 0x3F];
|
|
dst[di+2] = map[val >> 6 & 0x3F];
|
|
dst[di+3] = map[val & 0x3F];
|
|
|
|
si += 3;
|
|
di += 4;
|
|
}
|
|
|
|
remain = srcLen - si;
|
|
if (remain == 0)
|
|
{
|
|
*pDest = dst;
|
|
return NATS_OK;
|
|
}
|
|
|
|
// Add the remaining small block
|
|
val = (uint32_t)src[si+0] << 16;
|
|
if (remain == 2)
|
|
val |= (uint32_t)src[si+1] << 8;
|
|
|
|
dst[di+0] = map[val >> 18 & 0x3F];
|
|
dst[di+1] = map[val >> 12 & 0x3F];
|
|
|
|
if (remain == 2)
|
|
{
|
|
dst[di+2] = map[val >> 6 & 0x3F];
|
|
if (padding)
|
|
dst[di+3] = base64Padding;
|
|
}
|
|
else if (padding && (remain == 1))
|
|
{
|
|
dst[di+2] = base64Padding;
|
|
dst[di+3] = base64Padding;
|
|
}
|
|
|
|
*pDest = dst;
|
|
|
|
return NATS_OK;
|
|
}
|
|
|
|
natsStatus
|
|
nats_Base64RawURL_EncodeString(const unsigned char *src, int srcLen, char **pDest)
|
|
{
|
|
natsStatus s = _base64Encode(base64EncodeURL, false, src, srcLen, pDest);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_Base64_Encode(const unsigned char *src, int srcLen, char **pDest)
|
|
{
|
|
natsStatus s = _base64Encode(base64EncodeStd, true, src, srcLen, pDest);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
static bool
|
|
_base64IsValidChar(char c, bool paddingOk)
|
|
{
|
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
|
|
|| (c >= '0' && c <= '9') || c == '+' || c == '/')
|
|
{
|
|
return true;
|
|
}
|
|
if (c == base64Padding && paddingOk)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
natsStatus
|
|
nats_Base64_DecodeLen(const char *src, int *srcLen, int *dstLen)
|
|
{
|
|
int l;
|
|
int dl;
|
|
int i;
|
|
|
|
if (nats_IsStringEmpty(src))
|
|
return nats_setError(NATS_INVALID_ARG, "%s", "base64 content cannot be empty");
|
|
|
|
l = (int) strlen(src);
|
|
if (l % 4 != 0)
|
|
return nats_setError(NATS_INVALID_ARG, "invalid base64 length: %d", l);
|
|
|
|
dl = l / 4 * 3;
|
|
for (i=0; i<l; i++)
|
|
{
|
|
if (!_base64IsValidChar(src[i], i>=l-2))
|
|
return nats_setError(NATS_INVALID_ARG, "invalid base64 character: '%c'", src[i]);
|
|
|
|
if (src[i] == base64Padding)
|
|
dl--;
|
|
}
|
|
|
|
*srcLen = l;
|
|
*dstLen = dl;
|
|
return NATS_OK;
|
|
}
|
|
|
|
void
|
|
nats_Base64_DecodeInPlace(const char *src, int l, unsigned char *dst)
|
|
{
|
|
int i, j, v;
|
|
|
|
for (i=0, j=0; i<l; i+=4, j+=3)
|
|
{
|
|
v = base64Ints[src[i]-43];
|
|
v = (v << 6) | base64Ints[src[i+1]-43];
|
|
v = (src[i+2] == base64Padding ? v << 6 : (v << 6) | base64Ints[src[i+2]-43]);
|
|
v = (src[i+3] == base64Padding ? v << 6 : (v << 6) | base64Ints[src[i+3]-43]);
|
|
|
|
dst[j] = (v >> 16) & 0xFF;
|
|
if (src[i+2] != base64Padding)
|
|
dst[j+1] = (v >> 8) & 0xFF;
|
|
if (src[i+3] != base64Padding)
|
|
dst[j+2] = v & 0xFF;
|
|
}
|
|
}
|
|
|
|
natsStatus
|
|
nats_Base64_Decode(const char *src, unsigned char **dst, int *dstLen)
|
|
{
|
|
natsStatus s;
|
|
int sl = 0;
|
|
|
|
*dst = NULL;
|
|
*dstLen = 0;
|
|
|
|
s = nats_Base64_DecodeLen(src, &sl, dstLen);
|
|
if (s == NATS_OK)
|
|
{
|
|
*dst = (unsigned char*) NATS_MALLOC(*dstLen);
|
|
if (*dst == NULL)
|
|
{
|
|
*dstLen = 0;
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
}
|
|
nats_Base64_DecodeInPlace(src, sl, *dst);
|
|
}
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
// Returns the 2-byte crc for the data provided.
|
|
uint16_t
|
|
nats_CRC16_Compute(unsigned char *data, int len)
|
|
{
|
|
uint16_t crc = 0;
|
|
int i;
|
|
|
|
for (i=0; i<len; i++)
|
|
crc = ((crc << 8) & 0xFFFF) ^ crc16tab[((crc>>8)^(uint16_t)(data[i]))&0x00FF];
|
|
|
|
return crc;
|
|
}
|
|
|
|
// Checks the calculated crc16 checksum for data against the expected.
|
|
bool
|
|
nats_CRC16_Validate(unsigned char *data, int len, uint16_t expected)
|
|
{
|
|
uint16_t crc = nats_CRC16_Compute(data, len);
|
|
return crc == expected;
|
|
}
|
|
|
|
natsStatus
|
|
nats_ReadFile(natsBuffer **buffer, int initBufSize, const char *fn)
|
|
{
|
|
natsStatus s;
|
|
FILE *f = NULL;
|
|
natsBuffer *buf = NULL;
|
|
char *ptr = NULL;
|
|
int total = 0;
|
|
|
|
if ((initBufSize <= 0) || nats_IsStringEmpty(fn))
|
|
return nats_setDefaultError(NATS_INVALID_ARG);
|
|
|
|
f = fopen(fn, "r");
|
|
if (f == NULL)
|
|
return nats_setError(NATS_ERR, "error opening file '%s': %s", fn, strerror(errno));
|
|
|
|
s = natsBuf_Create(&buf, initBufSize);
|
|
if (s == NATS_OK)
|
|
ptr = natsBuf_Data(buf);
|
|
while (s == NATS_OK)
|
|
{
|
|
int r = (int) fread(ptr, 1, (size_t) natsBuf_Available(buf), f);
|
|
if (r == 0)
|
|
break;
|
|
|
|
total += r;
|
|
natsBuf_MoveTo(buf, total);
|
|
if (natsBuf_Available(buf) == 0)
|
|
s = natsBuf_Expand(buf, natsBuf_Capacity(buf)*2);
|
|
if (s == NATS_OK)
|
|
ptr = natsBuf_Data(buf) + total;
|
|
}
|
|
|
|
// Close file. If there was an error, do not report possible closing error
|
|
// as the actual error
|
|
if (s != NATS_OK)
|
|
fclose(f);
|
|
else if (fclose(f) != 0)
|
|
s = nats_setError(NATS_ERR, "error closing file '%s': '%s", fn, strerror(errno));
|
|
|
|
IFOK(s, natsBuf_AppendByte(buf, '\0'));
|
|
|
|
if (s == NATS_OK)
|
|
{
|
|
*buffer = buf;
|
|
}
|
|
else if (buf != NULL)
|
|
{
|
|
memset(natsBuf_Data(buf), 0, natsBuf_Capacity(buf));
|
|
natsBuf_Destroy(buf);
|
|
}
|
|
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
void
|
|
nats_FreeAddrInfo(struct addrinfo *res)
|
|
{
|
|
// Calling freeaddrinfo(NULL) is undefined behavior.
|
|
if (res == NULL)
|
|
return;
|
|
|
|
freeaddrinfo(res);
|
|
}
|
|
|
|
bool
|
|
nats_HostIsIP(const char *host)
|
|
{
|
|
struct addrinfo hint;
|
|
struct addrinfo *res = NULL;
|
|
bool isIP = true;
|
|
|
|
memset(&hint, '\0', sizeof hint);
|
|
|
|
hint.ai_family = PF_UNSPEC;
|
|
hint.ai_flags = AI_NUMERICHOST;
|
|
|
|
if (getaddrinfo(host, NULL, &hint, &res) != 0)
|
|
isIP = false;
|
|
|
|
nats_FreeAddrInfo(res);
|
|
|
|
return isIP;
|
|
}
|
|
|
|
static bool
|
|
_isLineAnHeader(const char *ptr)
|
|
{
|
|
char *last = NULL;
|
|
int len = 0;
|
|
int count = 0;
|
|
bool done = false;
|
|
|
|
// We are looking for a header. Based on the Go client's regex,
|
|
// the strict requirement is that it ends with at least 3 consecutive
|
|
// `-` characters. It must also have 3 consecutive `-` before that.
|
|
// So the minimum size would be 6.
|
|
len = (int) strlen(ptr);
|
|
if (len < 6)
|
|
return false;
|
|
|
|
// First make sure that we have at least 3 `-` at the end.
|
|
last = (char*) (ptr + len - 1);
|
|
|
|
while ((*last == '-') && (last != ptr))
|
|
{
|
|
count++;
|
|
last--;
|
|
if (count == 3)
|
|
break;
|
|
}
|
|
if (count != 3)
|
|
return false;
|
|
|
|
// Now from that point and going backward, we consider
|
|
// to have proper header if we find again 3 consecutive
|
|
// dashes.
|
|
count = 0;
|
|
while (!done)
|
|
{
|
|
if (*last == '-')
|
|
{
|
|
// We have at least `---`, we are done.
|
|
if (++count == 3)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Reset.. we need 3 consecutive dashes
|
|
count = 0;
|
|
}
|
|
if (last == ptr)
|
|
done = true;
|
|
else
|
|
last--;
|
|
}
|
|
// If we are here, it means we did not find `---`
|
|
return false;
|
|
}
|
|
|
|
natsStatus
|
|
nats_GetJWTOrSeed(char **val, const char *content, int item)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
char *pch = NULL;
|
|
char *str = NULL;
|
|
char *saved = NULL;
|
|
int curItem = 0;
|
|
int orgLen = 0;
|
|
char *nt = NULL;
|
|
|
|
// First, make a copy of the original content since
|
|
// we are going to call strtok on it, which alters it.
|
|
str = NATS_STRDUP(content);
|
|
if (str == NULL)
|
|
return nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
orgLen = (int) strlen(str);
|
|
|
|
pch = nats_strtok(str, "\n", &nt);
|
|
while (pch != NULL)
|
|
{
|
|
if (_isLineAnHeader(pch))
|
|
{
|
|
// We got the start of the section. Save the next line
|
|
// as the possible returned value if the following line
|
|
// is a header too.
|
|
pch = nats_strtok(NULL, "\n", &nt);
|
|
saved = pch;
|
|
|
|
while (pch != NULL)
|
|
{
|
|
pch = nats_strtok(NULL, "\n", &nt);
|
|
if (pch == NULL)
|
|
break;
|
|
|
|
// We tolerate empty string(s).
|
|
if (*pch == '\0')
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
if (pch == NULL)
|
|
break;
|
|
|
|
if (_isLineAnHeader(pch))
|
|
{
|
|
// Is this the item we were looking for?
|
|
if (curItem == item)
|
|
{
|
|
// Return a copy of the saved line
|
|
*val = NATS_STRDUP(saved);
|
|
if (*val == NULL)
|
|
s = nats_setDefaultError(NATS_NO_MEMORY);
|
|
|
|
break;
|
|
}
|
|
else if (++curItem > 1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pch = nats_strtok(NULL, "\n", &nt);
|
|
}
|
|
|
|
memset(str, 0, orgLen);
|
|
NATS_FREE(str);
|
|
|
|
// Nothing was found, return NATS_NOT_FOUND but don't set the stack error.
|
|
if ((s == NATS_OK) && (*val == NULL))
|
|
return NATS_NOT_FOUND;
|
|
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
static natsStatus
|
|
_marshalLongVal(natsBuffer *buf, bool comma, const char *fieldName, bool l, int64_t lval, uint64_t uval)
|
|
{
|
|
natsStatus s = NATS_OK;
|
|
char temp[32];
|
|
const char *start = (comma ? ",\"" : "\"");
|
|
|
|
if (l)
|
|
snprintf(temp, sizeof(temp), "%" PRId64, lval);
|
|
else
|
|
snprintf(temp, sizeof(temp), "%" PRIi64, uval);
|
|
|
|
s = natsBuf_Append(buf, start, -1);
|
|
IFOK(s, natsBuf_Append(buf, fieldName, -1));
|
|
IFOK(s, natsBuf_Append(buf, "\":", -1));
|
|
IFOK(s, natsBuf_Append(buf, temp, -1));
|
|
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_marshalLong(natsBuffer *buf, bool comma, const char *fieldName, int64_t lval)
|
|
{
|
|
natsStatus s = _marshalLongVal(buf, comma, fieldName, true, lval, 0);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
natsStatus
|
|
nats_marshalULong(natsBuffer *buf, bool comma, const char *fieldName, uint64_t uval)
|
|
{
|
|
natsStatus s = _marshalLongVal(buf, comma, fieldName, false, 0, uval);
|
|
return NATS_UPDATE_ERR_STACK(s);
|
|
}
|
|
|
|
|
|
bool
|
|
nats_IsSubjectValid(const char *subject, bool wcAllowed)
|
|
{
|
|
int i = 0;
|
|
int len = 0;
|
|
int lastDot = -1;
|
|
char c;
|
|
|
|
if (nats_IsStringEmpty(subject))
|
|
return false;
|
|
|
|
len = (int) strlen(subject);
|
|
for (i=0; i<len ; i++)
|
|
{
|
|
c = subject[i];
|
|
if (isspace((unsigned char) c))
|
|
return false;
|
|
|
|
if (c == '.')
|
|
{
|
|
if ((i == len-1) || (i == lastDot+1))
|
|
return false;
|
|
|
|
// If the last token was 1 character long...
|
|
if (i == lastDot+2)
|
|
{
|
|
char prevToken = subject[i-1];
|
|
|
|
// If that token was `>`, then it is not a valid subject,
|
|
// regardless if wildcards are allowed or not.
|
|
if (prevToken == '>')
|
|
return false;
|
|
else if (!wcAllowed && (prevToken == '*'))
|
|
return false;
|
|
}
|
|
|
|
lastDot = i;
|
|
}
|
|
|
|
// Check the last character to see if it is a wildcard. Others
|
|
// are handled when processing the next '.' character (since
|
|
// if not a token of their own, they are not considered wildcards).
|
|
if (i == len-1)
|
|
{
|
|
// If they are a token of their own, that is, the preceeding
|
|
// character is the `.` or they are the first and only character,
|
|
// then the result will depend if wilcards are allowed or not.
|
|
if (((c == '>') || (c == '*')) && (i == lastDot+1))
|
|
return wcAllowed;
|
|
}
|
|
}
|
|
return true;
|
|
}
|