Revert "Imported Upstream version 3.2"
[platform/upstream/libwebsockets.git] / plugins / generic-sessions / protocol_lws_messageboard.c
1 /*
2  * ws protocol handler plugin for messageboard "generic sessions" 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
22 #define LWS_DLL
23 #define LWS_INTERNAL
24 #include "../lib/libwebsockets.h"
25
26 #include <sqlite3.h>
27 #include <string.h>
28
29 struct per_vhost_data__gs_mb {
30         struct lws_vhost *vh;
31         const struct lws_protocols *gsp;
32         sqlite3 *pdb;
33         char message_db[256];
34         unsigned long last_idx;
35 };
36
37 struct per_session_data__gs_mb {
38         void *pss_gs; /* for use by generic-sessions */
39         struct lws_session_info sinfo;
40         struct lws_spa *spa;
41         unsigned long last_idx;
42         unsigned int our_form:1;
43 };
44
45 static const char * const param_names[] = {
46         "send",
47         "msg",
48 };
49 enum {
50         MBSPA_SUBMIT,
51         MBSPA_MSG,
52 };
53
54 #define MAX_MSG_LEN 512
55
56 struct message {
57         unsigned long idx;
58         unsigned long time;
59         char username[32];
60         char email[100];
61         char ip[72];
62         char content[MAX_MSG_LEN];
63 };
64
65 static int
66 lookup_cb(void *priv, int cols, char **col_val, char **col_name)
67 {
68         struct message *m = (struct message *)priv;
69         int n;
70
71         for (n = 0; n < cols; n++) {
72
73                 if (!strcmp(col_name[n], "idx") ||
74                     !strcmp(col_name[n], "MAX(idx)")) {
75                         if (!col_val[n])
76                                 m->idx = 0;
77                         else
78                                 m->idx = atol(col_val[n]);
79                         continue;
80                 }
81                 if (!strcmp(col_name[n], "time")) {
82                         m->time = atol(col_val[n]);
83                         continue;
84                 }
85                 if (!strcmp(col_name[n], "username")) {
86                         strncpy(m->username, col_val[n], sizeof(m->username) - 1);
87                         m->username[sizeof(m->username) - 1] = '\0';
88                         continue;
89                 }
90                 if (!strcmp(col_name[n], "email")) {
91                         strncpy(m->email, col_val[n], sizeof(m->email) - 1);
92                         m->email[sizeof(m->email) - 1] = '\0';
93                         continue;
94                 }
95                 if (!strcmp(col_name[n], "ip")) {
96                         strncpy(m->ip, col_val[n], sizeof(m->ip) - 1);
97                         m->ip[sizeof(m->ip) - 1] = '\0';
98                         continue;
99                 }
100                 if (!strcmp(col_name[n], "content")) {
101                         strncpy(m->content, col_val[n], sizeof(m->content) - 1);
102                         m->content[sizeof(m->content) - 1] = '\0';
103                         continue;
104                 }
105         }
106         return 0;
107 }
108
109 static unsigned long
110 get_last_idx(struct per_vhost_data__gs_mb *vhd)
111 {
112         struct message m;
113
114         if (sqlite3_exec(vhd->pdb, "SELECT MAX(idx) FROM msg;",
115                          lookup_cb, &m, NULL) != SQLITE_OK) {
116                 lwsl_err("Unable to lookup token: %s\n",
117                          sqlite3_errmsg(vhd->pdb));
118                 return 0;
119         }
120
121         return m.idx;
122 }
123
124 static int
125 post_message(struct lws *wsi, struct per_vhost_data__gs_mb *vhd,
126              struct per_session_data__gs_mb *pss)
127 {
128         struct lws_session_info sinfo;
129         char s[MAX_MSG_LEN + 512];
130         char esc[MAX_MSG_LEN + 256];
131
132         vhd->gsp->callback(wsi, LWS_CALLBACK_SESSION_INFO,
133                            pss->pss_gs, &sinfo, 0);
134
135         lws_snprintf((char *)s, sizeof(s) - 1,
136                  "insert into msg(time, username, email, ip, content)"
137                  " values (%lu, '%s', '%s', '%s', '%s');",
138                  (unsigned long)lws_now_secs(), sinfo.username, sinfo.email, sinfo.ip,
139                  lws_sql_purify(esc, lws_spa_get_string(pss->spa, MBSPA_MSG),
140                                 sizeof(esc) - 1));
141         if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
142                 lwsl_err("Unable to insert msg: %s\n", sqlite3_errmsg(vhd->pdb));
143                 return 1;
144         }
145         vhd->last_idx = get_last_idx(vhd);
146
147         /* let everybody connected by this protocol on this vhost know */
148         lws_callback_on_writable_all_protocol_vhost(lws_get_vhost(wsi),
149                                                     lws_get_protocol(wsi));
150
151         return 0;
152 }
153
154 static int
155 callback_messageboard(struct lws *wsi, enum lws_callback_reasons reason,
156                       void *user, void *in, size_t len)
157 {
158         struct per_session_data__gs_mb *pss = (struct per_session_data__gs_mb *)user;
159         const struct lws_protocol_vhost_options *pvo;
160         struct per_vhost_data__gs_mb *vhd = (struct per_vhost_data__gs_mb *)
161                 lws_protocol_vh_priv_get(lws_get_vhost(wsi), lws_get_protocol(wsi));
162         unsigned char *p, *start, *end, buffer[LWS_PRE + 256];
163         char s[512];
164         int n;
165
166         switch (reason) {
167         case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
168                 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
169                         lws_get_protocol(wsi), sizeof(struct per_vhost_data__gs_mb));
170                 if (!vhd)
171                         return 1;
172                 vhd->vh = lws_get_vhost(wsi);
173                 vhd->gsp = lws_vhost_name_to_protocol(vhd->vh,
174                                                 "protocol-generic-sessions");
175                 if (!vhd->gsp) {
176                         lwsl_err("messageboard: requires generic-sessions\n");
177                         return 1;
178                 }
179
180                 pvo = (const struct lws_protocol_vhost_options *)in;
181                 while (pvo) {
182                         if (!strcmp(pvo->name, "message-db"))
183                                 strncpy(vhd->message_db, pvo->value,
184                                         sizeof(vhd->message_db) - 1);
185                         pvo = pvo->next;
186                 }
187                 if (!vhd->message_db[0]) {
188                         lwsl_err("messageboard: \"message-db\" pvo missing\n");
189                         return 1;
190                 }
191
192                 if (sqlite3_open_v2(vhd->message_db, &vhd->pdb,
193                                     SQLITE_OPEN_READWRITE |
194                                     SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
195                         lwsl_err("Unable to open message db %s: %s\n",
196                                  vhd->message_db, sqlite3_errmsg(vhd->pdb));
197
198                         return 1;
199                 }
200                 if (sqlite3_exec(vhd->pdb, "create table if not exists msg ("
201                                  " idx integer primary key, time integer,"
202                                  " username varchar(32), email varchar(100),"
203                                  " ip varchar(80), content blob);",
204                                  NULL, NULL, NULL) != SQLITE_OK) {
205                         lwsl_err("Unable to create msg table: %s\n",
206                                  sqlite3_errmsg(vhd->pdb));
207
208                         return 1;
209                 }
210
211                 vhd->last_idx = get_last_idx(vhd);
212                 break;
213
214         case LWS_CALLBACK_PROTOCOL_DESTROY:
215                 if (vhd->pdb)
216                         sqlite3_close(vhd->pdb);
217                 goto passthru;
218
219         case LWS_CALLBACK_ESTABLISHED:
220                 vhd->gsp->callback(wsi, LWS_CALLBACK_SESSION_INFO,
221                                    pss->pss_gs, &pss->sinfo, 0);
222                 if (!pss->sinfo.username[0]) {
223                         lwsl_notice("messageboard ws attempt with no session\n");
224
225                         return -1;
226                 }
227
228                 lws_callback_on_writable(wsi);
229                 break;
230
231         case LWS_CALLBACK_SERVER_WRITEABLE:
232                 {
233                         struct message m;
234                         char j[MAX_MSG_LEN + 512], e[MAX_MSG_LEN + 512],
235                                 *p = j + LWS_PRE, *start = p,
236                                 *end = j + sizeof(j) - LWS_PRE;
237
238                         if (pss->last_idx == vhd->last_idx)
239                                 break;
240
241                         /* restrict to last 10 */
242                         if (!pss->last_idx)
243                                 if (vhd->last_idx >= 10)
244                                         pss->last_idx = vhd->last_idx - 10;
245
246                         sprintf(s, "select idx, time, username, email, ip, content "
247                                    "from msg where idx > %lu order by idx limit 1;",
248                                    pss->last_idx);
249                         if (sqlite3_exec(vhd->pdb, s, lookup_cb, &m, NULL) != SQLITE_OK) {
250                                 lwsl_err("Unable to lookup msg: %s\n",
251                                          sqlite3_errmsg(vhd->pdb));
252                                 return 0;
253                         }
254
255                         /* format in JSON */
256                         p += lws_snprintf(p, end - p,
257                                         "{\"idx\":\"%lu\",\"time\":\"%lu\",",
258                                         m.idx, m.time);
259                         p += lws_snprintf(p, end - p, " \"username\":\"%s\",",
260                                 lws_json_purify(e, m.username, sizeof(e)));
261                         p += lws_snprintf(p, end - p, " \"email\":\"%s\",",
262                                 lws_json_purify(e, m.email, sizeof(e)));
263                         p += lws_snprintf(p, end - p, " \"ip\":\"%s\",",
264                                 lws_json_purify(e, m.ip, sizeof(e)));
265                         p += lws_snprintf(p, end - p, " \"content\":\"%s\"}",
266                                 lws_json_purify(e, m.content, sizeof(e)));
267
268                         if (lws_write(wsi, (unsigned char *)start, p - start,
269                                       LWS_WRITE_TEXT) < 0)
270                                 return -1;
271
272                         pss->last_idx = m.idx;
273                         if (pss->last_idx == vhd->last_idx)
274                                 break;
275
276                         lws_callback_on_writable(wsi); /* more to do */
277                 }
278                 break;
279
280         case LWS_CALLBACK_HTTP:
281                 pss->our_form = 0;
282
283                 /* ie, it's our messageboard new message form */
284                 if (!strcmp((const char *)in, "/msg")) {
285                         pss->our_form = 1;
286                         break;
287                 }
288
289                 goto passthru;
290
291         case LWS_CALLBACK_HTTP_BODY:
292                 if (!pss->our_form)
293                         goto passthru;
294
295                 if (len < 2)
296                         break;
297                 if (!pss->spa) {
298                         pss->spa = lws_spa_create(wsi, param_names,
299                                                 ARRAY_SIZE(param_names),
300                                                 MAX_MSG_LEN + 1024, NULL, NULL);
301                         if (!pss->spa)
302                                 return -1;
303                 }
304
305                 if (lws_spa_process(pss->spa, in, len)) {
306                         lwsl_notice("spa process blew\n");
307                         return -1;
308                 }
309                 break;
310
311         case LWS_CALLBACK_HTTP_BODY_COMPLETION:
312                 if (!pss->our_form)
313                         goto passthru;
314
315                 if (post_message(wsi, vhd, pss))
316                         return -1;
317
318                 p = buffer + LWS_PRE;
319                 start = p;
320                 end = p + sizeof(buffer) - LWS_PRE;
321
322                 if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end))
323                         return -1;
324                 if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
325                                 (unsigned char *)"text/plain", 10, &p, end))
326                         return -1;
327                 if (lws_add_http_header_content_length(wsi, 1, &p, end))
328                         return -1;
329                 if (lws_finalize_http_header(wsi, &p, end))
330                         return -1;
331                 n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
332                 if (n != (p - start)) {
333                         lwsl_err("_write returned %d from %ld\n", n, (long)(p - start));
334                         return -1;
335                 }
336                 s[0] = '0';
337                 n = lws_write(wsi, (unsigned char *)s, 1, LWS_WRITE_HTTP);
338                 if (n != 1)
339                         return -1;
340
341                 goto try_to_reuse;
342
343         case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
344                 if (!pss || pss->pss_gs)
345                         break;
346
347                 pss->pss_gs = malloc(vhd->gsp->per_session_data_size);
348                 if (!pss->pss_gs)
349                         return -1;
350
351                 memset(pss->pss_gs, 0, vhd->gsp->per_session_data_size);
352                 break;
353
354         case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
355                 if (vhd->gsp->callback(wsi, reason, pss ? pss->pss_gs : NULL, in, len))
356                         return -1;
357
358                 if (pss && pss->spa) {
359                         lws_spa_destroy(pss->spa);
360                         pss->spa = NULL;
361                 }
362                 if (pss && pss->pss_gs) {
363                         free(pss->pss_gs);
364                         pss->pss_gs = NULL;
365                 }
366                 break;
367
368         default:
369 passthru:
370                 return vhd->gsp->callback(wsi, reason, pss ? pss->pss_gs : NULL, in, len);
371         }
372
373         return 0;
374
375
376 try_to_reuse:
377         if (lws_http_transaction_completed(wsi))
378                 return -1;
379
380         return 0;
381 }
382
383 static const struct lws_protocols protocols[] = {
384         {
385                 "protocol-lws-messageboard",
386                 callback_messageboard,
387                 sizeof(struct per_session_data__gs_mb),
388                 4096,
389         },
390 };
391
392 LWS_EXTERN LWS_VISIBLE int
393 init_protocol_lws_messageboard(struct lws_context *context,
394                                struct lws_plugin_capability *c)
395 {
396         if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
397                 lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
398                          c->api_magic);
399                 return 1;
400         }
401
402         c->protocols = protocols;
403         c->count_protocols = ARRAY_SIZE(protocols);
404         c->extensions = NULL;
405         c->count_extensions = 0;
406
407         return 0;
408 }
409
410 LWS_EXTERN LWS_VISIBLE int
411 destroy_protocol_lws_messageboard(struct lws_context *context)
412 {
413         return 0;
414 }