• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define LWS_DLL
26 #define LWS_INTERNAL
27 #include <libwebsockets.h>
28 
29 #include <stdlib.h>
30 #include <string.h>
31 #include <uv.h>
32 
33 struct fobj {
34 	struct fobj *next;
35 	const char *name, *uri, *icon, *date;
36 	time_t m;
37 	unsigned long size;
38 };
39 
40 struct per_session_data__tbl_dir {
41 	struct fobj base;
42 	char strings[64 * 1024];
43 	char reldir[256];
44 	char *p;
45 	const char *dir;
46 
47 #if UV_VERSION_MAJOR > 0
48 	uv_fs_event_t *event_req;
49 #endif
50 	struct lws *wsi;
51 };
52 
53 #if UV_VERSION_MAJOR > 0
54 static void
mon_cb(uv_fs_event_t * handle,const char * filename,int events,int status)55 mon_cb(uv_fs_event_t *handle, const char *filename, int events, int status)
56 {
57 	struct per_session_data__tbl_dir *pss = handle->data;
58 
59 	//lwsl_notice("%s\n", __func__);
60 
61 	if (pss && pss->wsi)
62 		lws_callback_on_writable(pss->wsi);
63 }
64 
lws_uv_close_cb(uv_handle_t * handle)65 static void lws_uv_close_cb(uv_handle_t *handle)
66 {
67 	free(handle);
68 }
69 
70 static void
lws_protocol_dir_kill_monitor(struct per_session_data__tbl_dir * pss)71 lws_protocol_dir_kill_monitor(struct per_session_data__tbl_dir *pss)
72 {
73 	if (!pss->event_req)
74 		return;
75 	pss->wsi = NULL;
76 	pss->event_req->data = NULL;
77 	uv_fs_event_stop(pss->event_req);
78 	uv_close((uv_handle_t *)pss->event_req, lws_uv_close_cb);
79 	pss->event_req = NULL;
80 }
81 #endif
82 
83 static int
scan_dir(struct lws * wsi,struct per_session_data__tbl_dir * pss)84 scan_dir(struct lws *wsi, struct per_session_data__tbl_dir *pss)
85 {
86 /* uuh travis... */
87 #if UV_VERSION_MAJOR > 0
88 	uv_loop_t *loop = lws_uv_getloop(lws_get_context(wsi), 0);
89 	char *end = &(pss->strings[sizeof(pss->strings) - 1]);
90 	struct fobj *prev = &pss->base;
91 	char path[512], da[200];
92 	const char *icon;
93 	uv_dirent_t dent;
94 	struct fobj *f;
95 	struct stat st;
96 	struct tm *tm;
97 	int ret = 0, n;
98 	uv_fs_t req;
99 
100 	lws_protocol_dir_kill_monitor(pss);
101 
102 	lws_snprintf(path, sizeof(path) - 1, "%s/%s", pss->dir, pss->reldir);
103 	//lwsl_notice("path = %s\n", path);
104 
105 	pss->event_req = malloc(sizeof(*pss->event_req));
106 	if (!pss->event_req)
107 		return 2;
108 
109 	pss->wsi = wsi;
110 	pss->event_req->data = pss;
111 
112         uv_fs_event_init(lws_uv_getloop(lws_get_context(wsi), 0),
113         		 pss->event_req);
114         // The recursive flag watches subdirectories too.
115         n = uv_fs_event_start(pss->event_req, mon_cb, path, UV_FS_EVENT_RECURSIVE);
116         //lwsl_notice("monitoring %s (%d)\n", path, n);
117 
118 	if (!uv_fs_scandir(loop, &req, path, 0, NULL)) {
119 		lwsl_err("Scandir on %s failed\n", path);
120 		return 2;
121 	}
122 
123 	pss->p = pss->strings;
124 
125 	while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
126 		lws_snprintf(path, sizeof(path) - 1, "%s/%s/%s", pss->dir, pss->reldir, dent.name);
127 
128 		if (stat(path, &st)) {
129 			lwsl_info("unable to stat %s\n", path);
130 			continue;
131 		}
132 		f = malloc(sizeof(*f));
133 		f->next = NULL;
134 		f->name = pss->p;
135 		n = lws_snprintf(pss->p, end - pss->p, "%s", dent.name);
136 		pss->p += n + 1;
137 		f->uri = NULL;
138 		if ((S_IFMT & st.st_mode) == S_IFDIR) {
139 			n = lws_snprintf(pss->p, end - pss->p, "=%s/%s", pss->reldir, dent.name);
140 			f->uri = pss->p;
141 		}
142 		if (lws_get_mimetype(dent.name, NULL)) {
143 			n = lws_snprintf(pss->p, end - pss->p, "./serve/%s/%s", pss->reldir, dent.name);
144 			f->uri = pss->p;
145 		}
146 		if (f->uri)
147 			pss->p += n + 1;
148 
149 		if (end - pss->p < 100) {
150 			free(f);
151 			break;
152 		}
153 
154 		icon = " ";
155 		if ((S_IFMT & st.st_mode) == S_IFDIR)
156 			icon = "&#x1f4c2;";
157 
158 		f->icon = pss->p;
159 		n = lws_snprintf(pss->p, end - pss->p, "%s", icon);
160 		pss->p += n + 1;
161 
162 		f->date = pss->p;
163 		tm = gmtime(&st.st_mtime);
164 		strftime(da, sizeof(da), "%Y-%b-%d %H:%M:%S %z", tm);
165 		n = lws_snprintf(pss->p, end - pss->p, "%s", da);
166 		pss->p += n + 1;
167 
168 		f->size = st.st_size;
169 		f->m = st.st_mtime;
170 		prev->next = f;
171 		prev = f;
172 	}
173 
174 	uv_fs_req_cleanup(&req);
175 
176 	return ret;
177 #else
178 	return 0;
179 #endif
180 }
181 
182 static void
free_scan_dir(struct per_session_data__tbl_dir * pss)183 free_scan_dir(struct per_session_data__tbl_dir *pss)
184 {
185 	struct fobj *f = pss->base.next, *f1;
186 
187 	while (f) {
188 		f1 = f->next;
189 		free(f);
190 		f = f1;
191 	}
192 
193 	pss->base.next = NULL;
194 }
195 
196 static int
callback_lws_table_dirlisting(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)197 callback_lws_table_dirlisting(struct lws *wsi, enum lws_callback_reasons reason,
198 			      void *user, void *in, size_t len)
199 {
200 	struct per_session_data__tbl_dir *pss = (struct per_session_data__tbl_dir *)user;
201 	char j[LWS_PRE + 16384], *p = j + LWS_PRE, *start = p, *q, *q1, *w,
202 		*end = j + sizeof(j) - LWS_PRE, e[384], s[384], s1[384];
203 	const struct lws_protocol_vhost_options *pmo;
204 	struct fobj *f;
205 	int n, first = 1;
206 
207 	switch (reason) {
208 	case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
209 		break;
210 
211 	case LWS_CALLBACK_ESTABLISHED:
212 		lwsl_debug("LWS_CALLBACK_ESTABLISHED\n");
213 		/*
214 		 * send client the lwsgt table layout
215 		 */
216 		start = "{\"cols\":["
217 			"  {\"name\": \"Date\"},"
218 			"  {\"name\": \"Size\", \"align\": \"right\"},"
219 			"  {\"name\": \"Icon\"},"
220 			"  {\"name\": \"Name\", \"href\": \"uri\"},"
221 			"  {\"name\": \"uri\", \"hide\": \"1\" }"
222 			" ]"
223 			"}";
224 		if (lws_write(wsi, (unsigned char *)start, strlen(start),
225 			      LWS_WRITE_TEXT) < 0)
226 			return -1;
227 
228 		/* send a view update next */
229 		lws_callback_on_writable(wsi);
230 		break;
231 
232 	case LWS_CALLBACK_RECEIVE:
233 		if (len > sizeof(pss->reldir) - 1)
234 			len = sizeof(pss->reldir) - 1;
235 		if (!strstr(in, "..") && !strchr(in, '~'))
236 			lws_strncpy(pss->reldir, in, len + 1);
237 		else
238 			len = 0;
239 		pss->reldir[len] = '\0';
240 		if (pss->reldir[0] == '/' && !pss->reldir[1])
241 			pss->reldir[0] = '\0';
242 		lwsl_info("%s\n", pss->reldir);
243 		lws_callback_on_writable(wsi);
244 		break;
245 
246 	case LWS_CALLBACK_SERVER_WRITEABLE:
247 
248 		if (scan_dir(wsi, pss))
249 			return 1;
250 
251 		p += lws_snprintf(p, end - p, "{\"breadcrumbs\":[");
252 		q = pss->reldir;
253 
254 		if (!q[0])
255 			p += lws_snprintf(p, end - p, "{\"name\":\"top\"}");
256 
257 		while (*q) {
258 
259 			q1 = strchr(q, '/');
260 			if (!q1) {
261 				if (first)
262 					strcpy(s, "top1");
263 				else
264 					strcpy(s, q);
265 				s1[0] = '\0';
266 				q += strlen(q);
267 			} else {
268 				n = lws_ptr_diff(q1, q);
269 				if (n > (int)sizeof(s) - 1)
270 					n = sizeof(s) - 1;
271 				if (first) {
272 					strcpy(s1, "/");
273 					strcpy(s, "top");
274 				} else {
275 					lws_strncpy(s, q, n + 1);
276 
277 					n = lws_ptr_diff(q1, pss->reldir);
278 					if (n > (int)sizeof(s1) - 1)
279 						n = sizeof(s1) - 1;
280 					lws_strncpy(s1, pss->reldir, n + 1);
281 				}
282 				q = q1 + 1;
283 			}
284 			if (!first)
285 				p += lws_snprintf(p, end - p, ",");
286 			else
287 				first = 0;
288 
289 			p += lws_snprintf(p, end - p, "{\"name\":\"%s\"",
290 					lws_json_purify(e, s, sizeof(e), NULL));
291 			if (*q) {
292 				w = s1;
293 				while (w[0] == '/' && w[1] == '/')
294 					w++;
295 				p += lws_snprintf(p, end - p, ",\"url\":\"%s\"",
296 					lws_json_purify(e, w, sizeof(e), NULL));
297 			}
298 			p += lws_snprintf(p, end - p, "}");
299 			if (!q1)
300 				break;
301 		}
302 
303 		p += lws_snprintf(p, end - p, "],\"data\":[");
304 
305 		f = pss->base.next;
306 		while (f) {
307 			/* format in JSON */
308 			p += lws_snprintf(p, end - p, "{\"Icon\":\"%s\",",
309 					lws_json_purify(e, f->icon, sizeof(e), NULL));
310 			p += lws_snprintf(p, end - p, " \"Date\":\"%s\",",
311 				lws_json_purify(e, f->date, sizeof(e), NULL));
312 			p += lws_snprintf(p, end - p, " \"Size\":\"%ld\",",
313 				f->size);
314 			if (f->uri)
315 				p += lws_snprintf(p, end - p, " \"uri\":\"%s\",",
316 						lws_json_purify(e, f->uri, sizeof(e), NULL));
317 			p += lws_snprintf(p, end - p, " \"Name\":\"%s\"}",
318 				lws_json_purify(e, f->name, sizeof(e), NULL));
319 
320 			f = f->next;
321 
322 			if (f)
323 				p += lws_snprintf(p, end - p, ",");
324 		}
325 
326 		p += lws_snprintf(p, end - p, "]}");
327 
328 		free_scan_dir(pss);
329 
330 		if (lws_write(wsi, (unsigned char *)start, p - start,
331 			      LWS_WRITE_TEXT) < 0)
332 			return -1;
333 
334 		break;
335 
336 	case LWS_CALLBACK_HTTP_PMO:
337 		/* find the per-mount options we're interested in */
338 		lwsl_debug("LWS_CALLBACK_HTTP_PMO\n");
339 		pmo = (struct lws_protocol_vhost_options *)in;
340 		while (pmo) {
341 			if (!strcmp(pmo->name, "dir")) /* path to list files */
342 				pss->dir = pmo->value;
343 			pmo = pmo->next;
344 		}
345 		if (!pss->dir[0]) {
346 			lwsl_err("dirlisting: \"dir\" pmo missing\n");
347 			return 1;
348 		}
349 		break;
350 
351 	case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
352 		//lwsl_notice("LWS_CALLBACK_HTTP_DROP_PROTOCOL\n");
353 #if UV_VERSION_MAJOR > 0
354 		lws_protocol_dir_kill_monitor(pss);
355 #endif
356 		break;
357 
358 	default:
359 		return 0;
360 	}
361 
362 	return 0;
363 
364 }
365 
366 static const struct lws_protocols protocols[] = {
367 	{
368 		"protocol-lws-table-dirlisting",
369 		callback_lws_table_dirlisting,
370 		sizeof(struct per_session_data__tbl_dir),
371 		0,
372 	},
373 };
374 
375 LWS_VISIBLE int
init_protocol_lws_table_dirlisting(struct lws_context * context,struct lws_plugin_capability * c)376 init_protocol_lws_table_dirlisting(struct lws_context *context,
377 			       struct lws_plugin_capability *c)
378 {
379 	if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
380 		lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
381 			 c->api_magic);
382 		return 1;
383 	}
384 
385 	c->protocols = protocols;
386 	c->count_protocols = LWS_ARRAY_SIZE(protocols);
387 	c->extensions = NULL;
388 	c->count_extensions = 0;
389 
390 	return 0;
391 }
392 
393 LWS_VISIBLE int
destroy_protocol_lws_table_dirlisting(struct lws_context * context)394 destroy_protocol_lws_table_dirlisting(struct lws_context *context)
395 {
396 	return 0;
397 }
398