spicetools/api/resources/arduino/spiceapi/connection.h

170 lines
4.2 KiB
C
Raw Permalink Normal View History

2024-08-28 15:10:34 +00:00
#ifndef SPICEAPI_CONNECTION_H
#define SPICEAPI_CONNECTION_H
#include <stdint.h>
#include "rc4.h"
#ifndef SPICEAPI_INTERFACE
#define SPICEAPI_INTERFACE Serial
#endif
namespace spiceapi {
class Connection {
private:
uint8_t* receive_buffer;
size_t receive_buffer_size;
const char* password;
RC4* cipher;
public:
Connection(size_t receive_buffer_size, const char* password = "");
~Connection();
void reset();
bool check();
void cipher_alloc(const char *session_key = nullptr);
void change_pass(const char* password, bool session = false);
const char* request(const char* json, size_t timeout = 1000);
const char* request(char* json, size_t timeout = 1000);
};
}
spiceapi::Connection::Connection(size_t receive_buffer_size, const char* password) {
this->receive_buffer = new uint8_t[receive_buffer_size];
this->receive_buffer_size = receive_buffer_size;
this->password = password;
this->cipher = nullptr;
this->reset();
}
spiceapi::Connection::~Connection() {
// clean up
if (this->cipher != nullptr)
delete this->cipher;
}
void spiceapi::Connection::reset() {
// drop all input
while (SPICEAPI_INTERFACE.available()) {
SPICEAPI_INTERFACE.read();
}
#ifdef SPICEAPI_INTERFACE_WIFICLIENT
// reconnect TCP client
SPICEAPI_INTERFACE.stop();
this->check();
#else
// 8 zeroes reset the password/session on serial
for (size_t i = 0; i < 8; i++) {
SPICEAPI_INTERFACE.write((int) 0);
}
#endif
// reset password
this->cipher_alloc();
}
void spiceapi::Connection::cipher_alloc(const char *session_key) {
// delete old cipher
if (this->cipher != nullptr) {
delete this->cipher;
this->cipher = nullptr;
}
// create new cipher if password is set
session_key = session_key ? session_key : this->password;
if (strlen(session_key) > 0) {
this->cipher = new RC4(
(uint8_t *) session_key,
strlen(session_key));
}
}
bool spiceapi::Connection::check() {
#ifdef SPICEAPI_INTERFACE_WIFICLIENT
if (!SPICEAPI_INTERFACE.connected()) {
return SPICEAPI_INTERFACE.connect(
SPICEAPI_INTERFACE_WIFICLIENT_HOST,
SPICEAPI_INTERFACE_WIFICLIENT_PORT);
} else {
return true;
}
#else
// serial is always valid
return true;
#endif
}
void spiceapi::Connection::change_pass(const char* password, bool session) {
if (!session) {
this->password = password;
}
this->cipher_alloc(password);
}
const char* spiceapi::Connection::request(const char* json, size_t timeout) {
auto json_len = strlen(json);
strncpy((char*) receive_buffer, json, receive_buffer_size);
return request((char*) receive_buffer, timeout);
}
const char* spiceapi::Connection::request(char* json_data, size_t timeout) {
// check connection
if (!this->check())
return "";
// crypt
auto json_len = strlen(json_data) + 1;
if (this->cipher != nullptr)
this->cipher->crypt((uint8_t*) json_data, json_len);
// send
auto send_result = SPICEAPI_INTERFACE.write((const char*) json_data, (int) json_len);
SPICEAPI_INTERFACE.flush();
if (send_result < (int) json_len) {
return "";
}
// receive
size_t receive_data_len = 0;
auto t_start = millis();
while (SPICEAPI_INTERFACE) {
// check for timeout
if (millis() - t_start > timeout) {
this->reset();
return "";
}
// read single byte
auto b = SPICEAPI_INTERFACE.read();
if (b < 0) continue;
receive_buffer[receive_data_len++] = b;
// check for buffer overflow
if (receive_data_len >= receive_buffer_size) {
this->reset();
return "";
}
// crypt
if (this->cipher != nullptr)
this->cipher->crypt(&receive_buffer[receive_data_len - 1], 1);
// check for message end
if (receive_buffer[receive_data_len - 1] == 0)
break;
}
// return resulting json
return (const char*) &receive_buffer[0];
}
#endif //SPICEAPI_CONNECTION_H