123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- /*
- * ws protocol handler plugin for dirlisting "generic table" demo
- *
- * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation:
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301 USA
- */
- #define LWS_DLL
- #define LWS_INTERNAL
- #include "../lib/libwebsockets.h"
- #include <string.h>
- #include <uv.h>
- struct fobj {
- struct fobj *next;
- const char *name, *uri, *icon, *date;
- time_t m;
- unsigned long size;
- };
- struct per_session_data__tbl_dir {
- struct fobj base;
- char strings[64 * 1024];
- char reldir[256];
- char *p;
- const char *dir;
- #if UV_VERSION_MAJOR > 0
- uv_fs_event_t *event_req;
- #endif
- struct lws *wsi;
- };
- #if UV_VERSION_MAJOR > 0
- static void
- mon_cb(uv_fs_event_t *handle, const char *filename, int events, int status)
- {
- struct per_session_data__tbl_dir *pss = handle->data;
- //lwsl_notice("%s\n", __func__);
- if (pss && pss->wsi)
- lws_callback_on_writable(pss->wsi);
- }
- static void lws_uv_close_cb(uv_handle_t *handle)
- {
- free(handle);
- }
- static void
- lws_protocol_dir_kill_monitor(struct per_session_data__tbl_dir *pss)
- {
- if (!pss->event_req)
- return;
- pss->wsi = NULL;
- pss->event_req->data = NULL;
- uv_fs_event_stop(pss->event_req);
- uv_close((uv_handle_t *)pss->event_req, lws_uv_close_cb);
- pss->event_req = NULL;
- }
- #endif
- static int
- scan_dir(struct lws *wsi, struct per_session_data__tbl_dir *pss)
- {
- /* uuh travis... */
- #if UV_VERSION_MAJOR > 0
- uv_loop_t *loop = lws_uv_getloop(lws_get_context(wsi), 0);
- char *end = &(pss->strings[sizeof(pss->strings) - 1]);
- struct fobj *prev = &pss->base;
- char path[512], da[200];
- const char *icon;
- uv_dirent_t dent;
- struct fobj *f;
- struct stat st;
- struct tm *tm;
- int ret = 0, n;
- uv_fs_t req;
- lws_protocol_dir_kill_monitor(pss);
- lws_snprintf(path, sizeof(path) - 1, "%s/%s", pss->dir, pss->reldir);
- //lwsl_notice("path = %s\n", path);
- pss->event_req = malloc(sizeof(*pss->event_req));
- if (!pss->event_req)
- return 2;
- pss->wsi = wsi;
- pss->event_req->data = pss;
- uv_fs_event_init(lws_uv_getloop(lws_get_context(wsi), 0),
- pss->event_req);
- // The recursive flag watches subdirectories too.
- n = uv_fs_event_start(pss->event_req, mon_cb, path, UV_FS_EVENT_RECURSIVE);
- //lwsl_notice("monitoring %s (%d)\n", path, n);
- if (!uv_fs_scandir(loop, &req, path, 0, NULL)) {
- lwsl_err("Scandir on %s failed\n", path);
- return 2;
- }
- pss->p = pss->strings;
- while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
- lws_snprintf(path, sizeof(path) - 1, "%s/%s/%s", pss->dir, pss->reldir, dent.name);
- if (stat(path, &st)) {
- lwsl_info("unable to stat %s\n", path);
- continue;
- }
- f = malloc(sizeof(*f));
- f->next = NULL;
- f->name = pss->p;
- n = lws_snprintf(pss->p, end - pss->p, "%s", dent.name);
- pss->p += n + 1;
- f->uri = NULL;
- if ((S_IFMT & st.st_mode) == S_IFDIR) {
- n = lws_snprintf(pss->p, end - pss->p, "=%s/%s", pss->reldir, dent.name);
- f->uri = pss->p;
- }
- if (lws_get_mimetype(dent.name, NULL)) {
- n = lws_snprintf(pss->p, end - pss->p, "./serve/%s/%s", pss->reldir, dent.name);
- f->uri = pss->p;
- }
- if (f->uri)
- pss->p += n + 1;
- if (end - pss->p < 100) {
- free(f);
- break;
- }
- icon = " ";
- if ((S_IFMT & st.st_mode) == S_IFDIR)
- icon = "📂";
- f->icon = pss->p;
- n = lws_snprintf(pss->p, end - pss->p, "%s", icon);
- pss->p += n + 1;
- f->date = pss->p;
- tm = gmtime(&st.st_mtime);
- strftime(da, sizeof(da), "%Y-%b-%d %H:%M:%S %z", tm);
- n = lws_snprintf(pss->p, end - pss->p, "%s", da);
- pss->p += n + 1;
- f->size = st.st_size;
- f->m = st.st_mtime;
- prev->next = f;
- prev = f;
- }
- uv_fs_req_cleanup(&req);
- return ret;
- #else
- return 0;
- #endif
- }
- static void
- free_scan_dir(struct per_session_data__tbl_dir *pss)
- {
- struct fobj *f = pss->base.next, *f1;
- while (f) {
- f1 = f->next;
- free(f);
- f = f1;
- }
- pss->base.next = NULL;
- }
- static int
- callback_lws_table_dirlisting(struct lws *wsi, enum lws_callback_reasons reason,
- void *user, void *in, size_t len)
- {
- struct per_session_data__tbl_dir *pss = (struct per_session_data__tbl_dir *)user;
- char j[LWS_PRE + 16384], *p = j + LWS_PRE, *start = p, *q, *q1, *w,
- *end = j + sizeof(j) - LWS_PRE, e[384], s[384], s1[384];
- const struct lws_protocol_vhost_options *pmo;
- struct fobj *f;
- int n, first = 1;
- switch (reason) {
- case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
- break;
- case LWS_CALLBACK_ESTABLISHED:
- lwsl_debug("LWS_CALLBACK_ESTABLISHED\n");
- /*
- * send client the lwsgt table layout
- */
- start = "{\"cols\":["
- " {\"name\": \"Date\"},"
- " {\"name\": \"Size\", \"align\": \"right\"},"
- " {\"name\": \"Icon\"},"
- " {\"name\": \"Name\", \"href\": \"uri\"},"
- " {\"name\": \"uri\", \"hide\": \"1\" }"
- " ]"
- "}";
- if (lws_write(wsi, (unsigned char *)start, strlen(start),
- LWS_WRITE_TEXT) < 0)
- return -1;
- /* send a view update next */
- lws_callback_on_writable(wsi);
- break;
- case LWS_CALLBACK_RECEIVE:
- if (len > sizeof(pss->reldir) - 1)
- len = sizeof(pss->reldir) - 1;
- if (!strstr(in, "..") && !strchr(in, '~'))
- strncpy(pss->reldir, in, len);
- else
- len = 0;
- pss->reldir[len] = '\0';
- if (pss->reldir[0] == '/' && !pss->reldir[1])
- pss->reldir[0] = '\0';
- lwsl_info("%s\n", pss->reldir);
- lws_callback_on_writable(wsi);
- break;
- case LWS_CALLBACK_SERVER_WRITEABLE:
- if (scan_dir(wsi, pss))
- return 1;
- p += lws_snprintf(p, end - p, "{\"breadcrumbs\":[");
- q = pss->reldir;
- if (!q[0])
- p += lws_snprintf(p, end - p, "{\"name\":\"top\"}");
- while (*q) {
- q1 = strchr(q, '/');
- if (!q1) {
- if (first)
- strcpy(s, "top1");
- else
- strcpy(s, q);
- s1[0] = '\0';
- q += strlen(q);
- } else {
- n = q1 - q;
- if (n > sizeof(s) - 1)
- n = sizeof(s) - 1;
- if (first) {
- strcpy(s1, "/");
- strcpy(s, "top");
- } else {
- strncpy(s, q, n);
- s[n] = '\0';
- n = q1 - pss->reldir;
- if (n > sizeof(s1) - 1)
- n = sizeof(s1) - 1;
- strncpy(s1, pss->reldir, n);
- s1[n] = '\0';
- }
- q = q1 + 1;
- }
- if (!first)
- p += lws_snprintf(p, end - p, ",");
- else
- first = 0;
- p += lws_snprintf(p, end - p, "{\"name\":\"%s\"",
- lws_json_purify(e, s, sizeof(e)));
- if (*q) {
- w = s1;
- while (w[0] == '/' && w[1] == '/')
- w++;
- p += lws_snprintf(p, end - p, ",\"url\":\"%s\"",
- lws_json_purify(e, w, sizeof(e)));
- }
- p += lws_snprintf(p, end - p, "}");
- if (!q1)
- break;
- }
- p += lws_snprintf(p, end - p, "],\"data\":[");
- f = pss->base.next;
- while (f) {
- /* format in JSON */
- p += lws_snprintf(p, end - p, "{\"Icon\":\"%s\",",
- lws_json_purify(e, f->icon, sizeof(e)));
- p += lws_snprintf(p, end - p, " \"Date\":\"%s\",",
- lws_json_purify(e, f->date, sizeof(e)));
- p += lws_snprintf(p, end - p, " \"Size\":\"%ld\",",
- f->size);
- if (f->uri)
- p += lws_snprintf(p, end - p, " \"uri\":\"%s\",",
- lws_json_purify(e, f->uri, sizeof(e)));
- p += lws_snprintf(p, end - p, " \"Name\":\"%s\"}",
- lws_json_purify(e, f->name, sizeof(e)));
- f = f->next;
- if (f)
- p += lws_snprintf(p, end - p, ",");
- }
- p += lws_snprintf(p, end - p, "]}");
- free_scan_dir(pss);
- if (lws_write(wsi, (unsigned char *)start, p - start,
- LWS_WRITE_TEXT) < 0)
- return -1;
- break;
- case LWS_CALLBACK_HTTP_PMO:
- /* find the per-mount options we're interested in */
- lwsl_debug("LWS_CALLBACK_HTTP_PMO\n");
- pmo = (struct lws_protocol_vhost_options *)in;
- while (pmo) {
- if (!strcmp(pmo->name, "dir")) /* path to list files */
- pss->dir = pmo->value;
- pmo = pmo->next;
- }
- if (!pss->dir[0]) {
- lwsl_err("dirlisting: \"dir\" pmo missing\n");
- return 1;
- }
- break;
- case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
- //lwsl_notice("LWS_CALLBACK_HTTP_DROP_PROTOCOL\n");
- #if UV_VERSION_MAJOR > 0
- lws_protocol_dir_kill_monitor(pss);
- #endif
- break;
- default:
- return 0;
- }
- return 0;
- }
- static const struct lws_protocols protocols[] = {
- {
- "protocol-lws-table-dirlisting",
- callback_lws_table_dirlisting,
- sizeof(struct per_session_data__tbl_dir),
- 0,
- },
- };
- LWS_EXTERN LWS_VISIBLE int
- init_protocol_lws_table_dirlisting(struct lws_context *context,
- struct lws_plugin_capability *c)
- {
- if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
- lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
- c->api_magic);
- return 1;
- }
- c->protocols = protocols;
- c->count_protocols = ARRAY_SIZE(protocols);
- c->extensions = NULL;
- c->count_extensions = 0;
- return 0;
- }
- LWS_EXTERN LWS_VISIBLE int
- destroy_protocol_lws_table_dirlisting(struct lws_context *context)
- {
- return 0;
- }
|