Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
6.6 KiB

  1. /*
  2. API MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if API_SUPPORT
  6. #include "api.h"
  7. #include "system.h"
  8. #include "web.h"
  9. #include "ws.h"
  10. typedef struct {
  11. char * key;
  12. api_get_callback_f getFn = NULL;
  13. api_put_callback_f putFn = NULL;
  14. } web_api_t;
  15. std::vector<web_api_t> _apis;
  16. // -----------------------------------------------------------------------------
  17. bool _apiEnabled() {
  18. return getSetting("apiEnabled", 1 == API_ENABLED);
  19. }
  20. bool _apiRestFul() {
  21. return getSetting("apiRestFul", 1 == API_RESTFUL);
  22. }
  23. String _apiKey() {
  24. return getSetting("apiKey", API_KEY);
  25. }
  26. bool _apiWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  27. return (strncmp(key, "api", 3) == 0);
  28. }
  29. void _apiWebSocketOnConnected(JsonObject& root) {
  30. root["apiEnabled"] = _apiEnabled();
  31. root["apiKey"] = _apiKey();
  32. root["apiRestFul"] = _apiRestFul();
  33. root["apiRealTime"] = getSetting("apiRealTime", 1 == API_REAL_TIME_VALUES);
  34. }
  35. void _apiConfigure() {
  36. // Nothing to do
  37. }
  38. // -----------------------------------------------------------------------------
  39. // API
  40. // -----------------------------------------------------------------------------
  41. bool _authAPI(AsyncWebServerRequest *request) {
  42. const auto key = _apiKey();
  43. if (!key.length() || !_apiEnabled()) {
  44. DEBUG_MSG_P(PSTR("[WEBSERVER] HTTP API is not enabled\n"));
  45. request->send(403);
  46. return false;
  47. }
  48. AsyncWebParameter* keyParam = request->getParam("apikey", (request->method() == HTTP_PUT));
  49. if (!keyParam || !keyParam->value().equals(key)) {
  50. DEBUG_MSG_P(PSTR("[WEBSERVER] Wrong / missing apikey parameter\n"));
  51. request->send(403);
  52. return false;
  53. }
  54. return true;
  55. }
  56. bool _asJson(AsyncWebServerRequest *request) {
  57. bool asJson = false;
  58. if (request->hasHeader("Accept")) {
  59. AsyncWebHeader* h = request->getHeader("Accept");
  60. asJson = h->value().equals("application/json");
  61. }
  62. return asJson;
  63. }
  64. void _onAPIsText(AsyncWebServerRequest *request) {
  65. AsyncResponseStream *response = request->beginResponseStream("text/plain");
  66. String output;
  67. output.reserve(48);
  68. for (auto& api : _apis) {
  69. output = "";
  70. output += api.key;
  71. output += " -> ";
  72. output += "/api/";
  73. output += api.key;
  74. output += '\n';
  75. response->write(output.c_str());
  76. }
  77. request->send(response);
  78. }
  79. constexpr const size_t API_JSON_BUFFER_SIZE = 1024;
  80. void _onAPIsJson(AsyncWebServerRequest *request) {
  81. DynamicJsonBuffer jsonBuffer(API_JSON_BUFFER_SIZE);
  82. JsonObject& root = jsonBuffer.createObject();
  83. constexpr const int BUFFER_SIZE = 48;
  84. for (unsigned int i=0; i < _apis.size(); i++) {
  85. char buffer[BUFFER_SIZE] = {0};
  86. int res = snprintf(buffer, sizeof(buffer), "/api/%s", _apis[i].key);
  87. if ((res < 0) || (res > (BUFFER_SIZE - 1))) {
  88. request->send(500);
  89. return;
  90. }
  91. root[_apis[i].key] = buffer;
  92. }
  93. AsyncResponseStream *response = request->beginResponseStream("application/json");
  94. root.printTo(*response);
  95. request->send(response);
  96. }
  97. void _onAPIs(AsyncWebServerRequest *request) {
  98. webLog(request);
  99. if (!_authAPI(request)) return;
  100. bool asJson = _asJson(request);
  101. String output;
  102. if (asJson) {
  103. _onAPIsJson(request);
  104. } else {
  105. _onAPIsText(request);
  106. }
  107. }
  108. void _onRPC(AsyncWebServerRequest *request) {
  109. webLog(request);
  110. if (!_authAPI(request)) return;
  111. //bool asJson = _asJson(request);
  112. int response = 404;
  113. if (request->hasParam("action")) {
  114. AsyncWebParameter* p = request->getParam("action");
  115. String action = p->value();
  116. DEBUG_MSG_P(PSTR("[RPC] Action: %s\n"), action.c_str());
  117. if (action.equals("reboot")) {
  118. response = 200;
  119. deferredReset(100, CUSTOM_RESET_RPC);
  120. }
  121. }
  122. request->send(response);
  123. }
  124. bool _apiRequestCallback(AsyncWebServerRequest *request) {
  125. String url = request->url();
  126. // Main API entry point
  127. if (url.equals("/api") || url.equals("/apis")) {
  128. _onAPIs(request);
  129. return true;
  130. }
  131. // Main RPC entry point
  132. if (url.equals("/rpc")) {
  133. _onRPC(request);
  134. return true;
  135. }
  136. // Not API request
  137. if (!url.startsWith("/api/")) return false;
  138. for (unsigned char i=0; i < _apis.size(); i++) {
  139. // Search API url
  140. web_api_t api = _apis[i];
  141. if (!url.endsWith(api.key)) continue;
  142. // Log and check credentials
  143. webLog(request);
  144. if (!_authAPI(request)) return false;
  145. // Check if its a PUT
  146. if (api.putFn != NULL) {
  147. if (!_apiRestFul() || (request->method() == HTTP_PUT)) {
  148. if (request->hasParam("value", request->method() == HTTP_PUT)) {
  149. AsyncWebParameter* p = request->getParam("value", request->method() == HTTP_PUT);
  150. (api.putFn)((p->value()).c_str());
  151. }
  152. }
  153. }
  154. // Get response from callback
  155. char value[API_BUFFER_SIZE] = {0};
  156. (api.getFn)(value, API_BUFFER_SIZE);
  157. // The response will be a 404 NOT FOUND if the resource is not available
  158. if (0 == value[0]) {
  159. DEBUG_MSG_P(PSTR("[API] Sending 404 response\n"));
  160. request->send(404);
  161. return false;
  162. }
  163. DEBUG_MSG_P(PSTR("[API] Sending response '%s'\n"), value);
  164. // Format response according to the Accept header
  165. if (_asJson(request)) {
  166. char buffer[64];
  167. if (isNumber(value)) {
  168. snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": %s }"), api.key, value);
  169. } else {
  170. snprintf_P(buffer, sizeof(buffer), PSTR("{ \"%s\": \"%s\" }"), api.key, value);
  171. }
  172. request->send(200, "application/json", buffer);
  173. } else {
  174. request->send(200, "text/plain", value);
  175. }
  176. return true;
  177. }
  178. return false;
  179. }
  180. // -----------------------------------------------------------------------------
  181. void apiRegister(const char * key, api_get_callback_f getFn, api_put_callback_f putFn) {
  182. // Store it
  183. web_api_t api;
  184. api.key = strdup(key);
  185. api.getFn = getFn;
  186. api.putFn = putFn;
  187. _apis.push_back(api);
  188. }
  189. void apiSetup() {
  190. _apiConfigure();
  191. wsRegister()
  192. .onVisible([](JsonObject& root) { root["apiVisible"] = 1; })
  193. .onConnected(_apiWebSocketOnConnected)
  194. .onKeyCheck(_apiWebSocketOnKeyCheck);
  195. webRequestRegister(_apiRequestCallback);
  196. espurnaRegisterReload(_apiConfigure);
  197. }
  198. #endif // API_SUPPORT