321 lines
10 KiB
C
321 lines
10 KiB
C
/*! Copyright 2023 Bogdan Pilyugin
|
|
*
|
|
* 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.
|
|
*
|
|
* \file SysComm.c
|
|
* \version 1.0
|
|
* \date 2023-07-26
|
|
* \author Bogdan Pilyugin
|
|
* \brief
|
|
* \details
|
|
* \copyright Apache License, Version 2.0
|
|
*/
|
|
|
|
/*
|
|
//Example of SET [msgtype=1] variables [payloadtype=1]
|
|
{
|
|
"data":{
|
|
"msgid":123456789,
|
|
"time":"2023-06-03T12:25:24+00:00",
|
|
"msgtype":1,
|
|
"payloadtype":1,
|
|
"payload":{
|
|
"applytype":1,
|
|
"variables":{
|
|
"wifi_mode":"",
|
|
"wifi_sta_ip":"",
|
|
"wifi_sta_mask":"",
|
|
}
|
|
}},
|
|
"signature":"6a11b872e8f766673eb82e127b6918a0dc96a42c5c9d184604f9787f3d27bcef"}
|
|
*/
|
|
|
|
#include "webguiapp.h"
|
|
#include "SystemApplication.h"
|
|
#include "mbedtls/md.h"
|
|
|
|
#define TAG "SysComm"
|
|
|
|
#define MAX_JSON_DATA_SIZE 1024
|
|
sys_error_code (*CustomPayloadTypeHandler)(data_message_t *MSG);
|
|
void (*CustomSaveConf)(void);
|
|
|
|
void regCustomPayloadTypeHandler(sys_error_code (*payload_handler)(data_message_t *MSG))
|
|
{
|
|
CustomPayloadTypeHandler = payload_handler;
|
|
}
|
|
void regCustomSaveConf(void (*custom_saveconf)(void))
|
|
{
|
|
CustomSaveConf = custom_saveconf;
|
|
}
|
|
|
|
static sys_error_code PayloadType_1_Handler(data_message_t *MSG)
|
|
{
|
|
struct jReadElement result;
|
|
const char *err_br;
|
|
const char *err_desc;
|
|
|
|
if (!(MSG->parsedData.msgType == DATA_MESSAGE_TYPE_COMMAND || MSG->parsedData.msgType == DATA_MESSAGE_TYPE_REQUEST))
|
|
return SYS_ERROR_PARSE_MSGTYPE;
|
|
struct jWriteControl jwc;
|
|
jwOpen(&jwc, MSG->outputDataBuffer, MSG->outputDataLength, JW_OBJECT, JW_COMPACT);
|
|
jwObj_object(&jwc, "data");
|
|
jwObj_int(&jwc, "msgid", MSG->parsedData.msgID);
|
|
char time[RFC3339_TIMESTAMP_LENGTH];
|
|
GetRFC3339Time(time);
|
|
jwObj_string(&jwc, "time", time);
|
|
jwObj_int(&jwc, "msgtype", DATA_MESSAGE_TYPE_RESPONSE);
|
|
jwObj_int(&jwc, "payloadtype", 1);
|
|
jwObj_object(&jwc, "payload");
|
|
jwObj_int(&jwc, "applytype", 0);
|
|
jwObj_object(&jwc, "variables");
|
|
|
|
jRead(MSG->inputDataBuffer, "{'data'{'payload'{'variables'", &result);
|
|
if (result.dataType == JREAD_OBJECT)
|
|
{ //Write variables
|
|
char VarName[VAR_MAX_NAME_LENGTH];
|
|
char *VarValue = malloc(VAR_MAX_VALUE_LENGTH);
|
|
if (!VarValue)
|
|
return SYS_ERROR_NO_MEMORY;
|
|
|
|
for (int i = 0; i < result.elements; ++i)
|
|
{
|
|
jRead_string(MSG->inputDataBuffer, "{'data'{'payload'{'variables'{*", VarName,
|
|
VAR_MAX_NAME_LENGTH,
|
|
&i);
|
|
const char parsevar[] = "{'data'{'payload'{'variables'{'";
|
|
char expr[sizeof(parsevar) + VAR_MAX_NAME_LENGTH];
|
|
strcpy(expr, parsevar);
|
|
strcat(expr, VarName);
|
|
strcat(expr, "'");
|
|
|
|
jRead_string(MSG->inputDataBuffer, expr, VarValue, VAR_MAX_VALUE_LENGTH, &i);
|
|
#if REAST_API_DEBUG_MODE
|
|
ESP_LOGI(TAG, "Got write variable %s:%s", VarName, VarValue);
|
|
#endif
|
|
esp_err_t res = ESP_ERR_INVALID_ARG;
|
|
rest_var_types tp = VAR_ERROR;
|
|
if (MSG->parsedData.msgType == DATA_MESSAGE_TYPE_COMMAND)
|
|
{ //Write variables
|
|
res = SetConfVar(VarName, VarValue, &tp);
|
|
if (res == ESP_OK)
|
|
GetConfVar(VarName, VarValue, &tp);
|
|
else
|
|
strcpy(VarValue, esp_err_to_name(res));
|
|
}
|
|
else
|
|
{ //Read variables
|
|
res = GetConfVar(VarName, VarValue, &tp);
|
|
if (res != ESP_OK)
|
|
strcpy(VarValue, esp_err_to_name(res));
|
|
}
|
|
//Response with actual data
|
|
if (tp == VAR_STRING || tp == VAR_IPADDR || tp == VAR_ERROR || tp == VAR_PASS)
|
|
jwObj_string(&jwc, VarName, VarValue);
|
|
else
|
|
jwObj_raw(&jwc, VarName, VarValue);
|
|
|
|
}
|
|
free(VarValue);
|
|
}
|
|
else
|
|
return SYS_ERROR_PARSE_VARIABLES;
|
|
|
|
jwEnd(&jwc);
|
|
jwEnd(&jwc);
|
|
GetSysErrorDetales((sys_error_code) MSG->err_code, &err_br, &err_desc);
|
|
jwObj_string(&jwc, "error", (char*) err_br);
|
|
jwObj_string(&jwc, "error_descr", (char*) err_desc);
|
|
jwEnd(&jwc);
|
|
|
|
char *datap = strstr(MSG->outputDataBuffer, "\"data\":");
|
|
if (datap)
|
|
{
|
|
datap += sizeof("\"data\":") - 1;
|
|
SHA256hmacHash((unsigned char*) datap, strlen(datap), (unsigned char*) "mykey", sizeof("mykey"),
|
|
MSG->parsedData.sha256);
|
|
unsigned char sha_print[32 * 2 + 1];
|
|
BytesToStr(MSG->parsedData.sha256, sha_print, 32);
|
|
sha_print[32 * 2] = 0x00;
|
|
#if REAST_API_DEBUG_MODE
|
|
ESP_LOGI(TAG, "SHA256 of DATA object is %s", sha_print);
|
|
#endif
|
|
jwObj_string(&jwc, "signature", (char*) sha_print);
|
|
}
|
|
else
|
|
return SYS_ERROR_SHA256_DATA;
|
|
jwEnd(&jwc);
|
|
jwClose(&jwc);
|
|
|
|
jRead(MSG->inputDataBuffer, "{'data'{'payload'{'applytype'", &result);
|
|
if (result.elements == 1)
|
|
{
|
|
int atype = atoi((char*) result.pValue);
|
|
switch (atype)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 1:
|
|
WriteNVSSysConfig(GetSysConf());
|
|
if (CustomSaveConf != NULL)
|
|
CustomSaveConf();
|
|
break;
|
|
case 2:
|
|
WriteNVSSysConfig(GetSysConf());
|
|
if (CustomSaveConf != NULL)
|
|
CustomSaveConf();
|
|
DelayedRestart();
|
|
break;
|
|
default:
|
|
return SYS_ERROR_PARSE_APPLYTYPE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (MSG->parsedData.msgType == DATA_MESSAGE_TYPE_COMMAND)
|
|
return SYS_ERROR_PARSE_APPLYTYPE;
|
|
}
|
|
|
|
return SYS_OK_DATA;
|
|
}
|
|
|
|
static sys_error_code DataHeaderParser(data_message_t *MSG)
|
|
{
|
|
struct jReadElement result;
|
|
jRead(MSG->inputDataBuffer, "{", &result);
|
|
if (result.dataType != JREAD_OBJECT)
|
|
return SYS_ERROR_WRONG_JSON_FORMAT;
|
|
MSG->parsedData.msgID = 0;
|
|
|
|
char *hashbuf = malloc(MSG->inputDataLength);
|
|
if (hashbuf == NULL)
|
|
return SYS_ERROR_NO_MEMORY;
|
|
jRead_string(MSG->inputDataBuffer, "{'data'", hashbuf, MSG->inputDataLength, 0);
|
|
if (strlen(hashbuf) > 0)
|
|
{
|
|
SHA256hmacHash((unsigned char*) hashbuf, strlen(hashbuf), (unsigned char*) "mykey", sizeof("mykey"),
|
|
MSG->parsedData.sha256);
|
|
unsigned char sha_print[32 * 2 + 1];
|
|
BytesToStr(MSG->parsedData.sha256, sha_print, 32);
|
|
sha_print[32 * 2] = 0x00;
|
|
#if REAST_API_DEBUG_MODE
|
|
ESP_LOGI(TAG, "SHA256 of DATA object is %s", sha_print);
|
|
#endif
|
|
free(hashbuf);
|
|
}
|
|
else
|
|
{
|
|
free(hashbuf);
|
|
return SYS_ERROR_PARSE_DATA;
|
|
}
|
|
|
|
jRead(MSG->inputDataBuffer, "{'signature'", &result);
|
|
if (result.elements == 1)
|
|
{
|
|
#if REAST_API_DEBUG_MODE
|
|
ESP_LOGI(TAG, "Signature is %.*s", 64, (char* )result.pValue);
|
|
#endif
|
|
|
|
//Here compare calculated and received signature;
|
|
}
|
|
else
|
|
return SYS_ERROR_PARSE_SIGNATURE;
|
|
|
|
//Extract 'messidx' or throw exception
|
|
jRead(MSG->inputDataBuffer, "{'data'{'msgid'", &result);
|
|
if (result.elements == 1)
|
|
{
|
|
MSG->parsedData.msgID = jRead_long(MSG->inputDataBuffer, "{'data'{'msgid'", 0);
|
|
if (MSG->parsedData.msgID == 0)
|
|
return SYS_ERROR_PARSE_MESSAGEID;
|
|
}
|
|
else
|
|
return SYS_ERROR_PARSE_MESSAGEID;
|
|
|
|
//Extract 'msgtype' or throw exception
|
|
jRead(MSG->inputDataBuffer, "{'data'{'msgtype'", &result);
|
|
if (result.elements == 1)
|
|
{
|
|
MSG->parsedData.msgType = atoi((char*) result.pValue);
|
|
if (MSG->parsedData.msgType > 2 || MSG->parsedData.msgType < 1)
|
|
return SYS_ERROR_PARSE_MSGTYPE;
|
|
}
|
|
else
|
|
return SYS_ERROR_PARSE_MSGTYPE;
|
|
|
|
//Extract 'payloadtype' or throw exception
|
|
jRead(MSG->inputDataBuffer, "{'data'{'payloadtype'", &result);
|
|
if (result.elements == 1)
|
|
{
|
|
MSG->parsedData.payloadType = atoi((char*) result.pValue);
|
|
if (MSG->parsedData.payloadType < 1 && MSG->parsedData.payloadType > 100)
|
|
return SYS_ERROR_PARSE_PAYLOADTYPE;
|
|
}
|
|
else
|
|
return SYS_ERROR_PARSE_PAYLOADTYPE;
|
|
|
|
switch (MSG->parsedData.payloadType)
|
|
{
|
|
case 1:
|
|
return PayloadType_1_Handler(MSG);
|
|
break;
|
|
|
|
default:
|
|
if (CustomPayloadTypeHandler)
|
|
CustomPayloadTypeHandler(MSG);
|
|
else
|
|
return SYS_ERROR_PARSE_PAYLOADTYPE;
|
|
}
|
|
|
|
return SYS_ERROR_UNKNOWN;
|
|
}
|
|
|
|
esp_err_t ServiceDataHandler(data_message_t *MSG)
|
|
{
|
|
if (MSG == NULL)
|
|
{
|
|
ESP_LOGE(TAG, "MSG object is NULL");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (MSG->inputDataLength == 0)
|
|
{
|
|
ESP_LOGE(TAG, "Data for parser is 0 length");
|
|
if (MSG != NULL)
|
|
MSG->err_code = SYS_ERROR_UNKNOWN;
|
|
}
|
|
else
|
|
MSG->err_code = (int) DataHeaderParser(MSG);
|
|
|
|
if (MSG->err_code)
|
|
{
|
|
struct jWriteControl jwc;
|
|
jwOpen(&jwc, MSG->outputDataBuffer, MSG->outputDataLength, JW_OBJECT, JW_PRETTY);
|
|
jwObj_int(&jwc, "msgid", MSG->parsedData.msgID);
|
|
char time[RFC3339_TIMESTAMP_LENGTH];
|
|
GetRFC3339Time(time);
|
|
jwObj_string(&jwc, "time", time);
|
|
jwObj_int(&jwc, "messtype", DATA_MESSAGE_TYPE_RESPONSE);
|
|
const char *err_br;
|
|
const char *err_desc;
|
|
GetSysErrorDetales((sys_error_code) MSG->err_code, &err_br, &err_desc);
|
|
jwObj_string(&jwc, "error", (char*) err_br);
|
|
jwObj_string(&jwc, "error_descr", (char*) err_desc);
|
|
jwEnd(&jwc);
|
|
jwClose(&jwc);
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|