protocol_table_dirlisting.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /*
  2. * ws protocol handler plugin for dirlisting "generic table" demo
  3. *
  4. * Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation:
  9. * version 2.1 of the License.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  19. * MA 02110-1301 USA
  20. */
  21. #define LWS_DLL
  22. #define LWS_INTERNAL
  23. #include "../lib/libwebsockets.h"
  24. #include <string.h>
  25. #include <uv.h>
  26. struct fobj {
  27. struct fobj *next;
  28. const char *name, *uri, *icon, *date;
  29. time_t m;
  30. unsigned long size;
  31. };
  32. struct per_session_data__tbl_dir {
  33. struct fobj base;
  34. char strings[64 * 1024];
  35. char reldir[256];
  36. char *p;
  37. const char *dir;
  38. #if UV_VERSION_MAJOR > 0
  39. uv_fs_event_t *event_req;
  40. #endif
  41. struct lws *wsi;
  42. };
  43. #if UV_VERSION_MAJOR > 0
  44. static void
  45. mon_cb(uv_fs_event_t *handle, const char *filename, int events, int status)
  46. {
  47. struct per_session_data__tbl_dir *pss = handle->data;
  48. //lwsl_notice("%s\n", __func__);
  49. if (pss && pss->wsi)
  50. lws_callback_on_writable(pss->wsi);
  51. }
  52. static void lws_uv_close_cb(uv_handle_t *handle)
  53. {
  54. free(handle);
  55. }
  56. static void
  57. lws_protocol_dir_kill_monitor(struct per_session_data__tbl_dir *pss)
  58. {
  59. if (!pss->event_req)
  60. return;
  61. pss->wsi = NULL;
  62. pss->event_req->data = NULL;
  63. uv_fs_event_stop(pss->event_req);
  64. uv_close((uv_handle_t *)pss->event_req, lws_uv_close_cb);
  65. pss->event_req = NULL;
  66. }
  67. #endif
  68. static int
  69. scan_dir(struct lws *wsi, struct per_session_data__tbl_dir *pss)
  70. {
  71. /* uuh travis... */
  72. #if UV_VERSION_MAJOR > 0
  73. uv_loop_t *loop = lws_uv_getloop(lws_get_context(wsi), 0);
  74. char *end = &(pss->strings[sizeof(pss->strings) - 1]);
  75. struct fobj *prev = &pss->base;
  76. char path[512], da[200];
  77. const char *icon;
  78. uv_dirent_t dent;
  79. struct fobj *f;
  80. struct stat st;
  81. struct tm *tm;
  82. int ret = 0, n;
  83. uv_fs_t req;
  84. lws_protocol_dir_kill_monitor(pss);
  85. lws_snprintf(path, sizeof(path) - 1, "%s/%s", pss->dir, pss->reldir);
  86. //lwsl_notice("path = %s\n", path);
  87. pss->event_req = malloc(sizeof(*pss->event_req));
  88. if (!pss->event_req)
  89. return 2;
  90. pss->wsi = wsi;
  91. pss->event_req->data = pss;
  92. uv_fs_event_init(lws_uv_getloop(lws_get_context(wsi), 0),
  93. pss->event_req);
  94. // The recursive flag watches subdirectories too.
  95. n = uv_fs_event_start(pss->event_req, mon_cb, path, UV_FS_EVENT_RECURSIVE);
  96. //lwsl_notice("monitoring %s (%d)\n", path, n);
  97. if (!uv_fs_scandir(loop, &req, path, 0, NULL)) {
  98. lwsl_err("Scandir on %s failed\n", path);
  99. return 2;
  100. }
  101. pss->p = pss->strings;
  102. while (uv_fs_scandir_next(&req, &dent) != UV_EOF) {
  103. lws_snprintf(path, sizeof(path) - 1, "%s/%s/%s", pss->dir, pss->reldir, dent.name);
  104. if (stat(path, &st)) {
  105. lwsl_info("unable to stat %s\n", path);
  106. continue;
  107. }
  108. f = malloc(sizeof(*f));
  109. f->next = NULL;
  110. f->name = pss->p;
  111. n = lws_snprintf(pss->p, end - pss->p, "%s", dent.name);
  112. pss->p += n + 1;
  113. f->uri = NULL;
  114. if ((S_IFMT & st.st_mode) == S_IFDIR) {
  115. n = lws_snprintf(pss->p, end - pss->p, "=%s/%s", pss->reldir, dent.name);
  116. f->uri = pss->p;
  117. }
  118. if (lws_get_mimetype(dent.name, NULL)) {
  119. n = lws_snprintf(pss->p, end - pss->p, "./serve/%s/%s", pss->reldir, dent.name);
  120. f->uri = pss->p;
  121. }
  122. if (f->uri)
  123. pss->p += n + 1;
  124. if (end - pss->p < 100) {
  125. free(f);
  126. break;
  127. }
  128. icon = " ";
  129. if ((S_IFMT & st.st_mode) == S_IFDIR)
  130. icon = "&#x1f4c2;";
  131. f->icon = pss->p;
  132. n = lws_snprintf(pss->p, end - pss->p, "%s", icon);
  133. pss->p += n + 1;
  134. f->date = pss->p;
  135. tm = gmtime(&st.st_mtime);
  136. strftime(da, sizeof(da), "%Y-%b-%d %H:%M:%S %z", tm);
  137. n = lws_snprintf(pss->p, end - pss->p, "%s", da);
  138. pss->p += n + 1;
  139. f->size = st.st_size;
  140. f->m = st.st_mtime;
  141. prev->next = f;
  142. prev = f;
  143. }
  144. uv_fs_req_cleanup(&req);
  145. return ret;
  146. #else
  147. return 0;
  148. #endif
  149. }
  150. static void
  151. free_scan_dir(struct per_session_data__tbl_dir *pss)
  152. {
  153. struct fobj *f = pss->base.next, *f1;
  154. while (f) {
  155. f1 = f->next;
  156. free(f);
  157. f = f1;
  158. }
  159. pss->base.next = NULL;
  160. }
  161. static int
  162. callback_lws_table_dirlisting(struct lws *wsi, enum lws_callback_reasons reason,
  163. void *user, void *in, size_t len)
  164. {
  165. struct per_session_data__tbl_dir *pss = (struct per_session_data__tbl_dir *)user;
  166. char j[LWS_PRE + 16384], *p = j + LWS_PRE, *start = p, *q, *q1, *w,
  167. *end = j + sizeof(j) - LWS_PRE, e[384], s[384], s1[384];
  168. const struct lws_protocol_vhost_options *pmo;
  169. struct fobj *f;
  170. int n, first = 1;
  171. switch (reason) {
  172. case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
  173. break;
  174. case LWS_CALLBACK_ESTABLISHED:
  175. lwsl_debug("LWS_CALLBACK_ESTABLISHED\n");
  176. /*
  177. * send client the lwsgt table layout
  178. */
  179. start = "{\"cols\":["
  180. " {\"name\": \"Date\"},"
  181. " {\"name\": \"Size\", \"align\": \"right\"},"
  182. " {\"name\": \"Icon\"},"
  183. " {\"name\": \"Name\", \"href\": \"uri\"},"
  184. " {\"name\": \"uri\", \"hide\": \"1\" }"
  185. " ]"
  186. "}";
  187. if (lws_write(wsi, (unsigned char *)start, strlen(start),
  188. LWS_WRITE_TEXT) < 0)
  189. return -1;
  190. /* send a view update next */
  191. lws_callback_on_writable(wsi);
  192. break;
  193. case LWS_CALLBACK_RECEIVE:
  194. if (len > sizeof(pss->reldir) - 1)
  195. len = sizeof(pss->reldir) - 1;
  196. if (!strstr(in, "..") && !strchr(in, '~'))
  197. strncpy(pss->reldir, in, len);
  198. else
  199. len = 0;
  200. pss->reldir[len] = '\0';
  201. if (pss->reldir[0] == '/' && !pss->reldir[1])
  202. pss->reldir[0] = '\0';
  203. lwsl_info("%s\n", pss->reldir);
  204. lws_callback_on_writable(wsi);
  205. break;
  206. case LWS_CALLBACK_SERVER_WRITEABLE:
  207. if (scan_dir(wsi, pss))
  208. return 1;
  209. p += lws_snprintf(p, end - p, "{\"breadcrumbs\":[");
  210. q = pss->reldir;
  211. if (!q[0])
  212. p += lws_snprintf(p, end - p, "{\"name\":\"top\"}");
  213. while (*q) {
  214. q1 = strchr(q, '/');
  215. if (!q1) {
  216. if (first)
  217. strcpy(s, "top1");
  218. else
  219. strcpy(s, q);
  220. s1[0] = '\0';
  221. q += strlen(q);
  222. } else {
  223. n = q1 - q;
  224. if (n > sizeof(s) - 1)
  225. n = sizeof(s) - 1;
  226. if (first) {
  227. strcpy(s1, "/");
  228. strcpy(s, "top");
  229. } else {
  230. strncpy(s, q, n);
  231. s[n] = '\0';
  232. n = q1 - pss->reldir;
  233. if (n > sizeof(s1) - 1)
  234. n = sizeof(s1) - 1;
  235. strncpy(s1, pss->reldir, n);
  236. s1[n] = '\0';
  237. }
  238. q = q1 + 1;
  239. }
  240. if (!first)
  241. p += lws_snprintf(p, end - p, ",");
  242. else
  243. first = 0;
  244. p += lws_snprintf(p, end - p, "{\"name\":\"%s\"",
  245. lws_json_purify(e, s, sizeof(e)));
  246. if (*q) {
  247. w = s1;
  248. while (w[0] == '/' && w[1] == '/')
  249. w++;
  250. p += lws_snprintf(p, end - p, ",\"url\":\"%s\"",
  251. lws_json_purify(e, w, sizeof(e)));
  252. }
  253. p += lws_snprintf(p, end - p, "}");
  254. if (!q1)
  255. break;
  256. }
  257. p += lws_snprintf(p, end - p, "],\"data\":[");
  258. f = pss->base.next;
  259. while (f) {
  260. /* format in JSON */
  261. p += lws_snprintf(p, end - p, "{\"Icon\":\"%s\",",
  262. lws_json_purify(e, f->icon, sizeof(e)));
  263. p += lws_snprintf(p, end - p, " \"Date\":\"%s\",",
  264. lws_json_purify(e, f->date, sizeof(e)));
  265. p += lws_snprintf(p, end - p, " \"Size\":\"%ld\",",
  266. f->size);
  267. if (f->uri)
  268. p += lws_snprintf(p, end - p, " \"uri\":\"%s\",",
  269. lws_json_purify(e, f->uri, sizeof(e)));
  270. p += lws_snprintf(p, end - p, " \"Name\":\"%s\"}",
  271. lws_json_purify(e, f->name, sizeof(e)));
  272. f = f->next;
  273. if (f)
  274. p += lws_snprintf(p, end - p, ",");
  275. }
  276. p += lws_snprintf(p, end - p, "]}");
  277. free_scan_dir(pss);
  278. if (lws_write(wsi, (unsigned char *)start, p - start,
  279. LWS_WRITE_TEXT) < 0)
  280. return -1;
  281. break;
  282. case LWS_CALLBACK_HTTP_PMO:
  283. /* find the per-mount options we're interested in */
  284. lwsl_debug("LWS_CALLBACK_HTTP_PMO\n");
  285. pmo = (struct lws_protocol_vhost_options *)in;
  286. while (pmo) {
  287. if (!strcmp(pmo->name, "dir")) /* path to list files */
  288. pss->dir = pmo->value;
  289. pmo = pmo->next;
  290. }
  291. if (!pss->dir[0]) {
  292. lwsl_err("dirlisting: \"dir\" pmo missing\n");
  293. return 1;
  294. }
  295. break;
  296. case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
  297. //lwsl_notice("LWS_CALLBACK_HTTP_DROP_PROTOCOL\n");
  298. #if UV_VERSION_MAJOR > 0
  299. lws_protocol_dir_kill_monitor(pss);
  300. #endif
  301. break;
  302. default:
  303. return 0;
  304. }
  305. return 0;
  306. }
  307. static const struct lws_protocols protocols[] = {
  308. {
  309. "protocol-lws-table-dirlisting",
  310. callback_lws_table_dirlisting,
  311. sizeof(struct per_session_data__tbl_dir),
  312. 0,
  313. },
  314. };
  315. LWS_EXTERN LWS_VISIBLE int
  316. init_protocol_lws_table_dirlisting(struct lws_context *context,
  317. struct lws_plugin_capability *c)
  318. {
  319. if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
  320. lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
  321. c->api_magic);
  322. return 1;
  323. }
  324. c->protocols = protocols;
  325. c->count_protocols = ARRAY_SIZE(protocols);
  326. c->extensions = NULL;
  327. c->count_extensions = 0;
  328. return 0;
  329. }
  330. LWS_EXTERN LWS_VISIBLE int
  331. destroy_protocol_lws_table_dirlisting(struct lws_context *context)
  332. {
  333. return 0;
  334. }