2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
|||||||
# could be handy for archiving the generated documentation or if some version
|
# could be handy for archiving the generated documentation or if some version
|
||||||
# control system is used.
|
# control system is used.
|
||||||
|
|
||||||
PROJECT_NUMBER = 2025.12.0b1
|
PROJECT_NUMBER = 2025.12.0b2
|
||||||
|
|
||||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||||
# for a project that appears at the top of each page and should give viewer a
|
# for a project that appears at the top of each page and should give viewer a
|
||||||
|
|||||||
@@ -579,7 +579,7 @@ message LightCommandRequest {
|
|||||||
bool has_flash_length = 16;
|
bool has_flash_length = 16;
|
||||||
uint32 flash_length = 17;
|
uint32 flash_length = 17;
|
||||||
bool has_effect = 18;
|
bool has_effect = 18;
|
||||||
string effect = 19;
|
string effect = 19 [(pointer_to_buffer) = true];
|
||||||
uint32 device_id = 28 [(field_ifdef) = "USE_DEVICES"];
|
uint32 device_id = 28 [(field_ifdef) = "USE_DEVICES"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -533,7 +533,7 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
|||||||
if (msg.has_flash_length)
|
if (msg.has_flash_length)
|
||||||
call.set_flash_length(msg.flash_length);
|
call.set_flash_length(msg.flash_length);
|
||||||
if (msg.has_effect)
|
if (msg.has_effect)
|
||||||
call.set_effect(msg.effect);
|
call.set_effect(reinterpret_cast<const char *>(msg.effect), msg.effect_len);
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1669,7 +1669,7 @@ bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryption
|
|||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "Failed to clear encryption key");
|
ESP_LOGW(TAG, "Failed to clear encryption key");
|
||||||
}
|
}
|
||||||
} else if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
|
} else if (base64_decode(msg.key, psk.data(), psk.size()) != psk.size()) {
|
||||||
ESP_LOGW(TAG, "Invalid encryption key length");
|
ESP_LOGW(TAG, "Invalid encryption key length");
|
||||||
} else if (!this->parent_->save_noise_psk(psk, true)) {
|
} else if (!this->parent_->save_noise_psk(psk, true)) {
|
||||||
ESP_LOGW(TAG, "Failed to save encryption key");
|
ESP_LOGW(TAG, "Failed to save encryption key");
|
||||||
|
|||||||
@@ -611,9 +611,12 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
}
|
}
|
||||||
bool LightCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
bool LightCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 19:
|
case 19: {
|
||||||
this->effect = value.as_string();
|
// Use raw data directly to avoid allocation
|
||||||
|
this->effect = value.data();
|
||||||
|
this->effect_len = value.size();
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -840,7 +840,7 @@ class LightStateResponse final : public StateResponseProtoMessage {
|
|||||||
class LightCommandRequest final : public CommandProtoMessage {
|
class LightCommandRequest final : public CommandProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint8_t MESSAGE_TYPE = 32;
|
static constexpr uint8_t MESSAGE_TYPE = 32;
|
||||||
static constexpr uint8_t ESTIMATED_SIZE = 112;
|
static constexpr uint8_t ESTIMATED_SIZE = 122;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
const char *message_name() const override { return "light_command_request"; }
|
const char *message_name() const override { return "light_command_request"; }
|
||||||
#endif
|
#endif
|
||||||
@@ -869,7 +869,8 @@ class LightCommandRequest final : public CommandProtoMessage {
|
|||||||
bool has_flash_length{false};
|
bool has_flash_length{false};
|
||||||
uint32_t flash_length{0};
|
uint32_t flash_length{0};
|
||||||
bool has_effect{false};
|
bool has_effect{false};
|
||||||
std::string effect{};
|
const uint8_t *effect{nullptr};
|
||||||
|
uint16_t effect_len{0};
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -999,7 +999,9 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
|||||||
dump_field(out, "has_flash_length", this->has_flash_length);
|
dump_field(out, "has_flash_length", this->has_flash_length);
|
||||||
dump_field(out, "flash_length", this->flash_length);
|
dump_field(out, "flash_length", this->flash_length);
|
||||||
dump_field(out, "has_effect", this->has_effect);
|
dump_field(out, "has_effect", this->has_effect);
|
||||||
dump_field(out, "effect", this->effect);
|
out.append(" effect: ");
|
||||||
|
out.append(format_hex_pretty(this->effect, this->effect_len));
|
||||||
|
out.append("\n");
|
||||||
#ifdef USE_DEVICES
|
#ifdef USE_DEVICES
|
||||||
dump_field(out, "device_id", this->device_id);
|
dump_field(out, "device_id", this->device_id);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ void HttpRequestUpdate::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HttpRequestUpdate::update() {
|
void HttpRequestUpdate::update() {
|
||||||
|
if (!network::is_connected()) {
|
||||||
|
ESP_LOGD(TAG, "Network not connected, skipping update check");
|
||||||
|
return;
|
||||||
|
}
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_);
|
xTaskCreate(HttpRequestUpdate::update_task, "update_task", 8192, (void *) this, 1, &this->update_task_handle_);
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -504,8 +504,8 @@ color_mode_bitmask_t LightCall::get_suitable_color_modes_mask_() {
|
|||||||
#undef KEY
|
#undef KEY
|
||||||
}
|
}
|
||||||
|
|
||||||
LightCall &LightCall::set_effect(const std::string &effect) {
|
LightCall &LightCall::set_effect(const char *effect, size_t len) {
|
||||||
if (strcasecmp(effect.c_str(), "none") == 0) {
|
if (len == 4 && strncasecmp(effect, "none", 4) == 0) {
|
||||||
this->set_effect(0);
|
this->set_effect(0);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -513,15 +513,16 @@ LightCall &LightCall::set_effect(const std::string &effect) {
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) {
|
for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) {
|
||||||
LightEffect *e = this->parent_->effects_[i];
|
LightEffect *e = this->parent_->effects_[i];
|
||||||
|
const char *name = e->get_name();
|
||||||
|
|
||||||
if (strcasecmp(effect.c_str(), e->get_name()) == 0) {
|
if (strncasecmp(effect, name, len) == 0 && name[len] == '\0') {
|
||||||
this->set_effect(i + 1);
|
this->set_effect(i + 1);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
ESP_LOGW(TAG, "'%s': no such effect '%s'", this->parent_->get_name().c_str(), effect.c_str());
|
ESP_LOGW(TAG, "'%s': no such effect '%.*s'", this->parent_->get_name().c_str(), (int) len, effect);
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,9 @@ class LightCall {
|
|||||||
/// Set the effect of the light by its name.
|
/// Set the effect of the light by its name.
|
||||||
LightCall &set_effect(optional<std::string> effect);
|
LightCall &set_effect(optional<std::string> effect);
|
||||||
/// Set the effect of the light by its name.
|
/// Set the effect of the light by its name.
|
||||||
LightCall &set_effect(const std::string &effect);
|
LightCall &set_effect(const std::string &effect) { return this->set_effect(effect.data(), effect.size()); }
|
||||||
|
/// Set the effect of the light by its name and length (zero-copy from API).
|
||||||
|
LightCall &set_effect(const char *effect, size_t len);
|
||||||
/// Set the effect of the light by its internal index number (only for internal use).
|
/// Set the effect of the light by its internal index number (only for internal use).
|
||||||
LightCall &set_effect(uint32_t effect_number);
|
LightCall &set_effect(uint32_t effect_number);
|
||||||
LightCall &set_effect(optional<uint32_t> effect_number);
|
LightCall &set_effect(optional<uint32_t> effect_number);
|
||||||
|
|||||||
@@ -14,13 +14,36 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
#include <coredecls.h> // For esp_schedule()
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace socket {
|
namespace socket {
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
// Flag to signal socket activity - checked by socket_delay() to exit early
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static volatile bool s_socket_woke = false;
|
||||||
|
|
||||||
|
void socket_delay(uint32_t ms) {
|
||||||
|
// Use esp_delay with a callback that checks if socket data arrived.
|
||||||
|
// This allows the delay to exit early when socket_wake() is called by
|
||||||
|
// lwip recv_fn/accept_fn callbacks, reducing socket latency.
|
||||||
|
s_socket_woke = false;
|
||||||
|
esp_delay(ms, []() { return !s_socket_woke; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void socket_wake() {
|
||||||
|
s_socket_woke = true;
|
||||||
|
esp_schedule();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *const TAG = "socket.lwip";
|
static const char *const TAG = "socket.lwip";
|
||||||
|
|
||||||
// set to 1 to enable verbose lwip logging
|
// set to 1 to enable verbose lwip logging
|
||||||
#if 0
|
#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if)
|
||||||
#define LWIP_LOG(msg, ...) ESP_LOGVV(TAG, "socket %p: " msg, this, ##__VA_ARGS__)
|
#define LWIP_LOG(msg, ...) ESP_LOGVV(TAG, "socket %p: " msg, this, ##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define LWIP_LOG(msg, ...)
|
#define LWIP_LOG(msg, ...)
|
||||||
@@ -323,9 +346,10 @@ class LWIPRawImpl : public Socket {
|
|||||||
for (int i = 0; i < iovcnt; i++) {
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
||||||
if (err == -1) {
|
if (err == -1) {
|
||||||
if (ret != 0)
|
if (ret != 0) {
|
||||||
// if we already read some don't return an error
|
// if we already read some don't return an error
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
ret += err;
|
ret += err;
|
||||||
@@ -393,9 +417,10 @@ class LWIPRawImpl : public Socket {
|
|||||||
ssize_t written = internal_write(buf, len);
|
ssize_t written = internal_write(buf, len);
|
||||||
if (written == -1)
|
if (written == -1)
|
||||||
return -1;
|
return -1;
|
||||||
if (written == 0)
|
if (written == 0) {
|
||||||
// no need to output if nothing written
|
// no need to output if nothing written
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
if (nodelay_) {
|
if (nodelay_) {
|
||||||
int err = internal_output();
|
int err = internal_output();
|
||||||
if (err == -1)
|
if (err == -1)
|
||||||
@@ -408,18 +433,20 @@ class LWIPRawImpl : public Socket {
|
|||||||
for (int i = 0; i < iovcnt; i++) {
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
||||||
if (err == -1) {
|
if (err == -1) {
|
||||||
if (written != 0)
|
if (written != 0) {
|
||||||
// if we already read some don't return an error
|
// if we already read some don't return an error
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
written += err;
|
written += err;
|
||||||
if ((size_t) err != iov[i].iov_len)
|
if ((size_t) err != iov[i].iov_len)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (written == 0)
|
if (written == 0) {
|
||||||
// no need to output if nothing written
|
// no need to output if nothing written
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
if (nodelay_) {
|
if (nodelay_) {
|
||||||
int err = internal_output();
|
int err = internal_output();
|
||||||
if (err == -1)
|
if (err == -1)
|
||||||
@@ -473,6 +500,10 @@ class LWIPRawImpl : public Socket {
|
|||||||
} else {
|
} else {
|
||||||
pbuf_cat(rx_buf_, pb);
|
pbuf_cat(rx_buf_, pb);
|
||||||
}
|
}
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
// Wake the main loop immediately so it can process the received data.
|
||||||
|
socket_wake();
|
||||||
|
#endif
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,7 +643,7 @@ class LWIPRawListenImpl : public LWIPRawImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
err_t accept_fn(struct tcp_pcb *newpcb, err_t err) {
|
err_t accept_fn_(struct tcp_pcb *newpcb, err_t err) {
|
||||||
LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err);
|
LWIP_LOG("accept(newpcb=%p err=%d)", newpcb, err);
|
||||||
if (err != ERR_OK || newpcb == nullptr) {
|
if (err != ERR_OK || newpcb == nullptr) {
|
||||||
// "An error code if there has been an error accepting. Only return ERR_ABRT if you have
|
// "An error code if there has been an error accepting. Only return ERR_ABRT if you have
|
||||||
@@ -633,12 +664,16 @@ class LWIPRawListenImpl : public LWIPRawImpl {
|
|||||||
sock->init();
|
sock->init();
|
||||||
accepted_sockets_[accepted_socket_count_++] = std::move(sock);
|
accepted_sockets_[accepted_socket_count_++] = std::move(sock);
|
||||||
LWIP_LOG("Accepted connection, queue size: %d", accepted_socket_count_);
|
LWIP_LOG("Accepted connection, queue size: %d", accepted_socket_count_);
|
||||||
|
#ifdef USE_ESP8266
|
||||||
|
// Wake the main loop immediately so it can accept the new connection.
|
||||||
|
socket_wake();
|
||||||
|
#endif
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
|
static err_t s_accept_fn(void *arg, struct tcp_pcb *newpcb, err_t err) {
|
||||||
LWIPRawListenImpl *arg_this = reinterpret_cast<LWIPRawListenImpl *>(arg);
|
LWIPRawListenImpl *arg_this = reinterpret_cast<LWIPRawListenImpl *>(arg);
|
||||||
return arg_this->accept_fn(newpcb, err);
|
return arg_this->accept_fn_(newpcb, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept queue - holds incoming connections briefly until the event loop calls accept()
|
// Accept queue - holds incoming connections briefly until the event loop calls accept()
|
||||||
|
|||||||
@@ -82,6 +82,15 @@ socklen_t set_sockaddr(struct sockaddr *addr, socklen_t addrlen, const std::stri
|
|||||||
/// Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
|
/// Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
|
||||||
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port);
|
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port);
|
||||||
|
|
||||||
|
#if defined(USE_ESP8266) && defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||||
|
/// Delay that can be woken early by socket activity.
|
||||||
|
/// On ESP8266, lwip callbacks set a flag and call esp_schedule() to wake the delay.
|
||||||
|
void socket_delay(uint32_t ms);
|
||||||
|
|
||||||
|
/// Called by lwip callbacks to signal socket activity and wake delay.
|
||||||
|
void socket_wake();
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace socket
|
} // namespace socket
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from enum import Enum
|
|||||||
|
|
||||||
from esphome.enum import StrEnum
|
from esphome.enum import StrEnum
|
||||||
|
|
||||||
__version__ = "2025.12.0b1"
|
__version__ = "2025.12.0b2"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
#include "esphome/components/status_led/status_led.h"
|
#include "esphome/components/status_led/status_led.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_ESP8266) && defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||||
|
#include "esphome/components/socket/socket.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SOCKET_SELECT_SUPPORT
|
#ifdef USE_SOCKET_SELECT_SUPPORT
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
@@ -627,6 +631,9 @@ void Application::yield_with_select_(uint32_t delay_ms) {
|
|||||||
// No sockets registered, use regular delay
|
// No sockets registered, use regular delay
|
||||||
delay(delay_ms);
|
delay(delay_ms);
|
||||||
}
|
}
|
||||||
|
#elif defined(USE_ESP8266) && defined(USE_SOCKET_IMPL_LWIP_TCP)
|
||||||
|
// No select support but can wake on socket activity via esp_schedule()
|
||||||
|
socket::socket_delay(delay_ms);
|
||||||
#else
|
#else
|
||||||
// No select support, use regular delay
|
// No select support, use regular delay
|
||||||
delay(delay_ms);
|
delay(delay_ms);
|
||||||
|
|||||||
@@ -480,22 +480,13 @@ std::string base64_encode(const uint8_t *buf, size_t buf_len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf_len) {
|
size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf_len) {
|
||||||
std::vector<uint8_t> decoded = base64_decode(encoded_string);
|
|
||||||
if (decoded.size() > buf_len) {
|
|
||||||
ESP_LOGW(TAG, "Base64 decode: buffer too small, truncating");
|
|
||||||
decoded.resize(buf_len);
|
|
||||||
}
|
|
||||||
memcpy(buf, decoded.data(), decoded.size());
|
|
||||||
return decoded.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> base64_decode(const std::string &encoded_string) {
|
|
||||||
int in_len = encoded_string.size();
|
int in_len = encoded_string.size();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
int in = 0;
|
int in = 0;
|
||||||
|
size_t out = 0;
|
||||||
uint8_t char_array_4[4], char_array_3[3];
|
uint8_t char_array_4[4], char_array_3[3];
|
||||||
std::vector<uint8_t> ret;
|
bool truncated = false;
|
||||||
|
|
||||||
// SAFETY: The loop condition checks is_base64() before processing each character.
|
// SAFETY: The loop condition checks is_base64() before processing each character.
|
||||||
// This ensures base64_find_char() is only called on valid base64 characters,
|
// This ensures base64_find_char() is only called on valid base64 characters,
|
||||||
@@ -511,8 +502,13 @@ std::vector<uint8_t> base64_decode(const std::string &encoded_string) {
|
|||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
for (i = 0; (i < 3); i++)
|
for (i = 0; i < 3; i++) {
|
||||||
ret.push_back(char_array_3[i]);
|
if (out < buf_len) {
|
||||||
|
buf[out++] = char_array_3[i];
|
||||||
|
} else {
|
||||||
|
truncated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -528,10 +524,28 @@ std::vector<uint8_t> base64_decode(const std::string &encoded_string) {
|
|||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||||
|
|
||||||
for (j = 0; (j < i - 1); j++)
|
for (j = 0; j < i - 1; j++) {
|
||||||
ret.push_back(char_array_3[j]);
|
if (out < buf_len) {
|
||||||
|
buf[out++] = char_array_3[j];
|
||||||
|
} else {
|
||||||
|
truncated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (truncated) {
|
||||||
|
ESP_LOGW(TAG, "Base64 decode: buffer too small, truncating");
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> base64_decode(const std::string &encoded_string) {
|
||||||
|
// Calculate maximum decoded size: every 4 base64 chars = 3 bytes
|
||||||
|
size_t max_len = ((encoded_string.size() + 3) / 4) * 3;
|
||||||
|
std::vector<uint8_t> ret(max_len);
|
||||||
|
size_t actual_len = base64_decode(encoded_string, ret.data(), max_len);
|
||||||
|
ret.resize(actual_len);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user