| /* |
| * 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" |
| |
| /* handle account confirmation links */ |
| |
| int |
| lwsgs_handler_confirm(struct per_vhost_data__gs *vhd, struct lws *wsi, |
| struct per_session_data__gs *pss) |
| { |
| char cookie[1024], s[256], esc[50]; |
| struct lws_gs_event_args a; |
| struct lwsgs_user u; |
| |
| if (lws_hdr_copy_fragment(wsi, cookie, sizeof(cookie), |
| WSI_TOKEN_HTTP_URI_ARGS, 0) < 0) |
| goto verf_fail; |
| |
| if (strncmp(cookie, "token=", 6)) |
| goto verf_fail; |
| |
| u.username[0] = '\0'; |
| lws_snprintf(s, sizeof(s) - 1, |
| "select username,email,verified from users where token = '%s';", |
| lws_sql_purify(esc, &cookie[6], sizeof(esc) - 1)); |
| 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)); |
| goto verf_fail; |
| } |
| |
| if (!u.username[0] || u.verified != 1) { |
| lwsl_notice("verify token doesn't map to unverified user\n"); |
| goto verf_fail; |
| } |
| |
| lwsl_notice("Verifying %s\n", u.username); |
| lws_snprintf(s, sizeof(s) - 1, |
| "update users set verified=%d where username='%s';", |
| LWSGS_VERIFIED_ACCEPTED, |
| lws_sql_purify(esc, u.username, sizeof(esc) - 1)); |
| 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)); |
| |
| goto verf_fail; |
| } |
| |
| lwsl_notice("deleting account\n"); |
| |
| a.event = LWSGSE_CREATED; |
| a.username = u.username; |
| a.email = u.email; |
| lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0); |
| |
| lws_snprintf(pss->onward, sizeof(pss->onward), |
| "%s/post-verify-ok.html", vhd->email_confirm_url); |
| |
| pss->login_expires = lws_now_secs() + vhd->timeout_absolute_secs; |
| |
| pss->delete_session.id[0] = '\0'; |
| lwsgs_get_sid_from_wsi(wsi, &pss->delete_session); |
| |
| /* we need to create a new, authorized session */ |
| |
| if (lwsgs_new_session_id(vhd, &pss->login_session, u.username, |
| pss->login_expires)) |
| goto verf_fail; |
| |
| lwsl_notice("Creating new session: %s, redir to %s\n", |
| pss->login_session.id, pss->onward); |
| |
| return 0; |
| |
| verf_fail: |
| pss->delete_session.id[0] = '\0'; |
| lwsgs_get_sid_from_wsi(wsi, &pss->delete_session); |
| pss->login_expires = 0; |
| |
| lws_snprintf(pss->onward, sizeof(pss->onward), "%s/post-verify-fail.html", |
| vhd->email_confirm_url); |
| |
| return 1; |
| } |
| |
| /* handle forgot password confirmation links */ |
| |
| int |
| lwsgs_handler_forgot(struct per_vhost_data__gs *vhd, struct lws *wsi, |
| struct per_session_data__gs *pss) |
| { |
| char cookie[1024], s[256], esc[50]; |
| struct lwsgs_user u; |
| const char *a; |
| |
| a = lws_get_urlarg_by_name(wsi, "token=", cookie, sizeof(cookie)); |
| if (!a) |
| goto forgot_fail; |
| |
| u.username[0] = '\0'; |
| lws_snprintf(s, sizeof(s) - 1, |
| "select username,verified from users where verified=%d and " |
| "token = '%s' and token_time != 0;", |
| LWSGS_VERIFIED_ACCEPTED, |
| lws_sql_purify(esc, &cookie[6], sizeof(esc) - 1)); |
| 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)); |
| |
| goto forgot_fail; |
| } |
| |
| if (!u.username[0]) { |
| puts(s); |
| lwsl_notice("forgot token doesn't map to verified user\n"); |
| goto forgot_fail; |
| } |
| |
| /* mark user as having validated forgot flow just now */ |
| |
| lws_snprintf(s, sizeof(s) - 1, |
| "update users set token_time=0,last_forgot_validated=%lu " |
| "where username='%s';", |
| (unsigned long)lws_now_secs(), |
| lws_sql_purify(esc, u.username, sizeof(esc) - 1)); |
| |
| 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)); |
| goto forgot_fail; |
| } |
| |
| a = lws_get_urlarg_by_name(wsi, "good=", cookie, sizeof(cookie)); |
| if (!a) |
| a = "broken-forget-post-good-url"; |
| |
| lws_snprintf(pss->onward, sizeof(pss->onward), |
| "%s/%s", vhd->email_confirm_url, a); |
| |
| pss->login_expires = lws_now_secs() + vhd->timeout_absolute_secs; |
| |
| pss->delete_session.id[0] = '\0'; |
| lwsgs_get_sid_from_wsi(wsi, &pss->delete_session); |
| |
| /* we need to create a new, authorized session */ |
| if (lwsgs_new_session_id(vhd, &pss->login_session, |
| u.username, |
| pss->login_expires)) |
| goto forgot_fail; |
| |
| lwsl_notice("Creating new session: %s, redir to %s\n", |
| pss->login_session.id, pss->onward); |
| |
| return 0; |
| |
| forgot_fail: |
| pss->delete_session.id[0] = '\0'; |
| lwsgs_get_sid_from_wsi(wsi, &pss->delete_session); |
| pss->login_expires = 0; |
| |
| a = lws_get_urlarg_by_name(wsi, "bad=", cookie, sizeof(cookie)); |
| if (!a) |
| a = "broken-forget-post-bad-url"; |
| |
| lws_snprintf(pss->onward, sizeof(pss->onward), "%s/%s", |
| vhd->email_confirm_url, a); |
| |
| return 1; |
| } |
| |
| /* support dynamic username / email checking */ |
| |
| int |
| lwsgs_handler_check(struct per_vhost_data__gs *vhd, |
| struct lws *wsi, struct per_session_data__gs *pss) |
| { |
| static const char * const colname[] = { "username", "email" }; |
| char cookie[1024], s[256], esc[50], *pc; |
| unsigned char *p, *start, *end, buffer[LWS_PRE + 256]; |
| struct lwsgs_user u; |
| int n; |
| |
| /* |
| * either /check?email=xxx@yyy or: /check?username=xxx |
| * returns '0' if not already registered, else '1' |
| */ |
| |
| u.username[0] = '\0'; |
| if (lws_hdr_copy_fragment(wsi, cookie, sizeof(cookie), |
| WSI_TOKEN_HTTP_URI_ARGS, 0) < 0) |
| goto reply; |
| |
| n = !strncmp(cookie, "email=", 6); |
| pc = strchr(cookie, '='); |
| if (!pc) { |
| lwsl_notice("cookie has no =\n"); |
| goto reply; |
| } |
| pc++; |
| |
| /* admin user cannot be registered in user db */ |
| if (!strcmp(vhd->admin_user, pc)) { |
| u.username[0] = 'a'; |
| goto reply; |
| } |
| |
| lws_snprintf(s, sizeof(s) - 1, |
| "select username, email from users where %s = '%s';", |
| colname[n], lws_sql_purify(esc, pc, sizeof(esc) - 1)); |
| 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)); |
| goto reply; |
| } |
| |
| reply: |
| s[0] = '0' + !!u.username[0]; |
| p = buffer + LWS_PRE; |
| start = p; |
| end = p + sizeof(buffer) - LWS_PRE; |
| |
| if (lws_add_http_header_status(wsi, 200, &p, end)) |
| return -1; |
| if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, |
| (unsigned char *)"text/plain", 10, |
| &p, end)) |
| return -1; |
| |
| if (lws_add_http_header_content_length(wsi, 1, &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 != (p - start)) { |
| lwsl_err("_write returned %d from %ld\n", n, (long)(p - start)); |
| return -1; |
| } |
| n = lws_write(wsi, (unsigned char *)s, 1, LWS_WRITE_HTTP); |
| if (n != 1) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* handle forgot password confirmation links */ |
| |
| int |
| lwsgs_handler_change_password(struct per_vhost_data__gs *vhd, struct lws *wsi, |
| struct per_session_data__gs *pss) |
| { |
| char s[256], esc[50], username[50]; |
| struct lwsgs_user u; |
| lwsgw_hash sid; |
| int n = 0; |
| |
| /* see if he's logged in */ |
| username[0] = '\0'; |
| if (!lwsgs_get_sid_from_wsi(wsi, &sid)) { |
| u.username[0] = '\0'; |
| if (!lwsgs_lookup_session(vhd, &sid, username, sizeof(username))) { |
| n = 1; /* yes, logged in */ |
| if (lwsgs_lookup_user(vhd, username, &u)) |
| return 1; |
| |
| /* did a forgot pw ? */ |
| if (u.last_forgot_validated > lws_now_secs() - 300) { |
| n |= LWSGS_AUTH_FORGOT_FLOW; |
| lwsl_debug("within forgot password flow\n"); |
| } |
| } |
| } |
| |
| lwsl_debug("auth value %d\n", n); |
| |
| /* if he just did forgot pw flow, don't need old pw */ |
| if ((n & (LWSGS_AUTH_FORGOT_FLOW | 1)) != (LWSGS_AUTH_FORGOT_FLOW | 1)) { |
| /* otherwise user:pass must be right */ |
| lwsl_debug("checking pw\n"); |
| if (lwsgs_check_credentials(vhd, |
| lws_spa_get_string(pss->spa, FGS_USERNAME), |
| lws_spa_get_string(pss->spa, FGS_CURPW))) { |
| lwsl_notice("credentials bad\n"); |
| return 1; |
| } |
| |
| lwsl_debug("current pw checks out\n"); |
| |
| strncpy(u.username, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(u.username) - 1); |
| u.username[sizeof(u.username) - 1] = '\0'; |
| } |
| |
| /* does he want to delete his account? */ |
| |
| if (lws_spa_get_length(pss->spa, FGS_DELETE)) { |
| struct lws_gs_event_args a; |
| |
| lwsl_notice("deleting account\n"); |
| |
| a.event = LWSGSE_DELETED; |
| a.username = u.username; |
| a.email = ""; |
| lws_callback_vhost_protocols(wsi, LWS_CALLBACK_GS_EVENT, &a, 0); |
| |
| lws_snprintf(s, sizeof(s) - 1, |
| "delete from users where username='%s';" |
| "delete from sessions where username='%s';", |
| lws_sql_purify(esc, u.username, sizeof(esc) - 1), |
| lws_sql_purify(esc, u.username, sizeof(esc) - 1)); |
| goto sql; |
| } |
| |
| if (lwsgs_hash_password(vhd, lws_spa_get_string(pss->spa, FGS_PASSWORD), &u)) |
| return 1; |
| |
| lwsl_notice("updating password hash\n"); |
| |
| lws_snprintf(s, sizeof(s) - 1, |
| "update users set pwhash='%s', pwsalt='%s', " |
| "last_forgot_validated=0 where username='%s';", |
| u.pwhash.id, u.pwsalt.id, |
| lws_sql_purify(esc, u.username, sizeof(esc) - 1)); |
| |
| sql: |
| if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) { |
| lwsl_err("Unable to update pw hash: %s\n", |
| sqlite3_errmsg(vhd->pdb)); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int |
| lwsgs_handler_forgot_pw_form(struct per_vhost_data__gs *vhd, |
| struct lws *wsi, |
| struct per_session_data__gs *pss) |
| { |
| char s[LWSGS_EMAIL_CONTENT_SIZE]; |
| unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE]; |
| char esc[50], esc1[50], esc2[50], esc3[50], esc4[50]; |
| struct lwsgs_user u; |
| lwsgw_hash hash; |
| unsigned char sid_rand[20]; |
| int n; |
| |
| lwsl_notice("FORGOT %s %s\n", |
| lws_spa_get_string(pss->spa, FGS_USERNAME), |
| lws_spa_get_string(pss->spa, FGS_EMAIL)); |
| |
| if (!lws_spa_get_string(pss->spa, FGS_USERNAME) && |
| !lws_spa_get_string(pss->spa, FGS_EMAIL)) { |
| lwsl_err("Form must provide either " |
| "username or email\n"); |
| return -1; |
| } |
| |
| if (!lws_spa_get_string(pss->spa, FGS_FORGOT_GOOD) || |
| !lws_spa_get_string(pss->spa, FGS_FORGOT_BAD) || |
| !lws_spa_get_string(pss->spa, FGS_FORGOT_POST_GOOD) || |
| !lws_spa_get_string(pss->spa, FGS_FORGOT_POST_BAD)) { |
| lwsl_err("Form must provide reg-good " |
| "and reg-bad (and post-*)" |
| "targets\n"); |
| return -1; |
| } |
| |
| u.username[0] = '\0'; |
| if (lws_spa_get_string(pss->spa, FGS_USERNAME)) |
| lws_snprintf(s, sizeof(s) - 1, |
| "select username,email " |
| "from users where username = '%s';", |
| lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME), |
| sizeof(esc) - 1)); |
| else |
| lws_snprintf(s, sizeof(s) - 1, |
| "select username,email " |
| "from users where email = '%s';", |
| lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc) - 1)); |
| 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)); |
| return 1; |
| } |
| if (!u.username[0]) { |
| lwsl_err("No match found %s\n", s); |
| return 1; |
| } |
| |
| lws_get_peer_simple(wsi, pss->ip, sizeof(pss->ip)); |
| if (lws_get_random(vhd->context, sid_rand, |
| sizeof(sid_rand)) != |
| sizeof(sid_rand)) { |
| lwsl_err("Problem getting random for token\n"); |
| return 1; |
| } |
| sha1_to_lwsgw_hash(sid_rand, &hash); |
| n = lws_snprintf(s, sizeof(s), |
| "From: Forgot Password Assistant Noreply <%s>\n" |
| "To: %s <%s>\n" |
| "Subject: Password reset request\n" |
| "\n" |
| "Hello, %s\n\n" |
| "We received a password reset request from IP %s for this email,\n" |
| "to confirm you want to do that, please click the link below.\n\n", |
| lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1), |
| lws_sql_purify(esc1, u.username, sizeof(esc1) - 1), |
| lws_sql_purify(esc2, u.email, sizeof(esc2) - 1), |
| lws_sql_purify(esc3, u.username, sizeof(esc3) - 1), |
| lws_sql_purify(esc4, pss->ip, sizeof(esc4) - 1)); |
| lws_snprintf(s + n, sizeof(s) -n, |
| "%s/lwsgs-forgot?token=%s" |
| "&good=%s" |
| "&bad=%s\n\n" |
| "If this request is unexpected, please ignore it and\n" |
| "no further action will be taken.\n\n" |
| "If you have any questions or concerns about this\n" |
| "automated email, you can contact a real person at\n" |
| "%s.\n" |
| "\n.\n", |
| vhd->email_confirm_url, hash.id, |
| lws_urlencode(esc1, |
| lws_spa_get_string(pss->spa, FGS_FORGOT_POST_GOOD), |
| sizeof(esc1) - 1), |
| lws_urlencode(esc3, |
| lws_spa_get_string(pss->spa, FGS_FORGOT_POST_BAD), |
| sizeof(esc3) - 1), |
| vhd->email_contact_person); |
| |
| lws_snprintf((char *)buffer, sizeof(buffer) - 1, |
| "insert into email(username, content)" |
| " values ('%s', '%s');", |
| lws_sql_purify(esc, u.username, sizeof(esc) - 1), s); |
| if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL, |
| NULL, NULL) != SQLITE_OK) { |
| lwsl_err("Unable to insert email: %s\n", |
| sqlite3_errmsg(vhd->pdb)); |
| return 1; |
| } |
| |
| lws_snprintf(s, sizeof(s) - 1, |
| "update users set token='%s',token_time='%ld' where username='%s';", |
| hash.id, (long)lws_now_secs(), |
| lws_sql_purify(esc, u.username, sizeof(esc) - 1)); |
| if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != |
| SQLITE_OK) { |
| lwsl_err("Unable to set token: %s\n", |
| sqlite3_errmsg(vhd->pdb)); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int |
| lwsgs_handler_register_form(struct per_vhost_data__gs *vhd, |
| struct lws *wsi, |
| struct per_session_data__gs *pss) |
| { |
| unsigned char buffer[LWS_PRE + LWSGS_EMAIL_CONTENT_SIZE]; |
| char esc[50], esc1[50], esc2[50], esc3[50], esc4[50]; |
| char s[LWSGS_EMAIL_CONTENT_SIZE]; |
| unsigned char sid_rand[20]; |
| struct lwsgs_user u; |
| lwsgw_hash hash; |
| |
| lwsl_notice("REGISTER %s %s %s\n", |
| lws_spa_get_string(pss->spa, FGS_USERNAME), |
| lws_spa_get_string(pss->spa, FGS_PASSWORD), |
| lws_spa_get_string(pss->spa, FGS_EMAIL)); |
| if (lwsgs_get_sid_from_wsi(wsi, |
| &pss->login_session)) |
| return 1; |
| |
| lws_get_peer_simple(wsi, pss->ip, sizeof(pss->ip)); |
| lwsl_notice("IP=%s\n", pss->ip); |
| |
| if (!lws_spa_get_string(pss->spa, FGS_REG_GOOD) || |
| !lws_spa_get_string(pss->spa, FGS_REG_BAD)) { |
| lwsl_info("Form must provide reg-good and reg-bad targets\n"); |
| return -1; |
| } |
| |
| /* admin user cannot be registered in user db */ |
| if (!strcmp(vhd->admin_user, |
| lws_spa_get_string(pss->spa, FGS_USERNAME))) |
| return 1; |
| |
| if (!lwsgs_lookup_user(vhd, |
| lws_spa_get_string(pss->spa, FGS_USERNAME), &u)) { |
| lwsl_notice("user %s already registered\n", |
| lws_spa_get_string(pss->spa, FGS_USERNAME)); |
| return 1; |
| } |
| |
| u.username[0] = '\0'; |
| lws_snprintf(s, sizeof(s) - 1, "select username, email from users where email = '%s';", |
| lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_EMAIL), |
| sizeof(esc) - 1)); |
| |
| 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)); |
| return 1; |
| } |
| |
| if (u.username[0]) { |
| lwsl_notice("email %s already in use\n", |
| lws_spa_get_string(pss->spa, FGS_USERNAME)); |
| return 1; |
| } |
| |
| if (lwsgs_hash_password(vhd, lws_spa_get_string(pss->spa, FGS_PASSWORD), |
| &u)) { |
| lwsl_err("Password hash failed\n"); |
| return 1; |
| } |
| |
| if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) != |
| sizeof(sid_rand)) { |
| lwsl_err("Problem getting random for token\n"); |
| return 1; |
| } |
| sha1_to_lwsgw_hash(sid_rand, &hash); |
| |
| lws_snprintf((char *)buffer, sizeof(buffer) - 1, |
| "insert into users(username," |
| " creation_time, ip, email, verified," |
| " pwhash, pwsalt, token, last_forgot_validated)" |
| " values ('%s', %lu, '%s', '%s', 0," |
| " '%s', '%s', '%s', 0);", |
| lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc) - 1), |
| (unsigned long)lws_now_secs(), |
| lws_sql_purify(esc1, pss->ip, sizeof(esc1) - 1), |
| lws_sql_purify(esc2, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc2) - 1), |
| u.pwhash.id, u.pwsalt.id, hash.id); |
| |
| if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL, NULL, NULL) != SQLITE_OK) { |
| lwsl_err("Unable to insert user: %s\n", |
| sqlite3_errmsg(vhd->pdb)); |
| return 1; |
| } |
| |
| lws_snprintf(s, sizeof(s), |
| "From: Noreply <%s>\n" |
| "To: %s <%s>\n" |
| "Subject: Registration verification\n" |
| "\n" |
| "Hello, %s\n\n" |
| "We received a registration from IP %s using this email,\n" |
| "to confirm it is legitimate, please click the link below.\n\n" |
| "%s/lwsgs-confirm?token=%s\n\n" |
| "If this request is unexpected, please ignore it and\n" |
| "no further action will be taken.\n\n" |
| "If you have any questions or concerns about this\n" |
| "automated email, you can contact a real person at\n" |
| "%s.\n" |
| "\n.\n", |
| lws_sql_purify(esc, vhd->email.email_from, sizeof(esc) - 1), |
| lws_sql_purify(esc1, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc1) - 1), |
| lws_sql_purify(esc2, lws_spa_get_string(pss->spa, FGS_EMAIL), sizeof(esc2) - 1), |
| lws_sql_purify(esc3, lws_spa_get_string(pss->spa, FGS_USERNAME), sizeof(esc3) - 1), |
| lws_sql_purify(esc4, pss->ip, sizeof(esc4) - 1), |
| vhd->email_confirm_url, hash.id, |
| vhd->email_contact_person); |
| |
| lws_snprintf((char *)buffer, sizeof(buffer) - 1, |
| "insert into email(username, content) values ('%s', '%s');", |
| lws_sql_purify(esc, lws_spa_get_string(pss->spa, FGS_USERNAME), |
| sizeof(esc) - 1), s); |
| |
| if (sqlite3_exec(vhd->pdb, (char *)buffer, NULL, NULL, NULL) != SQLITE_OK) { |
| lwsl_err("Unable to insert email: %s\n", |
| sqlite3_errmsg(vhd->pdb)); |
| return 1; |
| } |
| |
| return 0; |
| } |