
1) This makes lwsws run a parent process with the original permissions. But this process is only able to respond to SIGHUP, it doesn't do anything else. 2) You can send this parent process a SIGHUP now to cause it to - close listening sockets in existing lwsws processes - mark those processes as to exit when the number of active connections on the falls to zero - spawn a fresh child process from scratch, using latest configuration file content, latest plugins, etc. It can now reopen listening sockets if it chooses to, or open different listen ports or whatever. Notes: 1) lws_context_destroy() has been split into two pieces... the reason for the split is the first part closes the per-vhost protocols, but since they may have created libuv objects in the per-vhost protocol storage, these cannot be freed until after the loop has been run. That's the purpose of the second part of the context destruction, lws_context_destroy2(). For compatibility, if you are not using libuv, the first part calls the second part. However if you are using libuv, you must now call the second part from your own main.c after the first part.
909 lines
24 KiB
C
909 lines
24 KiB
C
/*
|
|
* ws protocol handler plugin for "generic sessions"
|
|
*
|
|
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public
|
|
* License as published by the Free Software Foundation:
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "private-lwsgs.h"
|
|
|
|
/* keep changes in sync with the enum in lwsgs.h */
|
|
static const char * const param_names[] = {
|
|
"username",
|
|
"password",
|
|
"password2",
|
|
"email",
|
|
"register",
|
|
"good",
|
|
"bad",
|
|
"reg-good",
|
|
"reg-bad",
|
|
"admin",
|
|
"forgot",
|
|
"forgot-good",
|
|
"forgot-bad",
|
|
"forgot-post-good",
|
|
"forgot-post-bad",
|
|
"change",
|
|
"curpw",
|
|
"delete"
|
|
};
|
|
|
|
struct lwsgs_fill_args {
|
|
char *buf;
|
|
int len;
|
|
};
|
|
|
|
static const struct lws_protocols protocols[];
|
|
|
|
static int
|
|
lwsgs_lookup_callback_email(void *priv, int cols, char **col_val,
|
|
char **col_name)
|
|
{
|
|
struct lwsgs_fill_args *a = (struct lwsgs_fill_args *)priv;
|
|
int n;
|
|
|
|
for (n = 0; n < cols; n++) {
|
|
if (!strcmp(col_name[n], "content")) {
|
|
strncpy(a->buf, col_val[n], a->len - 1);
|
|
a->buf[a->len - 1] = '\0';
|
|
continue;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lwsgs_email_cb_get_body(struct lws_email *email, char *buf, int len)
|
|
{
|
|
struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data;
|
|
struct lwsgs_fill_args a;
|
|
char ss[150], esc[50];
|
|
|
|
a.buf = buf;
|
|
a.len = len;
|
|
|
|
lws_snprintf(ss, sizeof(ss) - 1,
|
|
"select content from email where username='%s';",
|
|
lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
|
|
|
|
strncpy(buf, "failed", len);
|
|
if (sqlite3_exec(vhd->pdb, ss, lwsgs_lookup_callback_email, &a,
|
|
NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to lookup email: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lwsgs_email_cb_sent(struct lws_email *email)
|
|
{
|
|
struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data;
|
|
char s[200], esc[50];
|
|
|
|
/* mark the user as having sent the verification email */
|
|
lws_snprintf(s, sizeof(s) - 1,
|
|
"update users set verified=1 where username='%s' and verified==0;",
|
|
lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
|
|
if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
|
|
lwsl_err("%s: Unable to update user: %s\n", __func__,
|
|
sqlite3_errmsg(vhd->pdb));
|
|
return 1;
|
|
}
|
|
|
|
lws_snprintf(s, sizeof(s) - 1,
|
|
"delete from email where username='%s';",
|
|
lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
|
|
if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
|
|
lwsl_err("%s: Unable to delete email text: %s\n", __func__,
|
|
sqlite3_errmsg(vhd->pdb));
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lwsgs_email_cb_on_next(struct lws_email *email)
|
|
{
|
|
struct per_vhost_data__gs *vhd = lws_container_of(email,
|
|
struct per_vhost_data__gs, email);
|
|
char s[LWSGS_EMAIL_CONTENT_SIZE], esc[50];
|
|
time_t now = lws_now_secs();
|
|
|
|
/*
|
|
* users not verified in 24h get deleted
|
|
*/
|
|
lws_snprintf(s, sizeof(s) - 1, "delete from users where ((verified != %d)"
|
|
" and (creation_time <= %lu));", LWSGS_VERIFIED_ACCEPTED,
|
|
(unsigned long)now - vhd->timeout_email_secs);
|
|
if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to expire users: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
return 1;
|
|
}
|
|
|
|
lws_snprintf(s, sizeof(s) - 1, "update users set token_time=0 where "
|
|
"(token_time <= %lu);",
|
|
(unsigned long)now - vhd->timeout_email_secs);
|
|
if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to expire users: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
return 1;
|
|
}
|
|
|
|
vhd->u.username[0] = '\0';
|
|
lws_snprintf(s, sizeof(s) - 1, "select username from email limit 1;");
|
|
if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &vhd->u,
|
|
NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to lookup user: %s\n", sqlite3_errmsg(vhd->pdb));
|
|
return 1;
|
|
}
|
|
|
|
lws_snprintf(s, sizeof(s) - 1,
|
|
"select username, creation_time, email, ip, verified, token"
|
|
" from users where username='%s' limit 1;",
|
|
lws_sql_purify(esc, vhd->u.username, sizeof(esc) - 1));
|
|
if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &vhd->u,
|
|
NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to lookup user: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
return 1;
|
|
}
|
|
|
|
if (!vhd->u.username[0])
|
|
/*
|
|
* nothing to do, we are idle and no suitable
|
|
* accounts waiting for verification. When a new user
|
|
* is added we will get kicked to try again.
|
|
*/
|
|
return 1;
|
|
|
|
strncpy(email->email_to, vhd->u.email, sizeof(email->email_to) - 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct lwsgs_subst_args
|
|
{
|
|
struct per_session_data__gs *pss;
|
|
struct per_vhost_data__gs *vhd;
|
|
struct lws *wsi;
|
|
};
|
|
|
|
static const char *
|
|
lwsgs_subst(void *data, int index)
|
|
{
|
|
struct lwsgs_subst_args *a = (struct lwsgs_subst_args *)data;
|
|
struct lwsgs_user u;
|
|
lwsgw_hash sid;
|
|
char esc[50], s[100];
|
|
int n;
|
|
|
|
a->pss->result[0] = '\0';
|
|
u.email[0] = '\0';
|
|
if (!lwsgs_get_sid_from_wsi(a->wsi, &sid)) {
|
|
if (lwsgs_lookup_session(a->vhd, &sid, a->pss->result, 31)) {
|
|
lwsl_notice("sid lookup for %s failed\n", sid.id);
|
|
a->pss->delete_session = sid;
|
|
return NULL;
|
|
}
|
|
lws_snprintf(s, sizeof(s) - 1, "select username,email "
|
|
"from users where username = '%s';",
|
|
lws_sql_purify(esc, a->pss->result, sizeof(esc) - 1));
|
|
if (sqlite3_exec(a->vhd->pdb, s, lwsgs_lookup_callback_user,
|
|
&u, NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to lookup token: %s\n",
|
|
sqlite3_errmsg(a->vhd->pdb));
|
|
a->pss->delete_session = sid;
|
|
return NULL;
|
|
}
|
|
} else
|
|
lwsl_notice("no sid\n");
|
|
|
|
strncpy(a->pss->result + 32, u.email, 100);
|
|
|
|
switch (index) {
|
|
case 0:
|
|
return a->pss->result;
|
|
|
|
case 1:
|
|
n = lwsgs_get_auth_level(a->vhd, a->pss->result);
|
|
sprintf(a->pss->result, "%d", n);
|
|
return a->pss->result;
|
|
case 2:
|
|
return a->pss->result + 32;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
callback_generic_sessions(struct lws *wsi, enum lws_callback_reasons reason,
|
|
void *user, void *in, size_t len)
|
|
{
|
|
struct per_session_data__gs *pss = (struct per_session_data__gs *)user;
|
|
const struct lws_protocol_vhost_options *pvo;
|
|
struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)
|
|
lws_protocol_vh_priv_get(lws_get_vhost(wsi),
|
|
&protocols[0]);
|
|
char cookie[1024], username[32], *pc = cookie;
|
|
unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE];
|
|
struct lws_process_html_args *args;
|
|
struct lws_session_info *sinfo;
|
|
char s[LWSGS_EMAIL_CONTENT_SIZE];
|
|
unsigned char *p, *start, *end;
|
|
sqlite3_stmt *sm;
|
|
lwsgw_hash sid;
|
|
const char *cp;
|
|
int n;
|
|
|
|
switch (reason) {
|
|
case LWS_CALLBACK_PROTOCOL_INIT: /* per vhost */
|
|
|
|
vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
|
|
&protocols[0], sizeof(struct per_vhost_data__gs));
|
|
if (!vhd)
|
|
return 1;
|
|
vhd->context = lws_get_context(wsi);
|
|
|
|
/* defaults */
|
|
vhd->timeout_idle_secs = 600;
|
|
vhd->timeout_absolute_secs = 36000;
|
|
vhd->timeout_anon_absolute_secs = 1200;
|
|
vhd->timeout_email_secs = 24 * 3600;
|
|
strcpy(vhd->email.email_helo, "unconfigured.com");
|
|
strcpy(vhd->email.email_from, "noreply@unconfigured.com");
|
|
strcpy(vhd->email_title, "Registration Email from unconfigured");
|
|
strcpy(vhd->email.email_smtp_ip, "127.0.0.1");
|
|
|
|
vhd->email.on_next = lwsgs_email_cb_on_next;
|
|
vhd->email.on_get_body = lwsgs_email_cb_get_body;
|
|
vhd->email.on_sent = lwsgs_email_cb_sent;
|
|
vhd->email.data = (void *)vhd;
|
|
|
|
pvo = (const struct lws_protocol_vhost_options *)in;
|
|
while (pvo) {
|
|
if (!strcmp(pvo->name, "admin-user"))
|
|
strncpy(vhd->admin_user, pvo->value,
|
|
sizeof(vhd->admin_user) - 1);
|
|
if (!strcmp(pvo->name, "admin-password-sha1"))
|
|
strncpy(vhd->admin_password_sha1.id, pvo->value,
|
|
sizeof(vhd->admin_password_sha1.id) - 1);
|
|
if (!strcmp(pvo->name, "session-db"))
|
|
strncpy(vhd->session_db, pvo->value,
|
|
sizeof(vhd->session_db) - 1);
|
|
if (!strcmp(pvo->name, "confounder"))
|
|
strncpy(vhd->confounder, pvo->value,
|
|
sizeof(vhd->confounder) - 1);
|
|
if (!strcmp(pvo->name, "email-from"))
|
|
strncpy(vhd->email.email_from, pvo->value,
|
|
sizeof(vhd->email.email_from) - 1);
|
|
if (!strcmp(pvo->name, "email-helo"))
|
|
strncpy(vhd->email.email_helo, pvo->value,
|
|
sizeof(vhd->email.email_helo) - 1);
|
|
if (!strcmp(pvo->name, "email-template"))
|
|
strncpy(vhd->email_template, pvo->value,
|
|
sizeof(vhd->email_template) - 1);
|
|
if (!strcmp(pvo->name, "email-title"))
|
|
strncpy(vhd->email_title, pvo->value,
|
|
sizeof(vhd->email_title) - 1);
|
|
if (!strcmp(pvo->name, "email-contact-person"))
|
|
strncpy(vhd->email_contact_person, pvo->value,
|
|
sizeof(vhd->email_contact_person) - 1);
|
|
if (!strcmp(pvo->name, "email-confirm-url-base"))
|
|
strncpy(vhd->email_confirm_url, pvo->value,
|
|
sizeof(vhd->email_confirm_url) - 1);
|
|
if (!strcmp(pvo->name, "email-server-ip"))
|
|
strncpy(vhd->email.email_smtp_ip, pvo->value,
|
|
sizeof(vhd->email.email_smtp_ip) - 1);
|
|
|
|
if (!strcmp(pvo->name, "timeout-idle-secs"))
|
|
vhd->timeout_idle_secs = atoi(pvo->value);
|
|
if (!strcmp(pvo->name, "timeout-absolute-secs"))
|
|
vhd->timeout_absolute_secs = atoi(pvo->value);
|
|
if (!strcmp(pvo->name, "timeout-anon-absolute-secs"))
|
|
vhd->timeout_anon_absolute_secs = atoi(pvo->value);
|
|
if (!strcmp(pvo->name, "email-expire"))
|
|
vhd->timeout_email_secs = atoi(pvo->value);
|
|
pvo = pvo->next;
|
|
}
|
|
if (!vhd->admin_user[0] ||
|
|
!vhd->admin_password_sha1.id[0] ||
|
|
!vhd->session_db[0]) {
|
|
lwsl_err("generic-sessions: "
|
|
"You must give \"admin-user\", "
|
|
"\"admin-password-sha1\", "
|
|
"and \"session_db\" per-vhost options\n");
|
|
return 1;
|
|
}
|
|
|
|
if (sqlite3_open_v2(vhd->session_db, &vhd->pdb,
|
|
SQLITE_OPEN_READWRITE |
|
|
SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to open session db %s: %s\n",
|
|
vhd->session_db, sqlite3_errmsg(vhd->pdb));
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (sqlite3_prepare(vhd->pdb,
|
|
"create table if not exists sessions ("
|
|
" name char(40),"
|
|
" username varchar(32),"
|
|
" expire integer"
|
|
");",
|
|
-1, &sm, NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to prepare session table init: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (sqlite3_step(sm) != SQLITE_DONE) {
|
|
lwsl_err("Unable to run session table init: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
|
|
return 1;
|
|
}
|
|
sqlite3_finalize(sm);
|
|
|
|
if (sqlite3_exec(vhd->pdb,
|
|
"create table if not exists users ("
|
|
" username varchar(32),"
|
|
" creation_time integer,"
|
|
" ip varchar(46),"
|
|
" email varchar(100),"
|
|
" pwhash varchar(42),"
|
|
" pwsalt varchar(42),"
|
|
" pwchange_time integer,"
|
|
" token varchar(42),"
|
|
" verified integer,"
|
|
" token_time integer,"
|
|
" last_forgot_validated integer,"
|
|
" primary key (username)"
|
|
");",
|
|
NULL, NULL, NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to create user table: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
|
|
return 1;
|
|
}
|
|
|
|
sprintf(s, "create table if not exists email ("
|
|
" username varchar(32),"
|
|
" content blob,"
|
|
" primary key (username)"
|
|
");");
|
|
if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
|
|
lwsl_err("Unable to create user table: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
|
|
return 1;
|
|
}
|
|
|
|
lws_email_init(&vhd->email, lws_uv_getloop(vhd->context, 0),
|
|
LWSGS_EMAIL_CONTENT_SIZE);
|
|
|
|
vhd->email_inited = 1;
|
|
break;
|
|
|
|
case LWS_CALLBACK_PROTOCOL_DESTROY:
|
|
// lwsl_notice("gs: LWS_CALLBACK_PROTOCOL_DESTROY: v=%p, ctx=%p\n", vhd, vhd->context);
|
|
if (vhd->pdb) {
|
|
sqlite3_close(vhd->pdb);
|
|
vhd->pdb = NULL;
|
|
}
|
|
if (vhd->email_inited) {
|
|
lws_email_destroy(&vhd->email);
|
|
vhd->email_inited = 0;
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_HTTP:
|
|
lwsl_info("LWS_CALLBACK_HTTP: %s\n", in);
|
|
|
|
pss->login_session.id[0] = '\0';
|
|
pss->phs.pos = 0;
|
|
strncpy(pss->onward, (char *)in, sizeof(pss->onward) - 1);
|
|
pss->onward[sizeof(pss->onward) - 1] = '\0';
|
|
|
|
if (!strcmp((const char *)in, "/lwsgs-forgot")) {
|
|
lwsgs_handler_forgot(vhd, wsi, pss);
|
|
goto redirect_with_cookie;
|
|
}
|
|
|
|
if (!strcmp((const char *)in, "/lwsgs-confirm")) {
|
|
lwsgs_handler_confirm(vhd, wsi, pss);
|
|
goto redirect_with_cookie;
|
|
}
|
|
if (!strcmp((const char *)in, "/lwsgs-check")) {
|
|
lwsgs_handler_check(vhd, wsi, pss);
|
|
goto try_to_reuse;
|
|
}
|
|
|
|
if (!strcmp((const char *)in, "/lwsgs-login"))
|
|
break;
|
|
if (!strcmp((const char *)in, "/lwsgs-logout"))
|
|
break;
|
|
if (!strcmp((const char *)in, "/lwsgs-forgot"))
|
|
break;
|
|
if (!strcmp((const char *)in, "/lwsgs-change"))
|
|
break;
|
|
|
|
/* if no legitimate url for GET, return 404 */
|
|
|
|
lwsl_err("http doing 404 on %s\n", in);
|
|
lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
|
goto try_to_reuse;
|
|
|
|
case LWS_CALLBACK_CHECK_ACCESS_RIGHTS:
|
|
n = 0;
|
|
username[0] = '\0';
|
|
sid.id[0] = '\0';
|
|
args = (struct lws_process_html_args *)in;
|
|
lwsl_debug("LWS_CALLBACK_CHECK_ACCESS_RIGHTS\n");
|
|
if (!lwsgs_get_sid_from_wsi(wsi, &sid)) {
|
|
if (lwsgs_lookup_session(vhd, &sid, username, sizeof(username))) {
|
|
static const char * const oprot[] = {
|
|
"http://", "https://"
|
|
};
|
|
lwsl_notice("session lookup for %s failed, probably expired\n", sid.id);
|
|
pss->delete_session = sid;
|
|
args->final = 1; /* signal we dealt with it */
|
|
if (lws_hdr_copy(wsi, cookie, sizeof(cookie) - 1,
|
|
WSI_TOKEN_HOST) < 0)
|
|
return 1;
|
|
lws_snprintf(pss->onward, sizeof(pss->onward) - 1,
|
|
"%s%s%s", oprot[lws_is_ssl(wsi)],
|
|
cookie, args->p);
|
|
lwsl_notice("redirecting to ourselves with cookie refresh\n");
|
|
/* we need a redirect to ourselves, session cookie is expired */
|
|
goto redirect_with_cookie;
|
|
}
|
|
} else
|
|
lwsl_notice("failed to get sid from wsi\n");
|
|
|
|
n = lwsgs_get_auth_level(vhd, username);
|
|
|
|
if ((args->max_len & n) != args->max_len) {
|
|
lwsl_notice("Access rights fail 0x%X vs 0x%X (cookie %s)\n",
|
|
args->max_len, n, sid.id);
|
|
return 1;
|
|
}
|
|
lwsl_debug("Access rights OK\n");
|
|
break;
|
|
|
|
case LWS_CALLBACK_SESSION_INFO:
|
|
{
|
|
struct lwsgs_user u;
|
|
sinfo = (struct lws_session_info *)in;
|
|
sinfo->username[0] = '\0';
|
|
sinfo->email[0] = '\0';
|
|
sinfo->ip[0] = '\0';
|
|
sinfo->session[0] = '\0';
|
|
sinfo->mask = 0;
|
|
|
|
sid.id[0] = '\0';
|
|
lwsl_debug("LWS_CALLBACK_SESSION_INFO\n");
|
|
if (lwsgs_get_sid_from_wsi(wsi, &sid))
|
|
break;
|
|
if (lwsgs_lookup_session(vhd, &sid, username, sizeof(username)))
|
|
break;
|
|
|
|
lws_snprintf(s, sizeof(s) - 1,
|
|
"select username, email from users where username='%s';",
|
|
username);
|
|
if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, &u, NULL) !=
|
|
SQLITE_OK) {
|
|
lwsl_err("Unable to lookup token: %s\n",
|
|
sqlite3_errmsg(vhd->pdb));
|
|
break;
|
|
}
|
|
strncpy(sinfo->username, u.username, sizeof(sinfo->username) - 1);
|
|
sinfo->username[sizeof(sinfo->username) - 1] = '\0';
|
|
strncpy(sinfo->email, u.email, sizeof(sinfo->email) - 1);
|
|
strncpy(sinfo->session, sid.id, sizeof(sinfo->session) - 1);
|
|
sinfo->mask = lwsgs_get_auth_level(vhd, username);
|
|
lws_get_peer_simple(wsi, sinfo->ip, sizeof(sinfo->ip));
|
|
}
|
|
|
|
break;
|
|
|
|
case LWS_CALLBACK_PROCESS_HTML:
|
|
|
|
args = (struct lws_process_html_args *)in;
|
|
{
|
|
static const char * const vars[] = {
|
|
"$lwsgs_user",
|
|
"$lwsgs_auth",
|
|
"$lwsgs_email"
|
|
};
|
|
struct lwsgs_subst_args a;
|
|
|
|
a.vhd = vhd;
|
|
a.pss = pss;
|
|
a.wsi = wsi;
|
|
|
|
pss->phs.vars = vars;
|
|
pss->phs.count_vars = ARRAY_SIZE(vars);
|
|
pss->phs.replace = lwsgs_subst;
|
|
pss->phs.data = &a;
|
|
|
|
if (lws_chunked_html_process(args, &pss->phs))
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_HTTP_BODY:
|
|
if (len < 2)
|
|
break;
|
|
|
|
if (!pss->spa) {
|
|
pss->spa = lws_spa_create(wsi, param_names,
|
|
ARRAY_SIZE(param_names), 1024,
|
|
NULL, NULL);
|
|
if (!pss->spa)
|
|
return -1;
|
|
}
|
|
|
|
if (lws_spa_process(pss->spa, in, len)) {
|
|
lwsl_notice("spa process blew\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_HTTP_BODY_COMPLETION:
|
|
|
|
if (!pss->spa)
|
|
break;
|
|
|
|
lwsl_info("LWS_CALLBACK_HTTP_BODY_COMPLETION: %s\n", pss->onward);
|
|
lws_spa_finalize(pss->spa);
|
|
|
|
if (!strcmp((char *)pss->onward, "/lwsgs-change")) {
|
|
if (!lwsgs_handler_change_password(vhd, wsi, pss)) {
|
|
cp = lws_spa_get_string(pss->spa, FGS_GOOD);
|
|
goto pass;
|
|
}
|
|
|
|
cp = lws_spa_get_string(pss->spa, FGS_BAD);
|
|
lwsl_notice("user/password no good %s\n",
|
|
lws_spa_get_string(pss->spa, FGS_USERNAME));
|
|
strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
|
|
pss->onward[sizeof(pss->onward) - 1] = '\0';
|
|
goto completion_flow;
|
|
}
|
|
|
|
if (!strcmp((char *)pss->onward, "/lwsgs-login")) {
|
|
if (lws_spa_get_string(pss->spa, FGS_FORGOT) &&
|
|
lws_spa_get_string(pss->spa, FGS_FORGOT)[0]) {
|
|
if (lwsgs_handler_forgot_pw_form(vhd, wsi, pss)) {
|
|
n = FGS_FORGOT_BAD;
|
|
goto reg_done;
|
|
}
|
|
/* get the email monitor to take a look */
|
|
lws_email_check(&vhd->email);
|
|
n = FGS_FORGOT_GOOD;
|
|
goto reg_done;
|
|
}
|
|
|
|
if (!lws_spa_get_string(pss->spa, FGS_USERNAME) ||
|
|
!lws_spa_get_string(pss->spa, FGS_PASSWORD)) {
|
|
lwsl_notice("username '%s' or pw '%s' missing\n",
|
|
lws_spa_get_string(pss->spa, FGS_USERNAME),
|
|
lws_spa_get_string(pss->spa, FGS_PASSWORD));
|
|
return -1;
|
|
}
|
|
|
|
if (lws_spa_get_string(pss->spa, FGS_REGISTER) &&
|
|
lws_spa_get_string(pss->spa, FGS_REGISTER)[0]) {
|
|
|
|
if (lwsgs_handler_register_form(vhd, wsi, pss))
|
|
n = FGS_REG_BAD;
|
|
else {
|
|
n = FGS_REG_GOOD;
|
|
|
|
/* get the email monitor to take a look */
|
|
lws_email_check(&vhd->email);
|
|
}
|
|
reg_done:
|
|
strncpy(pss->onward, lws_spa_get_string(pss->spa, n),
|
|
sizeof(pss->onward) - 1);
|
|
pss->onward[sizeof(pss->onward) - 1] = '\0';
|
|
pss->login_expires = 0;
|
|
pss->logging_out = 1;
|
|
goto completion_flow;
|
|
}
|
|
|
|
/* we have the username and password... check if admin */
|
|
if (lwsgw_check_admin(vhd, lws_spa_get_string(pss->spa, FGS_USERNAME),
|
|
lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
|
|
if (lws_spa_get_string(pss->spa, FGS_ADMIN))
|
|
cp = lws_spa_get_string(pss->spa, FGS_ADMIN);
|
|
else
|
|
if (lws_spa_get_string(pss->spa, FGS_GOOD))
|
|
cp = lws_spa_get_string(pss->spa, FGS_GOOD);
|
|
else {
|
|
lwsl_info("No admin or good target url in form\n");
|
|
return -1;
|
|
}
|
|
lwsl_debug("admin\n");
|
|
goto pass;
|
|
}
|
|
|
|
/* check users in database */
|
|
|
|
if (!lwsgs_check_credentials(vhd, lws_spa_get_string(pss->spa, FGS_USERNAME),
|
|
lws_spa_get_string(pss->spa, FGS_PASSWORD))) {
|
|
lwsl_info("pw hash check met\n");
|
|
cp = lws_spa_get_string(pss->spa, FGS_GOOD);
|
|
goto pass;
|
|
} else
|
|
lwsl_notice("user/password no good %s\n",
|
|
lws_spa_get_string(pss->spa, FGS_USERNAME));
|
|
|
|
if (!lws_spa_get_string(pss->spa, FGS_BAD)) {
|
|
lwsl_info("No admin or good target url in form\n");
|
|
return -1;
|
|
}
|
|
|
|
strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_BAD),
|
|
sizeof(pss->onward) - 1);
|
|
pss->onward[sizeof(pss->onward) - 1] = '\0';
|
|
lwsl_debug("failed\n");
|
|
|
|
goto completion_flow;
|
|
}
|
|
|
|
if (!strcmp((char *)pss->onward, "/lwsgs-logout")) {
|
|
|
|
lwsl_notice("/logout\n");
|
|
|
|
if (lwsgs_get_sid_from_wsi(wsi, &pss->login_session)) {
|
|
lwsl_notice("not logged in...\n");
|
|
return 1;
|
|
}
|
|
|
|
lwsgw_update_session(vhd, &pss->login_session, "");
|
|
|
|
if (!lws_spa_get_string(pss->spa, FGS_GOOD)) {
|
|
lwsl_info("No admin or good target url in form\n");
|
|
return -1;
|
|
}
|
|
|
|
strncpy(pss->onward, lws_spa_get_string(pss->spa, FGS_GOOD), sizeof(pss->onward) - 1);
|
|
pss->onward[sizeof(pss->onward) - 1] = '\0';
|
|
|
|
pss->login_expires = 0;
|
|
pss->logging_out = 1;
|
|
|
|
goto completion_flow;
|
|
}
|
|
|
|
break;
|
|
|
|
pass:
|
|
strncpy(pss->onward, cp, sizeof(pss->onward) - 1);
|
|
pss->onward[sizeof(pss->onward) - 1] = '\0';
|
|
|
|
if (lwsgs_get_sid_from_wsi(wsi, &sid))
|
|
sid.id[0] = '\0';
|
|
|
|
pss->login_expires = lws_now_secs() +
|
|
vhd->timeout_absolute_secs;
|
|
|
|
if (!sid.id[0]) {
|
|
/* we need to create a new, authorized session */
|
|
|
|
if (lwsgs_new_session_id(vhd, &pss->login_session,
|
|
lws_spa_get_string(pss->spa, FGS_USERNAME),
|
|
pss->login_expires))
|
|
goto try_to_reuse;
|
|
|
|
lwsl_info("Creating new session: %s\n",
|
|
pss->login_session.id);
|
|
} else {
|
|
/*
|
|
* we can just update the existing session to be
|
|
* authorized
|
|
*/
|
|
lwsl_info("Authorizing existing session %s", sid.id);
|
|
lwsgw_update_session(vhd, &sid,
|
|
lws_spa_get_string(pss->spa, FGS_USERNAME));
|
|
pss->login_session = sid;
|
|
}
|
|
|
|
completion_flow:
|
|
lwsgw_expire_old_sessions(vhd);
|
|
goto redirect_with_cookie;
|
|
|
|
case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
|
|
if (pss && pss->spa) {
|
|
lws_spa_destroy(pss->spa);
|
|
pss->spa = NULL;
|
|
}
|
|
break;
|
|
|
|
case LWS_CALLBACK_ADD_HEADERS:
|
|
lwsgw_expire_old_sessions(vhd);
|
|
|
|
args = (struct lws_process_html_args *)in;
|
|
|
|
if (pss->delete_session.id[0]) {
|
|
pc = cookie;
|
|
lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
|
|
cookie + sizeof(cookie) - 1);
|
|
|
|
lwsl_info("deleting cookie '%s'\n", cookie);
|
|
|
|
if (lws_add_http_header_by_name(wsi,
|
|
(unsigned char *)"set-cookie:",
|
|
(unsigned char *)cookie, pc - cookie,
|
|
(unsigned char **)&args->p,
|
|
(unsigned char *)args->p + args->max_len))
|
|
return 1;
|
|
}
|
|
|
|
if (!pss->login_session.id[0])
|
|
lwsgs_get_sid_from_wsi(wsi, &pss->login_session);
|
|
|
|
if (!pss->login_session.id[0] && !pss->logging_out) {
|
|
|
|
pss->login_expires = lws_now_secs() +
|
|
vhd->timeout_anon_absolute_secs;
|
|
if (lwsgs_new_session_id(vhd, &pss->login_session, "",
|
|
pss->login_expires))
|
|
goto try_to_reuse;
|
|
pc = cookie;
|
|
lwsgw_cookie_from_session(&pss->login_session,
|
|
pss->login_expires, &pc,
|
|
cookie + sizeof(cookie) - 1);
|
|
|
|
lwsl_info("LWS_CALLBACK_ADD_HEADERS: setting cookie '%s'\n", cookie);
|
|
if (lws_add_http_header_by_name(wsi,
|
|
(unsigned char *)"set-cookie:",
|
|
(unsigned char *)cookie, pc - cookie,
|
|
(unsigned char **)&args->p,
|
|
(unsigned char *)args->p + args->max_len))
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
|
|
redirect_with_cookie:
|
|
p = buffer + LWS_PRE;
|
|
start = p;
|
|
end = p + sizeof(buffer) - LWS_PRE;
|
|
|
|
if (lws_add_http_header_status(wsi, HTTP_STATUS_SEE_OTHER, &p, end))
|
|
return 1;
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
|
|
(unsigned char *)pss->onward,
|
|
strlen(pss->onward), &p, end))
|
|
return 1;
|
|
|
|
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,
|
|
(unsigned char *)"text/html", 9, &p, end))
|
|
return 1;
|
|
if (lws_add_http_header_content_length(wsi, 0, &p, end))
|
|
return 1;
|
|
|
|
if (pss->delete_session.id[0]) {
|
|
lwsgw_cookie_from_session(&pss->delete_session, 0, &pc,
|
|
cookie + sizeof(cookie) - 1);
|
|
|
|
lwsl_notice("deleting cookie '%s'\n", cookie);
|
|
|
|
if (lws_add_http_header_by_name(wsi,
|
|
(unsigned char *)"set-cookie:",
|
|
(unsigned char *)cookie, pc - cookie,
|
|
&p, end))
|
|
return 1;
|
|
}
|
|
|
|
if (!pss->login_session.id[0]) {
|
|
pss->login_expires = lws_now_secs() +
|
|
vhd->timeout_anon_absolute_secs;
|
|
if (lwsgs_new_session_id(vhd, &pss->login_session, "",
|
|
pss->login_expires))
|
|
return 1;
|
|
} else
|
|
pss->login_expires = lws_now_secs() +
|
|
vhd->timeout_absolute_secs;
|
|
|
|
if (pss->login_session.id[0] || pss->logging_out) {
|
|
/*
|
|
* we succeeded to login, we must issue a login
|
|
* cookie with the prepared data
|
|
*/
|
|
pc = cookie;
|
|
|
|
lwsgw_cookie_from_session(&pss->login_session,
|
|
pss->login_expires, &pc,
|
|
cookie + sizeof(cookie) - 1);
|
|
|
|
lwsl_info("setting cookie '%s'\n", cookie);
|
|
|
|
pss->logging_out = 0;
|
|
|
|
if (lws_add_http_header_by_name(wsi,
|
|
(unsigned char *)"set-cookie:",
|
|
(unsigned char *)cookie, pc - cookie,
|
|
&p, end))
|
|
return 1;
|
|
}
|
|
|
|
if (lws_finalize_http_header(wsi, &p, end))
|
|
return 1;
|
|
|
|
n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
|
|
if (n < 0)
|
|
return 1;
|
|
|
|
/* fallthru */
|
|
|
|
try_to_reuse:
|
|
if (lws_http_transaction_completed(wsi))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct lws_protocols protocols[] = {
|
|
{
|
|
"protocol-generic-sessions",
|
|
callback_generic_sessions,
|
|
sizeof(struct per_session_data__gs),
|
|
1024,
|
|
},
|
|
};
|
|
|
|
LWS_EXTERN LWS_VISIBLE int
|
|
init_protocol_generic_sessions(struct lws_context *context,
|
|
struct lws_plugin_capability *c)
|
|
{
|
|
if (c->api_magic != LWS_PLUGIN_API_MAGIC) {
|
|
lwsl_err("Plugin API %d, library API %d", LWS_PLUGIN_API_MAGIC,
|
|
c->api_magic);
|
|
return 1;
|
|
}
|
|
|
|
c->protocols = protocols;
|
|
c->count_protocols = ARRAY_SIZE(protocols);
|
|
c->extensions = NULL;
|
|
c->count_extensions = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
LWS_EXTERN LWS_VISIBLE int
|
|
destroy_protocol_generic_sessions(struct lws_context *context)
|
|
{
|
|
return 0;
|
|
}
|