mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-01 13:10:43 +00:00
Dash OTA dogfooding nits
This commit is contained in:
1 parent
73037880e6
commit
27459efc61
29 files changed
+2635
-1478
No files matched your search
+487
-245
@@ -5,67 +5,21 @@
|
||||
#define MG_NO_CACHE_HEADERS "Cache-Control: no-cache\r\n"
|
||||
#define MG_JSON_HEADERS "Content-Type: application/json\r\n" MG_NO_CACHE_HEADERS
|
||||
|
||||
struct mg_dash_user {
|
||||
struct mg_dash_user *next;
|
||||
char name[32]; // User name
|
||||
char token[21]; // Login token
|
||||
int level; // Access level
|
||||
uint64_t expire; // Expiration timestamp
|
||||
#define CONN_HANDLED 'Z'
|
||||
|
||||
struct mg_dash_cdata {
|
||||
char marker;
|
||||
struct mg_dash_user *u;
|
||||
struct mg_dash *dash;
|
||||
};
|
||||
|
||||
#define CONN_HANDLED 'Z'
|
||||
static struct mg_dash_user s_guest;
|
||||
|
||||
static struct mg_str trimq(struct mg_str s) { // Trim double quotes
|
||||
if (s.len > 1 && s.buf[0] == '"') s.len -= 2, s.buf++;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void mg_dash_broadcast(struct mg_mgr *mgr, int level, const char *fmt,
|
||||
...) {
|
||||
struct mg_connection *c;
|
||||
va_list ap;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
int user_level = *(int *) c->data;
|
||||
if (!c->is_websocket) continue;
|
||||
if (level > 0 && user_level < level) continue;
|
||||
if (c->send.len > MG_DASH_MAX_SEND_BUF_SIZE) {
|
||||
mg_error(c, "%lu buffered data %lu > MG_DASH_MAX_SEND_BUF_SIZE", c->id,
|
||||
c->send.len);
|
||||
} else {
|
||||
va_start(ap, fmt);
|
||||
mg_ws_vprintf(c, WEBSOCKET_OP_TEXT, fmt, &ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t mg_print_fmt(mg_pfn_t out, void *param, va_list *ap) {
|
||||
const char *fmt = va_arg(*ap, const char *);
|
||||
ap = va_arg(*ap, va_list *);
|
||||
return mg_vxprintf(out, param, fmt, ap);
|
||||
}
|
||||
|
||||
static void mg_dash_success(struct mg_connection *c, struct mg_str req,
|
||||
const char *fmt, ...) {
|
||||
struct mg_str id = mg_json_get_tok(req, "$.id");
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%.*s,%m:%M}", MG_ESC("id"), id.len,
|
||||
id.buf, MG_ESC("result"), mg_print_fmt, fmt, &ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void mg_dash_error(struct mg_connection *c, struct mg_str req,
|
||||
const char *fmt, ...) {
|
||||
struct mg_str id = mg_json_get_tok(req, "$.id");
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%.*s,%m:{%m:%d,%m:%m}}", MG_ESC("id"),
|
||||
id.len, id.buf, MG_ESC("error"), MG_ESC("code"), -1,
|
||||
MG_ESC("message"), mg_print_fmt, fmt, &ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static struct mg_field_set *mg_dash_find_field_set(struct mg_dash *dash,
|
||||
struct mg_str name) {
|
||||
struct mg_field_set *fs;
|
||||
@@ -92,6 +46,8 @@ static size_t mg_print_field(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
n += mg_xprintf(fn, arg, "%s", *(bool *) f->value ? "true" : "false");
|
||||
} else if (f->type == MG_VAL_INT) {
|
||||
n += mg_xprintf(fn, arg, "%d", *(int *) f->value);
|
||||
} else if (f->type == MG_VAL_UINT64) {
|
||||
n += mg_xprintf(fn, arg, "%llu", (uint64_t) *(uint64_t *) f->value);
|
||||
} else if (f->type == MG_VAL_DBL) {
|
||||
n += mg_xprintf(fn, arg, "%.2f", *(double *) f->value);
|
||||
} else if (f->type == MG_VAL_STR) {
|
||||
@@ -116,10 +72,56 @@ static size_t mg_print_field_set(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
static int mg_dash_array_size(struct mg_field_set *set,
|
||||
struct mg_dash_user *u) {
|
||||
int saved = *set->index, sz = -1;
|
||||
*set->index = -1;
|
||||
if (set->fn) {
|
||||
if (set->fn(MG_DASH_READ, u)) sz = *set->index;
|
||||
} else if (set->get_dir) {
|
||||
mg_dash_dir_read(set, u);
|
||||
sz = *set->index;
|
||||
}
|
||||
*set->index = saved;
|
||||
return sz;
|
||||
}
|
||||
|
||||
static size_t mg_dash_print_array(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
struct mg_field_set *set = va_arg(*ap, struct mg_field_set *);
|
||||
int from = va_arg(*ap, int);
|
||||
int to = va_arg(*ap, int);
|
||||
struct mg_dash_user *u = va_arg(*ap, struct mg_dash_user *);
|
||||
bool started = false;
|
||||
int saved = *set->index;
|
||||
size_t n = 0;
|
||||
*set->index = from;
|
||||
n += mg_xprintf(fn, arg, "[");
|
||||
for (;;) {
|
||||
bool done = to >= 0 && *set->index > to;
|
||||
if (!done) {
|
||||
if (set->fn)
|
||||
set->fn(MG_DASH_READ, u);
|
||||
else if (set->get_dir)
|
||||
mg_dash_dir_read(set, u);
|
||||
done = *set->index < 0;
|
||||
}
|
||||
if (done) break;
|
||||
n += mg_xprintf(fn, arg, "%s%M", started ? "," : "", mg_print_field_set,
|
||||
set);
|
||||
started = true;
|
||||
(*set->index)++;
|
||||
}
|
||||
n += mg_xprintf(fn, arg, "]");
|
||||
*set->index = saved;
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t mg_dash_print_endpoint(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
struct mg_dash *dash = va_arg(*ap, struct mg_dash *);
|
||||
struct mg_dash_user *u = va_arg(*ap, struct mg_dash_user *);
|
||||
struct mg_str *name = va_arg(*ap, struct mg_str *);
|
||||
int level = va_arg(*ap, int);
|
||||
struct mg_str *from_str = va_arg(*ap, struct mg_str *);
|
||||
struct mg_str *to_str = va_arg(*ap, struct mg_str *);
|
||||
struct mg_field_set *set = mg_dash_find_field_set(dash, *name);
|
||||
size_t n = 0;
|
||||
if (name->len == 0) {
|
||||
@@ -127,37 +129,74 @@ static size_t mg_dash_print_name(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
const char *comma = "";
|
||||
n += mg_xprintf(fn, arg, "{");
|
||||
for (fs = dash->sets; fs != NULL; fs = fs->next) {
|
||||
if (fs->read_level > 0 && level < fs->read_level) continue;
|
||||
if (fs->reader) fs->reader();
|
||||
n += mg_xprintf(fn, arg, comma);
|
||||
n += mg_xprintf(fn, arg, "%m:", MG_ESC(fs->name));
|
||||
n += mg_xprintf(fn, arg, "%M", mg_print_field_set, fs);
|
||||
if (fs->index != NULL) {
|
||||
int sz = mg_dash_array_size(fs, u);
|
||||
if (sz < 0) continue;
|
||||
n += mg_xprintf(fn, arg, comma);
|
||||
n += mg_xprintf(fn, arg, "%m:%d", MG_ESC(fs->name), sz);
|
||||
} else {
|
||||
if (fs->fn && !fs->fn(MG_DASH_READ, u)) continue;
|
||||
n += mg_xprintf(fn, arg, comma);
|
||||
n += mg_xprintf(fn, arg, "%m:%M", MG_ESC(fs->name), mg_print_field_set,
|
||||
fs);
|
||||
}
|
||||
comma = ",";
|
||||
}
|
||||
n += mg_xprintf(fn, arg, "}");
|
||||
} else if (set != NULL &&
|
||||
(set->read_level <= 0 || level >= set->read_level)) {
|
||||
if (set->reader) set->reader();
|
||||
n += mg_xprintf(fn, arg, "%M", mg_print_field_set, set);
|
||||
} else if (set != NULL && (set->fn == NULL || set->fn(MG_DASH_READ, u))) {
|
||||
if (set->index != NULL && from_str != NULL && from_str->len > 0) {
|
||||
int from = 0, to = 0;
|
||||
mg_str_to_num(*from_str, 10, &from, sizeof(from));
|
||||
to = from;
|
||||
if (to_str != NULL && to_str->len > 0) {
|
||||
mg_str_to_num(*to_str, 10, &to, sizeof(to));
|
||||
}
|
||||
n += mg_xprintf(fn, arg, "%M", mg_dash_print_array, set, from, to, u);
|
||||
} else if (set->index != NULL) {
|
||||
n += mg_xprintf(fn, arg, "%d", mg_dash_array_size(set, u));
|
||||
} else {
|
||||
n += mg_xprintf(fn, arg, "%M", mg_print_field_set, set);
|
||||
}
|
||||
} else {
|
||||
n += mg_xprintf(fn, arg, "null");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t mg_dash_print(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
struct mg_str *req = va_arg(*ap, struct mg_str *);
|
||||
struct mg_dash *dash = va_arg(*ap, struct mg_dash *);
|
||||
int level = va_arg(*ap, int);
|
||||
struct mg_str name = trimq(mg_json_get_tok(*req, "$.params"));
|
||||
return mg_xprintf(fn, arg, "%M", mg_dash_print_name, dash, &name, level);
|
||||
}
|
||||
|
||||
void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *set) {
|
||||
if (set->reader) set->reader();
|
||||
mg_dash_broadcast(mgr, set->read_level, "{%m:%m,%m:{%m:%M}}",
|
||||
MG_ESC("method"), MG_ESC("change"), MG_ESC("params"),
|
||||
MG_ESC(set->name), mg_print_field_set, set);
|
||||
struct mg_connection *c;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
struct mg_dash_cdata *d = (struct mg_dash_cdata *) c->data;
|
||||
struct mg_dash_user *u = d->u;
|
||||
if (!c->is_websocket) continue;
|
||||
if (u == NULL) continue;
|
||||
if (set->index != NULL && *set->index < 0) {
|
||||
int sz = mg_dash_array_size(set, u);
|
||||
if (sz < 0) continue;
|
||||
mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m,%m:{%m:%d}}", MG_ESC("method"),
|
||||
MG_ESC("change"), MG_ESC("params"), MG_ESC(set->name), sz);
|
||||
} else {
|
||||
int saved_idx = set->index != NULL ? *set->index : 0;
|
||||
bool ok = set->fn ? set->fn(MG_DASH_READ, u)
|
||||
: (set->get_dir ? mg_dash_dir_read(set, u) : true);
|
||||
if (!ok) {
|
||||
if (set->index != NULL) *set->index = saved_idx;
|
||||
continue;
|
||||
}
|
||||
if (set->index != NULL) {
|
||||
char key[64];
|
||||
mg_snprintf(key, sizeof(key), "%s/%d", set->name, *set->index);
|
||||
mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m,%m:{%m:%M}}",
|
||||
MG_ESC("method"), MG_ESC("change"), MG_ESC("params"),
|
||||
MG_ESC(key), mg_print_field_set, set);
|
||||
*set->index = saved_idx;
|
||||
} else {
|
||||
mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m,%m:{%m:%M}}",
|
||||
MG_ESC("method"), MG_ESC("change"), MG_ESC("params"),
|
||||
MG_ESC(set->name), mg_print_field_set, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) {
|
||||
@@ -174,6 +213,13 @@ static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) {
|
||||
*(int *) f->value = (int) d;
|
||||
ok = true;
|
||||
}
|
||||
} else if (f->type == MG_VAL_UINT64) {
|
||||
double d;
|
||||
if (f->value_size == sizeof(uint64_t) &&
|
||||
mg_json_get_num(json, json_path, &d) && d == (double) (int64_t) d) {
|
||||
*(uint64_t *) f->value = (uint64_t) d;
|
||||
ok = true;
|
||||
}
|
||||
} else if (f->type == MG_VAL_DBL) {
|
||||
ok = f->value_size == sizeof(double) &&
|
||||
mg_json_get_num(json, json_path, (double *) f->value);
|
||||
@@ -187,97 +233,99 @@ static int mg_dash_parse_field(struct mg_str json, struct mg_field *f) {
|
||||
}
|
||||
|
||||
static int mg_dash_apply(struct mg_connection *c, struct mg_dash *dash,
|
||||
struct mg_str json, int level) {
|
||||
struct mg_str json, struct mg_dash_user *u) {
|
||||
struct mg_str key, val;
|
||||
size_t ofs = 0;
|
||||
int total_count = 0;
|
||||
while ((ofs = mg_json_next(json, ofs, &key, &val)) > 0) {
|
||||
struct mg_field_set *set = mg_dash_find_field_set(dash, trimq(key));
|
||||
int count = 0;
|
||||
if (set != NULL && (set->write_level <= 0 || level >= set->write_level)) {
|
||||
if (set == NULL) {
|
||||
MG_ERROR(("UNKNOWN SET: [%.*s]", key.len, key.buf));
|
||||
continue;
|
||||
}
|
||||
if (set->fn != NULL && !set->fn(MG_DASH_WRITE, u)) continue; // auth check
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; set->fields[i].name != NULL; i++) {
|
||||
if (mg_dash_parse_field(val, &set->fields[i])) count++;
|
||||
}
|
||||
if (count) {
|
||||
if (set->writer) set->writer();
|
||||
mg_dash_send_change(c->mgr, set);
|
||||
total_count += count;
|
||||
}
|
||||
} else if (set == NULL) {
|
||||
MG_ERROR(("UNKNOWN SET: [%.*s]", key.len, key.buf));
|
||||
}
|
||||
if (count) {
|
||||
if (set->fn) set->fn(MG_DASH_WRITE, u); // apply side effects
|
||||
mg_dash_send_change(c->mgr, set);
|
||||
total_count += count;
|
||||
}
|
||||
}
|
||||
return total_count;
|
||||
}
|
||||
|
||||
static void mg_dash_process_msg(struct mg_connection *c,
|
||||
struct mg_ws_message *wm,
|
||||
struct mg_dash *dash) {
|
||||
struct mg_str req = wm->data;
|
||||
struct mg_str method = trimq(mg_json_get_tok(req, "$.method"));
|
||||
int level = *(int *) c->data;
|
||||
if (mg_match(method, mg_str("get"), NULL)) {
|
||||
mg_dash_success(c, req, "%M", mg_dash_print, &req, dash, level);
|
||||
} else if (mg_match(method, mg_str("set"), NULL)) {
|
||||
struct mg_str params = trimq(mg_json_get_tok(req, "$.params"));
|
||||
int count = mg_dash_apply(c, dash, params, level);
|
||||
mg_dash_success(c, req, "%d", count);
|
||||
} else {
|
||||
mg_dash_error(c, req, "%s", "unknown method");
|
||||
bool mg_dash_dir_read(struct mg_field_set *set, struct mg_dash_user *u) {
|
||||
char dir[256], fname[128] = "";
|
||||
struct mg_fs *fs = u->dash->upload_fs ? u->dash->upload_fs : &mg_fs_posix;
|
||||
struct mg_field *name_field = NULL, *size_field = NULL;
|
||||
size_t i;
|
||||
|
||||
if (!set->get_dir(u, dir, sizeof(dir))) return false;
|
||||
|
||||
for (i = 0; set->fields[i].name != NULL; i++) {
|
||||
if (name_field == NULL && set->fields[i].type == MG_VAL_STR &&
|
||||
strcmp(set->fields[i].name, "name") == 0)
|
||||
name_field = &set->fields[i];
|
||||
if (size_field == NULL && strcmp(set->fields[i].name, "size") == 0)
|
||||
size_field = &set->fields[i];
|
||||
}
|
||||
if (name_field == NULL) return false;
|
||||
|
||||
if (*set->index == -1) { // Size query: count all files
|
||||
int count = 0;
|
||||
while (mg_fs_ls(fs, dir, fname, sizeof(fname))) count++;
|
||||
*set->index = count;
|
||||
return true;
|
||||
}
|
||||
|
||||
{ // Regular read: scan to *set->index
|
||||
int target = *set->index, cur = 0;
|
||||
while (mg_fs_ls(fs, dir, fname, sizeof(fname))) {
|
||||
if (cur++ == target) {
|
||||
mg_snprintf((char *) name_field->value, name_field->value_size, "%s",
|
||||
fname);
|
||||
if (size_field != NULL) {
|
||||
char path[512];
|
||||
size_t sz = 0;
|
||||
mg_snprintf(path, sizeof(path), "%s/%s", dir, fname);
|
||||
fs->st(path, &sz, NULL);
|
||||
if (size_field->type == MG_VAL_UINT64)
|
||||
*(uint64_t *) size_field->value = (uint64_t) sz;
|
||||
else if (size_field->type == MG_VAL_INT)
|
||||
*(int *) size_field->value = (int) sz;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*set->index = -1; // No more entries
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void mg_dash_file_add(struct mg_str name, size_t size) {
|
||||
struct mg_dash_file *f = (struct mg_dash_file *) mg_calloc(1, sizeof(*f));
|
||||
f->name = mg_strdup(name).buf;
|
||||
f->size = size;
|
||||
f->next = mg_dash_files;
|
||||
mg_dash_files = f;
|
||||
}
|
||||
|
||||
void mg_dash_file_del(struct mg_str name) {
|
||||
struct mg_dash_file **head, *f;
|
||||
for (head = &mg_dash_files, f = *head; f; head = &(*head)->next, f = *head) {
|
||||
if (mg_strcmp(mg_str(f->name), name) == 0) {
|
||||
MG_INFO(("Deleting %s", f->name));
|
||||
*head = f->next;
|
||||
mg_free(f->name);
|
||||
mg_free(f);
|
||||
return;
|
||||
static bool mg_dash_set_file_name(struct mg_field_set *set,
|
||||
struct mg_str name) {
|
||||
size_t i;
|
||||
for (i = 0; set->fields[i].name != NULL; i++) {
|
||||
struct mg_field *f = &set->fields[i];
|
||||
if (f->type == MG_VAL_STR && strcmp(f->name, "name") == 0) {
|
||||
mg_snprintf((char *) f->value, f->value_size, "%.*s", (int) name.len,
|
||||
name.buf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Files array: [{"name": "foo.txt", "size": 1234}]
|
||||
struct mg_dash_file *mg_dash_files;
|
||||
static char s_files[1024];
|
||||
|
||||
static void read_files(void) {
|
||||
size_t len = 0;
|
||||
struct mg_dash_file *f;
|
||||
len += mg_snprintf(s_files + len, sizeof(s_files) - len, "[");
|
||||
for (f = mg_dash_files; f; f = f->next) {
|
||||
len += mg_snprintf(s_files + len, sizeof(s_files) - len, "%s{%m:%m,%m:%u}",
|
||||
len > 1 ? "," : "", MG_ESC("name"), MG_ESC(f->name),
|
||||
MG_ESC("size"), f->size);
|
||||
}
|
||||
len += mg_snprintf(s_files + len, sizeof(s_files) - len, "]");
|
||||
}
|
||||
|
||||
static struct mg_field fields_files[] = {
|
||||
{"data", MG_VAL_RAW, s_files, sizeof(s_files)},
|
||||
{NULL, MG_VAL_INT, NULL, 0},
|
||||
};
|
||||
|
||||
static struct mg_field_set set_files = {
|
||||
"files", fields_files, read_files, NULL, 0, 0, NULL,
|
||||
};
|
||||
|
||||
static inline void mg_log_http_req(struct mg_connection *c,
|
||||
struct mg_http_message *hm) {
|
||||
int len = 0;
|
||||
size_t n, spaces = 0;
|
||||
size_t n, spaces = 0, body_n = hm->body.len;
|
||||
struct mg_http_message tmp;
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
len = mg_http_parse((char *) c->send.buf, c->send.len, &tmp);
|
||||
@@ -286,18 +334,24 @@ static inline void mg_log_http_req(struct mg_connection *c,
|
||||
(c->send.buf[c->send.len - spaces - 1] == '\r' ||
|
||||
c->send.buf[c->send.len - spaces - 1] == '\n'))
|
||||
spaces++;
|
||||
MG_DEBUG(("%lu %.*s %.*s %.*s: %lu %.*s -> %lu %.*s", c->id, hm->method.len,
|
||||
hm->method.buf, hm->uri.len, hm->uri.buf, c->send.len > 15 ? 3 : 0,
|
||||
&c->send.buf[9], hm->body.len, hm->body.len, hm->body.buf,
|
||||
c->send.len - n, c->send.len - n - spaces, c->send.buf + n));
|
||||
}
|
||||
|
||||
static struct mg_str mg_dash_file_name(struct mg_http_message *hm) {
|
||||
struct mg_str name = mg_str_n(hm->uri.buf + 4, hm->uri.len - 4); // - /fs/
|
||||
// Decode file name in-place - directly into the request buffer
|
||||
int len = mg_url_decode(name.buf, name.len, name.buf, name.len + 1, 0);
|
||||
if (len > 0 && (size_t) len <= name.len) name.len = (size_t) len;
|
||||
return name;
|
||||
// hm->body.len comes from Content-Length and can be larger than the bytes
|
||||
// actually buffered so far (e.g. mid-stream uploads); cap the preview to
|
||||
// what's actually present in c->recv, or we'd read past its end
|
||||
{
|
||||
char *recv_end = (char *) c->recv.buf + c->recv.len;
|
||||
if (hm->body.buf >= (char *) c->recv.buf && hm->body.buf <= recv_end) {
|
||||
size_t avail = (size_t) (recv_end - hm->body.buf);
|
||||
if (body_n > avail) body_n = avail;
|
||||
} else {
|
||||
body_n = 0;
|
||||
}
|
||||
}
|
||||
MG_DEBUG(("%lu %.*s %.*s%s%.*s %.*s: %lu %.*s -> %lu %.*s", c->id,
|
||||
hm->method.len, hm->method.buf, hm->uri.len, hm->uri.buf,
|
||||
hm->query.len > 0 ? "?" : "", hm->query.len, hm->query.buf,
|
||||
c->send.len > 15 ? 3 : 0, &c->send.buf[9], hm->body.len, body_n,
|
||||
hm->body.buf, c->send.len - n, c->send.len - n - spaces,
|
||||
c->send.buf + n));
|
||||
}
|
||||
|
||||
static void mg_dash_ota_cb(struct mg_connection *c, const char *errmsg) {
|
||||
@@ -309,13 +363,20 @@ static void mg_dash_upload_cb(struct mg_connection *c, const char *errmsg) {
|
||||
if (errmsg) {
|
||||
mg_http_reply(c, 500, NULL, "%s\n", errmsg);
|
||||
} else {
|
||||
char path[128];
|
||||
size_t size = 0;
|
||||
mg_snprintf(path, sizeof(path), "/tmp/%s", c->data);
|
||||
mg_fs_posix.st(path, &size, NULL);
|
||||
mg_dash_file_add(mg_str(c->data), size);
|
||||
// mg_http_start_upload() repurposes c->data for its own bookkeeping,
|
||||
// so the field set can't be cached there. Re-derive the dashboard from
|
||||
// c->fn_data instead, and notify every file-backed array: the upload
|
||||
// could belong to any of them, and re-querying get_dir() per recipient
|
||||
// is what mg_dash_send_change() does anyway (directories can be
|
||||
// user-specific)
|
||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||
struct mg_field_set *fs;
|
||||
mg_http_reply(c, 200, NULL, "ok\n");
|
||||
mg_dash_send_change(c->mgr, &set_files);
|
||||
for (fs = dash->sets; fs != NULL; fs = fs->next) {
|
||||
if (fs->get_dir == NULL) continue;
|
||||
*fs->index = -1; // Signal mg_dash_send_change() to broadcast new size
|
||||
mg_dash_send_change(c->mgr, fs);
|
||||
}
|
||||
}
|
||||
c->is_draining = 1;
|
||||
}
|
||||
@@ -326,16 +387,69 @@ static uint64_t mg_dash_make_expiration_time(struct mg_dash *dash) {
|
||||
return mg_millis() + t * 1000;
|
||||
}
|
||||
|
||||
static struct mg_dash_user *mg_dash_add_user(struct mg_dash_user **users,
|
||||
struct mg_dash *dash,
|
||||
const char *name,
|
||||
const char *token, int level) {
|
||||
struct mg_dash_user *u = (struct mg_dash_user *) mg_calloc(1, sizeof(*u));
|
||||
if (u != NULL) {
|
||||
mg_snprintf(u->name, sizeof(u->name), "%s", name);
|
||||
if (token == NULL) {
|
||||
mg_random_str(u->token, sizeof(u->token) - 1);
|
||||
} else {
|
||||
mg_snprintf(u->token, sizeof(u->token), "%s", token);
|
||||
}
|
||||
u->level = level;
|
||||
u->expire = mg_dash_make_expiration_time(dash);
|
||||
u->dash = dash;
|
||||
u->next = *users;
|
||||
*users = u;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
static struct mg_dash_user *mg_dash_find_user(struct mg_dash_user *users,
|
||||
const char *name) {
|
||||
struct mg_dash_user *u;
|
||||
for (u = users; u != NULL; u = u->next) {
|
||||
if (strcmp(u->name, name) == 0) return u;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mg_dash_user *mg_dash_find_token(struct mg_dash_user *users,
|
||||
const char *token) {
|
||||
struct mg_dash_user *u;
|
||||
for (u = users; u != NULL; u = u->next) {
|
||||
if (strcmp(u->token, token) == 0) return u;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void mg_dash_refresh_user(struct mg_dash *dash,
|
||||
struct mg_dash_user *user) {
|
||||
user->expire = mg_dash_make_expiration_time(dash);
|
||||
}
|
||||
|
||||
// Parse HTTP requests, return authenticated user or NULL
|
||||
static struct mg_dash_user *mg_dash_authenticate(struct mg_http_message *hm,
|
||||
static struct mg_dash_user *mg_dash_authenticate(struct mg_connection *c,
|
||||
struct mg_http_message *hm,
|
||||
struct mg_dash *dash) {
|
||||
static struct mg_dash_user *s_users; // List of authenticated users
|
||||
char user[100], pass[100];
|
||||
static struct mg_dash_user admin = {NULL, "admin", "admin", 9, (uint64_t) -1};
|
||||
struct mg_dash_user *u, *tmp, *result = NULL;
|
||||
struct mg_dash_user *u, *tmp;
|
||||
struct mg_str *ah;
|
||||
int level = 0, num_users = 0;
|
||||
|
||||
if (dash->authenticate == NULL) return &admin;
|
||||
if (dash->authenticate == NULL) {
|
||||
mg_snprintf(s_guest.name, sizeof(s_guest.name), "%s", "guest");
|
||||
s_guest.level = 9;
|
||||
s_guest.dash = dash;
|
||||
dash->guest = &s_guest;
|
||||
return dash->guest;
|
||||
}
|
||||
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
|
||||
ah = mg_http_get_header(hm, "Authorization");
|
||||
// MG_DEBUG(("user [%s], pass: [%s], h: %.*s", user, pass, hm->head.len,
|
||||
// hm->head.buf));
|
||||
|
||||
@@ -343,47 +457,48 @@ static struct mg_dash_user *mg_dash_authenticate(struct mg_http_message *hm,
|
||||
for (u = s_users; u != NULL; u = tmp) {
|
||||
tmp = u->next;
|
||||
if (u->expire < mg_millis()) {
|
||||
struct mg_connection *conn;
|
||||
MG_DEBUG(("Deleting expired auth %s/%d %llu %u", u->name, u->level,
|
||||
u->expire, mg_millis() - u->expire));
|
||||
for (conn = c->mgr->conns; conn != NULL; conn = conn->next) {
|
||||
struct mg_dash_cdata *d = (struct mg_dash_cdata *) conn->data;
|
||||
if (conn->is_websocket && d->u == u) {
|
||||
d->u = NULL;
|
||||
conn->is_closing = 1;
|
||||
}
|
||||
}
|
||||
LIST_DELETE(struct mg_dash_user, &s_users, u);
|
||||
mg_free(u);
|
||||
}
|
||||
}
|
||||
|
||||
if (pass[0] != '\0') {
|
||||
struct mg_str *ah = mg_http_get_header(hm, "Authorization");
|
||||
if (ah != NULL) {
|
||||
// Auth header and password are set, auth by user/password via glue API
|
||||
int num_users = 0, level = dash->authenticate(user, sizeof(user), pass);
|
||||
MG_DEBUG(("user %s, level: %d", user, level));
|
||||
if (level > 0) { // Proceed only if the firmware authenticated us
|
||||
for (u = s_users; u != NULL && result == NULL;
|
||||
u = u->next, num_users++) {
|
||||
if (strcmp(user, u->name) == 0) {
|
||||
u->expire = mg_dash_make_expiration_time(dash);
|
||||
result = u;
|
||||
}
|
||||
}
|
||||
// Not yet authenticated, add to the list
|
||||
if (result == NULL && num_users < 10) {
|
||||
result = (struct mg_dash_user *) mg_calloc(1, sizeof(*result));
|
||||
mg_snprintf(result->name, sizeof(result->name), "%s", user);
|
||||
mg_random_str(result->token, sizeof(result->token) - 1);
|
||||
result->level = level, result->next = s_users, s_users = result;
|
||||
result->expire = mg_dash_make_expiration_time(dash);
|
||||
}
|
||||
}
|
||||
} else if (ah == NULL) {
|
||||
for (u = s_users; u != NULL && result == NULL; u = u->next) {
|
||||
if (strcmp(u->token, pass) == 0) {
|
||||
u->expire = mg_dash_make_expiration_time(dash);
|
||||
result = u;
|
||||
}
|
||||
}
|
||||
if (pass[0] == '\0') return NULL;
|
||||
|
||||
for (u = s_users; u != NULL; u = u->next) num_users++;
|
||||
if (ah == NULL) {
|
||||
u = mg_dash_find_token(s_users, pass);
|
||||
if (u != NULL) {
|
||||
mg_dash_refresh_user(dash, u);
|
||||
return u;
|
||||
}
|
||||
}
|
||||
|
||||
level = dash->authenticate(user, sizeof(user), pass);
|
||||
MG_DEBUG(("user %s, level: %d", user, level));
|
||||
if (level <= 0) return NULL;
|
||||
|
||||
u = mg_dash_find_user(s_users, user);
|
||||
if (u != NULL) {
|
||||
if (ah == NULL) mg_snprintf(u->token, sizeof(u->token), "%s", pass);
|
||||
mg_dash_refresh_user(dash, u);
|
||||
return u;
|
||||
}
|
||||
|
||||
if (num_users < 10)
|
||||
return mg_dash_add_user(&s_users, dash, user, ah == NULL ? pass : NULL,
|
||||
level);
|
||||
// MG_DEBUG(("[%s/%s] -> %s", user, pass, result ? "OK" : "FAIL"));
|
||||
return result;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void mg_handle_login(struct mg_connection *c, struct mg_dash_user *u) {
|
||||
@@ -408,48 +523,157 @@ static void mg_handle_logout(struct mg_connection *c) {
|
||||
mg_http_reply(c, 401, cookie, "Unauthorized\n");
|
||||
}
|
||||
|
||||
static void mg_dash_handle_del(struct mg_connection *c, struct mg_dash *dash,
|
||||
struct mg_dash_user *u, struct mg_str *parts) {
|
||||
struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
|
||||
if (set == NULL || set->index == NULL) {
|
||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
|
||||
} else {
|
||||
int from = 0, to = 0, count = 0;
|
||||
if (parts[1].len) mg_str_to_num(parts[1], 10, &from, sizeof(from));
|
||||
to = parts[2].len ? 0 : from;
|
||||
if (parts[2].len) mg_str_to_num(parts[2], 10, &to, sizeof(to));
|
||||
for (*set->index = from; *set->index <= to; (*set->index)++) {
|
||||
if (set->fn && set->fn(MG_DASH_DELETE, u))
|
||||
count++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (count) {
|
||||
*set->index = -1;
|
||||
mg_dash_send_change(c->mgr, set);
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
|
||||
} else {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "false\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle "POST /api/get/<set>/<index>": modify one array element. Loads the
|
||||
// element at <index> first - that doubles as a read-access check and as a
|
||||
// pre-fill, so that JSON keys absent from the body keep their old values -
|
||||
// then overlays the values from the body and asks fn to persist them
|
||||
static void mg_dash_handle_mod(struct mg_connection *c, struct mg_dash *dash,
|
||||
struct mg_dash_user *u, struct mg_str *parts,
|
||||
struct mg_str body) {
|
||||
struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
|
||||
int index = 0, count = 0;
|
||||
size_t i;
|
||||
if (set == NULL || set->index == NULL || set->fn == NULL) {
|
||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
|
||||
return;
|
||||
}
|
||||
mg_str_to_num(parts[1], 10, &index, sizeof(index));
|
||||
*set->index = index;
|
||||
if (!set->fn(MG_DASH_READ, u) || *set->index != index) {
|
||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; set->fields[i].name != NULL; i++) {
|
||||
if (mg_dash_parse_field(body, &set->fields[i])) count++;
|
||||
}
|
||||
if (count && set->fn(MG_DASH_WRITE, u)) {
|
||||
mg_dash_send_change(c->mgr, set);
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
|
||||
} else {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "false\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Handle "POST /api/add/<set>": append a new array element. Parses the body
|
||||
// straight into the bound fields, then asks fn to accept and persist them as
|
||||
// a new element - fn returns false to reject, e.g. when a cap is reached
|
||||
static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
|
||||
struct mg_dash_user *u, struct mg_str *parts,
|
||||
struct mg_str body) {
|
||||
struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
|
||||
int count = 0;
|
||||
size_t i;
|
||||
if (set == NULL || set->index == NULL || set->fn == NULL) {
|
||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "null\n");
|
||||
return;
|
||||
}
|
||||
for (i = 0; set->fields[i].name != NULL; i++) {
|
||||
if (mg_dash_parse_field(body, &set->fields[i])) count++;
|
||||
}
|
||||
if (count && set->fn(MG_DASH_ADD, u)) {
|
||||
*set->index = -1; // Signal mg_dash_send_change() to send new size
|
||||
mg_dash_send_change(c->mgr, set);
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "true\n");
|
||||
} else {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "false\n");
|
||||
}
|
||||
}
|
||||
|
||||
void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||
struct mg_dash_cdata *d = (struct mg_dash_cdata *) c->data;
|
||||
|
||||
if (ev == MG_EV_OPEN) {
|
||||
if (mg_dash_find_field_set(dash, mg_str("files")) == NULL) {
|
||||
MG_DASH_ADD_FIELD_SET(dash, &set_files);
|
||||
}
|
||||
d->dash = dash;
|
||||
// c->is_hexdumping = 1;
|
||||
} else if (ev == MG_EV_HTTP_HDRS && c->data[0] == 0) {
|
||||
} else if (ev == MG_EV_HTTP_HDRS && d->marker == 0) {
|
||||
// Received headers - check authentication and possibly start uploads/ota
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
struct mg_dash_user *u = mg_dash_authenticate(hm, dash);
|
||||
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
|
||||
struct mg_str parts[3];
|
||||
memset(parts, 0, sizeof(parts));
|
||||
|
||||
if (mg_match(hm->uri, mg_str("/api/hi"), NULL) ||
|
||||
mg_match(hm->uri, mg_str("/api/logout"), NULL)) {
|
||||
// Do nothing, handle them MG_EV_HTTP_MSG. We bypass auth for those
|
||||
} else if (u == NULL && mg_match(hm->uri, mg_str("/api/#"), NULL)) {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
||||
c->data[0] = CONN_HANDLED;
|
||||
d->marker = CONN_HANDLED;
|
||||
} else if (mg_match(hm->uri, mg_str("/api/login"), NULL) && u != NULL) {
|
||||
mg_handle_login(c, u);
|
||||
c->data[0] = CONN_HANDLED;
|
||||
d->marker = CONN_HANDLED;
|
||||
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
||||
mg_http_start_ota(c, hm, mg_dash_ota_cb);
|
||||
} else if (mg_match(hm->uri, mg_str("/fs/#"), NULL) &&
|
||||
} else if (mg_match(hm->uri, mg_str("/fs/*/*"), parts) &&
|
||||
(mg_strcasecmp(hm->method, mg_str("POST")) == 0 ||
|
||||
mg_strcasecmp(hm->method, mg_str("PUT")) == 0)) {
|
||||
struct mg_str name = mg_dash_file_name(hm);
|
||||
mg_snprintf(c->data, sizeof(c->data), "%.*s", (int) name.len, name.buf);
|
||||
mg_http_start_upload(c, hm, name, mg_str("/tmp"), &mg_fs_posix,
|
||||
mg_dash_upload_cb);
|
||||
struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
|
||||
struct mg_str name = parts[1];
|
||||
int len =
|
||||
mg_url_decode(name.buf, name.len, (char *) name.buf, name.len + 1, 0);
|
||||
if (len > 0 && (size_t) len <= name.len) name.len = (size_t) len;
|
||||
if (set == NULL || set->get_dir == NULL) {
|
||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found\n");
|
||||
d->marker = CONN_HANDLED;
|
||||
} else if (u == NULL) {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
||||
d->marker = CONN_HANDLED;
|
||||
} else if (!mg_path_is_sane(name)) {
|
||||
mg_http_reply(c, 400, MG_JSON_HEADERS, "Bad file name\n");
|
||||
d->marker = CONN_HANDLED;
|
||||
} else {
|
||||
mg_dash_set_file_name(set, name);
|
||||
if (set->fn != NULL && !set->fn(MG_DASH_WRITE, u)) {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
||||
d->marker = CONN_HANDLED;
|
||||
} else {
|
||||
char dir[256];
|
||||
struct mg_fs *fs = dash->upload_fs ? dash->upload_fs : &mg_fs_posix;
|
||||
if (!set->get_dir(u, dir, sizeof(dir))) {
|
||||
mg_http_reply(c, 500, MG_JSON_HEADERS, "Upload dir error\n");
|
||||
d->marker = CONN_HANDLED;
|
||||
} else {
|
||||
mg_http_start_upload(c, hm, name, mg_str(dir), fs,
|
||||
mg_dash_upload_cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c->data[0] != '\0') mg_log_http_req(c, hm);
|
||||
} else if (ev == MG_EV_HTTP_MSG && c->data[0] != '\0') {
|
||||
if (d->marker != '\0') mg_log_http_req(c, hm);
|
||||
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
||||
// The response has been send in EV_HDRS path, so we're not reponding
|
||||
// anything but clearing the c->data[0] flag for the next request.
|
||||
c->data[0] = 0;
|
||||
// anything but clearing the marker for the next request.
|
||||
d->marker = 0;
|
||||
c->is_resp = 0;
|
||||
} else if (ev == MG_EV_HTTP_MSG && c->data[0] == '\0') {
|
||||
} else if (ev == MG_EV_HTTP_MSG && d->marker == '\0') {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
struct mg_dash_user *u = mg_dash_authenticate(hm, dash);
|
||||
int level = u == NULL ? 0 : u->level;
|
||||
struct mg_dash_user *u = mg_dash_authenticate(c, hm, dash);
|
||||
struct mg_str parts[5];
|
||||
memset(parts, 0, sizeof(parts));
|
||||
|
||||
@@ -460,27 +684,60 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
mg_ws_printf(c, WEBSOCKET_OP_TEXT, "{%m:%m}", MG_ESC("method"),
|
||||
MG_ESC("logout"));
|
||||
} else if (mg_match(hm->uri, mg_str("/api/websocket"), NULL)) {
|
||||
*(int *) c->data = level;
|
||||
d->u = u;
|
||||
mg_ws_upgrade(c, hm, NULL);
|
||||
} else if (mg_match(hm->uri, mg_str("/fs/#"), NULL)) {
|
||||
struct mg_str name = mg_dash_file_name(hm);
|
||||
char path[128];
|
||||
mg_snprintf(path, sizeof(path), "/tmp/%.*s", name.len, name.buf);
|
||||
if (mg_strcasecmp(hm->method, mg_str("DELETE")) == 0) {
|
||||
// Delete file
|
||||
mg_dash_file_del(name);
|
||||
mg_dash_send_change(c->mgr, &set_files);
|
||||
mg_http_reply(c, 200, NULL, "true");
|
||||
} else if (mg_match(hm->uri, mg_str("/fs/*/*"), parts)) {
|
||||
struct mg_field_set *set = mg_dash_find_field_set(dash, parts[0]);
|
||||
if (set == NULL || set->get_dir == NULL) {
|
||||
mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
|
||||
} else if (u == NULL) {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
||||
} else {
|
||||
// Serve file
|
||||
mg_http_serve_file(c, hm, path, NULL);
|
||||
char dir[256], path[512];
|
||||
struct mg_fs *fs = dash->upload_fs ? dash->upload_fs : &mg_fs_posix;
|
||||
struct mg_str name = parts[1];
|
||||
int len = mg_url_decode(name.buf, name.len, (char *) name.buf,
|
||||
name.len + 1, 0);
|
||||
if (len > 0 && (size_t) len <= name.len) name.len = (size_t) len;
|
||||
if (!mg_path_is_sane(name)) {
|
||||
mg_http_reply(c, 400, MG_JSON_HEADERS, "Bad file name\n");
|
||||
return;
|
||||
}
|
||||
if (!set->get_dir(u, dir, sizeof(dir))) {
|
||||
mg_http_reply(c, 500, MG_JSON_HEADERS, "Upload dir error\n");
|
||||
return;
|
||||
}
|
||||
mg_snprintf(path, sizeof(path), "%s/%.*s", dir, name.len, name.buf);
|
||||
if (mg_strcasecmp(hm->method, mg_str("DELETE")) == 0) {
|
||||
mg_dash_set_file_name(set, name);
|
||||
if (set->fn != NULL && !set->fn(MG_DASH_DELETE, u)) {
|
||||
mg_http_reply(c, 403, MG_JSON_HEADERS, "Not Authorised\n");
|
||||
} else {
|
||||
fs->rm(path);
|
||||
*set->index = -1; // Signal mg_dash_send_change() to send new size
|
||||
mg_dash_send_change(c->mgr, set);
|
||||
mg_http_reply(c, 200, NULL, "true");
|
||||
}
|
||||
} else {
|
||||
mg_http_serve_file(c, hm, path, NULL);
|
||||
}
|
||||
}
|
||||
} else if (mg_match(hm->uri, mg_str("/api/get/#"), parts) ||
|
||||
} else if (mg_match(hm->uri, mg_str("/api/del/*/*/*"), parts) ||
|
||||
mg_match(hm->uri, mg_str("/api/del/*/*"), parts)) {
|
||||
mg_dash_handle_del(c, dash, u, parts);
|
||||
} else if (mg_match(hm->uri, mg_str("/api/add/*"), parts)) {
|
||||
mg_dash_handle_add(c, dash, u, parts, hm->body);
|
||||
} else if (mg_match(hm->uri, mg_str("/api/get/*/*"), parts) &&
|
||||
mg_strcasecmp(hm->method, mg_str("POST")) == 0) {
|
||||
mg_dash_handle_mod(c, dash, u, parts, hm->body);
|
||||
} else if (mg_match(hm->uri, mg_str("/api/get/*/*/*"), parts) ||
|
||||
mg_match(hm->uri, mg_str("/api/get/*/*"), parts) ||
|
||||
mg_match(hm->uri, mg_str("/api/get/*"), parts) ||
|
||||
mg_match(hm->uri, mg_str("/api/get"), NULL)) {
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "%M\n", mg_dash_print_name, dash,
|
||||
&parts[0], level);
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "%M\n", mg_dash_print_endpoint,
|
||||
dash, u, &parts[0], &parts[1], &parts[2]);
|
||||
} else if (mg_match(hm->uri, mg_str("/api/set"), NULL)) {
|
||||
int count = mg_dash_apply(c, dash, hm->body, level);
|
||||
int count = mg_dash_apply(c, dash, hm->body, u);
|
||||
mg_http_reply(c, 200, MG_JSON_HEADERS, "%d\n", count);
|
||||
} else if (mg_match(hm->uri, mg_str("/"), NULL)) {
|
||||
struct mg_http_serve_opts opts;
|
||||
@@ -498,20 +755,5 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ch == NULL) mg_http_reply(c, 404, MG_JSON_HEADERS, "Not Found");
|
||||
mg_log_http_req(c, hm);
|
||||
}
|
||||
} else if (ev == MG_EV_WS_OPEN) {
|
||||
// WS connection established, send change notifications for all data
|
||||
struct mg_field_set *fs;
|
||||
for (fs = dash->sets; fs != NULL; fs = fs->next) {
|
||||
mg_dash_send_change(c->mgr, fs);
|
||||
}
|
||||
mg_dash_broadcast(c->mgr, 0, "{%m:%m}", MG_ESC("method"), MG_ESC("ready"));
|
||||
} else if (ev == MG_EV_WS_MSG) {
|
||||
// Add this to automatically handle "get" and "set" JSON-RPC calls
|
||||
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
|
||||
if (dash == NULL) {
|
||||
mg_dash_error(c, wm->data, "%s", "no dash defined");
|
||||
} else {
|
||||
mg_dash_process_msg(c, wm, dash);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user