You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

243 lines
5.9 KiB

/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <string.h>
#include <nvs.h>
#include <esp_ota_ops.h>
typedef enum {
GROUP_STATE_NONE,
GROUP_STATE_INITIAL,
GROUP_STATE_MEMBERS,
GROUP_STATE_FINAL
} group_state;
struct per_session_data__lws_group {
struct per_session_data__lws_group *next;
group_state group_state;
struct lws_group_member *member;
unsigned char subsequent:1;
unsigned char changed_partway:1;
};
struct per_vhost_data__lws_group {
struct per_session_data__lws_group *live_pss_list;
struct lws_context *context;
struct lws_vhost *vhost;
const struct lws_protocols *protocol;
int count_live_pss;
};
static void render_ip4(char *dest, int len, uint8_t *ip)
{
snprintf(dest, len, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
}
static int
callback_lws_group(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
struct per_session_data__lws_group *pss =
(struct per_session_data__lws_group *)user;
struct per_vhost_data__lws_group *vhd =
(struct per_vhost_data__lws_group *)
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
lws_get_protocol(wsi));
char buffer[1024 + LWS_PRE], ipv4[20];
char *start = buffer + LWS_PRE - 1, *p = start,
*end = buffer + sizeof(buffer) - 1;
struct lws_group_member *mbr;
int n, m;
switch (reason) {
case LWS_CALLBACK_PROTOCOL_INIT:
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
lws_get_protocol(wsi),
sizeof(struct per_vhost_data__lws_group));
vhd->context = lws_get_context(wsi);
vhd->protocol = lws_get_protocol(wsi);
vhd->vhost = lws_get_vhost(wsi);
break;
case LWS_CALLBACK_PROTOCOL_DESTROY:
if (!vhd)
break;
break;
case LWS_CALLBACK_ESTABLISHED:
lwsl_notice("%s: ESTABLISHED\n", __func__);
vhd->count_live_pss++;
pss->next = vhd->live_pss_list;
vhd->live_pss_list = pss;
pss->group_state = GROUP_STATE_INITIAL;
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
switch (pss->group_state) {
case GROUP_STATE_NONE:
/* fallthru */
case GROUP_STATE_INITIAL:
p += snprintf((char *)p, end - p,
"{\n"
" \"group\":\"%s\","
" \"members\":[\n",
lws_esp32.group);
n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
pss->group_state = GROUP_STATE_MEMBERS;
pss->subsequent = 0;
pss->changed_partway = 0;
pss->member = lws_esp32.first;
break;
case GROUP_STATE_MEMBERS:
/* confirm pss->member is still in the list... */
mbr = lws_esp32.first;
while (mbr && mbr != pss->member)
mbr = mbr->next;
if (!mbr) { /* no longer exists... */
if (lws_esp32.first || pss->member)
pss->changed_partway = 1;
*p++ = ' ';
pss->member = NULL;
/*
* finish the list where we got to, then
* immediately reissue it
*/
}
while (end - p > 100 && pss->member) {
if (pss->subsequent)
*p++ = ',';
pss->subsequent = 1;
render_ip4(ipv4, sizeof(ipv4), (uint8_t *)&pss->member->addr);
p += snprintf((char *)p, end - p,
" {\n"
" \"mac\":\"%s\",\n"
" \"model\":\"%s\",\n"
" \"role\":\"%s\",\n"
" \"width\":\"%d\",\n"
" \"height\":\"%d\",\n"
" \"ipv4\":\"%s\"\n"
" }\n",
pss->member->mac,
pss->member->model,
pss->member->role,
pss->member->width,
pss->member->height,
ipv4
);
pss->member = pss->member->next;
}
lwsl_notice("%s\n", p);
n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
if (!pss->member)
pss->group_state = GROUP_STATE_FINAL;
break;
case GROUP_STATE_FINAL:
n = LWS_WRITE_CONTINUATION;
p += sprintf((char *)p, "],\n \"discard\":\"%d\"}\n",
pss->changed_partway);
if (pss->changed_partway)
pss->group_state = GROUP_STATE_INITIAL;
else
pss->group_state = GROUP_STATE_NONE;
break;
default:
return 0;
}
// lwsl_notice("issue: %d (%d)\n", p - start, n);
m = lws_write(wsi, (unsigned char *)start, p - start, n);
if (m < 0) {
lwsl_err("ERROR %d writing to di socket\n", m);
return -1;
}
if (pss->group_state != GROUP_STATE_NONE)
lws_callback_on_writable(wsi);
break;
case LWS_CALLBACK_RECEIVE:
{
break;
}
case LWS_CALLBACK_CLOSED:
{
struct per_session_data__lws_group **p = &vhd->live_pss_list;
while (*p) {
if ((*p) == pss) {
*p = pss->next;
continue;
}
p = &((*p)->next);
}
vhd->count_live_pss--;
}
break;
case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
/* called when our wsi user_space is going to be destroyed */
break;
default:
break;
}
return 0;
}
#define LWS_PLUGIN_PROTOCOL_LWS_GROUP \
{ \
"lws-group", \
callback_lws_group, \
sizeof(struct per_session_data__lws_group), \
1024, 0, NULL, 900 \
}