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 cap.vcard = get_bool("vcard", false);
71 cap.net_listen = get_bool("net_listen", false);
72
73 cap.nvs_namespace = str_or(root, "nvs_namespace", "");
74
75 auto get_int_array_u8 = [&](const char* key, std::vector<uint8_t>& out) {
76 const cJSON* n = cJSON_GetObjectItemCaseSensitive(root, key);
77 if (!cJSON_IsArray(n)) return;
78 for (cJSON* it = n->child; it != nullptr; it = it->next) {
79 if (cJSON_IsNumber(it)) {
80 out.push_back(static_cast<uint8_t>(it->valueint));
81 }
82 }
83 };
84
85 auto get_str_array = [&](const char* key, std::vector<std::string>& out) {
86 const cJSON* n = cJSON_GetObjectItemCaseSensitive(root, key);
87 if (!cJSON_IsArray(n)) return;
88 for (cJSON* it = n->child; it != nullptr; it = it->next) {
89 if (cJSON_IsString(it) && it->valuestring) {
90 out.push_back(it->valuestring);
91 }
92 }
93 };
94
95 get_str_array ("rmem", cap.rmem);
96 get_str_array ("ecc", cap.ecc);
97 get_str_array ("ble_service_uuids", cap.ble_service_uuids);
98 get_str_array ("message_types", cap.message_types);
99 get_str_array ("provides", cap.provides);
100 get_int_array_u8 ("gpio_pins", cap.gpio_pins);
101 get_int_array_u8 ("pwm_pins", cap.pwm_pins);
102 get_int_array_u8 ("adc_pins", cap.adc_pins);
103 get_int_array_u8 ("i2c_bus", cap.i2c_bus);
104}
105
106static void parse_prereqs(const cJSON* root, std::vector<PrerequisiteSpec>& out)
107{
108 if (!cJSON_IsObject(root)) return;
109 for (cJSON* it = root->child; it != nullptr; it = it->next) {
110 if (!it->string || !cJSON_IsObject(it)) continue;
111 PrerequisiteSpec spec;
112 spec.name = it->string;
113 spec.on_fail = "abort";
114 for (cJSON* p = it->child; p != nullptr; p = p->next) {
115 if (!p->string) continue;
116 if (cJSON_IsString(p) && p->valuestring) {
117 if (std::strcmp(p->string, "on_fail") == 0) {
118 spec.on_fail = p->valuestring;
119 } else {
120 spec.params[p->string] = p->valuestring;
121 }
122 } else if (cJSON_IsNumber(p)) {
123 spec.params[p->string] = std::to_string(p->valueint);
124 } else if (cJSON_IsBool(p)) {
125 spec.params[p->string] = cJSON_IsTrue(p) ? "true" : "false";
126 }
127 }
128 out.push_back(std::move(spec));
129 }
130}
131
132bool PluginManifest::parse(const char* json, size_t len, PluginManifest& out)
133{
134 cJSON* root = cJSON_ParseWithLength(json, len);
135 if (!root) {
136 LOG_E(TAG, "JSON parse failed");
137 return false;
138 }
139
140 out.id = str_or(root, "id");
141 out.version = str_or(root, "version");
142 out.author = str_or(root, "author");
143 out.icon = str_or(root, "icon");
144 out.host_api_level_min = str_or(root, "host_api_level_min");
145
146 if (out.id.empty() || out.version.empty() || out.host_api_level_min.empty()) {
147 LOG_E(TAG, "manifest missing required fields");
148 cJSON_Delete(root);
149 return false;
150 }
151
153 LOG_E(TAG, "invalid host_api_level_min '%s'", out.host_api_level_min.c_str());
154 cJSON_Delete(root);
155 return false;
156 }
157
158 if (const cJSON* lm = cJSON_GetObjectItemCaseSensitive(root, "linear_memory_kb");
159 cJSON_IsNumber(lm)) {
160 out.linear_memory_kb = static_cast<uint32_t>(lm->valueint);
161 }
162
163 if (const cJSON* i18n = cJSON_GetObjectItemCaseSensitive(root, "i18n")) {
164 out.default_language = str_or(i18n, "default_language", "en");
165 parse_localized(cJSON_GetObjectItemCaseSensitive(i18n, "meta"), out.i18n_meta);
166 parse_localized(cJSON_GetObjectItemCaseSensitive(i18n, "strings"), out.i18n_strings);
167 }
168
169 parse_capabilities(cJSON_GetObjectItemCaseSensitive(root, "capabilities"), out.capabilities);
170 parse_prereqs (cJSON_GetObjectItemCaseSensitive(root, "prerequisites"), out.prerequisites);
171
172 cJSON_Delete(root);
173 return true;
174}
175
176} // 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 vcard
Allow read access to the badge's own vCard and the received vCard store via the host_vcard_* API.
std::vector< std::string > provides
bool net_listen
Allow the plugin to run an inbound TCP listener via the host_net_* API (the plugin picks the port)....
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