Files
MeshCore/src/helpers/ClientACLCLI.h
T

82 lines
2.9 KiB
C++

#pragma once
#include "ClientACL.h"
#include <Utils.h>
#include <stdio.h>
#include <string.h>
namespace mesh {
namespace cli {
// Call after the role's admin authorization check. Bare local `get acl`
// retains its streamed listing; radio requests and explicit pages use one
// bounded reply. Callers reserve three bytes for the companion CLI prefix.
inline bool handleACLGet(const ClientACL& acl, const char* command,
char* reply, size_t capacity, bool local) {
static const char prefix[] = "get acl";
const size_t prefix_len = sizeof(prefix) - 1;
if (strncmp(command, prefix, prefix_len) != 0) return false;
const char* arg = command + prefix_len;
if (*arg && *arg != ' ' && *arg != '\t' && *arg != '\r' && *arg != '\n') return false;
if (local && *arg == 0) return false;
if (reply == nullptr || capacity == 0) return true;
while (*arg == ' ' || *arg == '\t') ++arg;
unsigned page = 1;
if (*arg >= '0' && *arg <= '9') {
page = 0;
do {
page = page * 10 + unsigned(*arg++ - '0');
if (page > 65535) break;
} while (*arg >= '0' && *arg <= '9');
}
while (*arg == ' ' || *arg == '\t' || *arg == '\r' || *arg == '\n') ++arg;
if (*arg || page == 0 || page > 65535) {
snprintf(reply, capacity, "Err - usage: get acl [page]");
return true;
}
unsigned total = 0;
for (int i = 0; i < acl.getNumClients(); ++i) {
if (acl.getClientByIdx(i)->permissions != 0) ++total;
}
if (total == 0 && page == 1) {
snprintf(reply, capacity, "ACL: empty");
return true;
}
static constexpr unsigned PAGE_SIZE = 2;
const unsigned pages = total == 0 ? 1 : (total + PAGE_SIZE - 1) / PAGE_SIZE;
if (page > pages) {
snprintf(reply, capacity, "Err - ACL page range: 1-%u", pages);
return true;
}
const unsigned offset = (page - 1) * PAGE_SIZE;
const unsigned rows = total - offset < PAGE_SIZE ? total - offset : PAGE_SIZE;
const int header_len = snprintf(reply, capacity, "ACL %u/%u", page, pages);
// Never return a truncated key if a caller supplies a smaller buffer.
static constexpr size_t ROW_BYTES = 4 + PUB_KEY_SIZE * 2;
if (header_len < 0 || size_t(header_len) >= capacity
|| rows > (capacity - size_t(header_len) - 1) / ROW_BYTES) {
snprintf(reply, capacity, "Err - ACL reply too small");
return true;
}
size_t used = size_t(header_len);
unsigned active_index = 0;
unsigned written = 0;
for (int i = 0; i < acl.getNumClients() && written < rows; ++i) {
const auto* client = acl.getClientByIdx(i);
if (client->permissions == 0) continue; // match the local listing
if (active_index++ < offset) continue;
char key[PUB_KEY_SIZE * 2 + 1];
Utils::toHex(key, client->id.pub_key, PUB_KEY_SIZE);
used += snprintf(reply + used, capacity - used, "\n%02X %s",
unsigned(client->permissions), key);
++written;
}
return true;
}
} // namespace cli
} // namespace mesh