CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
PluginManifest.cpp
Go to the documentation of this file.
2#include "cdc_log.h"
3
4#include "cJSON.h"
5
6#include <cstdlib>
7#include <cstring>
8
9namespace cdc::plugin_manager {
10
11static const char* TAG = "PLG_MAN";
12
13static std::string str_or(const cJSON* node, const char* key, const char* dflt = "")
14{
15 const cJSON* item = cJSON_GetObjectItemCaseSensitive(node, key);
16 if (cJSON_IsString(item) && item->valuestring) {
17 return item->valuestring;
18 }
19 return dflt;
20}
21
22static bool parse_api_level(const std::string& s, uint16_t& major, uint16_t& minor)
23{
24 if (s.empty()) return false;
25 const char* dot = std::strchr(s.c_str(), '.');
26 if (!dot) return false;
27 major = static_cast<uint16_t>(std::atoi(s.c_str()));
28 minor = static_cast<uint16_t>(std::atoi(dot + 1));
29 return true;
30}
31
32static void parse_localized(const cJSON* root, std::map<std::string, LocalizedString>& out)
33{
34 if (!cJSON_IsObject(root)) return;
35 for (cJSON* it = root->child; it != nullptr; it = it->next) {
36 if (!it->string || !cJSON_IsObject(it)) continue;
38 for (cJSON* lang_it = it->child; lang_it != nullptr; lang_it = lang_it->next) {
39 if (lang_it->string && cJSON_IsString(lang_it) && lang_it->valuestring) {
40 ls.by_lang[lang_it->string] = lang_it->valuestring;
41 }
42 }
43 out[it->string] = std::move(ls);
44 }
45}
46
47static void parse_capabilities(const cJSON* root, PluginCapabilities& cap)
48{
49 if (!cJSON_IsObject(root)) return;
50
51 auto get_bool = [&](const char* key, bool dflt) {
52 const cJSON* n = cJSON_GetObjectItemCaseSensitive(root, key);
53 return cJSON_IsBool(n) ? cJSON_IsTrue(n) : dflt;
54 };
55
56 cap.wifi = get_bool("wifi", false);
57 cap.ble = get_bool("ble", false);
58 cap.http = get_bool("http", false);
59 cap.socket = get_bool("socket", false);
60 cap.ui_exclusive = get_bool("ui_exclusive", false);
61 cap.display_lowlevel = get_bool("display_lowlevel", false);
62 cap.sao = get_bool("sao", false);
63 cap.grove = get_bool("grove", false);
64 cap.pixel_strip = get_bool("pixel_strip", false);
65 cap.background = get_bool("background", false);
66 cap.usb_cdc = get_bool("usb_cdc", false);
67 cap.prevent_sleep = get_bool("prevent_sleep", false);
68 cap.autoload = get_bool("autoload", false);
69 cap.vfat = get_bool("vfat", false);
70
71 cap.nvs_namespace = str_or(root, "nvs_namespace", "");
72
73 auto get_int_array_u8 = [&](const char* key, std::vector<uint8_t>& out) {
74 const cJSON* n = cJSON_GetObjectItemCaseSensitive(root, key);
75 if (!cJSON_IsArray(n)) return;
76 for (cJSON* it = n->child; it != nullptr; it = it->next) {
77 if (cJSON_IsNumber(it)) {
78 out.push_back(static_cast<uint8_t>(it->valueint));
79 }
80 }
81 };
82
83 auto get_str_array = [&](const char* key, std::vector<std::string>& out) {
84 const cJSON* n = cJSON_GetObjectItemCaseSensitive(root, key);
85 if (!cJSON_IsArray(n)) return;
86 for (cJSON* it = n->child; it != nullptr; it = it->next) {
87 if (cJSON_IsString(it) && it->valuestring) {
88 out.push_back(it->valuestring);
89 }
90 }
91 };
92
93 get_str_array ("rmem", cap.rmem);
94 get_str_array ("ecc", cap.ecc);
95 get_str_array ("ble_service_uuids", cap.ble_service_uuids);
96 get_str_array ("message_types", cap.message_types);
97 get_int_array_u8 ("gpio_pins", cap.gpio_pins);
98 get_int_array_u8 ("pwm_pins", cap.pwm_pins);
99 get_int_array_u8 ("adc_pins", cap.adc_pins);
100 get_int_array_u8 ("i2c_bus", cap.i2c_bus);
101}
102
103static void parse_prereqs(const cJSON* root, std::vector<PrerequisiteSpec>& out)
104{
105 if (!cJSON_IsObject(root)) return;
106 for (cJSON* it = root->child; it != nullptr; it = it->next) {
107 if (!it->string || !cJSON_IsObject(it)) continue;
108 PrerequisiteSpec spec;
109 spec.name = it->string;
110 spec.on_fail = "abort";
111 for (cJSON* p = it->child; p != nullptr; p = p->next) {
112 if (!p->string) continue;
113 if (cJSON_IsString(p) && p->valuestring) {
114 if (std::strcmp(p->string, "on_fail") == 0) {
115 spec.on_fail = p->valuestring;
116 } else {
117 spec.params[p->string] = p->valuestring;
118 }
119 } else if (cJSON_IsNumber(p)) {
120 spec.params[p->string] = std::to_string(p->valueint);
121 } else if (cJSON_IsBool(p)) {
122 spec.params[p->string] = cJSON_IsTrue(p) ? "true" : "false";
123 }
124 }
125 out.push_back(std::move(spec));
126 }
127}
128
129bool PluginManifest::parse(const char* json, size_t len, PluginManifest& out)
130{
131 cJSON* root = cJSON_ParseWithLength(json, len);
132 if (!root) {
133 LOG_E(TAG, "JSON parse failed");
134 return false;
135 }
136
137 out.id = str_or(root, "id");
138 out.version = str_or(root, "version");
139 out.author = str_or(root, "author");
140 out.icon = str_or(root, "icon");
141 out.host_api_level_min = str_or(root, "host_api_level_min");
142
143 if (out.id.empty() || out.version.empty() || out.host_api_level_min.empty()) {
144 LOG_E(TAG, "manifest missing required fields");
145 cJSON_Delete(root);
146 return false;
147 }
148
150 LOG_E(TAG, "invalid host_api_level_min '%s'", out.host_api_level_min.c_str());
151 cJSON_Delete(root);
152 return false;
153 }
154
155 if (const cJSON* lm = cJSON_GetObjectItemCaseSensitive(root, "linear_memory_kb");
156 cJSON_IsNumber(lm)) {
157 out.linear_memory_kb = static_cast<uint32_t>(lm->valueint);
158 }
159
160 if (const cJSON* i18n = cJSON_GetObjectItemCaseSensitive(root, "i18n")) {
161 out.default_language = str_or(i18n, "default_language", "en");
162 parse_localized(cJSON_GetObjectItemCaseSensitive(i18n, "meta"), out.i18n_meta);
163 parse_localized(cJSON_GetObjectItemCaseSensitive(i18n, "strings"), out.i18n_strings);
164 }
165
166 parse_capabilities(cJSON_GetObjectItemCaseSensitive(root, "capabilities"), out.capabilities);
167 parse_prereqs (cJSON_GetObjectItemCaseSensitive(root, "prerequisites"), out.prerequisites);
168
169 cJSON_Delete(root);
170 return true;
171}
172
173} // namespace cdc::plugin_manager
In-memory representation of a plugin's meta.json.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_E(tag, fmt,...)
Definition cdc_log.h:145
static void parse_prereqs(const cJSON *root, std::vector< PrerequisiteSpec > &out)
static void parse_capabilities(const cJSON *root, PluginCapabilities &cap)
static const char * TAG
static std::string str_or(const cJSON *node, const char *key, const char *dflt="")
static void parse_localized(const cJSON *root, std::map< std::string, LocalizedString > &out)
static bool parse_api_level(const std::string &s, uint16_t &major, uint16_t &minor)
std::map< std::string, std::string > by_lang
bool vfat
Allow sandboxed file access on the plugins FAT partition via the host_fs_* API. The plugin can only t...
bool autoload
Start this plugin as a resident background instance at badge boot. Plugins without this flag stay unl...
std::vector< std::string > ble_service_uuids
std::vector< std::string > message_types
bool background
Keep running and ticking in the background after the user leaves the plugin's view,...
std::vector< PrerequisiteSpec > prerequisites
std::map< std::string, LocalizedString > i18n_meta
static bool parse(const char *json, size_t len, PluginManifest &out)
Parse meta.json content. Returns false on schema errors.
std::map< std::string, LocalizedString > i18n_strings
std::map< std::string, std::string > params