112 lines
3.7 KiB
C++
112 lines
3.7 KiB
C++
/* selector.cpp — request selector implementation for the Teensy 4.1.
|
|
*
|
|
* Phase 3 of plans/teensy41_role_path_migration.md.
|
|
*
|
|
* Ports the selector decision tree from src/selector.c:745. The JSON parsing
|
|
* (extracting role/role_path/nostr_index from the cJSON options object) is
|
|
* done in dispatch.cpp, which calls selector_resolve() with the populated
|
|
* selector_request_t.
|
|
*/
|
|
#include "selector.h"
|
|
|
|
#include <string.h>
|
|
|
|
/* ====================================================================
|
|
* Selector request
|
|
* ==================================================================== */
|
|
|
|
__attribute__((section(".flashmem")))
|
|
void selector_request_init(selector_request_t *req) {
|
|
if (req != NULL) {
|
|
memset(req, 0, sizeof(*req));
|
|
}
|
|
}
|
|
|
|
/* ====================================================================
|
|
* Selector resolution
|
|
* ==================================================================== */
|
|
|
|
/* Resolve a selector request against the role table.
|
|
*
|
|
* Ported from src/selector.c:745 (selector_resolve). The decision tree:
|
|
* 1. nostr_index present → DEPRECATED (error 2006 in the wire protocol)
|
|
* 2. role_path without role → ROLE_REQUIRED
|
|
* 3. role + role_path → find role, verify path matches template + range
|
|
* 4. role only, fixed path (no %d) → use it
|
|
* 5. role only, template path → PATH_REQUIRED
|
|
* 6. neither → default role ("main"), else NO_DEFAULT
|
|
*
|
|
* On success, *out points to the matching role entry in the table. The
|
|
* caller uses req->role_path (if has_role_path) or the role's role_path
|
|
* (if fixed) for key derivation. */
|
|
__attribute__((section(".flashmem")))
|
|
int selector_resolve(const selector_request_t *req, role_table_t *table,
|
|
role_entry_t **out) {
|
|
role_entry_t *match = NULL;
|
|
|
|
if (out != NULL) {
|
|
*out = NULL;
|
|
}
|
|
|
|
if (req == NULL || table == NULL || out == NULL) {
|
|
return SELECTOR_ERR_NOT_FOUND;
|
|
}
|
|
|
|
/* ---- Deprecated selectors: reject with clear error codes ---- */
|
|
|
|
/* nostr_index is deprecated */
|
|
if (req->has_nostr_index) {
|
|
return SELECTOR_ERR_NOSTR_INDEX_DEPRECATED;
|
|
}
|
|
|
|
/* role_path without role is not allowed */
|
|
if (req->has_role_path && !req->has_role) {
|
|
return SELECTOR_ERR_ROLE_REQUIRED;
|
|
}
|
|
|
|
/* ---- New model: role + role_path combined ---- */
|
|
|
|
if (req->has_role && req->has_role_path) {
|
|
/* Combined selector: look up role by name, verify path matches template */
|
|
match = role_table_find_by_name(table, req->role_name);
|
|
if (match == NULL) {
|
|
return SELECTOR_ERR_NOT_FOUND;
|
|
}
|
|
|
|
/* Verify the requested path matches the role's template AND that the
|
|
* extracted index falls within the role's allowed range. */
|
|
if (!role_path_matches_with_range(req->role_path, match)) {
|
|
return SELECTOR_ERR_PATH_MISMATCH;
|
|
}
|
|
|
|
*out = match;
|
|
return SELECTOR_OK;
|
|
}
|
|
|
|
if (req->has_role && !req->has_role_path) {
|
|
/* Role specified without path — check if role has a fixed path (no %d) */
|
|
match = role_table_find_by_name(table, req->role_name);
|
|
if (match == NULL) {
|
|
return SELECTOR_ERR_NOT_FOUND;
|
|
}
|
|
|
|
/* If the role has a fixed path (no variable segments), use it */
|
|
if (strstr(match->role_path, "%d") == NULL) {
|
|
*out = match;
|
|
return SELECTOR_OK;
|
|
}
|
|
|
|
/* Role has variable path template — path is required */
|
|
return SELECTOR_ERR_PATH_REQUIRED;
|
|
}
|
|
|
|
/* No selectors at all — try default role */
|
|
match = role_table_get_default(table);
|
|
if (match == NULL) {
|
|
return SELECTOR_ERR_NO_DEFAULT;
|
|
}
|
|
|
|
*out = match;
|
|
return SELECTOR_OK;
|
|
}
|