diff --git a/myHTTP2022-2010.sln b/myHTTP2022-2010.sln new file mode 100644 index 000000000..89ef31210 --- /dev/null +++ b/myHTTP2022-2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "myHTTP2022", "myHTTP2022-2010.vcxproj", "{EE06C1F3-28B1-4580-A0FA-9F7422C7E456}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EE06C1F3-28B1-4580-A0FA-9F7422C7E456}.Debug|Win32.ActiveCfg = Debug|Win32 + {EE06C1F3-28B1-4580-A0FA-9F7422C7E456}.Debug|Win32.Build.0 = Debug|Win32 + {EE06C1F3-28B1-4580-A0FA-9F7422C7E456}.Release|Win32.ActiveCfg = Release|Win32 + {EE06C1F3-28B1-4580-A0FA-9F7422C7E456}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/myHTTP2022-2010.vcproj b/myHTTP2022-2010.vcproj new file mode 100644 index 000000000..2f5056582 --- /dev/null +++ b/myHTTP2022-2010.vcproj @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/myHTTP2022-2010.vcxproj b/myHTTP2022-2010.vcxproj new file mode 100644 index 000000000..53866d752 --- /dev/null +++ b/myHTTP2022-2010.vcxproj @@ -0,0 +1,127 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + myHTTP2022 + {EE06C1F3-28B1-4580-A0FA-9F7422C7E456} + myHTTP2022 + + + + Application + MultiByte + true + + + Application + MultiByte + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + EditAndContinue + + + true + MachineX86 + + + + + MaxSpeed + true + WINDOWS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + Level3 + ProgramDatabase + + + true + true + true + MachineX86 + + + + + true + + + + + + + + true + + + + + true + + + true + + + true + + + true + + + + + + + + + + + true + + + true + + + true + + + + + + + + + \ No newline at end of file diff --git a/src/httpserver/http_tcp_server.c b/src/httpserver/http_tcp_server.c index 91ee5da44..e7e36232e 100644 --- a/src/httpserver/http_tcp_server.c +++ b/src/httpserver/http_tcp_server.c @@ -73,8 +73,10 @@ static void tcp_client_thread( beken_thread_arg_t arg ) // returns length to be sent if any request.received[request.receivedLen] = 0; int lenret = HTTP_ProcessPacket(&request); - addLog( "TCP sending reply len %i\n",lenret ); - send( fd, reply, lenret, 0 ); + if (lenret > 0){ + addLog( "TCP sending reply len %i\n",lenret ); + send( fd, reply, lenret, 0 ); + } rtos_delay_milliseconds(10); diff --git a/src/httpserver/new_http.c b/src/httpserver/new_http.c index 3fb9538c5..15b326000 100644 --- a/src/httpserver/new_http.c +++ b/src/httpserver/new_http.c @@ -51,6 +51,7 @@ Connection: keep-alive const char httpHeader[] = "HTTP/1.1 200 OK\nContent-type: " ; // HTTP header const char httpMimeTypeHTML[] = "text/html" ; // HTML MIME type const char httpMimeTypeText[] = "text/plain" ; // TEXT MIME type +const char httpMimeTypeJson[] = "application/json" ; // TEXT MIME type const char htmlHeader[] = "" ; const char htmlEnd[] = "" ; const char htmlReturnToMenu[] = "Return to menu";; @@ -61,8 +62,8 @@ const char httpCorsHeaders[] = "Access-Control-Allow-Origin: *\r\nAccess-Control const char *methodNames[] = { "GET", - "POST", "PUT", + "POST", "OPTIONS" }; @@ -110,6 +111,18 @@ int HTTP_RegisterCallback( const char *url, int method, http_callback_fn callbac return 0; } +int my_strnicmp(char *a, char *b, int len){ + int i; + for (i = 0; i < len; i++){ + char x = *a; + char y = *b; + if (!x || !y) return 1; + if ((x | 0x20) != (y | 0x20)) return 1; + a++; + b++; + } + return 0; +} bool http_startsWith(const char *base, const char *substr) { while(*substr != 0) { @@ -329,7 +342,7 @@ int HTTP_ProcessPacket(http_request_t *request) { char tmpB[64]; char tmpC[64]; //int bChanged = 0; - const char *urlStr = ""; + char *urlStr = ""; char *recvbuf = request->received; for (int i = 0; i < sizeof(methodNames)/sizeof(*methodNames); i++){ @@ -367,6 +380,9 @@ int HTTP_ProcessPacket(http_request_t *request) { printf("invalid request\n"); return 0; } + + request->url = urlStr; + // protocol is next, termed by \r\n char *protocol = p; p = strchr(protocol, '\r'); @@ -379,6 +395,8 @@ int HTTP_ProcessPacket(http_request_t *request) { return 0; } p++; + // i.e. not received + request->contentLength = -1; char *headers = p; do { p = strchr(headers, '\r'); @@ -389,7 +407,7 @@ int HTTP_ProcessPacket(http_request_t *request) { request->numheaders++; } // pick out contentLength - if (!strcmp(headers, "Content-Length:")){ + if (!my_strnicmp(headers, "Content-Length:", 15)){ request->contentLength = atoi(headers + 15); } @@ -436,7 +454,7 @@ int HTTP_ProcessPacket(http_request_t *request) { // look for a callback with this URL and method, or HTTP_ANY for (i = 0; i < numCallbacks; i++){ char *url = callbacks[i]->url; - if (http_checkUrlBase(urlStr, &url[1])){ + if (http_startsWith(urlStr, &url[1])){ int method = callbacks[i]->method; if(method == HTTP_ANY || method == request->method){ return callbacks[i]->callback(request); diff --git a/src/httpserver/new_http.h b/src/httpserver/new_http.h index 8f45f4787..558495081 100644 --- a/src/httpserver/new_http.h +++ b/src/httpserver/new_http.h @@ -3,10 +3,13 @@ extern const char httpHeader[]; // HTTP header extern const char httpMimeTypeHTML[]; // HTML MIME type extern const char httpMimeTypeText[]; // TEXT MIME type +extern const char httpMimeTypeJson[]; extern const char htmlHeader[]; extern const char htmlEnd[]; extern const char htmlReturnToMenu[]; +extern const char *htmlPinRoleNames[]; + #define MAX_QUERY 16 #define MAX_HEADERS 16 typedef struct http_request_tag { diff --git a/src/httpserver/rest_interface.c b/src/httpserver/rest_interface.c index d6a2e59c7..e71133b56 100644 --- a/src/httpserver/rest_interface.c +++ b/src/httpserver/rest_interface.c @@ -2,14 +2,19 @@ #include "../logging/logging.h" #include "../httpserver/new_http.h" #include "str_pub.h" +#include "../new_pins.h" +#include "../jsmn/jsmn_h.h" static int http_rest_get(http_request_t *request); static int http_rest_post(http_request_t *request); static int http_rest_app(http_request_t *request); +static int http_rest_post_pins(http_request_t *request); +static int http_rest_get_pins(http_request_t *request); + void init_rest(){ - HTTP_RegisterCallback( "/api", HTTP_GET, http_rest_get); - HTTP_RegisterCallback( "/api", HTTP_POST, http_rest_post); + HTTP_RegisterCallback( "/api/", HTTP_GET, http_rest_get); + HTTP_RegisterCallback( "/api/", HTTP_POST, http_rest_post); HTTP_RegisterCallback( "/app", HTTP_GET, http_rest_app); } @@ -54,9 +59,154 @@ static int http_rest_app(http_request_t *request){ } static int http_rest_get(http_request_t *request){ + if (!strcmp(request->url, "api/pins")){ + return http_rest_get_pins(request); + } + + http_setup(request, httpMimeTypeHTML); + poststr(request, "GET of "); + poststr(request, request->url); + poststr(request, htmlEnd); + poststr(request,NULL); return 0; } -static int http_rest_post(http_request_t *request){ +static int http_rest_get_pins(http_request_t *request){ + int i; + char tmp[20]; + /*typedef struct pinsState_s { + byte roles[32]; + byte channels[32]; + } pinsState_t; + + extern pinsState_t g_pins; + */ + http_setup(request, httpMimeTypeJson); + poststr(request, "{\"rolenames\":["); + for (i = 0; i < IOR_Total_Options; i++){ + if (i){ + sprintf(tmp, ",\"%s\"", htmlPinRoleNames[i]); + } else { + sprintf(tmp, "\"%s\"", htmlPinRoleNames[i]); + } + poststr(request, tmp); + } + poststr(request, "],\"roles\":["); + + for (i = 0; i < 32; i++){ + if (i){ + sprintf(tmp, ",%d", g_pins.roles[i]); + } else { + sprintf(tmp, "%d", g_pins.roles[i]); + } + poststr(request, tmp); + } + poststr(request, "],\"channels\":["); + for (i = 0; i < 32; i++){ + if (i){ + sprintf(tmp, ",%d", g_pins.channels[i]); + } else { + sprintf(tmp, "%d", g_pins.channels[i]); + } + poststr(request, tmp); + } + poststr(request, "]}"); + poststr(request, NULL); + return 0; +} + + + +static int http_rest_post(http_request_t *request){ + char tmp[20]; + if (!strcmp(request->url, "api/pins")){ + return http_rest_post_pins(request); + } + http_setup(request, httpMimeTypeHTML); + poststr(request, "POST to "); + poststr(request, request->url); + poststr(request, "
Content Length:"); + sprintf(tmp, "%d", request->contentLength); + poststr(request, tmp); + poststr(request, "
Content:["); + poststr(request, request->bodystart); + poststr(request, "]
"); + poststr(request, htmlEnd); + poststr(request,NULL); + return 0; +} + +static int http_rest_post_pins(http_request_t *request){ + int i; + int r; + char tmp[256]; + + //https://github.com/zserge/jsmn/blob/master/example/simple.c + jsmn_parser p; + jsmntok_t t[128]; /* We expect no more than 128 tokens */ + char *json_str = request->bodystart; + int json_len = strlen(json_str); + http_setup(request, httpMimeTypeText); + + jsmn_init(&p); + r = jsmn_parse(&p, json_str, json_len, t, + sizeof(t) / sizeof(t[0])); + if (r < 0) { + sprintf(tmp,"Failed to parse JSON: %d\n", r); + poststr(request, tmp); + poststr(request, NULL); + return 0; + } + + /* Assume the top-level element is an object */ + if (r < 1 || t[0].type != JSMN_OBJECT) { + sprintf(tmp,"Object expected\n"); + poststr(request, tmp); + poststr(request, NULL); + return 0; + } + + sprintf(tmp,"parsed JSON: %s\n", json_str); + poststr(request, tmp); + poststr(request, NULL); + return 0; + + + /* Loop over all keys of the root object */ + for (i = 1; i < r; i++) { + if (jsoneq(json_str, &t[i], "roles") == 0) { + int j; + sprintf(tmp,"- Roles:\n"); + poststr(request, tmp); + if (t[i + 1].type != JSMN_ARRAY) { + continue; /* We expect groups to be an array of strings */ + } + for (j = 0; j < t[i + 1].size; j++) { + jsmntok_t *g = &t[i + j + 2]; + sprintf(tmp," * %.*s\n", g->end - g->start, json_str + g->start); + poststr(request, tmp); + } + i += t[i + 1].size + 1; + } else if (jsoneq(json_str, &t[i], "channels") == 0) { + int j; + sprintf(tmp,"- Channels:\n"); + poststr(request, tmp); + if (t[i + 1].type != JSMN_ARRAY) { + continue; /* We expect groups to be an array of strings */ + } + for (j = 0; j < t[i + 1].size; j++) { + jsmntok_t *g = &t[i + j + 2]; + sprintf(tmp," * %.*s\n", g->end - g->start, json_str + g->start); + poststr(request, tmp); + } + i += t[i + 1].size + 1; + } else { + sprintf(tmp,"Unexpected key: %.*s\n", t[i].end - t[i].start, + json_str + t[i].start); + poststr(request, tmp); + } + } + + poststr(request, NULL); return 0; } diff --git a/src/httpserver/rest_interface.h b/src/httpserver/rest_interface.h new file mode 100644 index 000000000..4cd0d70c4 --- /dev/null +++ b/src/httpserver/rest_interface.h @@ -0,0 +1 @@ +void init_rest(); \ No newline at end of file diff --git a/src/jsmn/jsmn.c b/src/jsmn/jsmn.c new file mode 100644 index 000000000..aff46d9f8 --- /dev/null +++ b/src/jsmn/jsmn.c @@ -0,0 +1,12 @@ + +#include "string.h" +// this includes the source code. +#include "jsmn.h" + +int jsoneq(const char *json, jsmntok_t *tok, const char *s) { + if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return 0; + } + return -1; +} diff --git a/src/jsmn/jsmn.h b/src/jsmn/jsmn.h new file mode 100644 index 000000000..8ac14c1bd --- /dev/null +++ b/src/jsmn/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/src/jsmn/jsmn_h.h b/src/jsmn/jsmn_h.h new file mode 100644 index 000000000..c18b4f17c --- /dev/null +++ b/src/jsmn/jsmn_h.h @@ -0,0 +1,6 @@ +// prevent includsion of the source code +#define JSMN_HEADER +#include "string.h" +#include "jsmn.h" + +int jsoneq(const char *json, jsmntok_t *tok, const char *s); diff --git a/src/new_pins.c b/src/new_pins.c index 20b5da957..a6e32691b 100644 --- a/src/new_pins.c +++ b/src/new_pins.c @@ -2,9 +2,8 @@ -#include "new_common.h" -#include "httpserver/new_http.h" #include "new_pins.h" +#include "httpserver/new_http.h" #include "logging/logging.h" #if WINDOWS @@ -69,11 +68,6 @@ unsigned char g_channelValues[GPIO_MAX] = { 0 }; BUTTON_S g_buttons[GPIO_MAX]; #endif -typedef struct pinsState_s { - byte roles[32]; - byte channels[32]; -} pinsState_t; - pinsState_t g_pins; void (*g_channelChangeCallback)(int idx, int iVal) = 0; diff --git a/src/new_pins.h b/src/new_pins.h index 0d54a36ad..6a9a1abe8 100644 --- a/src/new_pins.h +++ b/src/new_pins.h @@ -1,5 +1,6 @@ #ifndef __NEW_PINS_H__ #define __NEW_PINS_H__ +#include "new_common.h" #define BIT_SET(PIN,N) (PIN |= (1<