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.

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