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 #if !defined (LWS_PLUGIN_STATIC)
26 #define LWS_DLL
27 #define LWS_INTERNAL
28 #include <libwebsockets.h>
29 #endif
30 
31 #include <stdlib.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <dirent.h>
37 #ifdef WIN32
38 #include <io.h>
39 #endif
40 #include <stdio.h>
41 #include <errno.h>
42 
43 struct dir_entry {
44 	lws_list_ptr next; /* sorted by mtime */
45 	char user[32];
46 	unsigned long long size;
47 	time_t mtime;
48 };
49 /* filename follows */
50 
51 #define lp_to_dir_entry(p, _n) lws_list_ptr_container(p, struct dir_entry, _n)
52 
53 struct pss_deaddrop;
54 
55 struct vhd_deaddrop {
56 	struct lws_context *context;
57 	struct lws_vhost *vh;
58 	const struct lws_protocols *protocol;
59 
60 	struct pss_deaddrop *pss_head;
61 
62 	const char *upload_dir;
63 
64 	struct lwsac *lwsac_head;
65 	struct dir_entry *dire_head;
66 	int filelist_version;
67 
68 	unsigned long long max_size;
69 };
70 
71 struct pss_deaddrop {
72 	struct lws_spa *spa;
73 	struct vhd_deaddrop *vhd;
74 	struct lws *wsi;
75 	char result[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE];
76 	char filename[256];
77 	char user[32];
78 	unsigned long long file_length;
79 	lws_filefd_type fd;
80 	int response_code;
81 
82 	struct pss_deaddrop *pss_list;
83 
84 	struct lwsac *lwsac_head;
85 	struct dir_entry *dire;
86 	int filelist_version;
87 
88 	uint8_t completed:1;
89 	uint8_t sent_headers:1;
90 	uint8_t sent_body:1;
91 	uint8_t first:1;
92 };
93 
94 static const char * const param_names[] = {
95 	"text",
96 	"send",
97 	"file",
98 	"upload",
99 };
100 
101 enum enum_param_names {
102 	EPN_TEXT,
103 	EPN_SEND,
104 	EPN_FILE,
105 	EPN_UPLOAD,
106 };
107 
108 static int
de_mtime_sort(lws_list_ptr a,lws_list_ptr b)109 de_mtime_sort(lws_list_ptr a, lws_list_ptr b)
110 {
111 	struct dir_entry *p1 = lp_to_dir_entry(a, next),
112 			 *p2 = lp_to_dir_entry(b, next);
113 
114 	return (int)(p2->mtime - p1->mtime);
115 }
116 
117 static void
start_sending_dir(struct pss_deaddrop * pss)118 start_sending_dir(struct pss_deaddrop *pss)
119 {
120 	if (pss->vhd->lwsac_head)
121 		lwsac_reference(pss->vhd->lwsac_head);
122 	pss->lwsac_head = pss->vhd->lwsac_head;
123 	pss->dire = pss->vhd->dire_head;
124 	pss->filelist_version = pss->vhd->filelist_version;
125 	pss->first = 1;
126 }
127 
128 static int
scan_upload_dir(struct vhd_deaddrop * vhd)129 scan_upload_dir(struct vhd_deaddrop *vhd)
130 {
131 	char filepath[256], subdir[3][128], *p;
132 	int m, sp = 0, initial, found = 0;
133 	struct lwsac *lwsac_head = NULL;
134 	lws_list_ptr sorted_head = NULL;
135 	struct dir_entry *dire;
136 	struct dirent *de;
137 	struct stat s;
138 	DIR *dir[3];
139 
140 	initial = strlen(vhd->upload_dir) + 1;
141 	lws_strncpy(subdir[sp], vhd->upload_dir, sizeof(subdir[sp]));
142 	dir[sp] = opendir(vhd->upload_dir);
143 	if (!dir[sp]) {
144 		lwsl_err("%s: Unable to walk upload dir '%s'\n", __func__,
145 			 vhd->upload_dir);
146 		return -1;
147 	}
148 
149 	do {
150 		de = readdir(dir[sp]);
151 		if (!de) {
152 			closedir(dir[sp]);
153 #if !defined(__COVERITY__)
154 			if (!sp)
155 #endif
156 				break;
157 #if !defined(__COVERITY__)
158 			sp--;
159 			continue;
160 #endif
161 		}
162 
163 		p = filepath;
164 
165 		for (m = 0; m <= sp; m++)
166 			p += lws_snprintf(p, (filepath + sizeof(filepath)) - p,
167 					  "%s/", subdir[m]);
168 
169 		lws_snprintf(p, (filepath + sizeof(filepath)) - p, "%s",
170 				  de->d_name);
171 
172 		/* ignore temp files */
173 		if (de->d_name[strlen(de->d_name) - 1] == '~')
174 			continue;
175 #if defined(__COVERITY__)
176 		s.st_size = 0;
177 		s.st_mtime = 0;
178 #else
179 		/* coverity[toctou] */
180 		if (stat(filepath, &s))
181 			continue;
182 
183 		if (S_ISDIR(s.st_mode)) {
184 			if (!strcmp(de->d_name, ".") ||
185 			    !strcmp(de->d_name, ".."))
186 				continue;
187 			sp++;
188 			if (sp == LWS_ARRAY_SIZE(dir)) {
189 				lwsl_err("%s: Skipping too-deep subdir %s\n",
190 					 __func__, filepath);
191 				sp--;
192 				continue;
193 			}
194 			lws_strncpy(subdir[sp], de->d_name, sizeof(subdir[sp]));
195 			dir[sp] = opendir(filepath);
196 			if (!dir[sp]) {
197 				lwsl_err("%s: Unable to open subdir '%s'\n",
198 					 __func__, filepath);
199 				goto bail;
200 			}
201 			continue;
202 		}
203 #endif
204 
205 		m = strlen(filepath + initial) + 1;
206 		dire = lwsac_use(&lwsac_head, sizeof(*dire) + m, 0);
207 		if (!dire) {
208 			lwsac_free(&lwsac_head);
209 
210 			goto bail;
211 		}
212 
213 		dire->next = NULL;
214 		dire->size = s.st_size;
215 		dire->mtime = s.st_mtime;
216 		dire->user[0] = '\0';
217 #if !defined(__COVERITY__)
218 		if (sp)
219 			lws_strncpy(dire->user, subdir[1], sizeof(dire->user));
220 #endif
221 
222 		found++;
223 
224 		memcpy(&dire[1], filepath + initial, m);
225 
226 		lws_list_ptr_insert(&sorted_head, &dire->next, de_mtime_sort);
227 	} while (1);
228 
229 	/* the old lwsac continues to live while someone else is consuming it */
230 	if (vhd->lwsac_head)
231 		lwsac_detach(&vhd->lwsac_head);
232 
233 	/* we replace it with the fresh one */
234 	vhd->lwsac_head = lwsac_head;
235 	if (sorted_head)
236 		vhd->dire_head = lp_to_dir_entry(sorted_head, next);
237 	else
238 		vhd->dire_head = NULL;
239 
240 	vhd->filelist_version++;
241 
242 	lwsl_info("%s: found %d\n", __func__, found);
243 
244 	lws_start_foreach_llp(struct pss_deaddrop **, ppss, vhd->pss_head) {
245 		start_sending_dir(*ppss);
246 		lws_callback_on_writable((*ppss)->wsi);
247 	} lws_end_foreach_llp(ppss, pss_list);
248 
249 	return 0;
250 
251 bail:
252 	while (sp >= 0)
253 		closedir(dir[sp--]);
254 
255 	return -1;
256 }
257 
258 static int
file_upload_cb(void * data,const char * name,const char * filename,char * buf,int len,enum lws_spa_fileupload_states state)259 file_upload_cb(void *data, const char *name, const char *filename,
260 	       char *buf, int len, enum lws_spa_fileupload_states state)
261 {
262 	struct pss_deaddrop *pss = (struct pss_deaddrop *)data;
263 	char filename2[256];
264 	int n;
265 
266 	(void)n;
267 
268 	switch (state) {
269 	case LWS_UFS_OPEN:
270 		lws_urldecode(filename2, filename, sizeof(filename2) - 1);
271 		lws_filename_purify_inplace(filename2);
272 		if (pss->user[0]) {
273 			lws_filename_purify_inplace(pss->user);
274 			lws_snprintf(pss->filename, sizeof(pss->filename),
275 				     "%s/%s", pss->vhd->upload_dir, pss->user);
276 			if (mkdir(pss->filename
277 #if !defined(WIN32)
278 				, 0700
279 #endif
280 				) < 0)
281 				lwsl_debug("%s: mkdir failed\n", __func__);
282 			lws_snprintf(pss->filename, sizeof(pss->filename),
283 				     "%s/%s/%s~", pss->vhd->upload_dir,
284 				     pss->user, filename2);
285 		} else
286 			lws_snprintf(pss->filename, sizeof(pss->filename),
287 				     "%s/%s~", pss->vhd->upload_dir, filename2);
288 		lwsl_notice("%s: filename '%s'\n", __func__, pss->filename);
289 
290 		pss->fd = (lws_filefd_type)(long long)lws_open(pss->filename,
291 			      O_CREAT | O_TRUNC | O_RDWR, 0600);
292 		if (pss->fd == LWS_INVALID_FILE) {
293 			pss->response_code = HTTP_STATUS_INTERNAL_SERVER_ERROR;
294 			lwsl_err("%s: unable to open %s (errno %d)\n", __func__,
295 					pss->filename, errno);
296 			return -1;
297 		}
298 		break;
299 
300 	case LWS_UFS_FINAL_CONTENT:
301 	case LWS_UFS_CONTENT:
302 		if (len) {
303 			pss->file_length += len;
304 
305 			/* if the file length is too big, drop it */
306 			if (pss->file_length > pss->vhd->max_size) {
307 				pss->response_code =
308 					HTTP_STATUS_REQ_ENTITY_TOO_LARGE;
309 				close((int)(long long)pss->fd);
310 				pss->fd = LWS_INVALID_FILE;
311 				unlink(pss->filename);
312 
313 				return -1;
314 			}
315 
316 			if (pss->fd != LWS_INVALID_FILE) {
317 				n = write((int)(long long)pss->fd, buf, len);
318 				lwsl_debug("%s: write %d says %d\n", __func__,
319 					   len, n);
320 				lws_set_timeout(pss->wsi, PENDING_TIMEOUT_HTTP_CONTENT, 30);
321 			}
322 		}
323 		if (state == LWS_UFS_CONTENT)
324 			break;
325 
326 		if (pss->fd != LWS_INVALID_FILE)
327 			close((int)(long long)pss->fd);
328 
329 		/* the temp filename without the ~ */
330 		lws_strncpy(filename2, pss->filename, sizeof(filename2));
331 		filename2[strlen(filename2) - 1] = '\0';
332 		if (rename(pss->filename, filename2) < 0)
333 			lwsl_err("%s: unable to rename\n", __func__);
334 
335 		pss->fd = LWS_INVALID_FILE;
336 		pss->response_code = HTTP_STATUS_OK;
337 		scan_upload_dir(pss->vhd);
338 
339 		break;
340 	case LWS_UFS_CLOSE:
341 		break;
342 	}
343 
344 	return 0;
345 }
346 
347 /*
348  * returns length in bytes
349  */
350 
351 static int
format_result(struct pss_deaddrop * pss)352 format_result(struct pss_deaddrop *pss)
353 {
354 	unsigned char *p, *start, *end;
355 
356 	p = (unsigned char *)pss->result + LWS_PRE;
357 	start = p;
358 	end = p + sizeof(pss->result) - LWS_PRE - 1;
359 
360 	p += lws_snprintf((char *)p, end -p,
361 			"<!DOCTYPE html><html lang=\"en\"><head>"
362 			"<meta charset=utf-8 http-equiv=\"Content-Language\" "
363 			"content=\"en\"/>"
364 			"</head>");
365 	p += lws_snprintf((char *)p, end - p, "</body></html>");
366 
367 	return (int)lws_ptr_diff(p, start);
368 }
369 
370 static int
callback_deaddrop(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)371 callback_deaddrop(struct lws *wsi, enum lws_callback_reasons reason,
372 		  void *user, void *in, size_t len)
373 {
374 	struct vhd_deaddrop *vhd = (struct vhd_deaddrop *)
375 				lws_protocol_vh_priv_get(lws_get_vhost(wsi),
376 							 lws_get_protocol(wsi));
377 	struct pss_deaddrop *pss = (struct pss_deaddrop *)user;
378 	uint8_t buf[LWS_PRE + LWS_RECOMMENDED_MIN_HEADER_SPACE],
379 		*start = &buf[LWS_PRE], *p = start,
380 		*end = &buf[sizeof(buf) - LWS_PRE - 1];
381 	char fname[256], *wp;
382 	const char *cp;
383 	int n, m, was;
384 
385 	switch (reason) {
386 
387 	case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
388 		lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
389 					    lws_get_protocol(wsi),
390 					    sizeof(struct vhd_deaddrop));
391 
392 		vhd = (struct vhd_deaddrop *)
393 			lws_protocol_vh_priv_get(lws_get_vhost(wsi),
394 						 lws_get_protocol(wsi));
395 
396 		vhd->context = lws_get_context(wsi);
397 		vhd->vh = lws_get_vhost(wsi);
398 		vhd->protocol = lws_get_protocol(wsi);
399 		vhd->max_size = 20 * 1024 * 1024; /* default without pvo */
400 
401 		if (!lws_pvo_get_str(in, "max-size", &cp))
402 			vhd->max_size = atoll(cp);
403 		if (lws_pvo_get_str(in, "upload-dir", &vhd->upload_dir)) {
404 			lwsl_err("%s: requires 'upload-dir' pvo\n", __func__);
405 			return -1;
406 		}
407 
408 		scan_upload_dir(vhd);
409 
410 		lwsl_notice("  deaddrop: vh %s, upload dir %s, max size %llu\n",
411 			    lws_get_vhost_name(vhd->vh), vhd->upload_dir,
412 			    vhd->max_size);
413 		break;
414 
415 	case LWS_CALLBACK_PROTOCOL_DESTROY:
416 		lwsac_free(&vhd->lwsac_head);
417 		break;
418 
419 	/* WS-related */
420 
421 	case LWS_CALLBACK_ESTABLISHED:
422 		pss->vhd = vhd;
423 		pss->wsi = wsi;
424 		/* add ourselves to the list of live pss held in the vhd */
425 		pss->pss_list = vhd->pss_head;
426 		vhd->pss_head = pss;
427 
428 		m = lws_hdr_copy(wsi, pss->user, sizeof(pss->user),
429 				 WSI_TOKEN_HTTP_AUTHORIZATION);
430 		if (m > 0)
431 			lwsl_info("%s: basic auth user: %s\n",
432 				  __func__, pss->user);
433 		else
434 			pss->user[0] = '\0';
435 
436 		start_sending_dir(pss);
437 		lws_callback_on_writable(wsi);
438 		return 0;
439 
440 	case LWS_CALLBACK_CLOSED:
441 		if (pss->lwsac_head)
442 			lwsac_unreference(&pss->lwsac_head);
443 		/* remove our closing pss from the list of live pss */
444 		lws_start_foreach_llp(struct pss_deaddrop **,
445 				      ppss, vhd->pss_head) {
446 			if (*ppss == pss) {
447 				*ppss = pss->pss_list;
448 				break;
449 			}
450 		} lws_end_foreach_llp(ppss, pss_list);
451 		return 0;
452 
453 	case LWS_CALLBACK_RECEIVE:
454 		/* we get this kind of thing {"del":"agreen/no-entry.svg"} */
455 		if (!pss || len < 10)
456 			break;
457 
458 		if (strncmp((const char *)in, "{\"del\":\"", 8))
459 			break;
460 
461 		cp = strchr((const char *)in, '/');
462 		if (cp) {
463 			n = ((void *)cp - in) - 8;
464 
465 			if ((int)strlen(pss->user) != n ||
466 			    memcmp(pss->user, ((const char *)in) + 8, n)) {
467 				lwsl_notice("%s: del: auth mismatch "
468 					    " '%s' '%s' (%d)\n",
469 					    __func__, pss->user,
470 					    ((const char *)in) + 8, n);
471 				break;
472 			}
473 		}
474 
475 		lws_strncpy(fname, ((const char *)in) + 8, sizeof(fname));
476 		lws_filename_purify_inplace(fname);
477 		wp = strchr((const char *)fname, '\"');
478 		if (wp)
479 			*wp = '\0';
480 
481 		lws_snprintf((char *)buf, sizeof(buf), "%s/%s", vhd->upload_dir,
482 			     fname);
483 
484 		lwsl_notice("%s: del: path %s\n", __func__, (const char *)buf);
485 
486 		if (unlink((const char *)buf) < 0)
487 			lwsl_err("%s: unlink %s failed\n", __func__,
488 					(const char *)buf);
489 
490 		scan_upload_dir(vhd);
491 		break;
492 
493 	case LWS_CALLBACK_SERVER_WRITEABLE:
494 		if (pss->lwsac_head && !pss->dire)
495 			return 0;
496 
497 		was = 0;
498 		if (pss->first) {
499 			p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
500 					  "{\"max_size\":%llu, \"files\": [",
501 					  vhd->max_size);
502 			was = 1;
503 		}
504 
505 		m = 5;
506 		while (m-- && pss->dire) {
507 			p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
508 					  "%c{\"name\":\"%s\", "
509 					  "\"size\":%llu,"
510 					  "\"mtime\":%llu,"
511 					  "\"yours\":%d}",
512 					  pss->first ? ' ' : ',',
513 					  (const char *)&pss->dire[1],
514 					  pss->dire->size,
515 					  (unsigned long long)pss->dire->mtime,
516 					  !strcmp(pss->user, pss->dire->user) &&
517 						  pss->user[0]);
518 			pss->first = 0;
519 			pss->dire = lp_to_dir_entry(pss->dire->next, next);
520 		}
521 
522 		if (!pss->dire) {
523 			p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
524 					  "]}");
525 			if (pss->lwsac_head) {
526 				lwsac_unreference(&pss->lwsac_head);
527 				pss->lwsac_head = NULL;
528 			}
529 		}
530 
531 		n = lws_write(wsi, start, lws_ptr_diff(p, start),
532 			      lws_write_ws_flags(LWS_WRITE_TEXT, was,
533 						 !pss->dire));
534 		if (n < 0) {
535 			lwsl_notice("%s: ws write failed\n", __func__);
536 			return 1;
537 		}
538 		if (pss->dire) {
539 			lws_callback_on_writable(wsi);
540 
541 			return 0;
542 		}
543 
544 		/* ie, we finished */
545 
546 		if (pss->filelist_version != pss->vhd->filelist_version) {
547 			lwsl_info("%s: restart send\n", __func__);
548 			/* what we just sent is already out of date */
549 			start_sending_dir(pss);
550 			lws_callback_on_writable(wsi);
551 		}
552 
553 		return 0;
554 
555 	/* POST-related */
556 
557 	case LWS_CALLBACK_HTTP_BODY:
558 
559 		/* create the POST argument parser if not already existing */
560 		if (!pss->spa) {
561 			pss->vhd = vhd;
562 			pss->wsi = wsi;
563 			pss->spa = lws_spa_create(wsi, param_names,
564 						  LWS_ARRAY_SIZE(param_names),
565 						  1024, file_upload_cb, pss);
566 			if (!pss->spa)
567 				return -1;
568 
569 			pss->filename[0] = '\0';
570 			pss->file_length = 0;
571 			/* catchall */
572 			pss->response_code = HTTP_STATUS_SERVICE_UNAVAILABLE;
573 
574 			m = lws_hdr_copy(wsi, pss->user, sizeof(pss->user),
575 					 WSI_TOKEN_HTTP_AUTHORIZATION);
576 			if (m > 0)
577 				lwsl_info("basic auth user: %s\n", pss->user);
578 			else
579 				pss->user[0] = '\0';
580 		}
581 
582 		/* let it parse the POST data */
583 		if (lws_spa_process(pss->spa, in, (int)len)) {
584 			lwsl_notice("spa saw a problem\n");
585 			/* some problem happened */
586 			lws_spa_finalize(pss->spa);
587 
588 			pss->completed = 1;
589 			lws_callback_on_writable(wsi);
590 		}
591 		break;
592 
593 	case LWS_CALLBACK_HTTP_BODY_COMPLETION:
594 		/* call to inform no more payload data coming */
595 		lws_spa_finalize(pss->spa);
596 
597 		pss->completed = 1;
598 		lws_callback_on_writable(wsi);
599 		break;
600 
601 	case LWS_CALLBACK_HTTP_WRITEABLE:
602 		if (!pss->completed)
603 			break;
604 
605 		p = (unsigned char *)pss->result + LWS_PRE;
606 		start = p;
607 		end = p + sizeof(pss->result) - LWS_PRE - 1;
608 
609 		if (!pss->sent_headers) {
610 			n = format_result(pss);
611 
612 			if (lws_add_http_header_status(wsi, pss->response_code,
613 						       &p, end))
614 				goto bail;
615 
616 			if (lws_add_http_header_by_token(wsi,
617 					WSI_TOKEN_HTTP_CONTENT_TYPE,
618 					(unsigned char *)"text/html", 9,
619 					&p, end))
620 				goto bail;
621 			if (lws_add_http_header_content_length(wsi, n, &p, end))
622 				goto bail;
623 			if (lws_finalize_http_header(wsi, &p, end))
624 				goto bail;
625 
626 			/* first send the headers ... */
627 			n = lws_write(wsi, start, lws_ptr_diff(p, start),
628 				      LWS_WRITE_HTTP_HEADERS |
629 				      LWS_WRITE_H2_STREAM_END);
630 			if (n < 0)
631 				goto bail;
632 
633 			pss->sent_headers = 1;
634 			lws_callback_on_writable(wsi);
635 			break;
636 		}
637 
638 		if (!pss->sent_body) {
639 			n = format_result(pss);
640 			n = lws_write(wsi, (unsigned char *)start, n,
641 				      LWS_WRITE_HTTP_FINAL);
642 
643 			pss->sent_body = 1;
644 			if (n < 0) {
645 				lwsl_err("%s: writing body failed\n", __func__);
646 				return 1;
647 			}
648 			goto try_to_reuse;
649 		}
650 		break;
651 
652 	case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
653 		/* called when our wsi user_space is going to be destroyed */
654 		if (pss->spa) {
655 			lws_spa_destroy(pss->spa);
656 			pss->spa = NULL;
657 		}
658 		break;
659 
660 	default:
661 		break;
662 	}
663 
664 	return 0;
665 
666 bail:
667 
668 	return 1;
669 
670 try_to_reuse:
671 	if (lws_http_transaction_completed(wsi))
672 		return -1;
673 
674 	return 0;
675 }
676 
677 #define LWS_PLUGIN_PROTOCOL_DEADDROP \
678 	{ \
679 		"lws-deaddrop", \
680 		callback_deaddrop, \
681 		sizeof(struct pss_deaddrop), \
682 		1024, \
683 		0, NULL, 0 \
684 	}
685 
686 #if !defined (LWS_PLUGIN_STATIC)
687 
688 static const struct lws_protocols protocols[] = {
689 	LWS_PLUGIN_PROTOCOL_DEADDROP
690 };
691 
692 LWS_VISIBLE int
init_protocol_deaddrop(struct lws_context * context,struct lws_plugin_capability * c)693 init_protocol_deaddrop(struct lws_context *context,
694 		       struct lws_plugin_capability *c)
695 {
696 	if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
697 		lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
698 			 c->api_magic);
699 		return 1;
700 	}
701 
702 	c->protocols = protocols;
703 	c->count_protocols = LWS_ARRAY_SIZE(protocols);
704 	c->extensions = NULL;
705 	c->count_extensions = 0;
706 
707 	return 0;
708 }
709 
710 LWS_VISIBLE int
destroy_protocol_deaddrop(struct lws_context * context)711 destroy_protocol_deaddrop(struct lws_context *context)
712 {
713 	return 0;
714 }
715 
716 #endif
717