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