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 #define LWS_DLL
23 #define LWS_INTERNAL
24 #include <libwebsockets.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef WIN32
33 #include <io.h>
34 #endif
35 #include <stdio.h>
36
37 struct vhd_fts_demo {
38 const char *indexpath;
39 };
40
41 struct pss_fts_demo {
42 struct lwsac *result;
43 struct lws_fts_result_autocomplete *ac;
44 struct lws_fts_result_filepath *fp;
45
46 uint32_t *li;
47 int done;
48
49 uint8_t first:1;
50 uint8_t ac_done:1;
51
52 uint8_t fp_init_done:1;
53 };
54
55 static int
callback_fts(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)56 callback_fts(struct lws *wsi, enum lws_callback_reasons reason, void *user,
57 void *in, size_t len)
58 {
59 struct vhd_fts_demo *vhd = (struct vhd_fts_demo *)
60 lws_protocol_vh_priv_get(lws_get_vhost(wsi),
61 lws_get_protocol(wsi));
62 struct pss_fts_demo *pss = (struct pss_fts_demo *)user;
63 uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start,
64 *end = &buf[sizeof(buf) - LWS_PRE - 1];
65 struct lws_fts_search_params params;
66 const char *ccp = (const char *)in;
67 struct lws_fts_result *result;
68 struct lws_fts_file *jtf;
69 int n;
70
71 switch (reason) {
72
73 case LWS_CALLBACK_PROTOCOL_INIT:
74 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
75 lws_get_protocol(wsi),sizeof(struct vhd_fts_demo));
76 if (!vhd)
77 return 1;
78 if (lws_pvo_get_str(in, "indexpath",
79 (const char **)&vhd->indexpath))
80 return 1;
81
82 return 0;
83
84 case LWS_CALLBACK_HTTP:
85
86 pss->first = 1;
87 pss->ac_done = 0;
88
89 /*
90 * we have a "subdirectory" selecting the task
91 *
92 * /a/ = autocomplete
93 * /r/ = results
94 */
95
96 if (strncmp(ccp, "/a/", 3) && strncmp(ccp, "/r/", 3))
97 goto reply_404;
98
99 memset(¶ms, 0, sizeof(params));
100
101 params.needle = ccp + 3;
102 if (*(ccp + 1) == 'a')
103 params.flags = LWSFTS_F_QUERY_AUTOCOMPLETE;
104 if (*(ccp + 1) == 'r')
105 params.flags = LWSFTS_F_QUERY_FILES |
106 LWSFTS_F_QUERY_FILE_LINES |
107 LWSFTS_F_QUERY_QUOTE_LINE;
108 params.max_autocomplete = 10;
109 params.max_files = 10;
110
111 jtf = lws_fts_open(vhd->indexpath);
112 if (!jtf) {
113 lwsl_err("unable to open %s\n", vhd->indexpath);
114 /* we'll inform the client in the JSON */
115 goto reply_200;
116 }
117
118 result = lws_fts_search(jtf, ¶ms);
119 lws_fts_close(jtf);
120 if (result) {
121 pss->result = params.results_head;
122 pss->ac = result->autocomplete_head;
123 pss->fp = result->filepath_head;
124 }
125 /* NULL result will be told in the json as "indexed": 0 */
126
127 reply_200:
128 if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK,
129 "text/html",
130 LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
131 return 1;
132
133 if (lws_finalize_write_http_header(wsi, start, &p, end))
134 return 1;
135
136 lws_callback_on_writable(wsi);
137 return 0;
138
139 reply_404:
140 if (lws_add_http_common_headers(wsi, HTTP_STATUS_NOT_FOUND,
141 "text/html",
142 LWS_ILLEGAL_HTTP_CONTENT_LEN, &p, end))
143 return 1;
144
145 if (lws_finalize_write_http_header(wsi, start, &p, end))
146 return 1;
147 return lws_http_transaction_completed(wsi);
148
149 case LWS_CALLBACK_CLOSED_HTTP:
150 if (pss && pss->result)
151 lwsac_free(&pss->result);
152 break;
153
154 case LWS_CALLBACK_HTTP_WRITEABLE:
155
156 if (!pss)
157 break;
158
159 n = LWS_WRITE_HTTP;
160 if (pss->first)
161 p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
162 "{\"indexed\": %d, \"ac\": [", !!pss->result);
163
164 while (pss->ac && lws_ptr_diff(end, p) > 256) {
165 p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
166 "%c{\"ac\": \"%s\",\"matches\": %d,"
167 "\"agg\": %d, \"elided\": %d}",
168 pss->first ? ' ' : ',', (char *)(pss->ac + 1),
169 pss->ac->instances, pss->ac->agg_instances,
170 pss->ac->elided);
171
172 pss->first = 0;
173 pss->ac = pss->ac->next;
174 }
175
176 if (!pss->ac_done && !pss->ac && pss->fp) {
177 pss->ac_done = 1;
178
179 p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
180 "], \"fp\": [");
181 }
182
183 while (pss->fp && lws_ptr_diff(end, p) > 256) {
184 if (!pss->fp_init_done) {
185 p += lws_snprintf((char *)p,
186 lws_ptr_diff(end, p),
187 "%c{\"path\": \"%s\",\"matches\": %d,"
188 "\"origlines\": %d,"
189 "\"hits\": [", pss->first ? ' ' : ',',
190 ((char *)(pss->fp + 1)) +
191 pss->fp->matches_length,
192 pss->fp->matches,
193 pss->fp->lines_in_file);
194
195 pss->li = ((uint32_t *)(pss->fp + 1));
196 pss->done = 0;
197 pss->fp_init_done = 1;
198 pss->first = 0;
199 } else {
200 while (pss->done < pss->fp->matches &&
201 lws_ptr_diff(end, p) > 256) {
202
203 p += lws_snprintf((char *)p,
204 lws_ptr_diff(end, p),
205 "%c\n{\"l\":%d,\"o\":%d,"
206 "\"s\":\"%s\"}",
207 !pss->done ? ' ' : ',',
208 pss->li[0], pss->li[1],
209 *((const char **)&pss->li[2]));
210 pss->li += 2 + (sizeof(const char *) /
211 sizeof(uint32_t));
212 pss->done++;
213 }
214
215 if (pss->done == pss->fp->matches) {
216 *p++ = ']';
217 pss->fp_init_done = 0;
218 pss->fp = pss->fp->next;
219 if (!pss->fp)
220 *p++ = '}';
221 }
222 }
223 }
224
225 if (!pss->ac && !pss->fp) {
226 n = LWS_WRITE_HTTP_FINAL;
227 p += lws_snprintf((char *)p, lws_ptr_diff(end, p),
228 "]}");
229 }
230
231 if (lws_write(wsi, (uint8_t *)start,
232 lws_ptr_diff(p, start), n) !=
233 lws_ptr_diff(p, start))
234 return 1;
235
236 if (n == LWS_WRITE_HTTP_FINAL) {
237 if (pss->result)
238 lwsac_free(&pss->result);
239 if (lws_http_transaction_completed(wsi))
240 return -1;
241 } else
242 lws_callback_on_writable(wsi);
243
244 return 0;
245
246 default:
247 break;
248 }
249
250 return lws_callback_http_dummy(wsi, reason, user, in, len);
251 }
252
253
254 #define LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO \
255 { \
256 "lws-test-fts", \
257 callback_fts, \
258 sizeof(struct pss_fts_demo), \
259 0, \
260 0, NULL, 0 \
261 }
262
263 #if !defined (LWS_PLUGIN_STATIC)
264
265 static const struct lws_protocols protocols[] = {
266 LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO
267 };
268
269 LWS_VISIBLE int
init_protocol_fulltext_demo(struct lws_context * context,struct lws_plugin_capability * c)270 init_protocol_fulltext_demo(struct lws_context *context,
271 struct lws_plugin_capability *c)
272 {
273 if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
274 lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
275 c->api_magic);
276 return 1;
277 }
278
279 c->protocols = protocols;
280 c->count_protocols = LWS_ARRAY_SIZE(protocols);
281 c->extensions = NULL;
282 c->count_extensions = 0;
283
284 return 0;
285 }
286
287 LWS_VISIBLE int
destroy_protocol_fulltext_demo(struct lws_context * context)288 destroy_protocol_fulltext_demo(struct lws_context *context)
289 {
290 return 0;
291 }
292
293 #endif
294