1 /*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25 #include <string.h>
26 #include <nvs.h>
27 #include <esp_ota_ops.h>
28
29 typedef enum {
30 GROUP_STATE_NONE,
31 GROUP_STATE_INITIAL,
32 GROUP_STATE_MEMBERS,
33 GROUP_STATE_FINAL
34 } group_state;
35
36 struct per_session_data__lws_group {
37 struct per_session_data__lws_group *next;
38 group_state group_state;
39
40 struct lws_group_member *member;
41
42 unsigned char subsequent:1;
43 unsigned char changed_partway:1;
44 };
45
46 struct per_vhost_data__lws_group {
47 struct per_session_data__lws_group *live_pss_list;
48 struct lws_context *context;
49 struct lws_vhost *vhost;
50 const struct lws_protocols *protocol;
51 int count_live_pss;
52 };
53
render_ip4(char * dest,int len,uint8_t * ip)54 static void render_ip4(char *dest, int len, uint8_t *ip)
55 {
56 snprintf(dest, len, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
57 }
58
59
60
61 static int
callback_lws_group(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)62 callback_lws_group(struct lws *wsi, enum lws_callback_reasons reason,
63 void *user, void *in, size_t len)
64 {
65 struct per_session_data__lws_group *pss =
66 (struct per_session_data__lws_group *)user;
67 struct per_vhost_data__lws_group *vhd =
68 (struct per_vhost_data__lws_group *)
69 lws_protocol_vh_priv_get(lws_get_vhost(wsi),
70 lws_get_protocol(wsi));
71 char buffer[1024 + LWS_PRE], ipv4[20];
72 char *start = buffer + LWS_PRE - 1, *p = start,
73 *end = buffer + sizeof(buffer) - 1;
74 struct lws_group_member *mbr;
75 int n, m;
76
77 switch (reason) {
78
79 case LWS_CALLBACK_PROTOCOL_INIT:
80 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
81 lws_get_protocol(wsi),
82 sizeof(struct per_vhost_data__lws_group));
83 vhd->context = lws_get_context(wsi);
84 vhd->protocol = lws_get_protocol(wsi);
85 vhd->vhost = lws_get_vhost(wsi);
86 break;
87
88 case LWS_CALLBACK_PROTOCOL_DESTROY:
89 if (!vhd)
90 break;
91 break;
92
93 case LWS_CALLBACK_ESTABLISHED:
94 lwsl_notice("%s: ESTABLISHED\n", __func__);
95 vhd->count_live_pss++;
96 pss->next = vhd->live_pss_list;
97 vhd->live_pss_list = pss;
98 pss->group_state = GROUP_STATE_INITIAL;
99 lws_callback_on_writable(wsi);
100 break;
101
102 case LWS_CALLBACK_SERVER_WRITEABLE:
103
104 switch (pss->group_state) {
105
106 case GROUP_STATE_NONE:
107 /* fallthru */
108
109 case GROUP_STATE_INITIAL:
110
111 p += snprintf((char *)p, end - p,
112 "{\n"
113 " \"group\":\"%s\","
114 " \"members\":[\n",
115 lws_esp32.group);
116
117 n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
118 pss->group_state = GROUP_STATE_MEMBERS;
119 pss->subsequent = 0;
120 pss->changed_partway = 0;
121 pss->member = lws_esp32.first;
122 break;
123
124 case GROUP_STATE_MEMBERS:
125
126 /* confirm pss->member is still in the list... */
127
128 mbr = lws_esp32.first;
129 while (mbr && mbr != pss->member)
130 mbr = mbr->next;
131
132 if (!mbr) { /* no longer exists... */
133 if (lws_esp32.first || pss->member)
134 pss->changed_partway = 1;
135 *p++ = ' ';
136 pss->member = NULL;
137
138 /*
139 * finish the list where we got to, then
140 * immediately reissue it
141 */
142 }
143
144 while (end - p > 100 && pss->member) {
145
146 if (pss->subsequent)
147 *p++ = ',';
148
149 pss->subsequent = 1;
150 render_ip4(ipv4, sizeof(ipv4), (uint8_t *)&pss->member->addr);
151
152 p += snprintf((char *)p, end - p,
153 " {\n"
154 " \"mac\":\"%s\",\n"
155 " \"model\":\"%s\",\n"
156 " \"role\":\"%s\",\n"
157 " \"width\":\"%d\",\n"
158 " \"height\":\"%d\",\n"
159 " \"ipv4\":\"%s\"\n"
160 " }\n",
161 pss->member->mac,
162 pss->member->model,
163 pss->member->role,
164 pss->member->width,
165 pss->member->height,
166 ipv4
167 );
168 pss->member = pss->member->next;
169 }
170
171 lwsl_notice("%s\n", p);
172
173 n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
174 if (!pss->member)
175 pss->group_state = GROUP_STATE_FINAL;
176 break;
177
178 case GROUP_STATE_FINAL:
179 n = LWS_WRITE_CONTINUATION;
180 p += sprintf((char *)p, "],\n \"discard\":\"%d\"}\n",
181 pss->changed_partway);
182 if (pss->changed_partway)
183 pss->group_state = GROUP_STATE_INITIAL;
184 else
185 pss->group_state = GROUP_STATE_NONE;
186 break;
187 default:
188 return 0;
189 }
190 // lwsl_notice("issue: %d (%d)\n", p - start, n);
191 m = lws_write(wsi, (unsigned char *)start, p - start, n);
192 if (m < 0) {
193 lwsl_err("ERROR %d writing to di socket\n", m);
194 return -1;
195 }
196
197 if (pss->group_state != GROUP_STATE_NONE)
198 lws_callback_on_writable(wsi);
199
200 break;
201
202 case LWS_CALLBACK_RECEIVE:
203 {
204 break;
205 }
206
207 case LWS_CALLBACK_CLOSED:
208 {
209 struct per_session_data__lws_group **p = &vhd->live_pss_list;
210
211 while (*p) {
212 if ((*p) == pss) {
213 *p = pss->next;
214 continue;
215 }
216
217 p = &((*p)->next);
218 }
219
220 vhd->count_live_pss--;
221 }
222 break;
223
224 case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
225 /* called when our wsi user_space is going to be destroyed */
226 break;
227
228 default:
229 break;
230 }
231
232 return 0;
233 }
234
235 #define LWS_PLUGIN_PROTOCOL_LWS_GROUP \
236 { \
237 "lws-group", \
238 callback_lws_group, \
239 sizeof(struct per_session_data__lws_group), \
240 1024, 0, NULL, 900 \
241 }
242
243