coverity 182069: coverity confused by use of bool as array index
[platform/upstream/libwebsockets.git] / plugins / generic-sessions / protocol_generic_sessions.c
1 /*
2  * ws protocol handler plugin for "generic sessions"
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 #include "private-lwsgs.h"
23
24 /* keep changes in sync with the enum in lwsgs.h */
25 static const char * const param_names[] = {
26         "username",
27         "password",
28         "password2",
29         "email",
30         "register",
31         "good",
32         "bad",
33         "reg-good",
34         "reg-bad",
35         "admin",
36         "forgot",
37         "forgot-good",
38         "forgot-bad",
39         "forgot-post-good",
40         "forgot-post-bad",
41         "change",
42         "curpw",
43         "delete"
44 };
45
46 struct lwsgs_fill_args {
47         char *buf;
48         int len;
49 };
50
51 static const struct lws_protocols protocols[];
52
53 static int
54 lwsgs_lookup_callback_email(void *priv, int cols, char **col_val,
55                             char **col_name)
56 {
57         struct lwsgs_fill_args *a = (struct lwsgs_fill_args *)priv;
58         int n;
59
60         for (n = 0; n < cols; n++) {
61                 if (!strcmp(col_name[n], "content")) {
62                         strncpy(a->buf, col_val[n], a->len - 1);
63                         a->buf[a->len - 1] = '\0';
64                         continue;
65                 }
66         }
67         return 0;
68 }
69
70 static int
71 lwsgs_email_cb_get_body(struct lws_email *email, char *buf, int len)
72 {
73         struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data;
74         struct lwsgs_fill_args a;
75         char ss[150], esc[50];
76
77         a.buf = buf;
78         a.len = len;
79
80         lws_snprintf(ss, sizeof(ss) - 1,
81                  "select content from email where username='%s';",
82                  lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
83
84         strncpy(buf, "failed", len);
85         if (sqlite3_exec(vhd->pdb, ss, lwsgs_lookup_callback_email, &a,
86                          NULL) != SQLITE_OK) {
87                 lwsl_err("Unable to lookup email: %s\n",
88                          sqlite3_errmsg(vhd->pdb));
89
90                 return 1;
91         }
92
93         return 0;
94 }
95
96 static int
97 lwsgs_email_cb_sent(struct lws_email *email)
98 {
99         struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data;
100         char s[200], esc[50];
101
102         /* mark the user as having sent the verification email */
103         lws_snprintf(s, sizeof(s) - 1,
104                  "update users set verified=1 where username='%s' and verified==0;",
105                  lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
106         if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
107                 lwsl_err("%s: Unable to update user: %s\n", __func__,
108                          sqlite3_errmsg(vhd->pdb));
109                 return 1;
110         }
111
112         lws_snprintf(s, sizeof(s) - 1,
113                  "delete from email where username='%s';",
114                  lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
115         if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
116                 lwsl_err("%s: Unable to delete email text: %s\n", __func__,
117                          sqlite3_errmsg(vhd->pdb));
118                 return 1;
119         }
120
121         return 0;
122 }
123
124 static int
125 lwsgs_email_cb_on_next(struct lws_email *email)
126 {
127         struct per_vhost_data__gs *vhd = lws_container_of(email,
128                         struct per_vhost_data__gs, email);
129         char s[LWSGS_EMAIL_CONTENT_SIZE], esc[50];
130         time_t now = lws_now_secs();
131
132         /*
133          * users not verified in 24h get deleted
134          */
135         lws_snprintf(s, sizeof(s) - 1, "delete from users where ((verified != %d)"
136                  " and (creation_time <= %lu));", LWSGS_VERIFIED_ACCEPTED,
137                  (unsigned long)now - vhd->timeout_email_secs);
138         if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
139                 lwsl_err("Unable to expire users: %s\n",
140                          sqlite3_errmsg(vhd->pdb));
141                 return 1;
142         }
143
144         lws_snprintf(s, sizeof(s) - 1, "update users set token_time=0 where "
145                  "(token_time <= %lu);",
146                  (unsigned long)now - vhd->timeout_email_secs);
147         if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
148                 lwsl_err("Unable to expire users: %s\n",
149                          sqlite3_errmsg(vhd->pdb));
150                 return 1;
151         }
152
153         vhd->u.username[0] = '\0';
154         lws_snprintf(s, sizeof(s) - 1, "select username from email limit 1;");
155         if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &vhd->u,
156                          NULL) != SQLITE_OK) {
157                 lwsl_err("Unable to lookup user: %s\n", sqlite3_errmsg(vhd->pdb));
158                 return 1;
159         }
160
161         lws_snprintf(s, sizeof(s) - 1,
162                  "select username, creation_time, email, ip, verified, token"
163                  " from users where username='%s' limit 1;",
164                  lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
165         if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &vhd->u,
166                          NULL) != SQLITE_OK) {
167                 lwsl_err("Unable to lookup user: %s\n",
168                          sqlite3_errmsg(vhd->pdb));
169                 return 1;
170         }
171
172         if (!vhd->u.username[0])
173                 /*
174                  * nothing to do, we are idle and no suitable
175                  * accounts waiting for verification.  When a new user
176                  * is added we will get kicked to try again.
177                  */
178                 return 1;
179
180         strncpy(email->email_to, vhd->u.email, sizeof(email->email_to) - 1);
181
182         return 0;
183 }
184
185
186 struct lwsgs_subst_args
187 {
188         struct per_session_data__gs *pss;
189         struct per_vhost_data__gs *vhd;
190         struct lws *wsi;
191 };
192
193 static const char *
194 lwsgs_subst(void *data, int index)
195 {
196         struct lwsgs_subst_args *a = (struct lwsgs_subst_args *)data;
197         struct lwsgs_user u;
198         lwsgw_hash sid;
199         char esc[50], s[100];
200         int n;
201
202         a->pss->result[0] = '\0';
203         u.email[0] = '\0';
204         if (!lwsgs_get_sid_from_wsi(a->wsi, &sid)) {
205                 if (lwsgs_lookup_session(a->vhd, &sid, a->pss->result, 31)) {
206                         lwsl_notice("sid lookup for %s failed\n", sid.id);
207                         a->pss->delete_session = sid;
208                         return NULL;
209                 }
210                 lws_snprintf(s, sizeof(s) - 1, "select username,email "
211                          "from users where username = '%s';",
212                          lws_sql_purify(esc, a->pss->result, sizeof(esc) - 1));
213                 if (sqlite3_exec(a->vhd->pdb, s, lwsgs_lookup_callback_user,
214                                  &u, NULL) != SQLITE_OK) {
215                         lwsl_err("Unable to lookup token: %s\n",
216                                  sqlite3_errmsg(a->vhd->pdb));
217                         a->pss->delete_session = sid;
218                         return NULL;
219                 }
220         } else
221                 lwsl_notice("no sid\n");
222
223         strncpy(a->pss->result + 32, u.email, 100);
224
225         switch (index) {
226         case 0:
227                 return a->pss->result;
228
229         case 1:
230                 n = lwsgs_get_auth_level(a->vhd, a->pss->result);
231                 sprintf(a->pss->result, "%d", n);
232                 return a->pss->result;
233         case 2:
234                 return a->pss->result + 32;
235         }
236
237         return NULL;
238 }
239
240 static int
241 callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
242                           void *user, void *in, size_t len)
243 {
244         struct per_session_data__gs *pss = (struct per_session_data__gs *)user;
245         const struct lws_protocol_vhost_options *pvo;
246         struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)
247                         lws_protocol_vh_priv_get(lws_get_vhost(wsi),
248                                         &protocols[0]);
249         char cookie[1024], username[32], *pc = cookie;
250         unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
251         struct lws_process_html_args *args;
252         struct lws_session_info *sinfo;
253         char s[LWSGS_EMAIL_CONTENT_SIZE];
254         unsigned char *p, *start, *end;
255         sqlite3_stmt *sm;
256         lwsgw_hash sid;
257         const char *cp;
258         int n;
259
260         switch (reason) {
261         case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
262
263                 vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
264                         &protocols[0], sizeof(struct per_vhost_data__gs));
265                 if (!vhd)
266                         return 1;
267                 vhd->context = lws_get_context(wsi);
268
269                 /* defaults */
270                 vhd->timeout_idle_secs = 600;
271                 vhd->timeout_absolute_secs = 36000;
272                 vhd->timeout_anon_absolute_secs = 1200;
273                 vhd->timeout_email_secs = 24 * 3600;
274                 strcpy(vhd->email.email_helo, "unconfigured.com");
275                 strcpy(vhd->email.email_from, "noreply@unconfigured.com");
276                 strcpy(vhd->email_title, "Registration Email from unconfigured");
277                 strcpy(vhd->email.email_smtp_ip, "127.0.0.1");
278
279                 vhd->email.on_next = lwsgs_email_cb_on_next;
280                 vhd->email.on_get_body = lwsgs_email_cb_get_body;
281                 vhd->email.on_sent = lwsgs_email_cb_sent;
282                 vhd->email.data = (void *)vhd;
283
284                 pvo = (const struct lws_protocol_vhost_options *)in;
285                 while (pvo) {
286                         if (!strcmp(pvo->name, "admin-user"))
287                                 strncpy(vhd->admin_user, pvo->value,
288                                         sizeof(vhd->admin_user) - 1);
289                         if (!strcmp(pvo->name, "admin-password-sha1"))
290                                 strncpy(vhd->admin_password_sha1.id, pvo->value,
291                                         sizeof(vhd->admin_password_sha1.id) - 1);
292                         if (!strcmp(pvo->name, "session-db"))
293                                 strncpy(vhd->session_db, pvo->value,
294                                         sizeof(vhd->session_db) - 1);
295                         if (!strcmp(pvo->name, "confounder"))
296                                 strncpy(vhd->confounder, pvo->value,
297                                         sizeof(vhd->confounder) - 1);
298                         if (!strcmp(pvo->name, "email-from"))
299                                 strncpy(vhd->email.email_from, pvo->value,
300                                         sizeof(vhd->email.email_from) - 1);
301                         if (!strcmp(pvo->name, "email-helo"))
302                                 strncpy(vhd->email.email_helo, pvo->value,
303                                         sizeof(vhd->email.email_helo) - 1);
304                         if (!strcmp(pvo->name, "email-template"))
305                                 strncpy(vhd->email_template, pvo->value,
306                                         sizeof(vhd->email_template) - 1);
307                         if (!strcmp(pvo->name, "email-title"))
308                                 strncpy(vhd->email_title, pvo->value,
309                                         sizeof(vhd->email_title) - 1);
310                         if (!strcmp(pvo->name, "email-contact-person"))
311                                 strncpy(vhd->email_contact_person, pvo->value,
312                                         sizeof(vhd->email_contact_person) - 1);
313                         if (!strcmp(pvo->name, "email-confirm-url-base"))
314                                 strncpy(vhd->email_confirm_url, pvo->value,
315                                         sizeof(vhd->email_confirm_url) - 1);
316                         if (!strcmp(pvo->name, "email-server-ip"))
317                                 strncpy(vhd->email.email_smtp_ip, pvo->value,
318                                         sizeof(vhd->email.email_smtp_ip) - 1);
319
320                         if (!strcmp(pvo->name, "timeout-idle-secs"))
321                                 vhd->timeout_idle_secs = atoi(pvo->value);
322                         if (!strcmp(pvo->name, "timeout-absolute-secs"))
323                                 vhd->timeout_absolute_secs = atoi(pvo->value);
324                         if (!strcmp(pvo->name, "timeout-anon-absolute-secs"))
325                                 vhd->timeout_anon_absolute_secs = atoi(pvo->value);
326                         if (!strcmp(pvo->name, "email-expire"))
327                                 vhd->timeout_email_secs = atoi(pvo->value);
328                         pvo = pvo->next;
329                 }
330                 if (!vhd->admin_user[0] ||
331                     !vhd->admin_password_sha1.id[0] ||
332                     !vhd->session_db[0]) {
333                         lwsl_err("generic-sessions: "
334                                  "You must give \"admin-user\", "
335                                  "\"admin-password-sha1\", "
336                                  "and \"session_db\" per-vhost options\n");
337                         return 1;
338                 }
339
340                 if (sqlite3_open_v2(vhd->session_db, &vhd->pdb,
341                                     SQLITE_OPEN_READWRITE |
342                                     SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
343                         lwsl_err("Unable to open session db %s: %s\n",
344                                  vhd->session_db, sqlite3_errmsg(vhd->pdb));
345
346                         return 1;
347                 }
348
349                 if (sqlite3_prepare(vhd->pdb,
350                                     "create table if not exists sessions ("
351                                     " name char(40),"
352                                     " username varchar(32),"
353                                     " expire integer"
354                                     ");",
355                                     -1, &sm, NULL) != SQLITE_OK) {
356                         lwsl_err("Unable to prepare session table init: %s\n",
357                                  sqlite3_errmsg(vhd->pdb));
358
359                         return 1;
360                 }
361
362                 if (sqlite3_step(sm) != SQLITE_DONE) {
363                         lwsl_err("Unable to run session table init: %s\n",
364                                  sqlite3_errmsg(vhd->pdb));
365
366                         return 1;
367                 }
368                 sqlite3_finalize(sm);
369
370                 if (sqlite3_exec(vhd->pdb,
371                                  "create table if not exists users ("
372                                  " username varchar(32),"
373                                  " creation_time integer,"
374                                  " ip varchar(46),"
375                                  " email varchar(100),"
376                                  " pwhash varchar(42),"
377                                  " pwsalt varchar(42),"
378                                  " pwchange_time integer,"
379                                  " token varchar(42),"
380                                  " verified integer,"
381                                  " token_time integer,"
382                                  " last_forgot_validated integer,"
383                                  " primary key (username)"
384                                  ");",
385                                  NULL, NULL, NULL) != SQLITE_OK) {
386                         lwsl_err("Unable to create user table: %s\n",
387                                  sqlite3_errmsg(vhd->pdb));
388
389                         return 1;
390                 }
391
392                 sprintf(s, "create table if not exists email ("
393                                  " username varchar(32),"
394                                  " content blob,"
395                                  " primary key (username)"
396                                  ");");
397                 if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
398                         lwsl_err("Unable to create user table: %s\n",
399                                  sqlite3_errmsg(vhd->pdb));
400
401                         return 1;
402                 }
403
404                 lws_email_init(&vhd->email, lws_uv_getloop(vhd->context, 0),
405                                 LWSGS_EMAIL_CONTENT_SIZE);
406
407                 vhd->email_inited = 1;
408                 break;
409
410         case LWS_CALLBACK_PROTOCOL_DESTROY:
411         //      lwsl_notice("gs: LWS_CALLBACK_PROTOCOL_DESTROY: v=%p, ctx=%p\n", vhd, vhd->context);
412                 if (vhd->pdb) {
413                         sqlite3_close(vhd->pdb);
414                         vhd->pdb = NULL;
415                 }
416                 if (vhd->email_inited) {
417                         lws_email_destroy(&vhd->email);
418                         vhd->email_inited = 0;
419                 }
420                 break;
421
422         case LWS_CALLBACK_HTTP:
423                 lwsl_info("LWS_CALLBACK_HTTP: %s\n", (const char *)in);
424
425                 pss->login_session.id[0] = '\0';
426                 pss->phs.pos = 0;
427                 strncpy(pss->onward, (char *)in, sizeof(pss->onward) - 1);
428                 pss->onward[sizeof(pss->onward) - 1] = '\0';
429
430                 if (!strcmp((const char *)in, "/lwsgs-forgot")) {
431                         lwsgs_handler_forgot(vhd, wsi, pss);
432                         goto redirect_with_cookie;
433                 }
434
435                 if (!strcmp((const char *)in, "/lwsgs-confirm")) {
436                         lwsgs_handler_confirm(vhd, wsi, pss);
437                         goto redirect_with_cookie;
438                 }
439                 if (!strcmp((const char *)in, "/lwsgs-check")) {
440                         lwsgs_handler_check(vhd, wsi, pss);
441                         goto try_to_reuse;
442                 }
443
444                 if (!strcmp((const char *)in, "/lwsgs-login"))
445                         break;
446                 if (!strcmp((const char *)in, "/lwsgs-logout"))
447                         break;
448                 if (!strcmp((const char *)in, "/lwsgs-forgot"))
449                         break;
450                 if (!strcmp((const char *)in, "/lwsgs-change"))
451                         break;
452
453                 /* if no legitimate url for GET, return 404 */
454
455                 lwsl_err("http doing 404 on %s\n", (const char *)in);
456                 lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
457                 goto try_to_reuse;
458
459         case LWS_CALLBACK_CHECK_ACCESS_RIGHTS:
460                 n = 0;
461                 username[0] = '\0';
462                 sid.id[0] = '\0';
463                 args = (struct lws_process_html_args *)in;
464                 lwsl_debug("LWS_CALLBACK_CHECK_ACCESS_RIGHTS\n");
465                 if (!lwsgs_get_sid_from_wsi(wsi, &sid)) {
466                         if (lwsgs_lookup_session(vhd, &sid, username, sizeof(username))) {
467                                 static const char * const oprot[] = {
468                                         "http://", "https://"
469                                 };
470                                 lwsl_notice("session lookup for %s failed, probably expired\n", sid.id);
471                                 pss->delete_session = sid;
472                                 args->final = 1; /* signal we dealt with it */
473                                 if (lws_hdr_copy(wsi, cookie, sizeof(cookie) - 1,
474                                              WSI_TOKEN_HOST) < 0)
475                                         return 1;
476                                 lws_snprintf(pss->onward, sizeof(pss->onward) - 1,
477                                          "%s%s%s", oprot[!!lws_is_ssl(wsi)],
478                                             cookie, args->p);
479                                 lwsl_notice("redirecting to ourselves with cookie refresh\n");
480                                 /* we need a redirect to ourselves, session cookie is expired */
481                                 goto redirect_with_cookie;
482                         }
483                 } else
484                         lwsl_notice("failed to get sid from wsi\n");
485
486                 n = lwsgs_get_auth_level(vhd, username);
487
488                 if ((args->max_len & n) != args->max_len) {
489                         lwsl_notice("Access rights fail 0x%X vs 0x%X (cookie %s)\n",
490                                         args->max_len, n, sid.id);
491                         return 1;
492                 }
493                 lwsl_debug("Access rights OK\n");
494                 break;
495
496         case LWS_CALLBACK_SESSION_INFO:
497         {
498                 struct lwsgs_user u;
499                 sinfo = (struct lws_session_info *)in;
500                 sinfo->username[0] = '\0';
501                 sinfo->email[0] = '\0';
502                 sinfo->ip[0] = '\0';
503                 sinfo->session[0] = '\0';
504                 sinfo->mask = 0;
505
506                 sid.id[0] = '\0';
507                 lwsl_debug("LWS_CALLBACK_SESSION_INFO\n");
508                 if (lwsgs_get_sid_from_wsi(wsi, &sid))
509                         break;
510                 if (lwsgs_lookup_session(vhd, &sid, username, sizeof(username)))
511                         break;
512
513                 lws_snprintf(s, sizeof(s) - 1,
514                          "select username, email from users where username='%s';",
515                          username);
516                 if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
517                     SQLITE_OK) {
518                         lwsl_err("Unable to lookup token: %s\n",
519                                  sqlite3_errmsg(vhd->pdb));
520                         break;
521                 }
522                 strncpy(sinfo->username, u.username, sizeof(sinfo->username) - 1);
523                 sinfo->username[sizeof(sinfo->username) - 1] = '\0';
524                 strncpy(sinfo->email, u.email, sizeof(sinfo->email) - 1);
525                 strncpy(sinfo->session, sid.id, sizeof(sinfo->session) - 1);
526                 sinfo->mask = lwsgs_get_auth_level(vhd, username);
527                 lws_get_peer_simple(wsi, sinfo->ip, sizeof(sinfo->ip));
528         }
529
530                 break;
531
532         case LWS_CALLBACK_PROCESS_HTML:
533
534                 args = (struct lws_process_html_args *)in;
535                 {
536                         static const char * const vars[] = {
537                                 "$lwsgs_user",
538                                 "$lwsgs_auth",
539                                 "$lwsgs_email"
540                         };
541                         struct lwsgs_subst_args a;
542
543                         a.vhd = vhd;
544                         a.pss = pss;
545                         a.wsi = wsi;
546
547                         pss->phs.vars = vars;
548                         pss->phs.count_vars = ARRAY_SIZE(vars);
549                         pss->phs.replace = lwsgs_subst;
550                         pss->phs.data = &a;
551
552                         if (lws_chunked_html_process(args, &pss->phs))
553                                 return -1;
554                 }
555                 break;
556
557         case LWS_CALLBACK_HTTP_BODY:
558                 if (len < 2)
559                         break;
560
561                 if (!pss->spa) {
562                         pss->spa = lws_spa_create(wsi, param_names,
563                                                 ARRAY_SIZE(param_names), 1024,
564                                                 NULL, NULL);
565                         if (!pss->spa)
566                                 return -1;
567                 }
568
569                 if (lws_spa_process(pss->spa, in, len)) {
570                         lwsl_notice("spa process blew\n");
571                         return -1;
572                 }
573                 break;
574
575         case LWS_CALLBACK_HTTP_BODY_COMPLETION:
576
577                 if (!pss->spa)
578                         break;
579
580                 lwsl_info("LWS_CALLBACK_HTTP_BODY_COMPLETION: %s\n", pss->onward);
581                 lws_spa_finalize(pss->spa);
582
583                 if (!strcmp((char *)pss->onward, "/lwsgs-change")) {
584                         if (!lwsgs_handler_change_password(vhd, wsi, pss)) {
585                                 cp = lws_spa_get_string(pss->spa, FGS_GOOD);
586                                 goto pass;
587                         }
588
589                         cp = lws_spa_get_string(pss->spa, FGS_BAD);
590                         lwsl_notice("user/password no good %s\n",
591                                 lws_spa_get_string(pss->spa, FGS_USERNAME));
592                         strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
593                         pss->onward[sizeof(pss->onward) - 1] = '\0';
594                         goto completion_flow;
595                 }
596
597                 if (!strcmp((char *)pss->onward, "/lwsgs-login")) {
598                         if (lws_spa_get_string(pss->spa, FGS_FORGOT) &&
599                             lws_spa_get_string(pss->spa, FGS_FORGOT)[0]) {
600                                 if (lwsgs_handler_forgot_pw_form(vhd, wsi, pss)) {
601                                         n = FGS_FORGOT_BAD;
602                                         goto reg_done;
603                                 }
604                                 /* get the email monitor to take a look */
605                                 lws_email_check(&vhd->email);
606                                 n = FGS_FORGOT_GOOD;
607                                 goto reg_done;
608                         }
609
610                         if (!lws_spa_get_string(pss->spa, FGS_USERNAME) ||
611                             !lws_spa_get_string(pss->spa, FGS_PASSWORD)) {
612                                 lwsl_notice("username '%s' or pw '%s' missing\n",
613                                                 lws_spa_get_string(pss->spa, FGS_USERNAME),
614                                                 lws_spa_get_string(pss->spa, FGS_PASSWORD));
615                                 return -1;
616                         }
617
618                         if (lws_spa_get_string(pss->spa, FGS_REGISTER) &&
619                             lws_spa_get_string(pss->spa, FGS_REGISTER)[0]) {
620
621                                 if (lwsgs_handler_register_form(vhd, wsi, pss))
622                                         n = FGS_REG_BAD;
623                                 else {
624                                         n = FGS_REG_GOOD;
625
626                                         /* get the email monitor to take a look */
627                                         lws_email_check(&vhd->email);
628                                 }
629 reg_done:
630                                 strncpy(pss->onward, lws_spa_get_string(pss->spa, n),
631                                         sizeof(pss->onward) - 1);
632                                 pss->onward[sizeof(pss->onward) - 1] = '\0';
633                                 pss->login_expires = 0;
634                                 pss->logging_out = 1;
635                                 goto completion_flow;
636                         }
637
638                         /* we have the username and password... check if admin */
639                         if (lwsgw_check_admin(vhd, lws_spa_get_string(pss->spa, FGS_USERNAME),
640                                               lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
641                                 if (lws_spa_get_string(pss->spa, FGS_ADMIN))
642                                         cp = lws_spa_get_string(pss->spa, FGS_ADMIN);
643                                 else
644                                         if (lws_spa_get_string(pss->spa, FGS_GOOD))
645                                                 cp = lws_spa_get_string(pss->spa, FGS_GOOD);
646                                         else {
647                                                 lwsl_info("No admin or good target url in form\n");
648                                                 return -1;
649                                         }
650                                 lwsl_debug("admin\n");
651                                 goto pass;
652                         }
653
654                         /* check users in database */
655
656                         if (!lwsgs_check_credentials(vhd, lws_spa_get_string(pss->spa, FGS_USERNAME),
657                                                      lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
658                                 lwsl_info("pw hash check met\n");
659                                 cp = lws_spa_get_string(pss->spa, FGS_GOOD);
660                                 goto pass;
661                         } else
662                                 lwsl_notice("user/password no good %s\n",
663                                                 lws_spa_get_string(pss->spa, FGS_USERNAME));
664
665                         if (!lws_spa_get_string(pss->spa, FGS_BAD)) {
666                                 lwsl_info("No admin or good target url in form\n");
667                                 return -1;
668                         }
669
670                         strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_BAD),
671                                 sizeof(pss->onward) - 1);
672                         pss->onward[sizeof(pss->onward) - 1] = '\0';
673                         lwsl_debug("failed\n");
674
675                         goto completion_flow;
676                 }
677
678                 if (!strcmp((char *)pss->onward, "/lwsgs-logout")) {
679
680                         lwsl_notice("/logout\n");
681
682                         if (lwsgs_get_sid_from_wsi(wsi, &pss->login_session)) {
683                                 lwsl_notice("not logged in...\n");
684                                 return 1;
685                         }
686
687                         lwsgw_update_session(vhd, &pss->login_session, "");
688
689                         if (!lws_spa_get_string(pss->spa, FGS_GOOD)) {
690                                 lwsl_info("No admin or good target url in form\n");
691                                 return -1;
692                         }
693
694                         strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_GOOD), sizeof(pss->onward) - 1);
695                         pss->onward[sizeof(pss->onward) - 1] = '\0';
696
697                         pss->login_expires = 0;
698                         pss->logging_out = 1;
699
700                         goto completion_flow;
701                 }
702
703                 break;
704
705 pass:
706                 strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
707                 pss->onward[sizeof(pss->onward) - 1] = '\0';
708
709                 if (lwsgs_get_sid_from_wsi(wsi, &sid))
710                         sid.id[0] = '\0';
711
712                 pss->login_expires = lws_now_secs() +
713                                      vhd->timeout_absolute_secs;
714
715                 if (!sid.id[0]) {
716                         /* we need to create a new, authorized session */
717
718                         if (lwsgs_new_session_id(vhd, &pss->login_session,
719                                                  lws_spa_get_string(pss->spa, FGS_USERNAME),
720                                                  pss->login_expires))
721                                 goto try_to_reuse;
722
723                         lwsl_info("Creating new session: %s\n",
724                                     pss->login_session.id);
725                 } else {
726                         /*
727                          * we can just update the existing session to be
728                          * authorized
729                          */
730                         lwsl_info("Authorizing existing session %s", sid.id);
731                         lwsgw_update_session(vhd, &sid,
732                                 lws_spa_get_string(pss->spa, FGS_USERNAME));
733                         pss->login_session = sid;
734                 }
735
736 completion_flow:
737                 lwsgw_expire_old_sessions(vhd);
738                 goto redirect_with_cookie;
739
740         case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
741                 if (pss && pss->spa) {
742                         lws_spa_destroy(pss->spa);
743                         pss->spa = NULL;
744                 }
745                 break;
746
747         case LWS_CALLBACK_ADD_HEADERS:
748                 lwsgw_expire_old_sessions(vhd);
749
750                 args = (struct lws_process_html_args *)in;
751
752                 if (pss->delete_session.id[0]) {
753                         pc = cookie;
754                         lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
755                                                   cookie + sizeof(cookie) - 1);
756
757                         lwsl_info("deleting cookie '%s'\n", cookie);
758
759                         if (lws_add_http_header_by_name(wsi,
760                                         (unsigned char *)"set-cookie:",
761                                         (unsigned char *)cookie, pc - cookie,
762                                         (unsigned char **)&args->p,
763                                         (unsigned char *)args->p + args->max_len))
764                                 return 1;
765                 }
766
767                 if (!pss->login_session.id[0])
768                         lwsgs_get_sid_from_wsi(wsi, &pss->login_session);
769
770                 if (!pss->login_session.id[0] && !pss->logging_out) {
771
772                         pss->login_expires = lws_now_secs() +
773                                              vhd->timeout_anon_absolute_secs;
774                         if (lwsgs_new_session_id(vhd, &pss->login_session, "",
775                                                  pss->login_expires))
776                                 goto try_to_reuse;
777                         pc = cookie;
778                         lwsgw_cookie_from_session(&pss->login_session,
779                                                   pss->login_expires, &pc,
780                                                   cookie + sizeof(cookie) - 1);
781
782                         lwsl_info("LWS_CALLBACK_ADD_HEADERS: setting cookie '%s'\n", cookie);
783                         if (lws_add_http_header_by_name(wsi,
784                                         (unsigned char *)"set-cookie:",
785                                         (unsigned char *)cookie, pc - cookie,
786                                         (unsigned char **)&args->p,
787                                         (unsigned char *)args->p + args->max_len))
788                                 return 1;
789                 }
790                 break;
791
792         default:
793                 break;
794         }
795
796         return 0;
797
798 redirect_with_cookie:
799         p = buffer + LWS_PRE;
800         start = p;
801         end = p + sizeof(buffer) - LWS_PRE;
802
803         if (lws_add_http_header_status(wsi, HTTP_STATUS_SEE_OTHER, &p, end))
804                 return 1;
805
806         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
807                         (unsigned char *)pss->onward,
808                         strlen(pss->onward), &p, end))
809                 return 1;
810
811         if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
812                         (unsigned char *)"text/html", 9, &p, end))
813                 return 1;
814         if (lws_add_http_header_content_length(wsi, 0, &p, end))
815                 return 1;
816
817         if (pss->delete_session.id[0]) {
818                 lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
819                                           cookie + sizeof(cookie) - 1);
820
821                 lwsl_notice("deleting cookie '%s'\n", cookie);
822
823                 if (lws_add_http_header_by_name(wsi,
824                                 (unsigned char *)"set-cookie:",
825                                 (unsigned char *)cookie, pc - cookie,
826                                 &p, end))
827                         return 1;
828         }
829
830         if (!pss->login_session.id[0]) {
831                 pss->login_expires = lws_now_secs() +
832                                      vhd->timeout_anon_absolute_secs;
833                 if (lwsgs_new_session_id(vhd, &pss->login_session, "",
834                                          pss->login_expires))
835                         return 1;
836         } else
837                 pss->login_expires = lws_now_secs() +
838                                      vhd->timeout_absolute_secs;
839
840         if (pss->login_session.id[0] || pss->logging_out) {
841                 /*
842                  * we succeeded to login, we must issue a login
843                  * cookie with the prepared data
844                  */
845                 pc = cookie;
846
847                 lwsgw_cookie_from_session(&pss->login_session,
848                                           pss->login_expires, &pc,
849                                           cookie + sizeof(cookie) - 1);
850
851                 lwsl_info("setting cookie '%s'\n", cookie);
852
853                 pss->logging_out = 0;
854
855                 if (lws_add_http_header_by_name(wsi,
856                                 (unsigned char *)"set-cookie:",
857                                 (unsigned char *)cookie, pc - cookie,
858                                 &p, end))
859                         return 1;
860         }
861
862         if (lws_finalize_http_header(wsi, &p, end))
863                 return 1;
864
865         n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
866         if (n < 0)
867                 return 1;
868
869         /* fallthru */
870
871 try_to_reuse:
872         if (lws_http_transaction_completed(wsi))
873                 return -1;
874
875         return 0;
876 }
877
878 static const struct lws_protocols protocols[] = {
879         {
880                 "protocol-generic-sessions",
881                 callback_generic_sessions,
882                 sizeof(struct per_session_data__gs),
883                 1024,
884         },
885 };
886
887 LWS_EXTERN LWS_VISIBLE int
888 init_protocol_generic_sessions(struct lws_context *context,
889                         struct lws_plugin_capability *c)
890 {
891         if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
892                 lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
893                          c->api_magic);
894                 return 1;
895         }
896
897         c->protocols = protocols;
898         c->count_protocols = ARRAY_SIZE(protocols);
899         c->extensions = NULL;
900         c->count_extensions = 0;
901
902         return 0;
903 }
904
905 LWS_EXTERN LWS_VISIBLE int
906 destroy_protocol_generic_sessions(struct lws_context *context)
907 {
908         return 0;
909 }