// 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 #include #include #include #include #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; isize; 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; isize; 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 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; i0; 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; isize; 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; isize; 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; jsize; } 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 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-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> 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>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`, 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; }