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