diff --git a/CMakeLists.txt b/CMakeLists.txt
index eceddacd0..33642ab15 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -99,8 +99,9 @@ option(LWS_WITH_ACCESS_LOG "Support generating Apache-compatible access logs" OF
option(LWS_WITH_SERVER_STATUS "Support json + jscript server monitoring" OFF)
option(LWS_WITH_LEJP "With the Lightweight JSON Parser" OFF)
option(LWS_WITH_LEJP_CONF "With LEJP configuration parser as used by lwsws" OFF)
+option(LWS_WITH_GENERIC_SESSIONS "With the Generic Sessions plugin" OFF)
+option(LWS_WITH_SQLITE3 "Require SQLITE3 support" OFF)
option(LWS_WITH_SMTP "Provide SMTP support" OFF)
-option(LWS_WITH_STATEFUL_URLDECODE "Provide stateful URLDECODE apis" OFF)
if (LWS_WITH_LWSWS)
message(STATUS "LWS_WITH_LWSWS --> Enabling LWS_WITH_PLUGINS and LWS_WITH_LIBUV")
@@ -112,10 +113,6 @@ if (LWS_WITH_LWSWS)
set(LWS_WITH_LEJP_CONF 1)
endif()
-if (LWS_WITH_PLUGINS)
- set(LWS_WITH_STATEFUL_URLDECODE 1)
-endif()
-
if (LWS_WITH_PLUGINS AND NOT LWS_WITH_LIBUV)
message(STATUS "LWS_WITH_PLUGINS --> Enabling LWS_WITH_LIBUV")
set(LWS_WITH_LIBUV 1)
@@ -126,6 +123,16 @@ message(STATUS "LWS_WITH_SMTP --> Enabling LWS_WITH_LIBUV")
set(LWS_WITH_LIBUV 1)
endif()
+if (LWS_WITH_GENERIC_SESSIONS)
+ set(LWS_WITH_SQLITE3 1)
+ set(LWS_WITH_SMTP 1)
+endif()
+
+if (LWS_WITH_SMTP AND NOT LWS_WITH_LIBUV)
+message(STATUS "LWS_WITH_SMTP --> Enabling LWS_WITH_LIBUV")
+ set(LWS_WITH_LIBUV 1)
+endif()
+
if (DEFINED YOTTA_WEBSOCKETS_VERSION_STRING)
set(LWS_WITH_SHARED OFF)
@@ -198,6 +205,9 @@ set( CACHE PATH "Path to the libev library")
set(LWS_LIBEV_INCLUDE_DIRS CACHE PATH "Path to the libev include directory")
set(LWS_LIBUV_LIBRARIES CACHE PATH "Path to the libuv library")
set(LWS_LIBUV_INCLUDE_DIRS CACHE PATH "Path to the libuv include directory")
+set(LWS_SQLITE3_LIBRARIES CACHE PATH "Path to the libuv library")
+set(LWS_SQLITE3_INCLUDE_DIRS CACHE PATH "Path to the libuv include directory")
+
if (NOT LWS_WITH_SSL)
set(LWS_WITHOUT_BUILTIN_SHA1 OFF)
@@ -285,6 +295,15 @@ if (LWS_WITH_LIBUV)
endif()
endif()
+if (LWS_WITH_SQLITE3)
+ if ("${LWS_SQLITE3_LIBRARIES}" STREQUAL "" OR "${LWS_SQLITE3_INCLUDE_DIRS}" STREQUAL "")
+ else()
+ set(SQLITE3_LIBRARIES ${LWS_SQLITE3_LIBRARIES})
+ set(SQLITE3_INCLUDE_DIRS ${LWS_SQLITE3_INCLUDE_DIRS})
+ set(SQLITE3_FOUND 1)
+ endif()
+endif()
+
# FIXME: This must be runtime-only option.
# The base dir where the test-apps look for the SSL certs.
@@ -612,6 +631,17 @@ endif()
if (WIN32)
set(WIN32_HELPERS_PATH win32port/win32helpers)
include_directories(${WIN32_HELPERS_PATH})
+
+ if (WIN32)
+ list(APPEND SOURCES
+ ${WIN32_HELPERS_PATH}/gettimeofday.c
+ )
+
+ list(APPEND HDR_PRIVATE
+ ${WIN32_HELPERS_PATH}/gettimeofday.h
+ )
+ endif(WIN32)
+
else()
# Unix.
if (NOT LWS_WITHOUT_DAEMONIZE)
@@ -870,6 +900,22 @@ if (LWS_WITH_LIBUV)
include_directories("${LIBUV_INCLUDE_DIRS}")
list(APPEND LIB_LIST ${LIBUV_LIBRARIES})
endif()
+
+if (LWS_WITH_SQLITE3)
+ if (NOT SQLITE3_FOUND)
+ find_path(SQLITE3_INCLUDE_DIRS NAMES sqlite3.h)
+ find_library(SQLITE3_LIBRARIES NAMES sqlite3)
+ if(SQLITE3_INCLUDE_DIRS AND SQLITE3_LIBRARIES)
+ set(SQLITE3_FOUND 1)
+ endif()
+ endif()
+ message("sqlite3 include dir: ${SQLITE3_INCLUDE_DIRS}")
+ message("sqlite3 libraries: ${SQLITE3_LIBRARIES}")
+ include_directories("${SQLITE3_INCLUDE_DIRS}")
+ list(APPEND LIB_LIST ${SQLITE3_LIBRARIES})
+endif()
+
+
if (LWS_WITH_HTTP_PROXY)
find_library(LIBHUBBUB_LIBRARIES NAMES libhubbub)
list(APPEND LIB_LIST ${LIBHUBBUB_LIBRARIES} )
@@ -1204,10 +1250,19 @@ if (NOT LWS_WITHOUT_TESTAPPS)
if (LWS_WITH_PLUGINS AND LWS_WITH_SHARED)
- macro(create_plugin PLUGIN_NAME MAIN_SRC)
+ macro(create_plugin PLUGIN_NAME MAIN_SRC S2 S3)
set(PLUGIN_SRCS ${MAIN_SRC})
+ if ("${S2}" STREQUAL "")
+ else()
+ list(APPEND PLUGIN_SRCS ${S2})
+ endif()
+ if ("${S3}" STREQUAL "")
+ else()
+ list(APPEND PLUGIN_SRCS ${S3})
+ endif()
+
if (WIN32)
list(APPEND PLUGIN_SRCS
${WIN32_HELPERS_PATH}/getopt.c
@@ -1239,27 +1294,42 @@ if (NOT LWS_WITHOUT_TESTAPPS)
# OUTPUT_NAME ${PLUGIN_NAME})
list(APPEND PLUGINS_LIST ${PLUGIN_NAME})
+
endmacro()
create_plugin(protocol_dumb_increment
- "plugins/protocol_dumb_increment.c")
+ "plugins/protocol_dumb_increment.c" "" "")
create_plugin(protocol_lws_mirror
- "plugins/protocol_lws_mirror.c")
+ "plugins/protocol_lws_mirror.c" "" "")
create_plugin(protocol_lws_status
- "plugins/protocol_lws_status.c")
+ "plugins/protocol_lws_status.c" "" "")
create_plugin(protocol_post_demo
- "plugins/protocol_post_demo.c")
+ "plugins/protocol_post_demo.c" "" "")
if (LWS_WITH_SERVER_STATUS)
create_plugin(protocol_lws_server_status
- "plugins/protocol_lws_server_status.c")
+ "plugins/protocol_lws_server_status.c" "" "")
endif()
if (NOT LWS_WITHOUT_CLIENT)
create_plugin(protocol_client_loopback_test
- "plugins/protocol_client_loopback_test.c")
+ "plugins/protocol_client_loopback_test.c" "" "")
endif(NOT LWS_WITHOUT_CLIENT)
+if (LWS_WITH_GENERIC_SESSIONS)
+ create_plugin(protocol_generic_sessions
+ "plugins/generic-sessions/protocol_generic_sessions.c"
+ "plugins/generic-sessions/utils.c"
+ "plugins/generic-sessions/handlers.c")
+
+ if (WIN32)
+ target_link_libraries(protocol_generic_sessions ${LWS_SQLITE3_LIBRARIES})
+ else()
+ target_link_libraries(protocol_generic_sessions sqlite3 )
+ endif(WIN32)
+endif(LWS_WITH_GENERIC_SESSIONS)
+
+
endif(LWS_WITH_PLUGINS AND LWS_WITH_SHARED)
#
@@ -1460,6 +1530,31 @@ if (LWS_WITH_SERVER_STATUS)
DESTINATION share/libwebsockets-test-server/server-status
COMPONENT examples)
endif()
+if (LWS_WITH_GENERIC_SESSIONS)
+ install(FILES
+ plugins/generic-sessions/assets/lwsgs-logo.png
+ plugins/generic-sessions/assets/seats.jpg
+ plugins/generic-sessions/assets/failed-login.html
+ plugins/generic-sessions/assets/lwsgs.js
+ plugins/generic-sessions/assets/post-register-fail.html
+ plugins/generic-sessions/assets/post-register-ok.html
+ plugins/generic-sessions/assets/post-verify-ok.html
+ plugins/generic-sessions/assets/post-verify-fail.html
+ plugins/generic-sessions/assets/sent-forgot-ok.html
+ plugins/generic-sessions/assets/sent-forgot-fail.html
+ plugins/generic-sessions/assets/post-forgot-ok.html
+ plugins/generic-sessions/assets/post-forgot-fail.html
+ plugins/generic-sessions/assets/index.html
+ DESTINATION share/libwebsockets-test-server/generic-sessions
+ COMPONENT examples)
+ install(FILES plugins/generic-sessions/assets/successful-login.html
+ DESTINATION share/libwebsockets-test-server/generic-sessions/needauth
+ COMPONENT examples)
+ install(FILES plugins/generic-sessions/assets/admin-login.html
+ DESTINATION share/libwebsockets-test-server/generic-sessions/needadmin
+ COMPONENT examples)
+endif()
+
endif()
# Install the LibwebsocketsConfig.cmake and LibwebsocketsConfigVersion.cmake
@@ -1531,7 +1626,8 @@ message(" LWS_WITH_SERVER_STATUS = ${LWS_WITH_SERVER_STATUS}")
message(" LWS_WITH_LEJP = ${LWS_WITH_LEJP}")
message(" LWS_WITH_LEJP_CONF = ${LWS_WITH_LEJP_CONF}")
message(" LWS_WITH_SMTP = ${LWS_WITH_SMTP}")
-message(" LWS_WITH_STATEFUL_URLDECODE = ${LWS_WITH_STATEFUL_URLDECODE}")
+message(" LWS_WITH_GENERIC_SESSIONS = ${LWS_WITH_GENERIC_SESSIONS}")
+
message("---------------------------------------------------------------------")
diff --git a/README.generic-sessions.md b/README.generic-sessions.md
new file mode 100644
index 000000000..dab287349
--- /dev/null
+++ b/README.generic-sessions.md
@@ -0,0 +1,383 @@
+Generic Sessions Plugin
+-----------------------
+
+Enabling for build
+------------------
+
+Enable at CMake with -DLWS_WITH_GENERIC_SESSIONS=1
+
+This also needs sqlite3 (libsqlite3-dev or similar package)
+
+
+Introduction
+------------
+
+The generic-sessions protocol plugin provides cookie-based login
+authentication for lws web and ws connections.
+
+The plugin handles everything about generic account registration,
+email verification, lost password, account deletion, and other generic account
+management.
+
+Other code, in another eg, ws protocol handler, only needs very high-level
+state information from generic-sessions, ie, which user the client is
+authenticated as. Everything underneath is managed in generic-sessions.
+
+
+ - random 20-byte session id managed in a cookie
+
+ - all information related to the session held at the server, nothing managed clientside
+
+ - sqlite3 used at the server to manage active sessions and users
+
+ - defaults to creating anonymous sessions with no user associated
+
+ - admin account (with user-selectable username) is defined in config with a SHA-1 of the password; rest of the accounts are in sqlite3
+
+ - user account passwords stored as salted SHA-1 with additional confounder
+ only stored in the JSON config, not the database
+
+ - login, logout, register account + email verification built-in with examples
+
+ - in a mount, some file suffixes (ie, .js) can be associated with a protocol for the purposes of rewriting symbolnames. These are read-only copies of logged-in server state.
+
+ - When your page fetches .js or other rewritten files from that mount, "$lwsgs_user" and so on are rewritten on the fly using chunked transfer encoding
+
+ - Eliminates server-side scripting with a few rewritten symbols and
+ javascript on client side
+
+ - 32-bit bitfield for authentication sectoring, mounts can provide a mask on the loggin-in session's associated server-side bitfield that must be set for access.
+
+ - No code (just config) required for, eg, private URL namespace that requires login to access.
+
+
+Integration to HTML
+-------------------
+
+Only three steps are needed to integrate lwsgs in your HTML.
+
+1) lwsgs HTML UI is bundled with the javascript it uses in `lwsgs.js`, so
+import that script file in your head section
+
+2) define an empty div of id "lwsgs" somewhere
+
+3) Call lwsgs_initial() in your page
+
+That's it. An example is below
+
+
+```
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+```
+
+Overall Flow
+------------
+
+When the protocol is initialized, it gets per-vhost information from the config, such
+as where the sqlite3 databases are to be stored. The admin username and sha-1 of the
+admin password are also taken from here.
+
+In the mounts using protocol-generic-sessions, a cookie is maintained against any requests; if no cookie was active on the initial request a new session is
+created with no attached user.
+
+So there should always be an active session after any transactions with the server.
+
+In the example html going to the mount /lwsgs loads a login / register page as the default.
+
+The
+
+
+
+
diff --git a/plugins/generic-sessions/assets/lwsgs-logo.png b/plugins/generic-sessions/assets/lwsgs-logo.png
new file mode 100644
index 000000000..723a12443
Binary files /dev/null and b/plugins/generic-sessions/assets/lwsgs-logo.png differ
diff --git a/plugins/generic-sessions/assets/lwsgs.js b/plugins/generic-sessions/assets/lwsgs.js
new file mode 100644
index 000000000..5362c9a91
--- /dev/null
+++ b/plugins/generic-sessions/assets/lwsgs.js
@@ -0,0 +1,476 @@
+
+
+var lwsgs_user = "$lwsgs_user";
+var lwsgs_auth = "$lwsgs_auth";
+var lwsgs_email = "$lwsgs_email";
+
+var lwsgs_html = '\
+ \
+\
+ \
+\
+ \
+ \
+ \
+ \
+ \
+ Admin settings TBD\
+
\
+';
+
+/*-- this came from
+ -- https://raw.githubusercontent.com/blueimp/JavaScript-MD5/master/js/md5.min.js
+ -- under MIT license */
+!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t),e=(n>>16)+(t>>16)+(r>>16);return e<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;16>r;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="0123456789abcdef",o="";for(r=0;r>>4&15)+e.charAt(15&t);return o}function v(n){return unescape(encodeURIComponent(n))}function m(n){return d(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this);
+
+if (lwsgs_user.substring(0, 1) == "$") {
+ alert("lwsgs.js: lws generic sessions misconfigured and not providing vars");
+}
+function lwsgs_san(s)
+{
+ if (s.search("<") != -1)
+ return "invalid string";
+
+ return s;
+}
+
+function lwsgs_update()
+{
+ var en_login = 1, en_forgot = 1;
+
+ if (document.getElementById('password').value.length &&
+ document.getElementById('password').value.length < 8)
+ en_login = 0;
+
+ if (!document.getElementById('username').value ||
+ !document.getElementById('password').value)
+ en_login = 0;
+
+ if (!document.getElementById('username').value ||
+ document.getElementById('password').value)
+ en_forgot = 0;
+
+ document.getElementById('login').disabled = !en_login;
+ document.getElementById('forgot').disabled = !en_forgot;
+
+ if (lwsgs_user)
+ document.getElementById("curuser").innerHTML = lwsgs_san(lwsgs_user);
+
+ if (lwsgs_user === "")
+ document.getElementById("dlogin").style.display = "inline";
+ else
+ document.getElementById("dlogout").style.display = "inline";
+ }
+
+function lwsgs_open_registration()
+{
+ document.getElementById("dadmin").style.display = "none";
+ document.getElementById("dlogin").style.display = "none";
+ document.getElementById("dlogout").style.display = "none";
+ document.getElementById("dchange").style.display = "none";
+ document.getElementById("dregister").style.display = "inline";
+}
+
+function lwsgs_cancel_registration()
+{
+ document.getElementById("dadmin").style.display = "none";
+ document.getElementById("dregister").style.display = "none";
+ document.getElementById("dchange").style.display = "none";
+
+ if (lwsgs_user === "")
+ document.getElementById("dlogin").style.display = "inline";
+ else
+ document.getElementById("dlogout").style.display = "inline";
+}
+
+function lwsgs_select_change()
+{
+ document.getElementById("dlogin").style.display = "none";
+ document.getElementById("dlogout").style.display = "none";
+ document.getElementById("dregister").style.display = "none";
+ if (lwsgs_auth & 2) {
+ document.getElementById("dadmin").style.display = "inline";
+ document.getElementById("dchange").style.display = "none";
+ } else {
+ document.getElementById("dadmin").style.display = "none";
+ document.getElementById("dchange").style.display = "inline";
+ }
+}
+
+var lwsgs_user_check = '0';
+var lwsgs_email_check = '0';
+
+function lwsgs_rupdate()
+{
+ var en_register = 1, en_forgot = 0;
+
+ if (document.getElementById('rpassword').value ==
+ document.getElementById('password2').value) {
+ if (document.getElementById('rpassword').value.length)
+ document.getElementById('match').innerHTML =
+ "\u2713";
+ else
+ document.getElementById('match').innerHTML = "";
+ document.getElementById('pw2').style = "";
+ } else {
+ if (document.getElementById('password2').value ||
+ document.getElementById('email').value) { // ie, he is filling in "register" path and cares
+ document.getElementById('match').innerHTML =
+ "\u2718 Passwords do not match";
+ } else
+ document.getElementById('match').innerHTML =
+ "\u2718 Passwords do not match";
+
+ en_register = 0;
+ }
+
+ if (document.getElementById('rpassword').value.length &&
+ document.getElementById('rpassword').value.length < 8) {
+ en_register = 0;
+ document.getElementById('rpw1').innerHTML = "Need 8 chars";
+ } else
+ if (document.getElementById('rpassword').value.length)
+ document.getElementById('rpw1').innerHTML = "\u2713";
+ else
+ document.getElementById('rpw1').innerHTML = "";
+
+ if (!document.getElementById('rpassword').value ||
+ !document.getElementById('password2').value ||
+ !document.getElementById('rusername').value ||
+ !document.getElementById('email').value ||
+ lwsgs_email_check === '1'||
+ lwsgs_user_check === '1')
+ en_register = 0;
+
+ document.getElementById('register').disabled = !en_register;
+ document.getElementById('rpassword').disabled = lwsgs_user_check === '1';
+ document.getElementById('password2').disabled = lwsgs_user_check === '1';
+ document.getElementById('email').disabled = lwsgs_user_check === '1';
+
+ if (lwsgs_user_check === '0') {
+ if (document.getElementById('rusername').value)
+ document.getElementById('uchk').innerHTML = "\u2713";
+ else
+ document.getElementById('uchk').innerHTML = "";
+ } else {
+ document.getElementById('uchk').innerHTML = "\u2718 Already registered";
+ en_forgot = 1;
+ }
+
+ if (lwsgs_email_check === '0') {
+ if (document.getElementById('email').value)
+ document.getElementById('echk').innerHTML = "\u2713";
+ else
+ document.getElementById('echk').innerHTML = "";
+ } else {
+ document.getElementById('echk').innerHTML = "\u2718 Already registered";
+ en_forgot = 1;
+ }
+
+ if (en_forgot)
+ document.getElementById('rforgot').style.display = "inline";
+ else
+ document.getElementById('rforgot').style.display = "none";
+
+ if (lwsgs_user_check === '1')
+ op = '0.5';
+ else
+ op = '1.0';
+ document.getElementById('rpassword').style.opacity = op;
+ document.getElementById('password2').style.opacity = op;
+ document.getElementById('email').style.opacity = op;
+ }
+
+function lwsgs_cupdate()
+{
+ var en_change = 1, en_forgot = 1, pwok = 1;
+
+ if (lwsgs_auth & 8) {
+ document.getElementById('ccurpw').style.display = "none";
+ document.getElementById('ccurpw_name').style.display = "none";
+ } else {
+ if (!document.getElementById('ccurpw').value ||
+ document.getElementById('ccurpw').value.length < 8) {
+ en_change = 0;
+ pwok = 0;
+ document.getElementById('cuchk').innerHTML = "\u2718";
+ } else {
+ en_forgot = 0;
+ document.getElementById('cuchk').innerHTML = "";
+ }
+ document.getElementById('ccurpw').style.display = "inline";
+ document.getElementById('ccurpw_name').style.display = "inline";
+ }
+
+ if (document.getElementById('cpassword').value ==
+ document.getElementById('cpassword2').value) {
+ if (document.getElementById('cpassword').value.length)
+ document.getElementById('cmatch').innerHTML = "\u2713";
+ else
+ document.getElementById('cmatch').innerHTML = "";
+ document.getElementById('pw2').style = "";
+ } else {
+ if (document.getElementById('cpassword2').value //||
+ //document.getElementById('cemail').value
+ ) { // ie, he is filling in "register" path and cares
+ document.getElementById('cmatch').innerHTML =
+ "\u2718 Passwords do not match";
+ } else
+ document.getElementById('cmatch').innerHTML = "\u2718 Passwords do not match";
+
+ en_change = 0;
+ }
+
+ if (document.getElementById('cpassword').value.length &&
+ document.getElementById('cpassword').value.length < 8) {
+ en_change = 0;
+ document.getElementById('cpw1').innerHTML = "Need 8 chars";
+ } else
+ if (document.getElementById('cpassword').value.length)
+ document.getElementById('cpw1').innerHTML = "\u2713";
+ else
+ document.getElementById('cpw1').innerHTML = "";
+
+ if (!document.getElementById('cpassword').value ||
+ !document.getElementById('cpassword2').value ||
+ pwok == 0)
+ en_change = 0;
+
+ if (document.getElementById('showdel').checked)
+ document.getElementById('delete').style.display = "inline";
+ else
+ document.getElementById('delete').style.display = "none";
+
+ document.getElementById('change').disabled = !en_change;
+ document.getElementById('cpassword').disabled = pwok === 0;
+ document.getElementById('cpassword2').disabled = pwok === 0;
+ document.getElementById('showdel').disabled = pwok === 0;
+ document.getElementById('delete').disabled = pwok === 0;
+ //document.getElementById('cemail').disabled = pwok === 0;
+
+ /*
+ if (lwsgs_auth & 8) {
+ document.getElementById('cemail').style.display = "none";
+ document.getElementById('cemail_name').style.display = "none";
+ } else {
+ document.getElementById('cemail').style.display = "inline";
+ document.getElementById('cemail_name').style.display = "inline";
+ if (lwsgs_email_check === '0' &&
+ document.getElementById('cemail').value != lwsgs_email) {
+ if (document.getElementById('cemail').value)
+ document.getElementById('cechk').innerHTML = "\u2713";
+ else
+ document.getElementById('cechk').innerHTML = "";
+ } else {
+ document.getElementById('cechk').innerHTML = "\u2718 Already registered";
+ en_forgot = 1;
+ }
+ } */
+
+ if (lwsgs_auth & 8)
+ en_forgot = 0;
+
+ if (en_forgot)
+ document.getElementById('cforgot').style.display = "inline";
+ else
+ document.getElementById('cforgot').style.display = "none";
+
+ if (pwok == 0)
+ op = '0.5';
+ else
+ op = '1.0';
+ document.getElementById('cpassword').style.opacity = op;
+ document.getElementById('cpassword2').style.opacity = op;
+ // document.getElementById('cemail').style.opacity = op;
+ }
+
+function lwsgs_check_user()
+{
+ var xmlHttp = new XMLHttpRequest();
+ xmlHttp.onreadystatechange = function() {
+ if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
+ lwsgs_user_check = xmlHttp.responseText;
+ lwsgs_rupdate();
+ }
+ }
+ xmlHttp.open("GET", "lwsgs-check?username="+document.getElementById('rusername').value, true);
+ xmlHttp.send(null);
+}
+
+function lwsgs_check_email(id)
+{
+ var xmlHttp = new XMLHttpRequest();
+ xmlHttp.onreadystatechange = function() {
+ if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
+ lwsgs_email_check = xmlHttp.responseText;
+ lwsgs_rupdate();
+ }
+ }
+ xmlHttp.open("GET", "lwsgs-check?email="+document.getElementById(id).value, true);
+ xmlHttp.send(null);
+}
+
+function lwsgs_initial()
+{
+ document.getElementById('lwsgs').innerHTML = lwsgs_html;
+ if (lwsgs_email)
+ document.getElementById('grav').innerHTML =
+ "
";
+ //if (lwsgs_email)
+ //document.getElementById('cemail').placeholder = lwsgs_email;
+ document.getElementById('cusername').value = lwsgs_user;
+ lwsgs_update();
+ lwsgs_cupdate();
+}
diff --git a/plugins/generic-sessions/assets/md5.min.js b/plugins/generic-sessions/assets/md5.min.js
new file mode 100644
index 000000000..4bd9de1e9
--- /dev/null
+++ b/plugins/generic-sessions/assets/md5.min.js
@@ -0,0 +1,2 @@
+!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t),e=(n>>16)+(t>>16)+(r>>16);return e<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;16>r;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="0123456789abcdef",o="";for(r=0;r>>4&15)+e.charAt(15&t);return o}function v(n){return unescape(encodeURIComponent(n))}function m(n){return d(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this);
+//# sourceMappingURL=md5.min.js.map
\ No newline at end of file
diff --git a/plugins/generic-sessions/assets/post-forgot-fail.html b/plugins/generic-sessions/assets/post-forgot-fail.html
new file mode 100644
index 000000000..ead3d13ec
--- /dev/null
+++ b/plugins/generic-sessions/assets/post-forgot-fail.html
@@ -0,0 +1,5 @@
+
+Sorry, something went wrong.
+
+Click here to continue.
+
diff --git a/plugins/generic-sessions/assets/post-forgot-ok.html b/plugins/generic-sessions/assets/post-forgot-ok.html
new file mode 100644
index 000000000..3e8e9cf59
--- /dev/null
+++ b/plugins/generic-sessions/assets/post-forgot-ok.html
@@ -0,0 +1,6 @@
+
+This is a one-time password recovery login.
+
+Please click here and click your username at the top to reset your password.
+
+
diff --git a/plugins/generic-sessions/assets/post-register-fail.html b/plugins/generic-sessions/assets/post-register-fail.html
new file mode 100644
index 000000000..063c3c50f
--- /dev/null
+++ b/plugins/generic-sessions/assets/post-register-fail.html
@@ -0,0 +1 @@
+Registration failed, sorry
diff --git a/plugins/generic-sessions/assets/post-register-ok.html b/plugins/generic-sessions/assets/post-register-ok.html
new file mode 100644
index 000000000..2d1503581
--- /dev/null
+++ b/plugins/generic-sessions/assets/post-register-ok.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+ Your registration as is accepted,
+ you will receive an email shortly with instructions
+ to verify and enable the account for normal use.
+ The link is only valid for an hour, after that if it has
+ not been verified your account will be deleted.
+ |
+
+
+
+
+
+
diff --git a/plugins/generic-sessions/assets/post-verify-fail.html b/plugins/generic-sessions/assets/post-verify-fail.html
new file mode 100644
index 000000000..d1d89ca56
--- /dev/null
+++ b/plugins/generic-sessions/assets/post-verify-fail.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+ Sorry, the link was invalid.
+ |
+
+
+
+
+
diff --git a/plugins/generic-sessions/assets/post-verify-ok.html b/plugins/generic-sessions/assets/post-verify-ok.html
new file mode 100644
index 000000000..e968f6a75
--- /dev/null
+++ b/plugins/generic-sessions/assets/post-verify-ok.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+ Thanks for signing up, your registration as is verified.
+
+ Click here to continue.
+ |
+
+
+
+
+
+
diff --git a/plugins/generic-sessions/assets/seats.jpg b/plugins/generic-sessions/assets/seats.jpg
new file mode 100644
index 000000000..5bed40d91
Binary files /dev/null and b/plugins/generic-sessions/assets/seats.jpg differ
diff --git a/plugins/generic-sessions/assets/sent-forgot-fail.html b/plugins/generic-sessions/assets/sent-forgot-fail.html
new file mode 100644
index 000000000..ead3d13ec
--- /dev/null
+++ b/plugins/generic-sessions/assets/sent-forgot-fail.html
@@ -0,0 +1,5 @@
+
+Sorry, something went wrong.
+
+Click here to continue.
+
diff --git a/plugins/generic-sessions/assets/sent-forgot-ok.html b/plugins/generic-sessions/assets/sent-forgot-ok.html
new file mode 100644
index 000000000..83df7510a
--- /dev/null
+++ b/plugins/generic-sessions/assets/sent-forgot-ok.html
@@ -0,0 +1,4 @@
+An email has been sent to your registered address.
+
+Please follow the instructions to reset your password.
+
diff --git a/plugins/generic-sessions/assets/successful-login.html b/plugins/generic-sessions/assets/successful-login.html
new file mode 100644
index 000000000..dfc25cf74
--- /dev/null
+++ b/plugins/generic-sessions/assets/successful-login.html
@@ -0,0 +1,4 @@
+
+This is an example destination that will appear after successful non-Admin login
+
+
diff --git a/plugins/generic-sessions/handlers.c b/plugins/generic-sessions/handlers.c
new file mode 100644
index 000000000..bd00c2b4c
--- /dev/null
+++ b/plugins/generic-sessions/handlers.c
@@ -0,0 +1,598 @@
+/*
+ * ws protocol handler plugin for "generic sessions"
+ *
+ * Copyright (C) 2010-2016 Andy Green
+ *
+ * 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';
+ 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);
+ 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);
+
+ 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;
+
+ 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';
+ 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 */
+
+ 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";
+
+ 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";
+
+ 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;
+ }
+
+ 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 %d\n", n, (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;
+ }
+ }
+
+ /* if he just did forgot pw flow, don't need old pw */
+ if (!(n & (LWSGS_AUTH_FORGOT_FLOW | 1))) {
+ /* otherwise user:pass must be right */
+ 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;
+ }
+
+ strcpy(u.username, lws_spa_get_string(pss->spa, FGS_USERNAME));
+ }
+
+ /* 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);
+
+ 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");
+
+ 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))
+ 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
+ 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 = 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));
+ 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);
+
+ 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;
+ }
+
+ 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';
+ 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);
+
+ 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;
+ }
+
+ 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);
+
+ 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;
+}
diff --git a/plugins/generic-sessions/private-lwsgs.h b/plugins/generic-sessions/private-lwsgs.h
new file mode 100644
index 000000000..cb408ae8c
--- /dev/null
+++ b/plugins/generic-sessions/private-lwsgs.h
@@ -0,0 +1,161 @@
+/*
+ * ws protocol handler plugin for "generic sessions"
+ *
+ * Copyright (C) 2010-2016 Andy Green
+ *
+ * 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
+ */
+
+#define LWS_DLL
+#define LWS_INTERNAL
+#include "../lib/libwebsockets.h"
+
+#include
+#include
+
+#define LWSGS_VERIFIED_ACCEPTED 100
+
+enum {
+ FGS_USERNAME,
+ FGS_PASSWORD,
+ FGS_PASSWORD2,
+ FGS_EMAIL,
+ FGS_REGISTER,
+ FGS_GOOD,
+ FGS_BAD,
+ FGS_REG_GOOD,
+ FGS_REG_BAD,
+ FGS_ADMIN,
+ FGS_FORGOT,
+ FGS_FORGOT_GOOD,
+ FGS_FORGOT_BAD,
+ FGS_FORGOT_POST_GOOD,
+ FGS_FORGOT_POST_BAD,
+ FGS_CHANGE,
+ FGS_CURPW,
+ FGS_DELETE,
+};
+
+struct lwsgs_user {
+ char username[32];
+ char ip[16];
+ lwsgw_hash pwhash;
+ lwsgw_hash pwsalt;
+ lwsgw_hash token;
+ time_t created;
+ time_t last_forgot_validated;
+ char email[100];
+ int verified;
+};
+
+struct per_vhost_data__gs {
+ struct lws_email email;
+ struct lws_context *context;
+ char session_db[256];
+ char admin_user[32];
+ char confounder[32];
+ char email_contact_person[128];
+ char email_title[128];
+ char email_template[128];
+ char email_confirm_url[128];
+ lwsgw_hash admin_password_sha1;
+ sqlite3 *pdb;
+ int timeout_idle_secs;
+ int timeout_absolute_secs;
+ int timeout_anon_absolute_secs;
+ int timeout_email_secs;
+ time_t last_session_expire;
+ struct lwsgs_user u;
+};
+
+struct per_session_data__gs {
+ struct lws_spa *spa;
+ lwsgw_hash login_session;
+ lwsgw_hash delete_session;
+ unsigned int login_expires;
+ char onward[256];
+ char result[500 + LWS_PRE];
+ char urldec[500 + LWS_PRE];
+ int result_len;
+ char ip[46];
+ struct lws_process_html_state phs;
+ int spos;
+
+ unsigned int logging_out:1;
+};
+
+/* utils.c */
+
+int
+lwsgs_lookup_callback_user(void *priv, int cols, char **col_val,
+ char **col_name);
+void
+lwsgw_cookie_from_session(lwsgw_hash *sid, time_t expires, char **p, char *end);
+int
+lwsgs_get_sid_from_wsi(struct lws *wsi, lwsgw_hash *sid);
+int
+lwsgs_lookup_session(struct per_vhost_data__gs *vhd,
+ const lwsgw_hash *sid, char *username, int len);
+int
+lwsgs_get_auth_level(struct per_vhost_data__gs *vhd,
+ const char *username);
+int
+lwsgs_check_credentials(struct per_vhost_data__gs *vhd,
+ const char *username, const char *password);
+void
+sha1_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash);
+unsigned int
+lwsgs_now_secs(void);
+int
+lwsgw_check_admin(struct per_vhost_data__gs *vhd,
+ const char *username, const char *password);
+int
+lwsgs_hash_password(struct per_vhost_data__gs *vhd,
+ const char *password, struct lwsgs_user *u);
+int
+lwsgs_new_session_id(struct per_vhost_data__gs *vhd,
+ lwsgw_hash *sid, const char *username, int exp);
+int
+lwsgs_lookup_user(struct per_vhost_data__gs *vhd,
+ const char *username, struct lwsgs_user *u);
+int
+lwsgw_update_session(struct per_vhost_data__gs *vhd,
+ lwsgw_hash *hash, const char *user);
+int
+lwsgw_expire_old_sessions(struct per_vhost_data__gs *vhd);
+
+
+/* handlers.c */
+
+int
+lwsgs_handler_confirm(struct per_vhost_data__gs *vhd, struct lws *wsi,
+ struct per_session_data__gs *pss);
+int
+lwsgs_handler_forgot(struct per_vhost_data__gs *vhd, struct lws *wsi,
+ struct per_session_data__gs *pss);
+int
+lwsgs_handler_check(struct per_vhost_data__gs *vhd, struct lws *wsi,
+ struct per_session_data__gs *pss);
+int
+lwsgs_handler_change_password(struct per_vhost_data__gs *vhd, struct lws *wsi,
+ struct per_session_data__gs *pss);
+int
+lwsgs_handler_forgot_pw_form(struct per_vhost_data__gs *vhd, struct lws *wsi,
+ struct per_session_data__gs *pss);
+int
+lwsgs_handler_register_form(struct per_vhost_data__gs *vhd, struct lws *wsi,
+ struct per_session_data__gs *pss);
+
diff --git a/plugins/generic-sessions/protocol_generic_sessions.c b/plugins/generic-sessions/protocol_generic_sessions.c
new file mode 100644
index 000000000..6bf5d2b2d
--- /dev/null
+++ b/plugins/generic-sessions/protocol_generic_sessions.c
@@ -0,0 +1,901 @@
+/*
+ * ws protocol handler plugin for "generic sessions"
+ *
+ * Copyright (C) 2010-2016 Andy Green
+ *
+ * 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;
+
+ 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 */
+ 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;
+ }
+
+ 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
+ */
+ 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;
+ }
+
+ 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';
+ 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;
+ }
+
+ 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;
+ }
+ 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);
+ break;
+
+ case LWS_CALLBACK_PROTOCOL_DESTROY:
+ if (vhd->pdb) {
+ sqlite3_close(vhd->pdb);
+ vhd->pdb = NULL;
+ }
+ lws_email_destroy(&vhd->email);
+ 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));
+
+ 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;
+ 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;
+
+ 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));
+ strncpy(sinfo->email, u.email, sizeof(sinfo->email));
+ strncpy(sinfo->session, sid.id, sizeof(sinfo->session));
+ 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->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;
+}
diff --git a/plugins/generic-sessions/utils.c b/plugins/generic-sessions/utils.c
new file mode 100644
index 000000000..0d458d7b0
--- /dev/null
+++ b/plugins/generic-sessions/utils.c
@@ -0,0 +1,450 @@
+/*
+ * ws protocol handler plugin for "generic sessions"
+ *
+ * Copyright (C) 2010-2016 Andy Green
+ *
+ * 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"
+
+void
+sha1_to_lwsgw_hash(unsigned char *hash, lwsgw_hash *shash)
+{
+ static const char *hex = "0123456789abcdef";
+ char *p = shash->id;
+ int n;
+
+ for (n = 0; n < 20; n++) {
+ *p++ = hex[hash[n] >> 4];
+ *p++ = hex[hash[n] & 15];
+ }
+
+ *p = '\0';
+}
+
+int
+lwsgw_check_admin(struct per_vhost_data__gs *vhd,
+ const char *username, const char *password)
+{
+ lwsgw_hash_bin hash_bin;
+ lwsgw_hash pw_hash;
+
+ if (strcmp(vhd->admin_user, username))
+ return 0;
+
+ lws_SHA1((unsigned char *)password, strlen(password), hash_bin.bin);
+ sha1_to_lwsgw_hash(hash_bin.bin, &pw_hash);
+
+ return !strcmp(vhd->admin_password_sha1.id, pw_hash.id);
+}
+
+/*
+ * secure cookie: it can only be passed over https where it cannot be
+ * snooped in transit
+ * HttpOnly: it can only be accessed via http[s] transport, it cannot be
+ * gotten at by JS
+ */
+void
+lwsgw_cookie_from_session(lwsgw_hash *sid, time_t expires, char **p, char *end)
+{
+ struct tm *tm = gmtime(&expires);
+ time_t n = lws_now_secs();
+
+ *p += snprintf(*p, end - *p, "id=%s;Expires=", sid->id);
+#ifdef WIN32
+ *p += strftime(*p, end - *p, "%Y %H:%M %Z", tm);
+#else
+ *p += strftime(*p, end - *p, "%F %H:%M %Z", tm);
+#endif
+ *p += snprintf(*p, end - *p, ";path=/");
+ *p += snprintf(*p, end - *p, ";Max-Age=%lu", (unsigned long)(expires - n));
+// *p += snprintf(*p, end - *p, ";secure");
+ *p += snprintf(*p, end - *p, ";HttpOnly");
+}
+
+int
+lwsgw_expire_old_sessions(struct per_vhost_data__gs *vhd)
+{
+ time_t n = lws_now_secs();
+ char s[200];
+
+ if (n - vhd->last_session_expire < 5)
+ return 0;
+
+ vhd->last_session_expire = n;
+
+ snprintf(s, sizeof(s) - 1,
+ "delete from sessions where "
+ "expire <= %lu;", (unsigned long)n);
+
+ if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
+ lwsl_err("Unable to expire sessions: %s\n",
+ sqlite3_errmsg(vhd->pdb));
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+lwsgw_update_session(struct per_vhost_data__gs *vhd,
+ lwsgw_hash *hash, const char *user)
+{
+ time_t n = lws_now_secs();
+ char s[200], esc[50], esc1[50];
+
+ if (user[0])
+ n += vhd->timeout_absolute_secs;
+ else
+ n += vhd->timeout_anon_absolute_secs;
+
+ snprintf(s, sizeof(s) - 1,
+ "update sessions set expire=%lu,username='%s' where name='%s';",
+ (unsigned long)n,
+ lws_sql_purify(esc, user, sizeof(esc)),
+ lws_sql_purify(esc1, hash->id, sizeof(esc1)));
+
+ if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
+ lwsl_err("Unable to update session: %s\n",
+ sqlite3_errmsg(vhd->pdb));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+lwsgw_session_from_cookie(const char *cookie, lwsgw_hash *sid)
+{
+ const char *p = cookie;
+ int n;
+
+ while (*p) {
+ if (p[0] == 'i' && p[1] == 'd' && p[2] == '=') {
+ p += 3;
+ break;
+ }
+ p++;
+ }
+ if (!*p) {
+ lwsl_info("no id= in cookie\n");
+ return 1;
+ }
+
+ for (n = 0; n < sizeof(sid->id) - 1 && *p; n++) {
+ /* our SID we issue only has these chars */
+ if ((*p >= '0' && *p <= '9') ||
+ (*p >= 'a' && *p <= 'f'))
+ sid->id[n] = *p++;
+ else {
+ lwsl_info("bad chars in cookie id %c\n", *p);
+ return 1;
+ }
+ }
+
+ if (n < sizeof(sid->id) - 1) {
+ lwsl_info("cookie id too short\n");
+ return 1;
+ }
+
+ sid->id[sizeof(sid->id) - 1] = '\0';
+
+ return 0;
+}
+
+int
+lwsgs_get_sid_from_wsi(struct lws *wsi, lwsgw_hash *sid)
+{
+ char cookie[1024];
+
+ /* fail it on no cookie */
+ if (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
+ lwsl_info("%s: no cookie\n", __func__);
+ return 1;
+ }
+ if (lws_hdr_copy(wsi, cookie, sizeof cookie, WSI_TOKEN_HTTP_COOKIE) < 0) {
+ lwsl_info("cookie copy failed\n");
+ return 1;
+ }
+ /* extract the sid from the cookie */
+ if (lwsgw_session_from_cookie(cookie, sid)) {
+ lwsl_info("session from cookie failed\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+struct lla {
+ char *username;
+ int len;
+ int results;
+};
+
+static int
+lwsgs_lookup_callback(void *priv, int cols, char **col_val, char **col_name)
+{
+ struct lla *lla = (struct lla *)priv;
+
+ //lwsl_err("%s: %d\n", __func__, cols);
+
+ if (cols)
+ lla->results = 0;
+ if (col_val && col_val[0]) {
+ strncpy(lla->username, col_val[0], lla->len);
+ lla->username[lla->len - 1] = '\0';
+ lwsl_info("%s: %s\n", __func__, lla->username);
+ }
+
+ return 0;
+}
+
+int
+lwsgs_lookup_session(struct per_vhost_data__gs *vhd,
+ const lwsgw_hash *sid, char *username, int len)
+{
+ struct lla lla = { username, len, 1 };
+ char s[150], esc[50];
+
+ lwsgw_expire_old_sessions(vhd);
+
+ snprintf(s, sizeof(s) - 1,
+ "select username from sessions where name = '%s';",
+ lws_sql_purify(esc, sid->id, sizeof(esc) - 1));
+
+ if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback, &lla, NULL) != SQLITE_OK) {
+ lwsl_err("Unable to create user table: %s\n",
+ sqlite3_errmsg(vhd->pdb));
+
+ return 1;
+ }
+
+ /* 0 if found */
+ return lla.results;
+}
+
+int
+lwsgs_lookup_callback_user(void *priv, int cols, char **col_val, char **col_name)
+{
+ struct lwsgs_user *u = (struct lwsgs_user *)priv;
+ int n;
+
+ for (n = 0; n < cols; n++) {
+ if (!strcmp(col_name[n], "username")) {
+ strncpy(u->username, col_val[n], sizeof(u->username) - 1);
+ u->username[sizeof(u->username) - 1] = '\0';
+ continue;
+ }
+ if (!strcmp(col_name[n], "ip")) {
+ strncpy(u->ip, col_val[n], sizeof(u->ip) - 1);
+ u->ip[sizeof(u->ip) - 1] = '\0';
+ continue;
+ }
+ if (!strcmp(col_name[n], "creation_time")) {
+ u->created = atol(col_val[n]);
+ continue;
+ }
+ if (!strcmp(col_name[n], "last_forgot_validated")) {
+ if (col_val[n])
+ u->last_forgot_validated = atol(col_val[n]);
+ else
+ u->last_forgot_validated = 0;
+ continue;
+ }
+ if (!strcmp(col_name[n], "email")) {
+ strncpy(u->email, col_val[n], sizeof(u->email) - 1);
+ u->email[sizeof(u->email) - 1] = '\0';
+ continue;
+ }
+ if (!strcmp(col_name[n], "verified")) {
+ u->verified = atoi(col_val[n]);
+ continue;
+ }
+ if (!strcmp(col_name[n], "pwhash")) {
+ strncpy(u->pwhash.id, col_val[n], sizeof(u->pwhash.id) - 1);
+ u->pwhash.id[sizeof(u->pwhash.id) - 1] = '\0';
+ continue;
+ }
+ if (!strcmp(col_name[n], "pwsalt")) {
+ strncpy(u->pwsalt.id, col_val[n], sizeof(u->pwsalt.id) - 1);
+ u->pwsalt.id[sizeof(u->pwsalt.id) - 1] = '\0';
+ continue;
+ }
+ if (!strcmp(col_name[n], "token")) {
+ strncpy(u->token.id, col_val[n], sizeof(u->token.id) - 1);
+ u->token.id[sizeof(u->token.id) - 1] = '\0';
+ continue;
+ }
+ }
+ return 0;
+}
+
+int
+lwsgs_lookup_user(struct per_vhost_data__gs *vhd,
+ const char *username, struct lwsgs_user *u)
+{
+ char s[150], esc[50];
+
+ u->username[0] = '\0';
+ snprintf(s, sizeof(s) - 1,
+ "select username,creation_time,ip,email,verified,pwhash,pwsalt,last_forgot_validated "
+ "from users where username = '%s';",
+ lws_sql_purify(esc, username, sizeof(esc) - 1));
+
+ if (sqlite3_exec(vhd->pdb, s, lwsgs_lookup_callback_user, u, NULL) !=
+ SQLITE_OK) {
+ lwsl_err("Unable to lookup user: %s\n",
+ sqlite3_errmsg(vhd->pdb));
+
+ return -1;
+ }
+
+ return !u->username[0];
+}
+
+int
+lwsgs_new_session_id(struct per_vhost_data__gs *vhd,
+ lwsgw_hash *sid, const char *username, int exp)
+{
+ unsigned char sid_rand[20];
+ const char *u;
+ char s[300], esc[50], esc1[50];
+
+ if (username)
+ u = username;
+ else
+ u = "";
+
+ if (!sid)
+ return 1;
+
+ memset(sid, 0, sizeof(*sid));
+
+ if (lws_get_random(vhd->context, sid_rand, sizeof(sid_rand)) !=
+ sizeof(sid_rand))
+ return 1;
+
+ sha1_to_lwsgw_hash(sid_rand, sid);
+
+ snprintf(s, sizeof(s) - 1,
+ "insert into sessions(name, username, expire) "
+ "values ('%s', '%s', %u);",
+ lws_sql_purify(esc, sid->id, sizeof(esc) - 1),
+ lws_sql_purify(esc1, u, sizeof(esc1) - 1), exp);
+
+ if (sqlite3_exec(vhd->pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
+ lwsl_err("Unable to insert session: %s\n",
+ sqlite3_errmsg(vhd->pdb));
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+lwsgs_get_auth_level(struct per_vhost_data__gs *vhd,
+ const char *username)
+{
+ struct lwsgs_user u;
+ int n = 0;
+
+ /* we are logged in as some kind of user */
+ if (username[0]) {
+ n |= LWSGS_AUTH_LOGGED_IN;
+ /* we are logged in as admin */
+ if (!strcmp(username, vhd->admin_user))
+ n |= LWSGS_AUTH_VERIFIED | LWSGS_AUTH_ADMIN; /* automatically verified */
+ }
+
+ if (!lwsgs_lookup_user(vhd, username, &u)) {
+ if ((u.verified & 0xff) == LWSGS_VERIFIED_ACCEPTED)
+ n |= LWSGS_AUTH_VERIFIED;
+
+ if (u.last_forgot_validated > lws_now_secs() - 300)
+ n |= LWSGS_AUTH_FORGOT_FLOW;
+ }
+
+ return n;
+}
+
+int
+lwsgs_check_credentials(struct per_vhost_data__gs *vhd,
+ const char *username, const char *password)
+{
+ unsigned char buffer[300];
+ lwsgw_hash_bin hash_bin;
+ struct lwsgs_user u;
+ lwsgw_hash hash;
+ int n;
+
+ if (lwsgs_lookup_user(vhd, username, &u))
+ return -1;
+
+ lwsl_info("user %s found, salt '%s'\n", username, u.pwsalt.id);
+
+ /* [password in ascii][salt] */
+ n = snprintf((char *)buffer, sizeof(buffer) - 1,
+ "%s-%s-%s", password, vhd->confounder, u.pwsalt.id);
+
+ /* sha1sum of password + salt */
+ lws_SHA1(buffer, n, hash_bin.bin);
+ sha1_to_lwsgw_hash(&hash_bin.bin[0], &hash);
+
+ return !!strcmp(hash.id, u.pwhash.id);
+}
+
+/* sets u->pwsalt and u->pwhash */
+
+int
+lwsgs_hash_password(struct per_vhost_data__gs *vhd,
+ const char *password, struct lwsgs_user *u)
+{
+ lwsgw_hash_bin hash_bin;
+ lwsgw_hash hash;
+ unsigned char sid_rand[20];
+ unsigned char buffer[150];
+ int n;
+
+ /* create a random salt as big as the hash */
+
+ if (lws_get_random(vhd->context, sid_rand,
+ sizeof(sid_rand)) !=
+ sizeof(sid_rand)) {
+ lwsl_err("Problem getting random for salt\n");
+ return 1;
+ }
+ sha1_to_lwsgw_hash(sid_rand, &u->pwsalt);
+
+ 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);
+
+ /* [password in ascii][salt] */
+ n = snprintf((char *)buffer, sizeof(buffer) - 1,
+ "%s-%s-%s", password, vhd->confounder, u->pwsalt.id);
+
+ /* sha1sum of password + salt */
+ lws_SHA1(buffer, n, hash_bin.bin);
+ sha1_to_lwsgw_hash(&hash_bin.bin[0], &u->pwhash);
+
+ return 0;
+}
diff --git a/plugins/protocol_lws_status.c b/plugins/protocol_lws_status.c
index 817fe76dd..912eec85c 100644
--- a/plugins/protocol_lws_status.c
+++ b/plugins/protocol_lws_status.c
@@ -25,6 +25,7 @@
#include
#include
#ifdef WIN32
+#include
#include
#endif
diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c
index 9f0cab899..8520a3153 100644
--- a/plugins/protocol_post_demo.c
+++ b/plugins/protocol_post_demo.c
@@ -23,6 +23,13 @@
#include "../lib/libwebsockets.h"
#include
+#include
+#include
+#include
+#ifdef WIN32
+#include
+#endif
+#include
struct per_session_data__post_demo {
struct lws_spa *spa;
@@ -31,7 +38,7 @@ struct per_session_data__post_demo {
char filename[256];
long file_length;
- int fd;
+ lws_filefd_type fd;
};
static const char * const param_names[] = {
diff --git a/test-server/test-server-v2.0.c b/test-server/test-server-v2.0.c
index b6feafe7d..d12c0b6ba 100644
--- a/test-server/test-server-v2.0.c
+++ b/test-server/test-server-v2.0.c
@@ -82,6 +82,9 @@ static const struct lws_http_mount mount_post = {
NULL, /* default filename if none given */
NULL,
NULL,
+ NULL,
+ NULL,
+ 0,
0,
0,
0,
@@ -104,6 +107,9 @@ static const struct lws_http_mount mount = {
"test.html", /* default filename if none given */
NULL,
NULL,
+ NULL,
+ NULL,
+ 0,
0,
0,
0,
diff --git a/test-server/test-server.h b/test-server/test-server.h
index 954740299..7158abc68 100644
--- a/test-server/test-server.h
+++ b/test-server/test-server.h
@@ -85,7 +85,7 @@ struct per_session_data__http {
char filename[256];
long file_length;
- int post_fd;
+ lws_filefd_type post_fd;
};
/*