|
0
|
1 /*
|
|
|
2 * wcc -- a shitty sockchat client
|
|
|
3 *
|
|
|
4 * Copyright (c) 2025 Paper
|
|
|
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 deal
|
|
|
8 * in the Software without restriction, including without limitation the rights
|
|
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
10 * 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 all
|
|
|
14 * 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 FROM,
|
|
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
22 * SOFTWARE.
|
|
|
23 */
|
|
|
24
|
|
|
25 #include "flashii.h"
|
|
|
26
|
|
|
27 #include <libwebsockets.h>
|
|
|
28 #include <assert.h>
|
|
|
29
|
|
|
30 #define HEARTBEAT_SECONDS (30)
|
|
|
31
|
|
|
32 /* internal linked list of messages to send */
|
|
|
33 struct flashii_internal_msgbuf {
|
|
|
34 struct flashii_internal_msgbuf *next;
|
|
|
35
|
|
|
36 char msg[]; /* meat */
|
|
|
37 };
|
|
|
38
|
|
|
39 struct flashii {
|
|
|
40 struct lws_context *ctx;
|
|
|
41 struct lws *lws;
|
|
|
42
|
|
|
43 struct flashii_internal_msgbuf *msgbuf;
|
|
|
44
|
|
|
45 flashii_msg_recv_spec msg_recv;
|
|
|
46 flashii_user_recv_spec user_recv;
|
|
|
47
|
|
|
48 /* user ID, retrieved after auth success */
|
|
|
49 char *id;
|
|
|
50 /* username */
|
|
|
51 char *username;
|
|
|
52 /* color */
|
|
|
53 /* char *color; -- don't care */
|
|
|
54
|
|
|
55 /* permissions
|
|
|
56 * odd, they can be separated by \f or ' ',
|
|
|
57 * so if we want to save them we'll have to write mempbrk */
|
|
|
58
|
|
|
59 /* maybe save channel name too? */
|
|
|
60
|
|
|
61 /* maximum message length in unicode characters */
|
|
|
62 int32_t max_msg_length;
|
|
|
63
|
|
|
64 time_t last_heartbeat;
|
|
|
65 int need_heartbeat;
|
|
|
66 };
|
|
|
67
|
|
|
68 static char *memduptostr(const char *in, size_t len)
|
|
|
69 {
|
|
|
70 char *q = malloc(len + 1);
|
|
|
71 if (!q)
|
|
|
72 return NULL;
|
|
|
73
|
|
|
74 memcpy(q, in, len);
|
|
|
75 q[len] = 0;
|
|
|
76
|
|
|
77 return q;
|
|
|
78 }
|
|
|
79
|
|
|
80 /* converts a string-like thing to i64 */
|
|
|
81 static int memtoi64(const char *mem, size_t len, int64_t *pr)
|
|
|
82 {
|
|
|
83 size_t i;
|
|
|
84 int64_t r;
|
|
|
85 int neg;
|
|
|
86
|
|
|
87 if (len <= 0)
|
|
|
88 return -1;
|
|
|
89
|
|
|
90 if (!(mem[0] == '-' || (mem[0] >= '0' && mem[0] <= '9')))
|
|
|
91 return -1;
|
|
|
92
|
|
|
93 r = 0;
|
|
|
94
|
|
|
95 neg = (mem[0] == '-');
|
|
|
96
|
|
|
97 for (i = (neg ? 1 : 0); i < len; i++) {
|
|
|
98 /* FIXME this does absolutely no checking for overflow etc */
|
|
|
99 if (!(mem[i] >= '0' && mem[i] <= '9'))
|
|
|
100 return -1;
|
|
|
101
|
|
|
102 r *= 10;
|
|
|
103 r += (mem[i] - '0');
|
|
|
104 }
|
|
|
105
|
|
|
106 if (mem[0] == '-')
|
|
|
107 r = -r;
|
|
|
108
|
|
|
109 *pr = r;
|
|
|
110
|
|
|
111 return 0;
|
|
|
112 }
|
|
|
113
|
|
|
114 /* converts a string-like thing to i32 */
|
|
|
115 static int memtoi32(const char *mem, size_t len, int32_t *pr)
|
|
|
116 {
|
|
|
117 int64_t r;
|
|
|
118 if (memtoi64(mem, len, &r) < 0)
|
|
|
119 return -1;
|
|
|
120
|
|
|
121 if (r > INT32_MAX || r < INT32_MIN)
|
|
|
122 return -1;
|
|
|
123
|
|
|
124 *pr = r;
|
|
|
125 return 0;
|
|
|
126 }
|
|
|
127
|
|
|
128 /* converts a binary string to u32
|
|
|
129 *
|
|
|
130 * e.x.:
|
|
|
131 * '10010' -> 0x09 */
|
|
|
132 static int memtou32flags(const char *mem, size_t len, uint32_t *pflags)
|
|
|
133 {
|
|
|
134 size_t i;
|
|
|
135 uint32_t u;
|
|
|
136
|
|
|
137 if (len >= 32)
|
|
|
138 return -1; /* totally funtastic! */
|
|
|
139
|
|
|
140 u = 0;
|
|
|
141
|
|
|
142 for (i = 0; i < len; i++) {
|
|
|
143 switch (mem[i]) {
|
|
|
144 case '0':
|
|
|
145 break;
|
|
|
146 case '1':
|
|
|
147 u |= (UINT32_C(1) << i);
|
|
|
148 break;
|
|
|
149 default:
|
|
|
150 /* invalid input */
|
|
|
151 return -1;
|
|
|
152 }
|
|
|
153 }
|
|
|
154
|
|
|
155 *pflags = u;
|
|
|
156
|
|
|
157 return 0;
|
|
|
158 }
|
|
|
159
|
|
|
160 /* ------------------------------------------------------------------------ */
|
|
|
161 /* packet receiving and parsing.
|
|
|
162 *
|
|
|
163 * packets are generally received as
|
|
|
164 * [type] (optional subtype) (data)
|
|
|
165 * hence I've put together a simple filter thing to be able to handle
|
|
|
166 * types and subtypes without duplicating a shit ton of code */
|
|
|
167
|
|
|
168 struct flashii_receive_packet_filter {
|
|
|
169 const char *prefix;
|
|
|
170 size_t len;
|
|
|
171 int (*func)(struct flashii *fls, const char *in, size_t len);
|
|
|
172 };
|
|
|
173
|
|
|
174 #define FLASHII_RECEIVE_PACKET_FILTER(prefix, func) \
|
|
|
175 { (prefix "\t"), sizeof(prefix), (func) }
|
|
|
176
|
|
|
177 static int flashii_receive_packet_filter(struct flashii *fls, const char *in,
|
|
|
178 size_t len, struct flashii_receive_packet_filter *filters)
|
|
|
179 {
|
|
|
180 struct flashii_receive_packet_filter *f;
|
|
|
181
|
|
|
182 for (f = filters; f->prefix; f++) {
|
|
|
183 if (f->len > len || memcmp(in, f->prefix, f->len))
|
|
|
184 continue; /* nope */
|
|
|
185
|
|
|
186 return f->func(fls, in + f->len, len - f->len);
|
|
|
187 }
|
|
|
188
|
|
|
189 return -1;
|
|
|
190 }
|
|
|
191
|
|
|
192 /* ------------------------------------------------------------------------ */
|
|
|
193 /* enumerate over parts of a packet
|
|
|
194 * note that whatever returned to the callback is not guaranteed to be
|
|
|
195 * NUL terminated; you should treat it simply as a memory buffer */
|
|
|
196
|
|
|
197 /* XXX needs to take in delim */
|
|
|
198 static int flashii_receive_packet_enum(struct flashii *fls, const char *in,
|
|
|
199 size_t len,
|
|
|
200 int (*cb)(struct flashii *fls,
|
|
|
201 const char *in, size_t len, size_t index, void *userdata),
|
|
|
202 void *userdata)
|
|
|
203 {
|
|
|
204 size_t index;
|
|
|
205
|
|
|
206 for (index = 0; /* none */; index++) {
|
|
|
207 size_t ilen;
|
|
|
208
|
|
|
209 {
|
|
|
210 const char *x;
|
|
|
211 x = memchr(in, '\t', len);
|
|
|
212 ilen = x ? (x - in) : len;
|
|
|
213 }
|
|
|
214
|
|
|
215 //printf("%d: %.*s\n", (int)index, (int)ilen, in);
|
|
|
216
|
|
|
217 if (cb(fls, in, ilen, index, userdata) < 0)
|
|
|
218 return -1;
|
|
|
219
|
|
|
220 /* hopefully this is right */
|
|
|
221 if (ilen + 1 >= len)
|
|
|
222 break;
|
|
|
223
|
|
|
224 in = in + ilen + 1;
|
|
|
225 len -= ilen + 1;
|
|
|
226 }
|
|
|
227
|
|
|
228 return index;
|
|
|
229 }
|
|
|
230
|
|
|
231 /* ------------------------------------------------------------------------ */
|
|
|
232 /* 0: pong */
|
|
|
233
|
|
|
234 static int flashii_receive_packet_pong(struct flashii *fls, const char *in,
|
|
|
235 size_t len)
|
|
|
236 {
|
|
|
237 /* nothing to do */
|
|
|
238 return 0;
|
|
|
239 }
|
|
|
240
|
|
|
241 /* ------------------------------------------------------------------------ */
|
|
|
242 /* 1: auth */
|
|
|
243
|
|
|
244 static int flashii_receive_auth_cb(struct flashii *fls, const char *in,
|
|
|
245 size_t len, size_t index, void *userdata)
|
|
|
246 {
|
|
|
247 switch (index) {
|
|
|
248 case 1:
|
|
|
249 case 2:
|
|
|
250 case 3:
|
|
|
251 case 4:
|
|
|
252 return 0; /* nothing */
|
|
|
253 case 0:
|
|
|
254 fls->id = memduptostr(in, len);
|
|
|
255 /* user ID */
|
|
|
256 return 0;
|
|
|
257 case 5:
|
|
|
258 if (memtoi32(in, len, &fls->max_msg_length) < 0)
|
|
|
259 return -1;
|
|
|
260
|
|
|
261 return 0;
|
|
|
262 }
|
|
|
263
|
|
|
264 return -1;
|
|
|
265
|
|
|
266 (void)userdata;
|
|
|
267 }
|
|
|
268
|
|
|
269 static int flashii_receive_packet_auth_success(struct flashii *fls,
|
|
|
270 const char *in, size_t len)
|
|
|
271 {
|
|
|
272 return (flashii_receive_packet_enum(fls, in, len, flashii_receive_auth_cb, NULL) != 5) ? -1 : 0;
|
|
|
273 }
|
|
|
274
|
|
|
275 static int flashii_receive_packet_auth(struct flashii *fls, const char *in,
|
|
|
276 size_t len)
|
|
|
277 {
|
|
|
278 struct flashii_receive_packet_filter f[] = {
|
|
|
279 FLASHII_RECEIVE_PACKET_FILTER("y", flashii_receive_packet_auth_success),
|
|
|
280 /* FLASHII_RECEIVE_PACKET_FILTER("n", flashii_receive_packet_auth_failure), */
|
|
|
281 /* FLASHII_RECEIVE_PACKET_FILTER("#", flashii_receive_packet_user_add), */
|
|
|
282 /* terminate */
|
|
|
283 { 0 }
|
|
|
284 };
|
|
|
285
|
|
|
286 return flashii_receive_packet_filter(fls, in, len, f);
|
|
|
287 }
|
|
|
288
|
|
|
289 /* ------------------------------------------------------------------------ */
|
|
|
290 /* 2: message add */
|
|
|
291
|
|
|
292 static int flashii_receive_packet_msg_cb(struct flashii *fls, const char *in,
|
|
|
293 size_t len, size_t index, void *userdata)
|
|
|
294 {
|
|
|
295 struct flashii_msg *msg = userdata;
|
|
|
296
|
|
|
297 switch (index) {
|
|
|
298 case 0:
|
|
|
299 /* UNIX timestamp */
|
|
|
300 if (memtoi64(in, len, &msg->timestamp) < 0)
|
|
|
301 return -1;
|
|
|
302 return 0;
|
|
|
303 case 1:
|
|
|
304 msg->id = memduptostr(in, len);
|
|
|
305 return 0;
|
|
|
306 case 2:
|
|
|
307 msg->body = memduptostr(in, len);
|
|
|
308 return 0;
|
|
|
309 case 3:
|
|
|
310 msg->msg_id = memduptostr(in, len);
|
|
|
311 return 0;
|
|
|
312 case 4:
|
|
|
313 if (memtou32flags(in, len, &msg->flags) < 0)
|
|
|
314 return -1;
|
|
|
315 return 0;
|
|
|
316 }
|
|
|
317
|
|
|
318 return -1;
|
|
|
319 }
|
|
|
320
|
|
|
321 static int flashii_receive_packet_msg(struct flashii *fls, const char *in,
|
|
|
322 size_t len)
|
|
|
323 {
|
|
|
324 struct flashii_msg msg;
|
|
|
325
|
|
|
326 if (flashii_receive_packet_enum(fls, in, len, flashii_receive_packet_msg_cb, &msg) != 4)
|
|
|
327 return -1;
|
|
|
328
|
|
|
329 fls->msg_recv(fls, &msg);
|
|
|
330 return 0;
|
|
|
331 }
|
|
|
332
|
|
|
333 /* ------------------------------------------------------------------------ */
|
|
|
334 /* 7: context */
|
|
|
335
|
|
|
336 static int flashii_receive_packet_context_user_list_cb(struct flashii *fls,
|
|
|
337 const char *in, size_t len, size_t index, void *userdata)
|
|
|
338 {
|
|
|
339 struct flashii_user *user = userdata;
|
|
|
340
|
|
|
341 if (!index)
|
|
|
342 return 0; /* ignored */
|
|
|
343
|
|
|
344 switch ((index - 1) % 5) {
|
|
|
345 case 0:
|
|
|
346 /* user ID */
|
|
|
347 user->id = memduptostr(in, len);
|
|
|
348 return 0;
|
|
|
349 case 1:
|
|
|
350 /* user name */
|
|
|
351 user->name = memduptostr(in, len);
|
|
|
352 return 0;
|
|
|
353 case 2:
|
|
|
354 /* color */
|
|
|
355 return 0;
|
|
|
356 case 3:
|
|
|
357 /* perms */
|
|
|
358 return 0;
|
|
|
359 case 4:
|
|
|
360 /* visiblility */
|
|
|
361 if (memtoi32(in, len, &user->visible) < 0)
|
|
|
362 return -1;
|
|
|
363 fls->user_recv(fls, user);
|
|
|
364 return 0;
|
|
|
365 }
|
|
|
366
|
|
|
367 return -1;
|
|
|
368 }
|
|
|
369
|
|
|
370 static int flashii_receive_packet_context_user_list(struct flashii *fls,
|
|
|
371 const char *in, size_t len)
|
|
|
372 {
|
|
|
373 struct flashii_user user;
|
|
|
374
|
|
|
375 if (flashii_receive_packet_enum(fls, in, len, flashii_receive_packet_context_user_list_cb, &user) < 0)
|
|
|
376 return -1;
|
|
|
377
|
|
|
378 return 0;
|
|
|
379 }
|
|
|
380
|
|
|
381 static int flashii_receive_packet_context_msg_add_cb(struct flashii *fls,
|
|
|
382 const char *in, size_t len, size_t index, void *userdata)
|
|
|
383 {
|
|
|
384 struct flashii_msg *msg = userdata;
|
|
|
385
|
|
|
386 switch (index) {
|
|
|
387 case 0:
|
|
|
388 /* UNIX timestamp */
|
|
|
389 if (memtoi64(in, len, &msg->timestamp) < 0)
|
|
|
390 return -1;
|
|
|
391 return 0;
|
|
|
392 case 1:
|
|
|
393 msg->id = memduptostr(in, len);
|
|
|
394 return 0;
|
|
|
395 case 2:
|
|
|
396 /* "user name" wtf */
|
|
|
397 return 0;
|
|
|
398 case 3:
|
|
|
399 /* color */
|
|
|
400 return 0;
|
|
|
401 case 4:
|
|
|
402 /* user permissions */
|
|
|
403 return 0;
|
|
|
404 case 5:
|
|
|
405 msg->body = memduptostr(in, len);
|
|
|
406 return 0;
|
|
|
407 case 6:
|
|
|
408 msg->msg_id = memduptostr(in, len);
|
|
|
409 return 0;
|
|
|
410 case 7:
|
|
|
411 /* notifications */
|
|
|
412 return 0;
|
|
|
413 case 8:
|
|
|
414 #if 0
|
|
|
415 if (memtou32flags(in, len, &msg->flags) < 0)
|
|
|
416 return -1;
|
|
|
417 #endif
|
|
|
418 return 0;
|
|
|
419 }
|
|
|
420
|
|
|
421 return -1;
|
|
|
422 }
|
|
|
423
|
|
|
424 static int flashii_receive_packet_context_message_add(struct flashii *fls,
|
|
|
425 const char *in, size_t len)
|
|
|
426 {
|
|
|
427 struct flashii_msg msg;
|
|
|
428
|
|
|
429 if (flashii_receive_packet_enum(fls, in, len, flashii_receive_packet_context_msg_add_cb, &msg) < 0)
|
|
|
430 return -1;
|
|
|
431
|
|
|
432 fls->msg_recv(fls, &msg);
|
|
|
433 return 0;
|
|
|
434 }
|
|
|
435
|
|
|
436 static int flashii_receive_packet_context(struct flashii *fls, const char *in,
|
|
|
437 size_t len)
|
|
|
438 {
|
|
|
439 struct flashii_receive_packet_filter f[] = {
|
|
|
440 FLASHII_RECEIVE_PACKET_FILTER("0", flashii_receive_packet_context_user_list),
|
|
|
441 FLASHII_RECEIVE_PACKET_FILTER("1", flashii_receive_packet_context_message_add),
|
|
|
442 /* FLASHII_RECEIVE_PACKET_FILTER("2", ...), */
|
|
|
443 /* terminate */
|
|
|
444 { 0 }
|
|
|
445 };
|
|
|
446
|
|
|
447 return flashii_receive_packet_filter(fls, in, len, f);
|
|
|
448 }
|
|
|
449
|
|
|
450 /* ------------------------------------------------------------------------ */
|
|
|
451
|
|
|
452 static int flashii_receive_packet(struct flashii *fls, const char *in,
|
|
|
453 size_t len)
|
|
|
454 {
|
|
|
455 struct flashii_receive_packet_filter f[] = {
|
|
|
456 FLASHII_RECEIVE_PACKET_FILTER("0", flashii_receive_packet_pong),
|
|
|
457 FLASHII_RECEIVE_PACKET_FILTER("1", flashii_receive_packet_auth),
|
|
|
458 FLASHII_RECEIVE_PACKET_FILTER("2", flashii_receive_packet_msg),
|
|
|
459 FLASHII_RECEIVE_PACKET_FILTER("7", flashii_receive_packet_context),
|
|
|
460 /* terminate */
|
|
|
461 { 0 }
|
|
|
462 };
|
|
|
463
|
|
|
464 return flashii_receive_packet_filter(fls, in, len, f);
|
|
|
465 }
|
|
|
466
|
|
|
467 /* ------------------------------------------------------------------------ */
|
|
|
468
|
|
|
469 static int flashii_send_vpacket(struct flashii *fls, const char *fmt, va_list ap)
|
|
|
470 {
|
|
|
471 /* this uses vsnprintf to assemble the packet and sends it off */
|
|
|
472 int r;
|
|
|
473 char *s;
|
|
|
474 va_list ap2;
|
|
|
475
|
|
|
476 va_copy(ap2, ap);
|
|
|
477
|
|
|
478 r = vsnprintf(NULL, 0, fmt, ap);
|
|
|
479
|
|
|
480 if (r < 0) {
|
|
|
481 va_end(ap2);
|
|
|
482 return -1;
|
|
|
483 }
|
|
|
484
|
|
|
485 s = malloc(LWS_PRE + r + 1);
|
|
|
486 if (!s) {
|
|
|
487 va_end(ap2);
|
|
|
488 return -1;
|
|
|
489 }
|
|
|
490
|
|
|
491 r = vsnprintf(s + LWS_PRE, r + 1, fmt, ap2);
|
|
|
492
|
|
|
493 va_end(ap2);
|
|
|
494
|
|
|
495 if (r < 0)
|
|
|
496 return -1;
|
|
|
497
|
|
|
498 lws_write(fls->lws, s + LWS_PRE, r, LWS_WRITE_TEXT);
|
|
|
499 free(s);
|
|
|
500
|
|
|
501 return 0;
|
|
|
502 }
|
|
|
503
|
|
|
504 static int flashii_send_packet(struct flashii *fls, const char *fmt, ...)
|
|
|
505 {
|
|
|
506 va_list ap;
|
|
|
507 int r;
|
|
|
508
|
|
|
509 va_start(ap, fmt);
|
|
|
510 r = flashii_send_vpacket(fls, fmt, ap);
|
|
|
511 va_end(ap);
|
|
|
512
|
|
|
513 return r;
|
|
|
514 }
|
|
|
515
|
|
|
516 static int flashii_send_ping(struct flashii *fls, const char *id)
|
|
|
517 {
|
|
|
518 assert(id);
|
|
|
519 return flashii_send_packet(fls, "0\t%s", id);
|
|
|
520 }
|
|
|
521
|
|
|
522 static int flashii_send_auth(struct flashii *fls, const char *scheme, const char *args)
|
|
|
523 {
|
|
|
524 assert(scheme);
|
|
|
525 assert(args);
|
|
|
526 return flashii_send_packet(fls, "1\t%s\t%s", scheme, args);
|
|
|
527 }
|
|
|
528
|
|
|
529 static int flashii_send_msg(struct flashii *fls, const char *id, const char *msg)
|
|
|
530 {
|
|
|
531 assert(id);
|
|
|
532 assert(msg);
|
|
|
533 return flashii_send_packet(fls, "2\t%s\t%s", id, msg);
|
|
|
534 }
|
|
|
535
|
|
|
536 static int flashii_process_msgbuf(struct flashii *fls)
|
|
|
537 {
|
|
|
538 while (fls->msgbuf) {
|
|
|
539 struct flashii_internal_msgbuf *tmp;
|
|
|
540
|
|
|
541 tmp = fls->msgbuf;
|
|
|
542 flashii_send_msg(fls, fls->id, tmp->msg);
|
|
|
543 fls->msgbuf = tmp->next;
|
|
|
544 free(tmp);
|
|
|
545 }
|
|
|
546 }
|
|
|
547
|
|
|
548 /* libwebsockets calls us back here */
|
|
|
549 static int flashii_cb(struct lws *wsi, enum lws_callback_reasons reason,
|
|
|
550 void *userdata, void *in, size_t len)
|
|
|
551 {
|
|
|
552 struct flashii *fls = (struct flashii *)userdata;
|
|
|
553 static const unsigned char token[] = {
|
|
|
554 #embed "token.txt"
|
|
|
555 , 0
|
|
|
556 };
|
|
|
557
|
|
|
558 switch (reason) {
|
|
|
559 case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
|
|
560 lws_callback_on_writable(wsi);
|
|
|
561 break;
|
|
|
562 case LWS_CALLBACK_CLIENT_RECEIVE:
|
|
|
563 /* incoming message -- print it out
|
|
|
564 * we should handle it based on the type */
|
|
|
565 flashii_receive_packet(fls, in, len);
|
|
|
566 break;
|
|
|
567 case LWS_CALLBACK_CLIENT_WRITEABLE:
|
|
|
568 if (fls->need_heartbeat) {
|
|
|
569 flashii_send_ping(fls, fls->id);
|
|
|
570 fls->need_heartbeat = 0;
|
|
|
571 }
|
|
|
572
|
|
|
573 /* process the buffer of messages, if any */
|
|
|
574 flashii_process_msgbuf(fls);
|
|
|
575
|
|
|
576 flashii_send_auth(fls, "Misuzu", token);
|
|
|
577 /* send message here using lws_write */
|
|
|
578 break;
|
|
|
579 case LWS_CALLBACK_CLIENT_CLOSED:
|
|
|
580 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
|
|
|
581 break;
|
|
|
582 #if 0
|
|
|
583 default:
|
|
|
584 printf("UNKNOWN REASON: %d\n", reason);
|
|
|
585 break;
|
|
|
586 #endif
|
|
|
587 }
|
|
|
588
|
|
|
589 return lws_callback_http_dummy(wsi, reason, userdata, in, len);
|
|
|
590 }
|
|
|
591
|
|
|
592 static const struct lws_protocols protocols[] = {
|
|
|
593 {
|
|
|
594 "sockchat",
|
|
|
595 flashii_cb,
|
|
|
596 sizeof(struct flashii),
|
|
|
597 0,
|
|
|
598 },
|
|
|
599 { 0 } /* terminator */
|
|
|
600 };
|
|
|
601
|
|
|
602 /* ------------------------------------------------------------------------ */
|
|
|
603
|
|
|
604 struct flashii *flashii_init(const char *protocol, const char *address,
|
|
|
605 uint16_t port, flashii_msg_recv_spec msg_recv,
|
|
|
606 flashii_user_recv_spec user_recv)
|
|
|
607 {
|
|
|
608 struct lws_context_creation_info info;
|
|
|
609 struct lws_client_connect_info ccinfo;
|
|
|
610 struct flashii *fls;
|
|
|
611
|
|
|
612 fls = calloc(1, sizeof(*fls));
|
|
|
613 if (!fls)
|
|
|
614 return NULL;
|
|
|
615
|
|
|
616 fls->msg_recv = msg_recv;
|
|
|
617 fls->user_recv = user_recv;
|
|
|
618
|
|
|
619 lws_context_info_defaults(&info, NULL);
|
|
|
620 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
|
|
621 info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
|
|
|
622 info.protocols = protocols;
|
|
|
623
|
|
|
624 fls->ctx = lws_create_context(&info);
|
|
|
625 if (!fls->ctx) {
|
|
|
626 lwsl_err("lws init failed\n");
|
|
|
627 free(fls);
|
|
|
628 return NULL;
|
|
|
629 }
|
|
|
630
|
|
|
631 /* konekuto */
|
|
|
632 memset(&ccinfo, 0, sizeof(ccinfo));
|
|
|
633
|
|
|
634 ccinfo.context = fls->ctx;
|
|
|
635 ccinfo.address = address;
|
|
|
636 ccinfo.port = port;
|
|
|
637 ccinfo.path = "/";
|
|
|
638 ccinfo.host = address;
|
|
|
639 ccinfo.origin = address;
|
|
|
640 ccinfo.protocol = "sockchat";
|
|
|
641 ccinfo.ssl_connection = LCCSCF_USE_SSL;
|
|
|
642 ccinfo.userdata = fls;
|
|
|
643
|
|
|
644 fls->lws = lws_client_connect_via_info(&ccinfo);
|
|
|
645 if (!fls->lws) {
|
|
|
646 lwsl_cx_err(fls->ctx, "lws connect failed\n");
|
|
|
647 lws_context_destroy(fls->ctx);
|
|
|
648 free(fls);
|
|
|
649 return NULL;
|
|
|
650 }
|
|
|
651
|
|
|
652 return fls;
|
|
|
653 }
|
|
|
654
|
|
|
655 /* worker
|
|
|
656 *
|
|
|
657 * this should be called in an event loop, on the same thread that
|
|
|
658 * initialized the structure.
|
|
|
659 *
|
|
|
660 * `noblock` is a boolean saying... whether we should block :) */
|
|
|
661 void flashii_work(struct flashii *fls, int noblock)
|
|
|
662 {
|
|
|
663 time_t thetime;
|
|
|
664
|
|
|
665 time(&thetime);
|
|
|
666
|
|
|
667 if (fls->id) {
|
|
|
668 if (difftime(thetime, fls->last_heartbeat) >= 30.0) {
|
|
|
669 fls->need_heartbeat = 1;
|
|
|
670 fls->last_heartbeat = thetime;
|
|
|
671 lws_callback_on_writable(fls->lws);
|
|
|
672 }
|
|
|
673 }
|
|
|
674
|
|
|
675 lws_service(fls->ctx, (noblock) ? -1 : 0);
|
|
|
676 }
|
|
|
677
|
|
|
678 int flashii_send_message(struct flashii *fls, const char *msg)
|
|
|
679 {
|
|
|
680 /* append to the internal linked list of messages */
|
|
|
681 size_t len = strlen(msg);
|
|
|
682
|
|
|
683 struct flashii_internal_msgbuf *msgbuf = malloc(sizeof(*msgbuf) + len + 1);
|
|
|
684 if (!msgbuf)
|
|
|
685 return -1; /* WHAT */
|
|
|
686
|
|
|
687 msgbuf->next = NULL;
|
|
|
688
|
|
|
689 /* copy it allll in (includes NUL terminator) */
|
|
|
690 memcpy(msgbuf->msg, msg, len + 1);
|
|
|
691
|
|
|
692 if (fls->msgbuf) {
|
|
|
693 struct flashii_internal_msgbuf *tail = fls->msgbuf;
|
|
|
694 while (tail->next)
|
|
|
695 tail = tail->next;
|
|
|
696 tail->next = msgbuf;
|
|
|
697 } else {
|
|
|
698 fls->msgbuf = msgbuf;
|
|
|
699 }
|
|
|
700
|
|
|
701 lws_callback_on_writable(fls->lws);
|
|
|
702
|
|
|
703 return 0; /* yay */
|
|
|
704 }
|