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