├── LICENSE
├── README.md
├── examples
├── BLE
│ └── BLE.ino
├── Minimal
│ └── Minimal.ino
└── RPC
│ └── RPC.ino
├── library.properties
├── posix
├── Makefile
└── main.c
└── src
├── mDash.c
├── mDash.h
├── mongoose.c
└── mongoose.h
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2019-2022 Cesanta Software Limited
2 | All rights reserved
3 |
4 | This software is dual-licensed: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License version 2 as
6 | published by the Free Software Foundation. For the terms of this
7 | license, see .
8 |
9 | You are free to use this software under the terms of the GNU General
10 | Public License, but WITHOUT ANY WARRANTY; without even the implied
11 | warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | See the GNU General Public License for more details.
13 |
14 | Alternatively, you can license this software under a commercial
15 | license, as set out in .
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Arduino / ESP-IDF client library for mdash.net
2 |
3 | See [Documentation](https://mdash.net/docs/) for the quick start guide.
4 |
5 | In order to simulate an mDash device on your Mac/Linux workstation,
6 | register a device on mDash and run the following command from the root of
7 | this repo:
8 |
9 | ```sh
10 | $ make -C posix ARGS="-pass DEVICE_MDASH_PASSWORD"
11 | ```
12 |
13 | # See also
14 | - [Mongoose Web Server Library](https://mongoose.ws/) - a robust, open-source solution licensed under GPLv2, designed to seamlessly integrate web server functionality into your embedded devices.
15 | - With complementary [Mongoose Wizard](https://mongoose.ws/wizard/) - a no-code visual tool that enables rapid WebUI creation without the need for frontend expertise.
16 |
--------------------------------------------------------------------------------
/examples/BLE/BLE.ino:
--------------------------------------------------------------------------------
1 | // This sketch catches BLE advertisements and sends them to MQTT
2 | // 1. In the Arduino IDE, choose Tools / Board / ESP32 Dev module
3 | // 2. In the Arduino IDE, choose Tools / Partition Scheme / Minimal SPIFFS
4 | // 3. Flash this sketch to the ESP32 board
5 |
6 | #define MDASH_APP_NAME "ble-gateway"
7 | #include
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include
15 |
16 | #define WIFI_NETWORK "MyWifiNetworkName"
17 | #define WIFI_PASSWORD "MyWifiPassword"
18 | #define DEVICE_PASSWORD "mDashDeviceToken"
19 |
20 | int scanTime = 3; // In seconds
21 | BLEScan *pBLEScan;
22 |
23 | class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
24 | void onResult(BLEAdvertisedDevice d) {
25 | char addr[20];
26 | const unsigned char *p = d.getPayload();
27 | int plen = d.getPayloadLength();
28 |
29 | snprintf(addr, sizeof(addr), "%s", d.getAddress().toString().c_str());
30 | mDashNotify("DB.Save", "{\"mac\":%Q,\"adv\":%H}", addr, plen, p);
31 | Serial.printf("Advertised Device: %s\n", addr);
32 | }
33 | };
34 |
35 | void setup() {
36 | Serial.begin(115200);
37 |
38 | WiFi.begin(WIFI_NETWORK, WIFI_PASSWORD);
39 | while (WiFi.status() != WL_CONNECTED) delay(500);
40 |
41 | mDashBegin(DEVICE_PASSWORD);
42 |
43 | BLEDevice::init("");
44 | pBLEScan = BLEDevice::getScan(); // create new scan
45 | pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
46 | pBLEScan->setActiveScan(true); // active scan uses more power
47 | pBLEScan->setInterval(100);
48 | pBLEScan->setWindow(99); // less or equal setInterval value
49 | }
50 |
51 | void loop() {
52 | BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
53 | pBLEScan->clearResults();
54 | delay(50);
55 | }
56 |
--------------------------------------------------------------------------------
/examples/Minimal/Minimal.ino:
--------------------------------------------------------------------------------
1 | // Minimal Arduino sketch for mdash.net
2 | //
3 | // - Install mDash library:
4 | // - Select "Sketch" → "Include Library" → "Manage Libraries"
5 | // - In the search field, type "mDash" and press Enter
6 | // - Click on "Install" to install the library
7 | // - Select "Tools" → "Board" → "ESP32 Dev Module"
8 | // - Select "Tools" → "Partitioning Scheme" → "Minimal SPIFFS"
9 | // - Select "Tools" → "Port" → your serial port
10 | // - Click on "Upload" button to build and flash the firmware
11 | //
12 | // See https://mdash.net/docs/ for the full IoT product reference impementation
13 |
14 | #define MDASH_APP_NAME "MinimalApp"
15 | #include
16 |
17 | #include
18 |
19 | #define WIFI_NETWORK "MyWifiNetworkName"
20 | #define WIFI_PASSWORD "MyWifiPassword"
21 | #define DEVICE_PASSWORD "mDashDeviceToken"
22 |
23 | void setup() {
24 | Serial.begin(115200);
25 | WiFi.begin(WIFI_NETWORK, WIFI_PASSWORD);
26 | while (WiFi.status() != WL_CONNECTED) {
27 | delay(500);
28 | Serial.print(".");
29 | }
30 | Serial.println("\nWiFi connected, IP address:");
31 | Serial.println(WiFi.localIP());
32 | mDashBegin(DEVICE_PASSWORD);
33 | }
34 |
35 | void loop() {
36 | delay(500);
37 | }
38 |
--------------------------------------------------------------------------------
/examples/RPC/RPC.ino:
--------------------------------------------------------------------------------
1 | // Custom RPC function example.
2 | // Read more on RPC at https://mdash.net/docs/userguide/remote_control.md
3 | // JSON-RPC API is documented at: https://mongoose.ws/documentation/
4 | //
5 | // - Install mDash library:
6 | // - Select "Sketch" → "Include Library" → "Manage Libraries"
7 | // - In the search field, type "mDash" and press Enter
8 | // - Click on "Install" to install the library
9 | // - Select "Tools" → "Board" → "ESP32 Dev Module"
10 | // - Select "Tools" → "Partitioning Scheme" → "Minimal SPIFFS"
11 | // - Select "Tools" → "Port" → your serial port
12 | // - Click on "Upload" button to build and flash the firmware
13 |
14 | #define MDASH_APP_NAME "RpcApp"
15 | #include
16 |
17 | #include
18 |
19 | #define WIFI_NETWORK "MyWifiNetworkName"
20 | #define WIFI_PASSWORD "MyWifiPassword"
21 | #define DEVICE_PASSWORD "mDashDeviceToken"
22 |
23 | static void rpc_gpio_write(struct mg_rpc_req *r) {
24 | long pin = mg_json_get_long(r->frame, "$.params.pin", -1);
25 | long val = mg_json_get_long(r->frame, "$.params.val", -1);
26 | if (pin < 0 || val < 0) {
27 | mg_rpc_err(r, 500, "%Q", "pin and val required");
28 | } else {
29 | pinMode(pin, OUTPUT);
30 | digitalWrite(pin, val);
31 | mg_rpc_ok(r, "true");
32 | }
33 | }
34 |
35 | void setup() {
36 | Serial.begin(115200);
37 | WiFi.begin(WIFI_NETWORK, WIFI_PASSWORD);
38 | mDashBegin(DEVICE_PASSWORD);
39 | mg_rpc_add(&g_rpcs, mg_str("GPIO.Write"), rpc_gpio_write, NULL);
40 | }
41 |
42 | void loop() {
43 | delay(3000);
44 | }
45 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=mDash
2 | version=1.2.16
3 | author=Cesanta Software Limited
4 | maintainer=Cesanta Software Limited
5 | sentence=Remote control and OTA for ESP32 via mdash.net IoT backend
6 | paragraph=Provides OTA, MQTT, device shadow, Filesystem management for ESP32
7 | category=Communication
8 | url=https://github.com/cesanta/mDash
9 | architectures=esp32
10 | includes=mDash.h
11 |
--------------------------------------------------------------------------------
/posix/Makefile:
--------------------------------------------------------------------------------
1 | PROG ?= mDashDeviceSimulator
2 | CFLAGS ?= -W -Wall -Wextra -g -I../src -pthread
3 | LFLAGS ?= -L/usr/lib -L/usr/local/opt/mbedtls/lib -I/opt/homebrew/opt/mbedtls/include -I/usrc/local -I/usr/local/opt -L/opt/homebrew/opt/mbedtls/lib -lmbedtls -lmbedx509 -lmbedcrypto -lmbedtls -lmbedx509 -lmbedcrypto
4 | SOURCES ?= main.c ../src/mDash.c ../src/mongoose.c
5 |
6 | all: $(PROG)
7 |
8 | $(PROG): $(SOURCES)
9 | $(CC) $(SOURCES) $(CFLAGS) $(LFLAGS) $(EXTRA) -o $(PROG)
10 |
11 | run: $(PROG)
12 | $(RUN) ./$(PROG) $(ARGS)
13 |
14 | clean:
15 | rm -rf $(PROG) _CL* *.o *.dSYM *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb
16 |
--------------------------------------------------------------------------------
/posix/main.c:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019-2022 Cesanta Software Limited
2 | // All rights reserved
3 | //
4 | // This is mdash.net device simulator on POSIX systems.
5 |
6 | #include "mDash.h"
7 |
8 | static int s_sig;
9 | static void sighandler(int sig) {
10 | s_sig = sig;
11 | }
12 |
13 | int main(int argc, char *argv[]) {
14 | const char *pass = NULL; // *url = NULL, *report_interval = "5";
15 | int loglevel = -1;
16 |
17 | for (int i = 1; i < argc; i++) {
18 | if (strcmp(argv[i], "-pass") == 0) {
19 | pass = argv[++i];
20 | } else if (strcmp(argv[i], "-v") == 0) {
21 | loglevel = atoi(argv[++i]);
22 | } else if (strcmp(argv[i], "-ap") == 0) {
23 | // wifi = NULL; // if WiFi is NULL, mDash lib starts HTTP server
24 | } else {
25 | MG_ERROR(("Invalid option: [%s]\n", argv[i]));
26 | MG_ERROR(("Usage: %s --pass DEVICE_PASSWORD", argv[0]));
27 | return 1;
28 | }
29 | }
30 |
31 | if (pass == NULL) {
32 | MG_ERROR(("%s", "Please specify --pass DEVICE_PASSWORD"));
33 | return 1;
34 | }
35 |
36 | signal(SIGINT, sighandler);
37 | signal(SIGTERM, sighandler);
38 | signal(SIGPIPE, SIG_IGN);
39 |
40 | mDashBegin(pass);
41 | if (loglevel > 0) mg_log_set(loglevel);
42 | while (s_sig == 0) mDashPoll();
43 | MG_INFO(("Got signal %d, exiting...", s_sig));
44 |
45 | return 0;
46 | }
47 |
--------------------------------------------------------------------------------
/src/mDash.c:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019-2022 Cesanta Software Limited
2 | // All rights reserved
3 | //
4 | // This software is dual-licensed: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License version 2 as
6 | // published by the Free Software Foundation. For the terms of this
7 | // license, see http://www.gnu.org/licenses/
8 | //
9 | // You are free to use this software under the terms of the GNU General
10 | // Public License, but WITHOUT ANY WARRANTY; without even the implied
11 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | // See the GNU General Public License for more details.
13 | //
14 | // Alternatively, you can license this software under a commercial
15 | // license, as set out in https://www.mongoose.ws/licensing/
16 | //
17 | // SPDX-License-Identifier: GPL-2.0 or commercial
18 |
19 | #include "mDash.h"
20 |
21 | static const char *s_mDashAppName = "arduino";
22 | static const char *s_mDashFramework = "?";
23 | static const char *s_mDashPassword = NULL;
24 | static const char *s_mDashPublicKey = NULL;
25 | static const char *s_mDashBuildTime = NULL;
26 | static const char *s_mDashURL = "wss://mdash.net/api/v2/rpc";
27 | static int s_mDashLogLevel = MG_LL_INFO;
28 | static int s_mDashState = MDASH_STATE_NETWORK_LOST;
29 | static struct mg_connection *s_conn; // Cloud connection
30 | static struct mg_connection *s_sntp = NULL; // SNTP connection
31 | static struct mg_connection *s_http = NULL; // HTTP server connection
32 | static struct mg_mgr s_mgr; // Mongoose event manager
33 | static time_t s_start_time = 0; // Starting timestamp
34 | static size_t s_ota_size = 0; // Firmware size
35 | static size_t s_ota_written = 0; // Written firmware size
36 | struct mg_rpc *g_rpcs; // Registered RPC handlers
37 |
38 | static const char *s_pem =
39 | "-----BEGIN CERTIFICATE-----\n"
40 | "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
41 | "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
42 | "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
43 | "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
44 | "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
45 | "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
46 | "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
47 | "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
48 | "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
49 | "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
50 | "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
51 | "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
52 | "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
53 | "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
54 | "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
55 | "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
56 | "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
57 | "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
58 | "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
59 | "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
60 | "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
61 | "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
62 | "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
63 | "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
64 | "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
65 | "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
66 | "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
67 | "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
68 | "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
69 | "-----END CERTIFICATE-----\n";
70 |
71 | #if defined(ESP_PLATFORM) //////////////////////////////////// ESP32 specific
72 | #include
73 | #include
74 | #include
75 | #include
76 | #define HTTP_URL "http://0.0.0.0:80"
77 | #define MDASH_ARCH "esp32"
78 | #define CPU_CORE 0
79 | #define FS_ROOT "/spiffs"
80 | #define MDASH_CONFIG_FILE_NAME FS_ROOT "/mdash.cfg"
81 | static const esp_partition_t *s_update_partition;
82 | static esp_ota_handle_t s_update_handle;
83 |
84 | static void enable_wdt(void) {
85 | esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(CPU_CORE));
86 | }
87 | static void disable_wdt(void) {
88 | esp_task_wdt_delete(xTaskGetIdleTaskHandleForCPU(CPU_CORE));
89 | }
90 | static unsigned long mDashGetFreeRam(void) {
91 | return xPortGetFreeHeapSize();
92 | }
93 | static void reboot_with_delay(int delay_ms) {
94 | void *h = xTimerCreate("reboot", pdMS_TO_TICKS(delay_ms), 0, NULL,
95 | (void (*)(void *)) esp_restart);
96 | xTimerStart(h, 0);
97 | }
98 | static SemaphoreHandle_t s_sem = NULL;
99 | static void MDashMutexInit(void) {
100 | s_sem = xSemaphoreCreateMutex();
101 | }
102 | static void MDashMutexLock(void) {
103 | xSemaphoreTakeRecursive(s_sem, portMAX_DELAY);
104 | }
105 | static void MDashMutexUnlock(void) {
106 | xSemaphoreGiveRecursive(s_sem);
107 | }
108 | static bool mount_fs(const char *path) {
109 | disable_wdt();
110 | esp_vfs_spiffs_conf_t conf = {
111 | .base_path = path,
112 | .max_files = 20,
113 | .format_if_mount_failed = true,
114 | };
115 | int res = esp_vfs_spiffs_register(&conf);
116 | enable_wdt();
117 | MG_INFO(("FS -> %s: %d", conf.base_path, res));
118 | return res == ESP_OK;
119 | }
120 | static uint64_t get_chip_id(void) {
121 | uint64_t id = 0;
122 | // efuse_hal_get_mac((uint8_t *) &id);
123 | esp_read_mac((uint8_t *) &id, ESP_MAC_WIFI_STA);
124 | return id;
125 | }
126 | #else /////////////////////////////////////////////////////// POSIX specific
127 | #define HTTP_URL "http://0.0.0.0:8080"
128 | #define xTaskCreatePinnedToCore(fn, b, c, d, e, f, g)
129 | #define MDASH_ARCH "posix"
130 | #define FS_ROOT "/tmp/spiffs"
131 | #define MDASH_CONFIG_FILE_NAME FS_ROOT "/mdash.cfg"
132 | static unsigned long mDashGetFreeRam(void) {
133 | return 42;
134 | }
135 | static void reboot_with_delay(int delay_ms) {
136 | MG_INFO(("outta reboot in %d milliseconds", delay_ms));
137 | }
138 | static void MDashMutexInit(void) {
139 | }
140 | static void MDashMutexLock(void) {
141 | }
142 | static void MDashMutexUnlock(void) {
143 | }
144 | static bool mount_fs(const char *path) {
145 | if (path != NULL) mkdir(path, 0644);
146 | return true;
147 | }
148 | static int esp_reset_reason(void) {
149 | return 0;
150 | }
151 | static uint64_t get_chip_id(void) {
152 | return 0;
153 | }
154 | #endif ///////////////////////////////////////////// END of plaform-specific
155 |
156 | static const char *get_reset_reason(void) {
157 | switch (esp_reset_reason()) {
158 | #if defined(ESP_PLATFORM)
159 | case ESP_RST_POWERON:
160 | return "power-on";
161 | case ESP_RST_EXT:
162 | return "external pin";
163 | case ESP_RST_SW:
164 | return "soft reset";
165 | case ESP_RST_PANIC:
166 | return "crash";
167 | case ESP_RST_INT_WDT:
168 | return "interrupt watchdog (software or hardware)";
169 | case ESP_RST_TASK_WDT:
170 | return "task watchdog";
171 | case ESP_RST_WDT:
172 | return "other watchdog";
173 | case ESP_RST_DEEPSLEEP:
174 | return "deep sleep wakeup";
175 | case ESP_RST_BROWNOUT:
176 | return "Brownout (software or hardware)";
177 | case ESP_RST_SDIO:
178 | return "Reset over SDIO";
179 | #endif
180 | default:
181 | return "unknown";
182 | }
183 | }
184 |
185 | static void asctimetoiso(char *buf, const char *ts) {
186 | const char *month = "01";
187 | if (ts == NULL || strlen(ts) < 20) ts = "Jan 1 1970-00:00:00";
188 | if (ts[0] == 'F') {
189 | month = "02";
190 | } else if (ts[0] == 'M') {
191 | month = ts[2] == 'y' ? "05" : "03";
192 | } else if (ts[0] == 'A') {
193 | month = ts[1] == 'p' ? "04" : "08";
194 | } else if (ts[0] == 'J') {
195 | month = ts[1] == 'a' ? "01" : ts[2] == 'n' ? "06" : "07";
196 | } else if (ts[0] == 'S') {
197 | month = "09";
198 | } else if (ts[0] == 'O') {
199 | month = "10";
200 | } else if (ts[0] == 'N') {
201 | month = "11";
202 | } else if (ts[0] == 'D') {
203 | month = "12";
204 | }
205 |
206 | memcpy(buf, ts + 7, 4);
207 | memcpy(buf + 4, month, 2);
208 | buf[6] = ts[4] == ' ' ? '0' : ts[4];
209 | buf[7] = ts[5];
210 | buf[8] = '-';
211 | memcpy(buf + 9, ts + 12, 2);
212 | memcpy(buf + 11, ts + 15, 2);
213 | memcpy(buf + 13, ts + 18, 2);
214 | buf[15] = '\0';
215 | }
216 |
217 | static void rpc_get_info(struct mg_rpc_req *r) {
218 | unsigned long uptime = time(NULL) - s_start_time;
219 | uint64_t chipid = get_chip_id();
220 | char buf[21];
221 | asctimetoiso(buf, s_mDashBuildTime);
222 | mg_rpc_ok(r,
223 | "{%Q:\"%s-%s\",%Q:%Q,%Q:%Q,%Q:%Q,%Q:%lu,%Q:%lu,%Q:%Q,%Q:%H,%Q:%Q}",
224 | "fw_version", MDASH_VERSION, s_mDashFramework, "arch", MDASH_ARCH,
225 | "fw_id", buf, "app", s_mDashAppName, "uptime", uptime, "ram_free",
226 | mDashGetFreeRam(), "reboot_reason", get_reset_reason(), "id",
227 | sizeof(chipid), &chipid, "public_key",
228 | s_mDashPublicKey == NULL ? "" : s_mDashPublicKey);
229 | }
230 |
231 | static void rpc_reboot(struct mg_rpc_req *r) {
232 | mg_rpc_ok(r, "true");
233 | reboot_with_delay(1000);
234 | }
235 |
236 | static void rpc_sys_set(struct mg_rpc_req *r) {
237 | char *name = mg_json_get_str(r->frame, "$.params.name");
238 | char *value = mg_json_get_str(r->frame, "$.params.value");
239 | if (name == NULL) {
240 | mg_rpc_err(r, 400, "%Q", "name and value expected");
241 | } else {
242 | int result = mDashConfigSet(name, value);
243 | mg_rpc_ok(r, "{%Q:%d}", "result", result);
244 | }
245 | free(name);
246 | free(value);
247 | }
248 |
249 | static void rpc_fs_format(struct mg_rpc_req *r) {
250 | #if defined(ESP_PLATFORM)
251 | disable_wdt();
252 | esp_err_t err = esp_spiffs_format(NULL);
253 | enable_wdt();
254 | if (err) {
255 | mg_rpc_err(r, 500, "\"spiffs_format err %d\"", err);
256 | } else {
257 | mg_rpc_ok(r, "true");
258 | }
259 | #else
260 | mg_rpc_ok(r, "true");
261 | #endif
262 | }
263 |
264 | static void rpc_fs_mount(struct mg_rpc_req *r) {
265 | // char path[20] = "/";
266 | char *path = mg_json_get_str(r->frame, "$.params.path");
267 | #if defined(ESP_PLATFORM)
268 | if (esp_spiffs_mounted(NULL)) {
269 | mg_rpc_err(r, 500, "%Q", "already mounted");
270 | } else if (mount_fs(path == NULL ? FS_ROOT : path)) {
271 | mg_rpc_ok(r, "true");
272 | } else {
273 | mg_rpc_err(r, 500, "%Q", "mount failed");
274 | }
275 | #else
276 | mg_rpc_ok(r, "true");
277 | #endif
278 | free(path);
279 | }
280 |
281 | static void rpc_fs_put(struct mg_rpc_req *r) {
282 | bool append = false;
283 | char *name = mg_json_get_str(r->frame, "$.params.filename");
284 | int len = 0, ofs = mg_json_get(r->frame, "$.params.data", &len);
285 | mg_json_get_bool(r->frame, "$.params.append", &append);
286 | if (len <= 0 || r->frame.ptr[ofs] != '"' || name == NULL) {
287 | mg_rpc_err(r, 500, "%Q", "data and filename required");
288 | } else {
289 | char path[MG_PATH_MAX];
290 | FILE *fp;
291 | mg_snprintf(path, sizeof(path), "%s/%s", FS_ROOT, name);
292 | if ((fp = fopen(path, append ? "ab" : "wb")) == NULL) {
293 | mg_rpc_err(r, 500, "\"fopen(%q): %d\"", path, errno);
294 | } else {
295 | char *data = (char *) &r->frame.ptr[ofs];
296 | int n = mg_base64_decode(data + 1, len - 2, data); // Decode in-place
297 | if ((int) fwrite(data, 1, n, fp) != n) {
298 | mg_rpc_err(r, 500, "\"fwrite(%q) %d, errno %d\"", path, n, errno);
299 | } else {
300 | mg_rpc_ok(r, "{%Q:%Q,%Q:%d}", "filename", name, "written", n);
301 | }
302 | fclose(fp);
303 | }
304 | }
305 | free(name);
306 | }
307 |
308 | static void rpc_fs_get(struct mg_rpc_req *r) {
309 | long offset = mg_json_get_long(r->frame, "$.params.offset", 0);
310 | long len = mg_json_get_long(r->frame, "$.params.len", 512);
311 | char *chunk = NULL;
312 | char *name = mg_json_get_str(r->frame, "$.params.filename");
313 | if (name == NULL) {
314 | mg_rpc_err(r, 500, "%Q", "filename required");
315 | } else if ((chunk = (char *) malloc(len)) == NULL) {
316 | mg_rpc_err(r, 500, "%Q", "OOM");
317 | } else {
318 | FILE *fp = NULL;
319 | char path[MG_PATH_MAX];
320 | mg_snprintf(path, sizeof(path), "%s/%s", FS_ROOT, name);
321 | if ((fp = fopen(path, "rb")) == NULL) {
322 | mg_rpc_err(r, 500, "\"fopen(%q): %d\"", path, errno);
323 | } else {
324 | fseek(fp, offset, SEEK_SET);
325 | long n = fread(chunk, 1, len, fp);
326 | fseek(fp, 0, SEEK_END);
327 | long total = ftell(fp);
328 | mg_rpc_ok(r, "{%Q:%Q,%Q:%ld,%Q:%ld,%Q:%V}", "filename", name, "offset",
329 | offset, "left", total - (n + offset), "data", (int) n, chunk);
330 | fclose(fp);
331 | }
332 | }
333 | free(chunk);
334 | free(name);
335 | }
336 |
337 | static void rpc_fs_rename(struct mg_rpc_req *r) {
338 | char *src = mg_json_get_str(r->frame, "$.params.src");
339 | char *dst = mg_json_get_str(r->frame, "$.params.dst");
340 | if (src == NULL || dst == NULL) {
341 | mg_rpc_err(r, 500, "%Q", "src and dst missing");
342 | } else {
343 | char a[MG_PATH_MAX], b[MG_PATH_MAX];
344 | mg_snprintf(a, sizeof(a), "%s/%s", FS_ROOT, src);
345 | mg_snprintf(b, sizeof(b), "%s/%s", FS_ROOT, dst);
346 | remove(b);
347 | if (rename(a, b) != 0) {
348 | mg_rpc_err(r, 500, "\"rename(%s, %s) failed: %d\"", a, a, errno);
349 | } else {
350 | mg_rpc_ok(r, "true");
351 | }
352 | }
353 | free(src);
354 | free(dst);
355 | }
356 |
357 | static void rpc_fs_remove(struct mg_rpc_req *r) {
358 | char *name = mg_json_get_str(r->frame, "$.params.filename");
359 | if (name == NULL) {
360 | mg_rpc_err(r, 500, "%Q", "filename required");
361 | } else {
362 | char path[MG_PATH_MAX];
363 | mg_snprintf(path, sizeof(path), "%s/%s", FS_ROOT, name);
364 | if (remove(path) != 0) {
365 | mg_rpc_err(r, 500, "", "\"remove(%s) failed: %d\"", path, errno);
366 | } else {
367 | mg_rpc_ok(r, "true");
368 | }
369 | }
370 | free(name);
371 | }
372 |
373 | static size_t fslister(mg_pfn_t pfn, void *pfn_data, va_list *ap) {
374 | DIR *dirp = va_arg(*ap, DIR *);
375 | struct dirent *dp;
376 | const char *comma = "";
377 | size_t n = 0;
378 | while ((dp = readdir(dirp)) != NULL) {
379 | if (strcmp((const char *) dp->d_name, ".") == 0 ||
380 | strcmp((const char *) dp->d_name, "..") == 0) {
381 | continue;
382 | }
383 | n += mg_rprintf(pfn, pfn_data, "%s%Q", comma, dp->d_name);
384 | comma = ",";
385 | }
386 | return n;
387 | }
388 |
389 | static void rpc_fs_list(struct mg_rpc_req *r) {
390 | DIR *dirp = opendir(FS_ROOT);
391 | if (dirp == NULL) {
392 | mg_rpc_err(r, 500, "%Q", "cannot open fs root");
393 | } else {
394 | mg_rpc_ok(r, "[%M]", fslister, dirp);
395 | closedir(dirp);
396 | }
397 | }
398 |
399 | int mDashNotify(const char *name, const char *fmt, ...) {
400 | int res = 0;
401 | MDashMutexLock();
402 | va_list ap;
403 | if (s_conn != NULL) {
404 | struct mg_iobuf io = {0, 0, 0, 512};
405 | mg_rprintf(mg_pfn_iobuf, &io, "{%Q:%Q,%Q:", "method", name, "params");
406 | va_start(ap, fmt);
407 | mg_vrprintf(mg_pfn_iobuf, &io, fmt, &ap);
408 | va_end(ap);
409 | mg_rprintf(mg_pfn_iobuf, &io, "}");
410 | if (io.buf != NULL) {
411 | mg_ws_send(s_conn, (char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
412 | }
413 | mg_iobuf_free(&io);
414 | res = 1;
415 | }
416 | MDashMutexUnlock();
417 | return res;
418 | }
419 |
420 | static void rpc_ota_begin(struct mg_rpc_req *r) {
421 | if (s_ota_size) {
422 | mg_rpc_err(r, 500, "%Q", "OTA already in progress");
423 | } else {
424 | double dv;
425 | if (mg_json_get_num(r->frame, "$.params.size", &dv)) s_ota_size = dv;
426 | #if defined(ESP_PLATFORM)
427 | disable_wdt();
428 | s_update_partition = esp_ota_get_next_update_partition(NULL);
429 | esp_err_t err =
430 | esp_ota_begin(s_update_partition, OTA_SIZE_UNKNOWN, &s_update_handle);
431 | enable_wdt();
432 | MG_INFO(("Starting OTA. partition=%p, err=%d", s_update_partition, err));
433 | #else
434 | MG_INFO(("Starting OTA: %.*s", (int) r->frame.len, r->frame.ptr));
435 | #endif
436 | mDashNotify("State.Set", "{%Q:{%Q:0}}", "ota", "percent");
437 | mg_rpc_ok(r, "true");
438 | }
439 | }
440 |
441 | static void rpc_ota_write(struct mg_rpc_req *r) {
442 | int len = 0, ofs = 0;
443 | if (s_ota_size == 0) {
444 | mg_rpc_err(r, 500, "%Q", "call OTA.Begin");
445 | } else if ((ofs = mg_json_get(r->frame, "$.params.data", &len)) <= 0 ||
446 | r->frame.ptr[ofs] != '"') {
447 | mg_rpc_err(r, 500, "%Q", "data required");
448 | } else {
449 | // Decode in-place
450 | char *data = (char *) &r->frame.ptr[ofs];
451 | int n = mg_base64_decode(data + 1, len - 2, data); // Decode in-place
452 |
453 | #if defined(ESP_PLATFORM)
454 | disable_wdt();
455 | esp_err_t err = esp_ota_write(s_update_handle, data, n);
456 | enable_wdt();
457 | if (err != ESP_OK) {
458 | MG_ERROR(("ota finished, err %d", err));
459 | mg_rpc_err(r, 500, "\"esp_ota_write: %d\"", err);
460 | return;
461 | }
462 | #endif
463 |
464 | s_ota_written += n;
465 | MG_INFO(("OTA write: got %d bytes", n));
466 | mDashNotify("State.Set", "{%Q:{%Q:%d}}", "ota", "percent",
467 | (int) (s_ota_written * 100 / s_ota_size));
468 | mg_rpc_ok(r, "{%Q:%d}", "queued", n);
469 | }
470 | }
471 |
472 | static void rpc_ota_end(struct mg_rpc_req *r) {
473 | s_ota_size = s_ota_written = 0;
474 | mDashNotify("State.Set", "{%Q:{%Q:null}}", "ota", "percent");
475 | MG_INFO(("OTA finished"));
476 | #if defined(ESP_PLATFORM)
477 | esp_err_t err = esp_ota_end(s_update_handle);
478 | if (err != ESP_OK) {
479 | MG_ERROR(("ota finished, err %d", err));
480 | mg_rpc_err(r, 500, "\"esp_ota_end: %d\"", err);
481 | return;
482 | }
483 | esp_ota_set_boot_partition(s_update_partition);
484 | reboot_with_delay(500);
485 | #endif
486 | mg_rpc_ok(r, "true");
487 | }
488 |
489 | static void init_rpcs(void) {
490 | mg_rpc_add(&g_rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
491 | mg_rpc_add(&g_rpcs, mg_str("Sys.GetInfo"), rpc_get_info, NULL);
492 | mg_rpc_add(&g_rpcs, mg_str("Sys.Reboot"), rpc_reboot, NULL);
493 | mg_rpc_add(&g_rpcs, mg_str("Sys.Set"), rpc_sys_set, NULL);
494 | mg_rpc_add(&g_rpcs, mg_str("OTA.Begin"), rpc_ota_begin, NULL);
495 | mg_rpc_add(&g_rpcs, mg_str("OTA.Write"), rpc_ota_write, NULL);
496 | mg_rpc_add(&g_rpcs, mg_str("OTA.End"), rpc_ota_end, NULL);
497 | mg_rpc_add(&g_rpcs, mg_str("FS.List"), rpc_fs_list, NULL);
498 | mg_rpc_add(&g_rpcs, mg_str("FS.Remove"), rpc_fs_remove, NULL);
499 | mg_rpc_add(&g_rpcs, mg_str("FS.Rename"), rpc_fs_rename, NULL);
500 | mg_rpc_add(&g_rpcs, mg_str("FS.Get"), rpc_fs_get, NULL);
501 | mg_rpc_add(&g_rpcs, mg_str("FS.Put"), rpc_fs_put, NULL);
502 | mg_rpc_add(&g_rpcs, mg_str("FS.Mount"), rpc_fs_mount, NULL);
503 | mg_rpc_add(&g_rpcs, mg_str("FS.Format"), rpc_fs_format, NULL);
504 | }
505 |
506 | int mDashConfigGet(const char *name, char *buf, int bufsize) {
507 | MDashMutexLock();
508 |
509 | char line[256], var[sizeof(line)], val[sizeof(line)];
510 | FILE *fp = fopen(MDASH_CONFIG_FILE_NAME, "r");
511 | int result = 0;
512 | if (fp == NULL) {
513 | MG_DEBUG(("%s: cannot open config file: %d", name, errno));
514 | result = -1; // Fail - cannot open file
515 | goto out;
516 | }
517 | while (fgets(line, sizeof(line), fp) != NULL) {
518 | if (sscanf(line, "%s %[^\r\n]", var, val) != 2) continue;
519 | if (var[0] == '#' || var[0] == '\0' || val[0] == '\0') continue;
520 | if (strcmp(name, var) != 0) continue;
521 | if ((int) strlen(val) > bufsize) {
522 | MG_ERROR(("%s: dest buffer is too small", name));
523 | result = -2; // Fail - dest buffer too small
524 | goto out;
525 | }
526 | strcpy(buf, val);
527 | goto out; // Success
528 | }
529 | result = -3; // Fail - variable not found
530 | out:
531 | if (fp != NULL) fclose(fp);
532 | MDashMutexUnlock();
533 | return result;
534 | }
535 |
536 | int mDashConfigSet(const char *name, const char *value) {
537 | MDashMutexLock();
538 | char line[128];
539 | FILE *fp = fopen(MDASH_CONFIG_FILE_NAME, "r");
540 | FILE *fp2 = fopen(MDASH_CONFIG_FILE_NAME ".tmp", "w");
541 | if (fp2 == NULL) return -1; // Fail - cannot open temp file
542 | if (fp != NULL) {
543 | int len = strlen(name), found = 0;
544 | while (fgets(line, sizeof(line), fp) != NULL) {
545 | if (memcmp(line, name, len) == 0 && line[len] == ' ') {
546 | if (!found && value && value[0]) fprintf(fp2, "%s %s\n", name, value);
547 | found = 1;
548 | } else {
549 | fprintf(fp2, "%s", line);
550 | }
551 | }
552 | if (!found && value && value[0]) fprintf(fp2, "%s %s\n", name, value);
553 | fclose(fp);
554 | } else {
555 | if (value && value[0]) fprintf(fp2, "%s %s\n", name, value);
556 | }
557 | fclose(fp2);
558 | remove(MDASH_CONFIG_FILE_NAME);
559 | rename(MDASH_CONFIG_FILE_NAME ".tmp", MDASH_CONFIG_FILE_NAME);
560 | MDashMutexUnlock();
561 | return 0;
562 | }
563 |
564 | int mDashConfigReset(void) {
565 | MG_ERROR(("%s", "Resetting config"));
566 | mDashConfigSet("wifi.sta.ssid", NULL);
567 | mDashConfigSet("wifi.sta.pass", NULL);
568 | mDashConfigSet("device.public_key", NULL);
569 | return 0;
570 | }
571 |
572 | #define MDASH_DATA_FILE_NAME FS_ROOT "/mdash.unsent"
573 | #define MDASH_DATA_FILE_SIZE_LIMIT 32768
574 |
575 | static void pfn_file(char c, void *data) {
576 | fputc(c, (FILE *) data);
577 | }
578 |
579 | int mDashStore(const char *topic, const char *fmt, ...) {
580 | FILE *fp = fopen(MDASH_DATA_FILE_NAME, "a");
581 | if (fp == NULL) return 0;
582 |
583 | fprintf(fp, "%s ", topic);
584 | va_list ap;
585 | va_start(ap, fmt);
586 | mg_vrprintf(pfn_file, fp, fmt, &ap);
587 | va_end(ap);
588 | fputc('\n', fp);
589 | long size = ftell(fp);
590 | fclose(fp);
591 |
592 | if (size > MDASH_DATA_FILE_SIZE_LIMIT) {
593 | const char *to = MDASH_DATA_FILE_NAME ".old";
594 | remove(to);
595 | rename(MDASH_DATA_FILE_NAME, to);
596 | }
597 |
598 | mDashNotify("DB.NewData", "true"); // Tell mDash we have unsaved data
599 | return 1;
600 | }
601 |
602 | static char *gen_public_key(char *buf, size_t len) {
603 | uint8_t tmp[len * 3 / 4 - 3];
604 | mg_random(tmp, sizeof(tmp));
605 | mg_base64_encode(tmp, sizeof(tmp), buf);
606 | return buf;
607 | }
608 |
609 | static const char *fromcfg(const char *ptr, const char *var) {
610 | char buf[256];
611 | if (mDashConfigGet(var, buf, sizeof(buf)) == 0) ptr = strdup(buf);
612 | MG_DEBUG(("%s -> [%s]", var, ptr == NULL ? "NULL" : ptr));
613 | return ptr;
614 | }
615 |
616 | static bool s_time_set;
617 | static void sntpcb(struct mg_connection *c, int ev, void *evd, void *fnd) {
618 | if (ev == MG_EV_SNTP_TIME) {
619 | uint64_t ms = *(uint64_t *) evd;
620 | struct timeval tv = {.tv_sec = ms / 1000, .tv_usec = (ms % 1000) * 1000};
621 | MG_INFO(("SNTP: setting time to %lu.%lu", tv.tv_sec, tv.tv_usec));
622 | settimeofday(&tv, NULL);
623 | s_time_set = true;
624 | } else if (ev == MG_EV_CLOSE) {
625 | s_sntp = NULL;
626 | }
627 | (void) c, (void) fnd;
628 | }
629 |
630 | static void dev_cb(struct mg_connection *c, int ev, void *evd, void *fnd) {
631 | if (ev == MG_EV_CONNECT) {
632 | if (mg_url_is_ssl(s_mDashURL)) {
633 | struct mg_str host = mg_url_host(s_mDashURL);
634 | struct mg_tls_opts opts = {.ca = s_pem, .srvname = host};
635 | mg_tls_init(s_conn, &opts);
636 | }
637 | }
638 | if (ev == MG_EV_WS_OPEN) {
639 | MG_INFO(("connected to %s", s_mDashURL));
640 | s_mDashState = MDASH_STATE_CLOUD_CONNECTED;
641 | // mDashTriggerEvent(s_mDashState, NULL);
642 | } else if (ev == MG_EV_WS_MSG) {
643 | struct mg_ws_message *wm = evd;
644 | struct mg_str s = wm->data;
645 | while (s.len > 0 && isspace((uint8_t) s.ptr[s.len - 1])) s.len--;
646 | MG_INFO(("RPC: %.*s", (int) s.len, s.ptr));
647 | struct mg_iobuf io = {0, 0, 0, 512};
648 | struct mg_rpc_req r = {.head = &g_rpcs,
649 | .rpc = NULL,
650 | .pfn = mg_pfn_iobuf,
651 | .pfn_data = &io,
652 | .req_data = NULL,
653 | .frame = s};
654 | mg_rpc_process(&r);
655 | if (io.buf != NULL) {
656 | MG_INFO((" %s", io.buf));
657 | mg_ws_send(c, (const char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
658 | } else {
659 | MG_VERBOSE(("%s", "RPC unhandled"));
660 | }
661 | mg_iobuf_free(&io);
662 | } else if (ev == MG_EV_CLOSE) {
663 | s_conn = NULL;
664 | MG_INFO(("disconnected, ram %lu", mDashGetFreeRam()));
665 | if (s_mDashState == MDASH_STATE_CLOUD_CONNECTED)
666 | s_mDashState = MDASH_STATE_NETWORK_CONNECTED;
667 | // mDashTriggerEvent(s_mDashState, NULL);
668 | }
669 | (void) c, (void) fnd;
670 | }
671 |
672 | static int sv(struct mg_str json, const char *path, const char *name) {
673 | bool set = false;
674 | char *val = mg_json_get_str(json, path);
675 | if (val != NULL) {
676 | mDashConfigSet(name, val);
677 | free(val);
678 | set = true;
679 | }
680 | return set;
681 | }
682 |
683 | static void httpcb(struct mg_connection *c, int ev, void *evd, void *fnd) {
684 | const char *extra_headers =
685 | "Access-Control-Allow-Origin: *\r\n"
686 | "Content-Type: application/json\r\n";
687 | if (ev == MG_EV_HTTP_MSG) {
688 | struct mg_http_message *hm = evd;
689 | if (mg_http_match_uri(hm, "/GetKey")) {
690 | if (s_mDashPublicKey != NULL) {
691 | mg_http_reply(c, 200, extra_headers, "{%Q:%Q}\n", "result",
692 | s_mDashPublicKey);
693 | } else {
694 | mg_http_reply(c, 200, extra_headers, "{%Q:%Q}\n", "error",
695 | "key not set");
696 | }
697 | } else if (mg_http_match_uri(hm, "/setup") && hm->body.len > 0) {
698 | if (sv(hm->body, "$.ssid", "wifi.sta.ssid") &&
699 | sv(hm->body, "$.pass", "wifi.sta.pass")) {
700 | mg_http_reply(c, 200, extra_headers, "{%Q:true}\n", "result");
701 | reboot_with_delay(3000);
702 | } else {
703 | mg_http_reply(c, 200, extra_headers, "{%Q:%Q}\n", "error",
704 | "set ssid and pass");
705 | }
706 | } else {
707 | mg_http_reply(c, 200, extra_headers, "{%Q:%Q}\n", "error",
708 | "use /setup endpoint");
709 | }
710 | }
711 | (void) fnd;
712 | }
713 |
714 | static void timercb(void *userdata) {
715 | struct mg_mgr *mgr = userdata;
716 | MG_DEBUG(("ram: %lu, %d %p/%p/%p", mDashGetFreeRam(), s_mDashState, s_conn,
717 | s_http, s_sntp));
718 | if (s_conn == NULL && s_mDashState == MDASH_STATE_NETWORK_CONNECTED &&
719 | s_mDashPassword != NULL && s_time_set) {
720 | s_conn = mg_ws_connect(mgr, s_mDashURL, dev_cb, NULL,
721 | "Origin: %s\r\n"
722 | "Authorization: Bearer %s\r\n",
723 | s_mDashURL, s_mDashPassword);
724 | }
725 | if (s_sntp == NULL && (s_mDashState == MDASH_STATE_NETWORK_CONNECTED ||
726 | s_mDashState == MDASH_STATE_CLOUD_CONNECTED)) {
727 | s_sntp = mg_sntp_connect(mgr, NULL, sntpcb, NULL);
728 | }
729 | // Trigger periodic STNP reconnect, and time resync, by closing s_sntp
730 | if (s_sntp && ((time(0) % 3600) == 0)) s_sntp->is_closing = 1;
731 | if (s_http == NULL && s_mDashState == MDASH_STATE_NETWORK_AP) {
732 | s_http = mg_http_listen(mgr, HTTP_URL, httpcb, NULL);
733 | }
734 | }
735 |
736 | void mDashPoll(void) {
737 | MDashMutexLock();
738 | mg_mgr_poll(&s_mgr, 50);
739 | MDashMutexUnlock();
740 | }
741 |
742 | void mDashTask(void *data) {
743 | for (;;) mDashPoll();
744 | (void) data;
745 | }
746 |
747 | int mDashGetState(void) {
748 | return s_mDashState;
749 | }
750 |
751 | static void doinit(const char *id, const char *pass, const char *name,
752 | const char *ts, const char *framework) {
753 | mount_fs(FS_ROOT); // Do it first, to enable configuration API
754 | s_start_time = time(NULL);
755 | s_mDashPassword = fromcfg(pass, "device.pass");
756 | s_mDashPublicKey = fromcfg(NULL, "device.public_key");
757 | if (s_mDashPublicKey == NULL) {
758 | char buf[40];
759 | s_mDashPublicKey = strdup(gen_public_key(buf, sizeof(buf)));
760 | mDashConfigSet("device.public_key", s_mDashPublicKey);
761 | }
762 | s_mDashBuildTime = ts;
763 | const char *p = strrchr(name, '/');
764 | if (p == NULL) p = strrchr(name, '\\');
765 | if (p != NULL) name = p + 1;
766 | s_mDashAppName = name;
767 | s_mDashFramework = framework;
768 | s_mDashURL = (char *) fromcfg(s_mDashURL, "mdash.url");
769 |
770 | char ll[2] = "2"; // Default log level
771 | if (s_mDashLogLevel == 2 &&
772 | mDashConfigGet("debug.level", ll, sizeof(ll)) == 0) {
773 | MG_INFO(("Setting log level (from config) to: %s", ll));
774 | s_mDashLogLevel = atoi(ll);
775 | }
776 | mg_log_set(s_mDashLogLevel);
777 | init_rpcs();
778 | mg_mgr_init(&s_mgr);
779 | mg_timer_add(&s_mgr, 1000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timercb,
780 | &s_mgr);
781 | MG_INFO(("Initialised, id %s", id));
782 | }
783 |
784 | void mDashInit(const char *id, const char *pass, const char *name,
785 | const char *ts, const char *framework) {
786 | s_mDashState = MDASH_STATE_NETWORK_CONNECTED;
787 | MDashMutexInit();
788 | MDashMutexLock();
789 | doinit(id, pass, name, ts, framework);
790 | xTaskCreatePinnedToCore(&mDashTask, "mDashTask", 16384, 0, 5, 0, CPU_CORE);
791 | MDashMutexUnlock();
792 | }
793 |
--------------------------------------------------------------------------------
/src/mDash.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2019-2022 Cesanta Software Limited
2 | // All rights reserved
3 | //
4 | // This software is dual-licensed: you can redistribute it and/or modify
5 | // it under the terms of the GNU General Public License version 2 as
6 | // published by the Free Software Foundation. For the terms of this
7 | // license, see http://www.gnu.org/licenses/
8 | //
9 | // You are free to use this software under the terms of the GNU General
10 | // Public License, but WITHOUT ANY WARRANTY; without even the implied
11 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 | // See the GNU General Public License for more details.
13 | //
14 | // Alternatively, you can license this software under a commercial
15 | // license, as set out in https://www.mongoose.ws/licensing/
16 | //
17 | // SPDX-License-Identifier: GPL-2.0 or commercial
18 |
19 | #pragma once
20 |
21 | #include "mongoose.h"
22 |
23 | #define MDASH_VERSION "1.2.16"
24 |
25 | #ifdef __cplusplus
26 | extern "C" {
27 | #endif
28 |
29 | // Get current connection state
30 | int mDashGetState(void);
31 | #define MDASH_STATE_NETWORK_LOST 0
32 | #define MDASH_STATE_NETWORK_AP 1
33 | #define MDASH_STATE_NETWORK_CONNECTED 2
34 | #define MDASH_STATE_CLOUD_CONNECTED 3
35 |
36 | // Store data
37 | int mDashStore(const char *topic, const char *json_fmt, ...);
38 |
39 | // Send notification frame to mDash
40 | int mDashNotify(const char *name, const char *fmt, ...);
41 | #define mDashShadowUpdate(fmt, ...) \
42 | mDashNotify("Dash.Shadow.Update", (fmt), __VA_ARGS__)
43 |
44 | int mDashConfigGet(const char *name, char *buf, int bufsize);
45 | int mDashConfigSet(const char *name, const char *value);
46 | int mDashConfigReset(void);
47 | void mDashPoll(void);
48 |
49 | // Registered RPC handlers. Use mg_rpc_add() to add new handlers
50 | extern struct mg_rpc *g_rpcs;
51 |
52 | void mDashInit(const char *device_id, const char *device_pass,
53 | const char *app_name, const char *build_time,
54 | const char *framework);
55 | #if ARDUINO
56 | #define __TOSTR(x) #x
57 | #define STR(x) __TOSTR(x)
58 | #ifndef ARDUINO_BOARD
59 | #define ARDUINO_BOARD "?"
60 | #endif
61 | #define MDASH_FRAMEWORK "a-" STR(ARDUINO) "-" ARDUINO_BOARD
62 | #else
63 | #define MDASH_FRAMEWORK "idf"
64 | #endif
65 |
66 | #ifndef MDASH_APP_NAME
67 | #define MDASH_APP_NAME __FILE__
68 | #endif
69 |
70 | #define MDASH_BUILD_TIME __DATE__ "-" __TIME__
71 | #define mDashBegin(b) \
72 | mDashInit(NULL, (b), MDASH_APP_NAME, MDASH_BUILD_TIME, MDASH_FRAMEWORK)
73 |
74 | #ifdef __cplusplus
75 | }
76 | #endif
77 |
--------------------------------------------------------------------------------
/src/mongoose.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2004-2013 Sergey Lyubka
2 | // Copyright (c) 2013-2022 Cesanta Software Limited
3 | // All rights reserved
4 | //
5 | // This software is dual-licensed: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License version 2 as
7 | // published by the Free Software Foundation. For the terms of this
8 | // license, see http://www.gnu.org/licenses/
9 | //
10 | // You are free to use this software under the terms of the GNU General
11 | // Public License, but WITHOUT ANY WARRANTY; without even the implied
12 | // warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 | // See the GNU General Public License for more details.
14 | //
15 | // Alternatively, you can license this software under a commercial
16 | // license, as set out in https://www.mongoose.ws/licensing/
17 | //
18 | // SPDX-License-Identifier: GPL-2.0 or commercial
19 |
20 | #ifndef MONGOOSE_H
21 | #define MONGOOSE_H
22 |
23 | #define MG_VERSION "7.7"
24 |
25 | #ifdef __cplusplus
26 | extern "C" {
27 | #endif
28 |
29 |
30 | #define MG_ARCH_CUSTOM 0
31 | #define MG_ARCH_UNIX 1
32 | #define MG_ARCH_WIN32 2
33 | #define MG_ARCH_ESP32 3
34 | #define MG_ARCH_ESP8266 4
35 | #define MG_ARCH_FREERTOS_TCP 5
36 | #define MG_ARCH_FREERTOS_LWIP 6
37 | #define MG_ARCH_AZURERTOS 7
38 | #define MG_ARCH_RTX_LWIP 8
39 | #define MG_ARCH_ZEPHYR 9
40 | #define MG_ARCH_NEWLIB 10
41 | #define MG_ARCH_RTX 11
42 | #define MG_ARCH_TIRTOS 12
43 | #define MG_ARCH_RP2040 13
44 |
45 | #if !defined(MG_ARCH)
46 | #if defined(__unix__) || defined(__APPLE__)
47 | #define MG_ARCH MG_ARCH_UNIX
48 | #elif defined(_WIN32)
49 | #define MG_ARCH MG_ARCH_WIN32
50 | #elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
51 | #define MG_ARCH MG_ARCH_ESP8266
52 | #elif defined(ESP_PLATFORM)
53 | #define MG_ARCH MG_ARCH_ESP32
54 | #elif defined(FREERTOS_IP_H)
55 | #define MG_ARCH MG_ARCH_FREERTOS_TCP
56 | #elif defined(AZURE_RTOS_THREADX)
57 | #define MG_ARCH MG_ARCH_AZURERTOS
58 | #elif defined(__ZEPHYR__)
59 | #define MG_ARCH MG_ARCH_ZEPHYR
60 | #elif defined(PICO_TARGET_NAME)
61 | #define MG_ARCH MG_ARCH_RP2040
62 | #endif
63 |
64 | #if !defined(MG_ARCH)
65 | #error "MG_ARCH is not specified and we couldn't guess it. Set -D MG_ARCH=..."
66 | #endif
67 | #endif // !defined(MG_ARCH)
68 |
69 | #if MG_ARCH == MG_ARCH_CUSTOM
70 | #include
71 | #endif
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | #if MG_ARCH == MG_ARCH_AZURERTOS
86 |
87 | #include
88 | #include
89 | #include
90 | #include
91 | #include
92 |
93 | #include
94 | #include
95 |
96 | #include
97 | #include
98 | #include
99 | #include
100 |
101 | #ifdef __REDLIB__
102 | #define va_copy(d, s) __builtin_va_copy(d, s)
103 | #endif
104 |
105 | #define PATH_MAX FX_MAXIMUM_PATH
106 | #define MG_DIRSEP '\\'
107 |
108 | #define socklen_t int
109 | #define closesocket(x) soc_close(x)
110 |
111 | #undef FOPEN_MAX
112 |
113 | #endif
114 |
115 |
116 | #if MG_ARCH == MG_ARCH_ESP32
117 |
118 | #include
119 | #include
120 | #include
121 | #include
122 | #include
123 | #include
124 | #include
125 | #include
126 | #include
127 | #include
128 | #include
129 | #include
130 | #include
131 | #include
132 |
133 | #include
134 |
135 | #define MG_PATH_MAX 128
136 |
137 | #endif
138 |
139 |
140 | #if MG_ARCH == MG_ARCH_ESP8266
141 |
142 | #include
143 | #include
144 | #include
145 | #include
146 | #include
147 | #include
148 | #include
149 | #include
150 | #include
151 | #include
152 | #include
153 | #include
154 | #include
155 | #include
156 | #include
157 | #include
158 |
159 | #include
160 |
161 | #define MG_PATH_MAX 128
162 |
163 | #endif
164 |
165 |
166 | #if MG_ARCH == MG_ARCH_FREERTOS_LWIP
167 |
168 | #include
169 | #include
170 | #include
171 | #include
172 | #include
173 | #include
174 |
175 | #if defined(__GNUC__)
176 | #include
177 | #include
178 | #else
179 | struct timeval {
180 | time_t tv_sec;
181 | long tv_usec;
182 | };
183 | #endif
184 |
185 | #include
186 | #include
187 |
188 | #include
189 |
190 | #if LWIP_SOCKET != 1
191 | // Sockets support disabled in LWIP by default
192 | #error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
193 | #endif
194 |
195 | // Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
196 | static inline void *mg_calloc(int cnt, size_t size) {
197 | void *p = pvPortMalloc(cnt * size);
198 | if (p != NULL) memset(p, 0, size * cnt);
199 | return p;
200 | }
201 | #define calloc(a, b) mg_calloc((a), (b))
202 | #define free(a) vPortFree(a)
203 | #define malloc(a) pvPortMalloc(a)
204 |
205 | #define mkdir(a, b) (-1)
206 |
207 | #ifndef MG_IO_SIZE
208 | #define MG_IO_SIZE 512
209 | #endif
210 |
211 | #ifndef MG_PATH_MAX
212 | #define MG_PATH_MAX 128
213 | #endif
214 |
215 | #endif // MG_ARCH == MG_ARCH_FREERTOS_LWIP
216 |
217 |
218 | #if MG_ARCH == MG_ARCH_FREERTOS_TCP
219 |
220 | #include
221 | #include
222 | #include
223 | #include
224 | #include
225 | #include
226 | #include
227 | #include
228 | #include
229 | #include
230 | #include
231 |
232 | #include
233 | #include
234 | #include
235 | #include
236 |
237 | // Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
238 | // with FreeRTOS_ prefix, is beyond me
239 | #define IPPROTO_TCP FREERTOS_IPPROTO_TCP
240 | #define IPPROTO_UDP FREERTOS_IPPROTO_UDP
241 | #define AF_INET FREERTOS_AF_INET
242 | #define SOCK_STREAM FREERTOS_SOCK_STREAM
243 | #define SOCK_DGRAM FREERTOS_SOCK_DGRAM
244 | #define SO_BROADCAST 0
245 | #define SO_ERROR 0
246 | #define SOL_SOCKET 0
247 | #define SO_REUSEADDR 0
248 | #define sockaddr_in freertos_sockaddr
249 | #define sockaddr freertos_sockaddr
250 | #define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
251 | #define connect(a, b, c) FreeRTOS_connect((a), (b), (c))
252 | #define bind(a, b, c) FreeRTOS_bind((a), (b), (c))
253 | #define listen(a, b) FreeRTOS_listen((a), (b))
254 | #define socket(a, b, c) FreeRTOS_socket((a), (b), (c))
255 | #define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d))
256 | #define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d))
257 | #define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e))
258 | #define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
259 | #define recvfrom(a, b, c, d, e, f) \
260 | FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
261 | #define closesocket(x) FreeRTOS_closesocket(x)
262 | #define gethostbyname(x) FreeRTOS_gethostbyname(x)
263 | #define getsockname(a, b, c) (-1)
264 | #define getpeername(a, b, c) 0
265 |
266 | // Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
267 | static inline void *mg_calloc(int cnt, size_t size) {
268 | void *p = pvPortMalloc(cnt * size);
269 | if (p != NULL) memset(p, 0, size * cnt);
270 | return p;
271 | }
272 | #define calloc(a, b) mg_calloc((a), (b))
273 | #define free(a) vPortFree(a)
274 | #define malloc(a) pvPortMalloc(a)
275 | #define mkdir(a, b) (-1)
276 |
277 | #if !defined(__GNUC__)
278 | // copied from GCC on ARM; for some reason useconds are signed
279 | struct timeval {
280 | time_t tv_sec;
281 | long tv_usec;
282 | };
283 | #endif
284 |
285 | #ifndef EINPROGRESS
286 | #define EINPROGRESS pdFREERTOS_ERRNO_EINPROGRESS
287 | #endif
288 | #ifndef EWOULDBLOCK
289 | #define EWOULDBLOCK pdFREERTOS_ERRNO_EWOULDBLOCK
290 | #endif
291 | #ifndef EAGAIN
292 | #define EAGAIN pdFREERTOS_ERRNO_EAGAIN
293 | #endif
294 | #ifndef EINTR
295 | #define EINTR pdFREERTOS_ERRNO_EINTR
296 | #endif
297 |
298 | #endif // MG_ARCH == MG_ARCH_FREERTOS_TCP
299 |
300 |
301 | #if MG_ARCH == MG_ARCH_NEWLIB
302 | #define _POSIX_TIMERS
303 |
304 | #include
305 | #include
306 | #include
307 | #include
308 | #include
309 | #include
310 | #include
311 | #include
312 | #include
313 | #include
314 | #include
315 | #include
316 |
317 | #define MG_PATH_MAX 100
318 | #define MG_ENABLE_SOCKET 0
319 | #define MG_ENABLE_DIRLIST 0
320 |
321 | #endif
322 |
323 |
324 | #if MG_ARCH == MG_ARCH_RP2040
325 | #include
326 | #include
327 | #include
328 | #include
329 | #include
330 | #include
331 | #include
332 | #include
333 |
334 | #include
335 | int mkdir(const char *, mode_t);
336 | #endif
337 |
338 |
339 | #if MG_ARCH == MG_ARCH_RTX
340 |
341 | #include
342 | #include
343 | #include
344 | #include
345 | #include
346 | #include
347 | #include
348 | #include
349 | #include
350 | #include
351 |
352 | #include
353 |
354 | #define MG_ENABLE_CUSTOM_MILLIS 1
355 | typedef int socklen_t;
356 | #define closesocket(x) closesocket(x)
357 | #define mkdir(a, b) (-1)
358 | #define EWOULDBLOCK BSD_EWOULDBLOCK
359 | #define EAGAIN BSD_EWOULDBLOCK
360 | #define EINPROGRESS BSD_EWOULDBLOCK
361 | #define EINTR BSD_EWOULDBLOCK
362 | #define ECONNRESET BSD_ECONNRESET
363 | #define EPIPE BSD_ECONNRESET
364 | #define TCP_NODELAY SO_KEEPALIVE
365 |
366 | #endif
367 |
368 |
369 | #if MG_ARCH == MG_ARCH_RTX_LWIP
370 |
371 | #include
372 | #include
373 | #include
374 | #include
375 | #include
376 | #include
377 |
378 | #if defined(__GNUC__)
379 | #include
380 | #include
381 | #else
382 | struct timeval {
383 | time_t tv_sec;
384 | long tv_usec;
385 | };
386 | #endif
387 |
388 | #include
389 |
390 | #if LWIP_SOCKET != 1
391 | // Sockets support disabled in LWIP by default
392 | #error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
393 | #endif
394 |
395 | #define mkdir(a, b) (-1)
396 |
397 | #ifndef MG_IO_SIZE
398 | #define MG_IO_SIZE 512
399 | #endif
400 |
401 | #ifndef MG_PATH_MAX
402 | #define MG_PATH_MAX 128
403 | #endif
404 |
405 |
406 | #endif
407 |
408 |
409 | #if MG_ARCH == MG_ARCH_TIRTOS
410 |
411 | #include
412 | #include
413 | #include
414 | #include
415 | #include
416 | #include
417 | #include
418 | #include
419 | #include
420 | #include
421 |
422 | #include
423 |
424 | extern int SockStatus(SOCKET hSock, int request, int *results );
425 | extern int SockSet(SOCKET hSock, int Type, int Prop, void *pbuf, int size);
426 |
427 | #endif
428 |
429 |
430 | #if MG_ARCH == MG_ARCH_UNIX
431 |
432 | #define _DARWIN_UNLIMITED_SELECT 1 // No limit on file descriptors
433 |
434 | #if defined(__APPLE__)
435 | #include
436 | #endif
437 |
438 | #if !defined(MG_ENABLE_EPOLL) && defined(__linux__)
439 | #define MG_ENABLE_EPOLL 1
440 | #elif !defined(MG_ENABLE_POLL)
441 | #define MG_ENABLE_POLL 1
442 | #endif
443 |
444 | #include
445 | #include
446 | #include
447 | #include
448 | #include
449 | #include
450 | #include
451 | #include
452 | #include
453 | #include
454 | #include
455 | #include
456 | #include
457 | #include
458 | #include
459 | #include
460 | #include
461 | #include
462 |
463 | #if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL
464 | #include
465 | #elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL
466 | #include
467 | #else
468 | #include
469 | #endif
470 |
471 | #include
472 | #include
473 | #include
474 | #include
475 | #include
476 | #include
477 |
478 | #ifndef MG_ENABLE_DIRLIST
479 | #define MG_ENABLE_DIRLIST 1
480 | #endif
481 |
482 | #ifndef MG_PATH_MAX
483 | #define MG_PATH_MAX FILENAME_MAX
484 | #endif
485 |
486 | #endif
487 |
488 |
489 | #if MG_ARCH == MG_ARCH_WIN32
490 |
491 | #ifndef WIN32_LEAN_AND_MEAN
492 | #define WIN32_LEAN_AND_MEAN
493 | #endif
494 |
495 | #ifndef _CRT_SECURE_NO_WARNINGS
496 | #define _CRT_SECURE_NO_WARNINGS
497 | #endif
498 |
499 | #ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
500 | #define _WINSOCK_DEPRECATED_NO_WARNINGS
501 | #endif
502 |
503 | #include
504 | #include
505 | #include
506 | #include
507 | #include
508 | #include
509 | #include
510 | #include
511 | #include
512 | #include
513 | #include
514 | #include
515 | #include
516 | #include
517 |
518 | #if defined(_MSC_VER) && _MSC_VER < 1700
519 | #define __func__ ""
520 | typedef __int64 int64_t;
521 | typedef unsigned __int64 uint64_t;
522 | typedef unsigned char uint8_t;
523 | typedef char int8_t;
524 | typedef unsigned short uint16_t;
525 | typedef short int16_t;
526 | typedef unsigned int uint32_t;
527 | typedef int int32_t;
528 | typedef enum { false = 0, true = 1 } bool;
529 | #else
530 | #include
531 | #include
532 | #include
533 | #endif
534 |
535 | #include
536 | #include
537 | #include
538 |
539 | // Protect from calls like std::snprintf in app code
540 | // See https://github.com/cesanta/mongoose/issues/1047
541 | #ifndef __cplusplus
542 | #define snprintf _snprintf
543 | #define vsnprintf _vsnprintf
544 | #ifndef strdup // For MSVC with _DEBUG, see #1359
545 | #define strdup(x) _strdup(x)
546 | #endif
547 | #endif
548 |
549 | typedef int socklen_t;
550 | #define MG_DIRSEP '\\'
551 |
552 | #ifndef MG_PATH_MAX
553 | #define MG_PATH_MAX FILENAME_MAX
554 | #endif
555 |
556 | #ifndef EINPROGRESS
557 | #define EINPROGRESS WSAEINPROGRESS
558 | #endif
559 | #ifndef EWOULDBLOCK
560 | #define EWOULDBLOCK WSAEWOULDBLOCK
561 | #endif
562 |
563 | #define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
564 | #define sleep(x) Sleep(x)
565 | #define mkdir(a, b) _mkdir(a)
566 |
567 | #ifndef va_copy
568 | #ifdef __va_copy
569 | #define va_copy __va_copy
570 | #else
571 | #define va_copy(x, y) (x) = (y)
572 | #endif
573 | #endif
574 | #ifndef S_ISDIR
575 | #define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
576 | #endif
577 |
578 | #ifndef MG_ENABLE_DIRLIST
579 | #define MG_ENABLE_DIRLIST 1
580 | #endif
581 |
582 | #endif
583 |
584 |
585 | #if MG_ARCH == MG_ARCH_ZEPHYR
586 |
587 | #include
588 |
589 | #include
590 | #include
591 | #include
592 | #include
593 | #include
594 | #include
595 | #include
596 | #include
597 | #include
598 | #include
599 | #include
600 | #include
601 |
602 | #define MG_PUTCHAR(x) printk("%c", x)
603 | #define strerror(x) zsock_gai_strerror(x)
604 | #define FD_CLOEXEC 0
605 | #define F_SETFD 0
606 | #define MG_ENABLE_SSI 0
607 |
608 | int rand(void);
609 | int sscanf(const char *, const char *, ...);
610 |
611 | #endif
612 |
613 |
614 | #ifndef MG_ENABLE_LOG
615 | #define MG_ENABLE_LOG 1
616 | #endif
617 |
618 | #ifndef MG_ENABLE_MIP
619 | #define MG_ENABLE_MIP 0
620 | #endif
621 |
622 | #ifndef MG_ENABLE_POLL
623 | #define MG_ENABLE_POLL 0
624 | #endif
625 |
626 | #ifndef MG_ENABLE_EPOLL
627 | #define MG_ENABLE_EPOLL 0
628 | #endif
629 |
630 | #ifndef MG_ENABLE_FATFS
631 | #define MG_ENABLE_FATFS 0
632 | #endif
633 |
634 | #ifndef MG_ENABLE_SOCKET
635 | #define MG_ENABLE_SOCKET 1
636 | #endif
637 |
638 | #ifndef MG_ENABLE_MBEDTLS
639 | #define MG_ENABLE_MBEDTLS 0
640 | #endif
641 |
642 | #ifndef MG_ENABLE_OPENSSL
643 | #define MG_ENABLE_OPENSSL 0
644 | #endif
645 |
646 | #ifndef MG_ENABLE_CUSTOM_TLS
647 | #define MG_ENABLE_CUSTOM_TLS 0
648 | #endif
649 |
650 | #ifndef MG_ENABLE_SSI
651 | #define MG_ENABLE_SSI 0
652 | #endif
653 |
654 | #ifndef MG_ENABLE_IPV6
655 | #define MG_ENABLE_IPV6 0
656 | #endif
657 |
658 | #ifndef MG_ENABLE_MD5
659 | #define MG_ENABLE_MD5 0
660 | #endif
661 |
662 | // Set MG_ENABLE_WINSOCK=0 for Win32 builds with external IP stack (like LWIP)
663 | #ifndef MG_ENABLE_WINSOCK
664 | #define MG_ENABLE_WINSOCK 1
665 | #endif
666 |
667 | #ifndef MG_ENABLE_DIRLIST
668 | #define MG_ENABLE_DIRLIST 0
669 | #endif
670 |
671 | #ifndef MG_ENABLE_CUSTOM_RANDOM
672 | #define MG_ENABLE_CUSTOM_RANDOM 0
673 | #endif
674 |
675 | #ifndef MG_ENABLE_CUSTOM_MILLIS
676 | #define MG_ENABLE_CUSTOM_MILLIS 0
677 | #endif
678 |
679 | #ifndef MG_ENABLE_PACKED_FS
680 | #define MG_ENABLE_PACKED_FS 0
681 | #endif
682 |
683 | // Granularity of the send/recv IO buffer growth
684 | #ifndef MG_IO_SIZE
685 | #define MG_IO_SIZE 2048
686 | #endif
687 |
688 | // Maximum size of the recv IO buffer
689 | #ifndef MG_MAX_RECV_SIZE
690 | #define MG_MAX_RECV_SIZE (3 * 1024 * 1024)
691 | #endif
692 |
693 | #ifndef MG_MAX_HTTP_HEADERS
694 | #define MG_MAX_HTTP_HEADERS 40
695 | #endif
696 |
697 | #ifndef MG_HTTP_INDEX
698 | #define MG_HTTP_INDEX "index.html"
699 | #endif
700 |
701 | #ifndef MG_PATH_MAX
702 | #ifdef PATH_MAX
703 | #define MG_PATH_MAX PATH_MAX
704 | #else
705 | #define MG_PATH_MAX 128
706 | #endif
707 | #endif
708 |
709 | #ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
710 | #define MG_SOCK_LISTEN_BACKLOG_SIZE 3
711 | #endif
712 |
713 | #ifndef MG_DIRSEP
714 | #define MG_DIRSEP '/'
715 | #endif
716 |
717 | #ifndef MG_ENABLE_FILE
718 | #if defined(FOPEN_MAX)
719 | #define MG_ENABLE_FILE 1
720 | #else
721 | #define MG_ENABLE_FILE 0
722 | #endif
723 | #endif
724 |
725 | #if MG_ENABLE_EPOLL
726 | #define MG_EPOLL_ADD(c) \
727 | do { \
728 | struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
729 | epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_ADD, (int) (size_t) c->fd, &ev); \
730 | } while (0)
731 | #define MG_EPOLL_MOD(c, wr) \
732 | do { \
733 | struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
734 | if (wr) ev.events |= EPOLLOUT; \
735 | epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_MOD, (int) (size_t) c->fd, &ev); \
736 | } while (0)
737 | #else
738 | #define MG_EPOLL_ADD(c)
739 | #define MG_EPOLL_MOD(c, wr)
740 | #endif
741 |
742 |
743 |
744 |
745 | struct mg_str {
746 | const char *ptr; // Pointer to string data
747 | size_t len; // String len
748 | };
749 |
750 | #define MG_NULL_STR \
751 | { NULL, 0 }
752 |
753 | #define MG_C_STR(a) \
754 | { (a), sizeof(a) - 1 }
755 |
756 | // Using macro to avoid shadowing C++ struct constructor, see #1298
757 | #define mg_str(s) mg_str_s(s)
758 |
759 | struct mg_str mg_str(const char *s);
760 | struct mg_str mg_str_n(const char *s, size_t n);
761 | int mg_lower(const char *s);
762 | int mg_ncasecmp(const char *s1, const char *s2, size_t len);
763 | int mg_casecmp(const char *s1, const char *s2);
764 | int mg_vcmp(const struct mg_str *s1, const char *s2);
765 | int mg_vcasecmp(const struct mg_str *str1, const char *str2);
766 | int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
767 | struct mg_str mg_strstrip(struct mg_str s);
768 | struct mg_str mg_strdup(const struct mg_str s);
769 | const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
770 | bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
771 | bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
772 | bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
773 | bool mg_split(struct mg_str *s, struct mg_str *k, struct mg_str *v, char delim);
774 | char *mg_hex(const void *buf, size_t len, char *dst);
775 | void mg_unhex(const char *buf, size_t len, unsigned char *to);
776 | unsigned long mg_unhexn(const char *s, size_t len);
777 | int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip);
778 | int64_t mg_to64(struct mg_str str);
779 | uint64_t mg_tou64(struct mg_str str);
780 | char *mg_remove_double_dots(char *s);
781 |
782 |
783 |
784 |
785 |
786 | typedef void (*mg_pfn_t)(char, void *); // Custom putchar
787 | typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer
788 | void mg_pfn_iobuf(char ch, void *param); // iobuf printer
789 |
790 | size_t mg_lld(char *buf, int64_t val, bool is_signed, bool is_hex);
791 | double mg_atod(const char *buf, int len, int *numlen);
792 | size_t mg_dtoa(char *buf, size_t len, double d, int width);
793 |
794 | size_t mg_vrprintf(void (*)(char, void *), void *, const char *fmt, va_list *);
795 | size_t mg_rprintf(void (*fn)(char, void *), void *, const char *fmt, ...);
796 | size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
797 | size_t mg_snprintf(char *, size_t, const char *fmt, ...);
798 | size_t mg_asprintf(char **, size_t, const char *fmt, ...);
799 | size_t mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap);
800 | char *mg_mprintf(const char *fmt, ...);
801 | char *mg_vmprintf(const char *fmt, va_list ap);
802 |
803 |
804 |
805 |
806 |
807 | enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE };
808 | void mg_log(const char *fmt, ...);
809 | bool mg_log_prefix(int ll, const char *file, int line, const char *fname);
810 | void mg_log_set(int log_level);
811 | void mg_hexdump(const void *buf, size_t len);
812 | void mg_log_set_fn(void (*logfunc)(unsigned char ch));
813 |
814 | #if MG_ENABLE_LOG
815 | #define MG_LOG(level, args) \
816 | do { \
817 | if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \
818 | } while (0)
819 | #else
820 | #define MG_LOG(level, args) \
821 | do { \
822 | if (0) mg_log args; \
823 | } while (0)
824 | #endif
825 |
826 | #define MG_ERROR(args) MG_LOG(MG_LL_ERROR, args)
827 | #define MG_INFO(args) MG_LOG(MG_LL_INFO, args)
828 | #define MG_DEBUG(args) MG_LOG(MG_LL_DEBUG, args)
829 | #define MG_VERBOSE(args) MG_LOG(MG_LL_VERBOSE, args)
830 |
831 |
832 |
833 |
834 | struct mg_timer {
835 | unsigned long id; // Timer ID
836 | uint64_t period_ms; // Timer period in milliseconds
837 | uint64_t prev_ms; // Timestamp of a previous poll
838 | uint64_t expire; // Expiration timestamp in milliseconds
839 | unsigned flags; // Possible flags values below
840 | #define MG_TIMER_ONCE 0 // Call function once
841 | #define MG_TIMER_REPEAT 1 // Call function periodically
842 | #define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
843 | void (*fn)(void *); // Function to call
844 | void *arg; // Function argument
845 | struct mg_timer *next; // Linkage
846 | };
847 |
848 | void mg_timer_init(struct mg_timer **head, struct mg_timer *timer,
849 | uint64_t milliseconds, unsigned flags, void (*fn)(void *),
850 | void *arg);
851 | void mg_timer_free(struct mg_timer **head, struct mg_timer *);
852 | void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);
853 |
854 |
855 |
856 |
857 |
858 | enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4 };
859 |
860 | // Filesystem API functions
861 | // st() returns MG_FS_* flags and populates file size and modification time
862 | // ls() calls fn() for every directory entry, allowing to list a directory
863 | //
864 | // NOTE: UNIX-style shorthand names for the API functions are deliberately
865 | // chosen to avoid conflicts with some libraries that make macros for e.g.
866 | // stat(), write(), read() calls.
867 | struct mg_fs {
868 | int (*st)(const char *path, size_t *size, time_t *mtime); // stat file
869 | void (*ls)(const char *path, void (*fn)(const char *, void *), void *);
870 | void *(*op)(const char *path, int flags); // Open file
871 | void (*cl)(void *fd); // Close file
872 | size_t (*rd)(void *fd, void *buf, size_t len); // Read file
873 | size_t (*wr)(void *fd, const void *buf, size_t len); // Write file
874 | size_t (*sk)(void *fd, size_t offset); // Set file position
875 | bool (*mv)(const char *from, const char *to); // Rename file
876 | bool (*rm)(const char *path); // Delete file
877 | bool (*mkd)(const char *path); // Create directory
878 | };
879 |
880 | extern struct mg_fs mg_fs_posix; // POSIX open/close/read/write/seek
881 | extern struct mg_fs mg_fs_packed; // Packed FS, see examples/device-dashboard
882 | extern struct mg_fs mg_fs_fat; // FAT FS
883 |
884 | // File descriptor
885 | struct mg_fd {
886 | void *fd;
887 | struct mg_fs *fs;
888 | };
889 |
890 | struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags);
891 | void mg_fs_close(struct mg_fd *fd);
892 | char *mg_file_read(struct mg_fs *fs, const char *path, size_t *size);
893 | bool mg_file_write(struct mg_fs *fs, const char *path, const void *, size_t);
894 | bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
895 |
896 |
897 |
898 |
899 |
900 |
901 | void mg_random(void *buf, size_t len);
902 | char *mg_random_str(char *buf, size_t len);
903 | uint16_t mg_ntohs(uint16_t net);
904 | uint32_t mg_ntohl(uint32_t net);
905 | uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
906 | uint64_t mg_millis(void);
907 |
908 | #define mg_htons(x) mg_ntohs(x)
909 | #define mg_htonl(x) mg_ntohl(x)
910 |
911 | // Linked list management macros
912 | #define LIST_ADD_HEAD(type_, head_, elem_) \
913 | do { \
914 | (elem_)->next = (*head_); \
915 | *(head_) = (elem_); \
916 | } while (0)
917 |
918 | #define LIST_ADD_TAIL(type_, head_, elem_) \
919 | do { \
920 | type_ **h = head_; \
921 | while (*h != NULL) h = &(*h)->next; \
922 | *h = (elem_); \
923 | } while (0)
924 |
925 | #define LIST_DELETE(type_, head_, elem_) \
926 | do { \
927 | type_ **h = head_; \
928 | while (*h != (elem_)) h = &(*h)->next; \
929 | *h = (elem_)->next; \
930 | } while (0)
931 |
932 |
933 |
934 | unsigned short mg_url_port(const char *url);
935 | int mg_url_is_ssl(const char *url);
936 | struct mg_str mg_url_host(const char *url);
937 | struct mg_str mg_url_user(const char *url);
938 | struct mg_str mg_url_pass(const char *url);
939 | const char *mg_url_uri(const char *url);
940 |
941 |
942 |
943 |
944 | struct mg_iobuf {
945 | unsigned char *buf; // Pointer to stored data
946 | size_t size; // Total size available
947 | size_t len; // Current number of bytes
948 | size_t align; // Alignment during allocation
949 | };
950 |
951 | int mg_iobuf_init(struct mg_iobuf *, size_t, size_t);
952 | int mg_iobuf_resize(struct mg_iobuf *, size_t);
953 | void mg_iobuf_free(struct mg_iobuf *);
954 | size_t mg_iobuf_add(struct mg_iobuf *, size_t, const void *, size_t);
955 | size_t mg_iobuf_del(struct mg_iobuf *, size_t ofs, size_t len);
956 |
957 | int mg_base64_update(unsigned char p, char *to, int len);
958 | int mg_base64_final(char *to, int len);
959 | int mg_base64_encode(const unsigned char *p, int n, char *to);
960 | int mg_base64_decode(const char *src, int n, char *dst);
961 |
962 |
963 |
964 |
965 | typedef struct {
966 | uint32_t buf[4];
967 | uint32_t bits[2];
968 | unsigned char in[64];
969 | } mg_md5_ctx;
970 |
971 | void mg_md5_init(mg_md5_ctx *c);
972 | void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
973 | void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);
974 |
975 |
976 |
977 |
978 | typedef struct {
979 | uint32_t state[5];
980 | uint32_t count[2];
981 | unsigned char buffer[64];
982 | } mg_sha1_ctx;
983 |
984 | void mg_sha1_init(mg_sha1_ctx *);
985 | void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
986 | void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);
987 |
988 |
989 | struct mg_connection;
990 | typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
991 | void *ev_data, void *fn_data);
992 | void mg_call(struct mg_connection *c, int ev, void *ev_data);
993 | void mg_error(struct mg_connection *c, const char *fmt, ...);
994 |
995 | enum {
996 | MG_EV_ERROR, // Error char *error_message
997 | MG_EV_OPEN, // Connection created NULL
998 | MG_EV_POLL, // mg_mgr_poll iteration uint64_t *milliseconds
999 | MG_EV_RESOLVE, // Host name is resolved NULL
1000 | MG_EV_CONNECT, // Connection established NULL
1001 | MG_EV_ACCEPT, // Connection accepted NULL
1002 | MG_EV_READ, // Data received from socket struct mg_str *
1003 | MG_EV_WRITE, // Data written to socket long *bytes_written
1004 | MG_EV_CLOSE, // Connection closed NULL
1005 | MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
1006 | MG_EV_HTTP_CHUNK, // HTTP chunk (partial msg) struct mg_http_message *
1007 | MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
1008 | MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
1009 | MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
1010 | MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
1011 | MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
1012 | MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
1013 | MG_EV_SNTP_TIME, // SNTP time received uint64_t *milliseconds
1014 | MG_EV_USER, // Starting ID for user events
1015 | };
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 | struct mg_dns {
1025 | const char *url; // DNS server URL
1026 | struct mg_connection *c; // DNS server connection
1027 | };
1028 |
1029 | struct mg_addr {
1030 | uint16_t port; // TCP or UDP port in network byte order
1031 | uint32_t ip; // IP address in network byte order
1032 | uint8_t ip6[16]; // IPv6 address
1033 | bool is_ip6; // True when address is IPv6 address
1034 | };
1035 |
1036 | struct mg_mgr {
1037 | struct mg_connection *conns; // List of active connections
1038 | struct mg_dns dns4; // DNS for IPv4
1039 | struct mg_dns dns6; // DNS for IPv6
1040 | int dnstimeout; // DNS resolve timeout in milliseconds
1041 | bool use_dns6; // Use DNS6 server by default, see #1532
1042 | unsigned long nextid; // Next connection ID
1043 | unsigned long timerid; // Next timer ID
1044 | void *userdata; // Arbitrary user data pointer
1045 | uint16_t mqtt_id; // MQTT IDs for pub/sub
1046 | void *active_dns_requests; // DNS requests in progress
1047 | struct mg_timer *timers; // Active timers
1048 | int epoll_fd; // Used when MG_EPOLL_ENABLE=1
1049 | void *priv; // Used by the MIP stack
1050 | size_t extraconnsize; // Used by the MIP stack
1051 | #if MG_ARCH == MG_ARCH_FREERTOS_TCP
1052 | SocketSet_t ss; // NOTE(lsm): referenced from socket struct
1053 | #endif
1054 | };
1055 |
1056 | struct mg_connection {
1057 | struct mg_connection *next; // Linkage in struct mg_mgr :: connections
1058 | struct mg_mgr *mgr; // Our container
1059 | struct mg_addr loc; // Local address
1060 | struct mg_addr rem; // Remote address
1061 | void *fd; // Connected socket, or LWIP data
1062 | unsigned long id; // Auto-incrementing unique connection ID
1063 | struct mg_iobuf recv; // Incoming data
1064 | struct mg_iobuf send; // Outgoing data
1065 | mg_event_handler_t fn; // User-specified event handler function
1066 | void *fn_data; // User-specified function parameter
1067 | mg_event_handler_t pfn; // Protocol-specific handler function
1068 | void *pfn_data; // Protocol-specific function parameter
1069 | char label[50]; // Arbitrary label
1070 | void *tls; // TLS specific data
1071 | unsigned is_listening : 1; // Listening connection
1072 | unsigned is_client : 1; // Outbound (client) connection
1073 | unsigned is_accepted : 1; // Accepted (server) connection
1074 | unsigned is_resolving : 1; // Non-blocking DNS resolution is in progress
1075 | unsigned is_connecting : 1; // Non-blocking connect is in progress
1076 | unsigned is_tls : 1; // TLS-enabled connection
1077 | unsigned is_tls_hs : 1; // TLS handshake is in progress
1078 | unsigned is_udp : 1; // UDP connection
1079 | unsigned is_websocket : 1; // WebSocket connection
1080 | unsigned is_mqtt5 : 1; // For MQTT connection, v5 indicator
1081 | unsigned is_hexdumping : 1; // Hexdump in/out traffic
1082 | unsigned is_draining : 1; // Send remaining data, then close and free
1083 | unsigned is_closing : 1; // Close and free the connection immediately
1084 | unsigned is_full : 1; // Stop reads, until cleared
1085 | unsigned is_resp : 1; // Response is still being generated
1086 | unsigned is_readable : 1; // Connection is ready to read
1087 | unsigned is_writable : 1; // Connection is ready to write
1088 | };
1089 |
1090 | void mg_mgr_poll(struct mg_mgr *, int ms);
1091 | void mg_mgr_init(struct mg_mgr *);
1092 | void mg_mgr_free(struct mg_mgr *);
1093 |
1094 | struct mg_connection *mg_listen(struct mg_mgr *, const char *url,
1095 | mg_event_handler_t fn, void *fn_data);
1096 | struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
1097 | mg_event_handler_t fn, void *fn_data);
1098 | struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
1099 | mg_event_handler_t fn, void *fn_data);
1100 | void mg_connect_resolved(struct mg_connection *);
1101 | bool mg_send(struct mg_connection *, const void *, size_t);
1102 | size_t mg_printf(struct mg_connection *, const char *fmt, ...);
1103 | size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
1104 | char *mg_straddr(struct mg_addr *, char *, size_t);
1105 | bool mg_aton(struct mg_str str, struct mg_addr *addr);
1106 | char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len);
1107 | int mg_mkpipe(struct mg_mgr *, mg_event_handler_t, void *, bool udp);
1108 |
1109 | // These functions are used to integrate with custom network stacks
1110 | struct mg_connection *mg_alloc_conn(struct mg_mgr *);
1111 | void mg_close_conn(struct mg_connection *c);
1112 | bool mg_open_listener(struct mg_connection *c, const char *url);
1113 | struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
1114 | unsigned flags, void (*fn)(void *), void *arg);
1115 |
1116 |
1117 |
1118 |
1119 |
1120 |
1121 |
1122 |
1123 | struct mg_http_header {
1124 | struct mg_str name; // Header name
1125 | struct mg_str value; // Header value
1126 | };
1127 |
1128 | struct mg_http_message {
1129 | struct mg_str method, uri, query, proto; // Request/response line
1130 | struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Headers
1131 | struct mg_str body; // Body
1132 | struct mg_str head; // Request + headers
1133 | struct mg_str chunk; // Chunk for chunked encoding, or partial body
1134 | struct mg_str message; // Request + headers + body
1135 | };
1136 |
1137 | // Parameter for mg_http_serve_dir()
1138 | struct mg_http_serve_opts {
1139 | const char *root_dir; // Web root directory, must be non-NULL
1140 | const char *ssi_pattern; // SSI file name pattern, e.g. #.shtml
1141 | const char *extra_headers; // Extra HTTP headers to add in responses
1142 | const char *mime_types; // Extra mime types, ext1=type1,ext2=type2,..
1143 | const char *page404; // Path to the 404 page, or NULL by default
1144 | struct mg_fs *fs; // Filesystem implementation. Use NULL for POSIX
1145 | };
1146 |
1147 | // Parameter for mg_http_next_multipart
1148 | struct mg_http_part {
1149 | struct mg_str name; // Form field name
1150 | struct mg_str filename; // Filename for file uploads
1151 | struct mg_str body; // Part contents
1152 | };
1153 |
1154 | int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
1155 | int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
1156 | void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
1157 | void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
1158 | void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
1159 | struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
1160 | mg_event_handler_t fn, void *fn_data);
1161 | struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
1162 | mg_event_handler_t fn, void *fn_data);
1163 | void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
1164 | const struct mg_http_serve_opts *);
1165 | void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
1166 | const char *path, const struct mg_http_serve_opts *);
1167 | void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
1168 | const char *body_fmt, ...);
1169 | struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
1170 | struct mg_str mg_http_var(struct mg_str buf, struct mg_str name);
1171 | int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t);
1172 | int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
1173 | size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
1174 | void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
1175 | bool mg_http_match_uri(const struct mg_http_message *, const char *glob);
1176 | long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
1177 | struct mg_fs *fs, const char *path, size_t max_size);
1178 | void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
1179 | struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
1180 | size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
1181 | int mg_http_status(const struct mg_http_message *hm);
1182 |
1183 |
1184 | void mg_http_serve_ssi(struct mg_connection *c, const char *root,
1185 | const char *fullpath);
1186 |
1187 |
1188 |
1189 |
1190 |
1191 |
1192 | struct mg_tls_opts {
1193 | const char *ca; // CA certificate file. For both listeners and clients
1194 | const char *crl; // Certificate Revocation List. For clients
1195 | const char *cert; // Certificate
1196 | const char *certkey; // Certificate key
1197 | const char *ciphers; // Cipher list
1198 | struct mg_str srvname; // If not empty, enables server name verification
1199 | struct mg_fs *fs; // FS API for reading certificate files
1200 | };
1201 |
1202 | void mg_tls_init(struct mg_connection *, const struct mg_tls_opts *);
1203 | void mg_tls_free(struct mg_connection *);
1204 | long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
1205 | long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
1206 | size_t mg_tls_pending(struct mg_connection *);
1207 | void mg_tls_handshake(struct mg_connection *);
1208 |
1209 |
1210 |
1211 |
1212 |
1213 |
1214 |
1215 | #if MG_ENABLE_MBEDTLS
1216 | #include
1217 | #include
1218 | #include
1219 |
1220 | struct mg_tls {
1221 | char *cafile; // CA certificate path
1222 | mbedtls_x509_crt ca; // Parsed CA certificate
1223 | mbedtls_x509_crt cert; // Parsed certificate
1224 | mbedtls_ssl_context ssl; // SSL/TLS context
1225 | mbedtls_ssl_config conf; // SSL-TLS config
1226 | mbedtls_pk_context pk; // Private key context
1227 | };
1228 | #endif
1229 |
1230 |
1231 | #if MG_ENABLE_OPENSSL
1232 |
1233 | #include
1234 | #include
1235 |
1236 | struct mg_tls {
1237 | SSL_CTX *ctx;
1238 | SSL *ssl;
1239 | };
1240 | #endif
1241 |
1242 |
1243 | #define WEBSOCKET_OP_CONTINUE 0
1244 | #define WEBSOCKET_OP_TEXT 1
1245 | #define WEBSOCKET_OP_BINARY 2
1246 | #define WEBSOCKET_OP_CLOSE 8
1247 | #define WEBSOCKET_OP_PING 9
1248 | #define WEBSOCKET_OP_PONG 10
1249 |
1250 |
1251 |
1252 | struct mg_ws_message {
1253 | struct mg_str data; // Websocket message data
1254 | uint8_t flags; // Websocket message flags
1255 | };
1256 |
1257 | struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
1258 | mg_event_handler_t fn, void *fn_data,
1259 | const char *fmt, ...);
1260 | void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
1261 | const char *fmt, ...);
1262 | size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op);
1263 | size_t mg_ws_wrap(struct mg_connection *, size_t len, int op);
1264 | size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...);
1265 | size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt, va_list);
1266 |
1267 |
1268 |
1269 |
1270 | struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
1271 | mg_event_handler_t fn, void *fn_data);
1272 | void mg_sntp_request(struct mg_connection *c);
1273 | int64_t mg_sntp_parse(const unsigned char *buf, size_t len);
1274 |
1275 |
1276 |
1277 |
1278 |
1279 | #define MQTT_CMD_CONNECT 1
1280 | #define MQTT_CMD_CONNACK 2
1281 | #define MQTT_CMD_PUBLISH 3
1282 | #define MQTT_CMD_PUBACK 4
1283 | #define MQTT_CMD_PUBREC 5
1284 | #define MQTT_CMD_PUBREL 6
1285 | #define MQTT_CMD_PUBCOMP 7
1286 | #define MQTT_CMD_SUBSCRIBE 8
1287 | #define MQTT_CMD_SUBACK 9
1288 | #define MQTT_CMD_UNSUBSCRIBE 10
1289 | #define MQTT_CMD_UNSUBACK 11
1290 | #define MQTT_CMD_PINGREQ 12
1291 | #define MQTT_CMD_PINGRESP 13
1292 | #define MQTT_CMD_DISCONNECT 14
1293 | #define MQTT_CMD_AUTH 15
1294 |
1295 | enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED };
1296 |
1297 | struct mg_mqtt_opts {
1298 | struct mg_str user; // Username, can be empty
1299 | struct mg_str pass; // Password, can be empty
1300 | struct mg_str client_id; // Client ID
1301 | struct mg_str will_topic; // Will topic
1302 | struct mg_str will_message; // Will message
1303 | uint8_t will_qos; // Will message quality of service
1304 | uint8_t version; // Can be 4 (3.1.1), or 5. If 0, assume 4.
1305 | uint16_t keepalive; // Keep-alive timer in seconds
1306 | bool will_retain; // Retain last will
1307 | bool clean; // Use clean session, 0 or 1
1308 | };
1309 |
1310 | struct mg_mqtt_message {
1311 | struct mg_str topic; // Parsed topic
1312 | struct mg_str data; // Parsed message
1313 | struct mg_str dgram; // Whole MQTT datagram, including headers
1314 | uint16_t id; // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
1315 | uint8_t cmd; // MQTT command, one of MQTT_CMD_*
1316 | uint8_t qos; // Quality of service
1317 | uint8_t ack; // Connack return code. 0 - success
1318 | };
1319 |
1320 | struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
1321 | const struct mg_mqtt_opts *opts,
1322 | mg_event_handler_t fn, void *fn_data);
1323 | struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
1324 | mg_event_handler_t fn, void *fn_data);
1325 | void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
1326 | void mg_mqtt_pub(struct mg_connection *c, struct mg_str topic,
1327 | struct mg_str data, int qos, bool retain);
1328 | void mg_mqtt_sub(struct mg_connection *, struct mg_str topic, int qos);
1329 | int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *);
1330 | void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
1331 | uint32_t len);
1332 | size_t mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
1333 | uint8_t *qos, size_t pos);
1334 | size_t mg_mqtt_next_unsub(struct mg_mqtt_message *msg, struct mg_str *topic,
1335 | size_t pos);
1336 | void mg_mqtt_ping(struct mg_connection *);
1337 | void mg_mqtt_pong(struct mg_connection *);
1338 | void mg_mqtt_disconnect(struct mg_connection *);
1339 |
1340 |
1341 |
1342 |
1343 |
1344 | // Mongoose sends DNS queries that contain only one question:
1345 | // either A (IPv4) or AAAA (IPv6) address lookup.
1346 | // Therefore, we expect zero or one answer.
1347 | // If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
1348 | struct mg_dns_message {
1349 | uint16_t txnid; // Transaction ID
1350 | bool resolved; // Resolve successful, addr is set
1351 | struct mg_addr addr; // Resolved address
1352 | char name[256]; // Host name
1353 | };
1354 |
1355 | struct mg_dns_header {
1356 | uint16_t txnid; // Transaction ID
1357 | uint16_t flags;
1358 | uint16_t num_questions;
1359 | uint16_t num_answers;
1360 | uint16_t num_authority_prs;
1361 | uint16_t num_other_prs;
1362 | };
1363 |
1364 | // DNS resource record
1365 | struct mg_dns_rr {
1366 | uint16_t nlen; // Name or pointer length
1367 | uint16_t atype; // Address type
1368 | uint16_t aclass; // Address class
1369 | uint16_t alen; // Address length
1370 | };
1371 |
1372 | void mg_resolve(struct mg_connection *, const char *url);
1373 | void mg_resolve_cancel(struct mg_connection *);
1374 | bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
1375 | size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
1376 | bool is_question, struct mg_dns_rr *);
1377 |
1378 |
1379 |
1380 |
1381 |
1382 | #ifndef MG_JSON_MAX_DEPTH
1383 | #define MG_JSON_MAX_DEPTH 30
1384 | #endif
1385 |
1386 | // Error return values - negative. Successful returns are >= 0
1387 | enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
1388 | int mg_json_get(struct mg_str json, const char *path, int *toklen);
1389 |
1390 | bool mg_json_get_num(struct mg_str json, const char *path, double *v);
1391 | bool mg_json_get_bool(struct mg_str json, const char *path, bool *v);
1392 | long mg_json_get_long(struct mg_str json, const char *path, long dflt);
1393 | char *mg_json_get_str(struct mg_str json, const char *path);
1394 | char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
1395 | char *mg_json_get_b64(struct mg_str json, const char *path, int *len);
1396 |
1397 |
1398 |
1399 |
1400 | // JSON-RPC request descriptor
1401 | struct mg_rpc_req {
1402 | struct mg_rpc **head; // RPC handlers list head
1403 | struct mg_rpc *rpc; // RPC handler being called
1404 | mg_pfn_t pfn; // Response printing function
1405 | void *pfn_data; // Response printing function data
1406 | void *req_data; // Arbitrary request data
1407 | struct mg_str frame; // Request, e.g. {"id":1,"method":"add","params":[1,2]}
1408 | };
1409 |
1410 | // JSON-RPC method handler
1411 | struct mg_rpc {
1412 | struct mg_rpc *next; // Next in list
1413 | struct mg_str method; // Method pattern
1414 | void (*fn)(struct mg_rpc_req *); // Handler function
1415 | void *fn_data; // Handler function argument
1416 | };
1417 |
1418 | void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern,
1419 | void (*handler)(struct mg_rpc_req *), void *handler_data);
1420 | void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *));
1421 | void mg_rpc_process(struct mg_rpc_req *);
1422 |
1423 | // Helper functions to print result or error frame
1424 | void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);
1425 | void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap);
1426 | void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...);
1427 | void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *);
1428 | void mg_rpc_list(struct mg_rpc_req *r);
1429 |
1430 |
1431 |
1432 |
1433 |
1434 | struct mip_driver {
1435 | void *data; // Driver-specific data
1436 | void (*init)(void *data); // Initialise driver
1437 | size_t (*tx)(const void *, size_t, void *data); // Transmit frame
1438 | size_t (*rx)(void *buf, size_t len, void *data); // Receive frame (polling)
1439 | bool (*status)(void *data); // Up/down status
1440 | // Set receive callback for interrupt-driven drivers
1441 | void (*rxcb)(void (*fn)(void *buf, size_t len, void *rxdata), void *rxdata);
1442 | };
1443 |
1444 | struct mip_ipcfg {
1445 | uint8_t mac[6]; // MAC address. Must not be 0
1446 | uint32_t ip, mask, gw; // IP, netmask, GW. If IP is 0, DHCP is used
1447 | };
1448 |
1449 | void mip_init(struct mg_mgr *, struct mip_ipcfg *, struct mip_driver *);
1450 |
1451 | extern struct mip_driver mip_driver_stm32;
1452 |
1453 | #ifdef __cplusplus
1454 | }
1455 | #endif
1456 | #endif // MONGOOSE_H
1457 |
--------------------------------------------------------------------------------