1 /*
2 * ws protocol handler plugin for "fulltext demo"
3 *
4 * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 *
9 * The person who associated a work with this deed has dedicated
10 * the work to the public domain by waiving all of his or her rights
11 * to the work worldwide under copyright law, including all related
12 * and neighboring rights, to the extent allowed by law. You can copy,
13 * modify, distribute and perform the work, even for commercial purposes,
14 * all without asking permission.
15 *
16 * These test plugins are intended to be adapted for use in your code, which
17 * may be proprietary. So unlike the library itself, they are licensed
18 * Public Domain.
19 */
20
21 #if !defined (LWS_PLUGIN_STATIC)
22 #if !defined(LWS_DLL)
23 #define LWS_DLL
24 #endif
25 #if !defined(LWS_INTERNAL)
26 #define LWS_INTERNAL
27 #endif
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 #ifdef WIN32
37 #include <io.h>
38 #endif
39 #include <stdio.h>
40
41 struct vhd_fts_demo {
42 const char *indexpath;
43 };
44
45 struct pss_fts_demo {
46 struct lwsac *result;
47 struct lws_fts_result_autocomplete *ac;
48 struct lws_fts_result_filepath *fp;
49
50 uint32_t *li;
51 int done;
52
53 uint8_t first:1;
54 uint8_t ac_done:1;
55
56 uint8_t fp_init_done:1;
57 };
58
59 static int
callback_fts(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)60 callback_fts(struct lws *wsi, enum lws_callback_reasons reason, void *user,
61 void *in, size_t len)
62 {
63 struct vhd_fts_demo *vhd = (struct vhd_fts_demo *)
64 lws_protocol_vh_priv_get(lws_get_vhost(wsi),
65 lws_get_protocol(wsi));
66 struct pss_fts_demo *pss = (struct pss_fts_demo *)user;
67 uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start,
68 *end = &buf[sizeof(buf) - LWS_PRE - 1];
69 struct lws_fts_search_params params;
70 const char *ccp = (const char *)in;
71 struct lws_fts_result *result;
72 struct lws_fts_file *jtf;
73 int n;
74
75 switch (reason) {
76
77 case LWS_CALLBACK_PROTOCOL_INIT:
78 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
79 lws_get_protocol(wsi),sizeof(struct vhd_fts_demo));
80 if (!vhd)
81 return 0;
82 if (lws_pvo_get_str(in, "indexpath",
83 (const char **)&vhd->indexpath))
84 return 1;
85
86 return 0;
87
88 case LWS_CALLBACK_HTTP:
89
90 pss->first = 1;
91 pss->ac_done = 0;
92
93 /*
94 * we have a "subdirectory" selecting the task
95 *
96 * /a/ = autocomplete
97 * /r/ = results
98 */
99
100 if (strncmp(ccp, "/a/", 3) && strncmp(ccp, "/r/", 3))
101 goto reply_404;
102
103 memset(¶ms, 0, sizeof(params));
104
105 params.needle = ccp + 3;
106 if (*(ccp + 1) == 'a')
107 params.flags = LWSFTS_F_QUERY_AUTOCOMPLETE;
108 if (*(ccp + 1) == 'r')
109 params.flags = LWSFTS_F_QUERY_FILES |
110 LWSFTS_F_QUERY_FILE_LINES |
111 LWSFTS_F_QUERY_QUOTE_LINE;
112 params.max_autocomplete = 10;
113 params.max_files = 10;
114
115 jtf = lws_fts_open(vhd->indexpath);
116 if (!jtf) {
117 lwsl_err("unable to open %s\n", vhd->indexpath);
118 /* we'll inform the client in the JSON */
119 goto reply_200;
120 }
121
122 result = lws_fts_search(jtf, ¶ms);
123 lws_fts_close(jtf);
124 if (result) {
125 pss->result = params.results_head;
126 pss->ac = result->autocomplete_head;
127 pss->fp = result->filepath_head;
128 }
129 /* NULL result will be told in the json as "indexed": 0 */
130
131 reply_200:
132 if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
133 "text/html",
134 LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
135 return 1;
136
137 if (lws_finalize_write_http_header(wsi, start, &p, end))
138 return 1;
139
140 lws_callback_on_writable(wsi);
141 return 0;
142
143 reply_404:
144 if (lws_add_http_common_headers(wsi, HTTP_STATUS_NOT_FOUND,
145 "text/html",
146 LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
147 return 1;
148
149 if (lws_finalize_write_http_header(wsi, start, &p, end))
150 return 1;
151 return lws_http_transaction_completed(wsi);
152
153 case LWS_CALLBACK_CLOSED_HTTP:
154 if (pss && pss->result)
155 lwsac_free(&pss->result);
156 break;
157
158 case LWS_CALLBACK_HTTP_WRITEABLE:
159
160 if (!pss)
161 break;
162
163 n = LWS_WRITE_HTTP;
164 if (pss->first)
165 p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
166 "{\"indexed\": %d, \"ac\": [", !!pss->result);
167
168 while (pss->ac && lws_ptr_diff(end, p) > 256) {
169 p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
170 "%c{\"ac\": \"%s\",\"matches\": %d,"
171 "\"agg\": %d, \"elided\": %d}",
172 pss->first ? ' ' : ',', (char *)(pss->ac + 1),
173 pss->ac->instances, pss->ac->agg_instances,
174 pss->ac->elided);
175
176 pss->first = 0;
177 pss->ac = pss->ac->next;
178 }
179
180 if (!pss->ac_done && !pss->ac && pss->fp) {
181 pss->ac_done = 1;
182
183 p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
184 "], \"fp\": [");
185 }
186
187 while (pss->fp && lws_ptr_diff_size_t(end, p) > 256) {
188 if (!pss->fp_init_done) {
189 p += lws_snprintf((char *)p,
190 lws_ptr_diff_size_t(end, p),
191 "%c{\"path\": \"%s\",\"matches\": %d,"
192 "\"origlines\": %d,"
193 "\"hits\": [", pss->first ? ' ' : ',',
194 ((char *)(pss->fp + 1)) +
195 pss->fp->matches_length,
196 pss->fp->matches,
197 pss->fp->lines_in_file);
198
199 pss->li = ((uint32_t *)(pss->fp + 1));
200 pss->done = 0;
201 pss->fp_init_done = 1;
202 pss->first = 0;
203 } else {
204 while (pss->done < pss->fp->matches &&
205 lws_ptr_diff(end, p) > 256) {
206
207 p += lws_snprintf((char *)p,
208 lws_ptr_diff_size_t(end, p),
209 "%c\n{\"l\":%d,\"o\":%d,"
210 "\"s\":\"%s\"}",
211 !pss->done ? ' ' : ',',
212 pss->li[0], pss->li[1],
213 *((const char **)&pss->li[2]));
214 pss->li += 2 + (sizeof(const char *) /
215 sizeof(uint32_t));
216 pss->done++;
217 }
218
219 if (pss->done == pss->fp->matches) {
220 *p++ = ']';
221 pss->fp_init_done = 0;
222 pss->fp = pss->fp->next;
223 if (!pss->fp)
224 *p++ = '}';
225 }
226 }
227 }
228
229 if (!pss->ac && !pss->fp) {
230 n = LWS_WRITE_HTTP_FINAL;
231 p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p),
232 "]}");
233 }
234
235 if (lws_write(wsi, (uint8_t *)start,
236 lws_ptr_diff_size_t(p, start), (enum lws_write_protocol)n) !=
237 lws_ptr_diff(p, start))
238 return 1;
239
240 if (n == LWS_WRITE_HTTP_FINAL) {
241 if (pss->result)
242 lwsac_free(&pss->result);
243 if (lws_http_transaction_completed(wsi))
244 return -1;
245 } else
246 lws_callback_on_writable(wsi);
247
248 return 0;
249
250 default:
251 break;
252 }
253
254 return lws_callback_http_dummy(wsi, reason, user, in, len);
255 }
256
257
258 #define LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO \
259 { \
260 "lws-test-fts", \
261 callback_fts, \
262 sizeof(struct pss_fts_demo), \
263 0, \
264 0, NULL, 0 \
265 }
266
267 #if !defined (LWS_PLUGIN_STATIC)
268
269 LWS_VISIBLE const struct lws_protocols fulltext_demo_protocols[] = {
270 LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO
271 };
272
273 LWS_VISIBLE const lws_plugin_protocol_t fulltext_demo = {
274 .hdr = {
275 "fulltext demo",
276 "lws_protocol_plugin",
277 LWS_BUILD_HASH,
278 LWS_PLUGIN_API_MAGIC
279 },
280
281 .protocols = fulltext_demo_protocols,
282 .count_protocols = LWS_ARRAY_SIZE(fulltext_demo_protocols),
283 .extensions = NULL,
284 .count_extensions = 0,
285 };
286
287 #endif
288