mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-09 00:00:04 +01:00
protocol generic sessions
Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
parent
920daf10a1
commit
7a2fc442b6
34 changed files with 3596 additions and 102 deletions
122
CMakeLists.txt
122
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("---------------------------------------------------------------------")
|
||||
|
||||
|
|
383
README.generic-sessions.md
Normal file
383
README.generic-sessions.md
Normal file
|
@ -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
|
||||
|
||||
|
||||
```
|
||||
<html>
|
||||
<head>
|
||||
<script src="lwsgs.js"></script>
|
||||
<style>
|
||||
.body { font-size: 12 }
|
||||
.gstitle { font-size: 18 }
|
||||
</style>
|
||||
</head>
|
||||
<body style="background-image:url(seats.jpg)">
|
||||
<table style="width:100%;transition: max-height 2s;">
|
||||
<tr>
|
||||
<td style="vertical-align:top;text-align:left;width=200px">
|
||||
<img src="lwsgs-logo.png">
|
||||
</td>
|
||||
<td style="vertical-align:top;float:right">
|
||||
<div id=lwsgs style="text-align:right;background-color: rgba(255, 255, 255, 0.8);"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<script>lwsgs_initial();</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
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 <form> in the login page contains 'next url' hidden inputs that let the html 'program' where the form handler will go after a successful admin login, a successful user login and a failed login.
|
||||
|
||||
After a successful login, the sqlite record at the server for the current session is updated to have the logged-in username associated with it.
|
||||
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
"auth-mask" defines the autorization sector bits that must be enabled on the session to gain access.
|
||||
|
||||
"auth-mask" 0 is the default.
|
||||
|
||||
- b0 is set if you are logged in as a user at all.
|
||||
- b1 is set if you are logged in with the user configured to be admin
|
||||
- b2 is set if the account has been verified (the account configured for admin is always verified)
|
||||
|
||||
```
|
||||
{
|
||||
# things in here can always be served
|
||||
"mountpoint": "/lwsgs",
|
||||
"origin": "file:///usr/share/libwebsockets-test-server/generic-sessions",
|
||||
"origin": "callback://protocol-lws-messageboard",
|
||||
"default": "generic-sessions-login-example.html",
|
||||
"auth-mask": "0",
|
||||
"interpret": {
|
||||
".js": "protocol-lws-messageboard"
|
||||
}
|
||||
}, {
|
||||
# things in here can only be served if logged in as a user
|
||||
"mountpoint": "/lwsgs/needauth",
|
||||
"origin": "file:///usr/share/libwebsockets-test-server/generic-sessions/needauth",
|
||||
"origin": "callback://protocol-lws-messageboard",
|
||||
"default": "generic-sessions-login-example.html",
|
||||
"auth-mask": "5", # logged in as a verified user
|
||||
"interpret": {
|
||||
".js": "protocol-lws-messageboard"
|
||||
}
|
||||
}, {
|
||||
# things in here can only be served if logged in as admin
|
||||
"mountpoint": "/lwsgs/needadmin",
|
||||
"origin": "file:///usr/share/libwebsockets-test-server/generic-sessions/needadmin",
|
||||
"origin": "callback://protocol-lws-messageboard",
|
||||
"default": "generic-sessions-login-example.html",
|
||||
"auth-mask": "7", # b2 = verified (by email / or admin), b1 = admin, b0 = logged in with any user name
|
||||
"interpret": {
|
||||
".js": "protocol-lws-messageboard"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that the name of the real application protocol that uses generic-sessions
|
||||
is used, not generic-sessions itself.
|
||||
|
||||
The vhost configures the storage dir, admin credentials and session cookie lifetimes:
|
||||
|
||||
```
|
||||
"ws-protocols": [{
|
||||
"protocol-generic-sessions": {
|
||||
"status": "ok",
|
||||
"admin-user": "admin",
|
||||
|
||||
# create the pw hash like this (for the example pw, "jipdocesExunt" )
|
||||
# $ echo -n "jipdocesExunt" | sha1sum
|
||||
# 046ce9a9cca769e85798133be06ef30c9c0122c9 -
|
||||
#
|
||||
# Obviously ** change this password hash to a secret one before deploying **
|
||||
#
|
||||
"admin-password-sha1": "046ce9a9cca769e85798133be06ef30c9c0122c9",
|
||||
"session-db": "/var/www/sessions/lws.sqlite3",
|
||||
"timeout-idle-secs": "600",
|
||||
"timeout-anon-idle-secs": "1200",
|
||||
"timeout-absolute-secs": "6000",
|
||||
# the confounder is part of the salted password hashes. If this config
|
||||
# file is in a 0700 root:root dir, an attacker with apache credentials
|
||||
# will have to get the confounder out of the process image to even try
|
||||
# to guess the password hashes.
|
||||
"confounder": "Change to <=31 chars of junk",
|
||||
|
||||
"email-from": "noreply@example.com",
|
||||
"email-smtp-ip": "127.0.0.1",
|
||||
"email-expire": "3600",
|
||||
"email-helo": "myhost.com",
|
||||
"email-contact-person": "Set Me <real-person@email.com>",
|
||||
"email-confirm-url-base": "http://localhost:7681/lwsgs"
|
||||
}
|
||||
```
|
||||
|
||||
The email- related settings control generation of automatic emails for
|
||||
registration and forgotten password.
|
||||
|
||||
- `email-from`: The email address automatic emails are sent from
|
||||
|
||||
- `email-smtp-ip`: Normally 127.0.0.1, if you have a suitable server on port
|
||||
25 on your lan you can use this instead here.
|
||||
|
||||
- `email-expire`: Seconds that links sent in email will work before being
|
||||
deleted
|
||||
|
||||
- `email-helo`: HELO to use when communicating with your SMTP server
|
||||
|
||||
- `email-contact-person`: mentioned in the automatic emails as a human who can
|
||||
answer questions
|
||||
|
||||
- `email-confirm-url-base`: the URL to start links with in the emails, so the
|
||||
recipient can get back to the web server
|
||||
|
||||
The real protocol that makes use of generic-sessions must also be listed and
|
||||
any configuration it needs given
|
||||
|
||||
```
|
||||
"protocol-lws-messageboard": {
|
||||
"status": "ok",
|
||||
"message-db": "/var/www/sessions/messageboard.sqlite3"
|
||||
},
|
||||
```
|
||||
Notice the real application uses his own sqlite db, no details about how
|
||||
generic-sessions works or how it stores data are available to it.
|
||||
|
||||
|
||||
Password Confounder
|
||||
-------------------
|
||||
|
||||
You can also define a per-vhost confounder shown in the example above, used
|
||||
when aggregating the password with the salt when it is hashed. Any attacker
|
||||
will also need to get the confounder along with the database, which you can
|
||||
make harder by making the config dir only eneterable / readable by root.
|
||||
|
||||
|
||||
Preparing the db directory
|
||||
--------------------------
|
||||
|
||||
You will have to prepare the db directory so it's suitable for the lwsws user to use,
|
||||
that usually means apache, eg
|
||||
|
||||
```
|
||||
# mkdir -p /var/www/sessions
|
||||
# chown root:apache /var/www/sessions
|
||||
# chmod 770 /var/www/sessions
|
||||
```
|
||||
|
||||
Email configuration
|
||||
-------------------
|
||||
|
||||
lwsgs will can send emails by talking to an SMTP server on localhost:25. That
|
||||
will usually be sendmail or postfix, you should confirm that works first by
|
||||
itself using the `mail` application to send on it.
|
||||
|
||||
lwsgs has been tested on stock Fedora sendmail and postfix.
|
||||
|
||||
|
||||
Integration with another protocol
|
||||
---------------------------------
|
||||
|
||||
lwsgs is designed to provide sessions and accounts in a standalone and generic way.
|
||||
|
||||
But it's not useful by itself, there will always be the actual application who wants
|
||||
to make use of generic-sessions features.
|
||||
|
||||
The basic approach is the 'real' protocol handler (usually a plugin itself)
|
||||
subclasses the generic-sessions plugin and calls through to it by default.
|
||||
|
||||
The "real" protocol handler entirely deals with ws-related stuff itself, since
|
||||
generic-sessions does not use ws. But for
|
||||
|
||||
- LWS_CALLBACK_HTTP
|
||||
- LWS_CALLBACK_HTTP_BODY
|
||||
- LWS_CALLBACK_HTTP_BODY_COMPLETION
|
||||
- LWS_CALLBACK_HTTP_DROP_PROTOCOL
|
||||
|
||||
the "real" protocol handler checks if it recognizes the activity (eg, his own
|
||||
POST form URL) and if not, passes stuff through to the generic-sessions protocol callback to handle it. To simplify matters the real protocol can just pass
|
||||
through any unhandled messages to generic-sessions.
|
||||
|
||||
The "real" protocol can get a pointer to generic-sessions protocol on the
|
||||
same vhost using
|
||||
|
||||
```
|
||||
vhd->gsp = lws_vhost_name_to_protocol(vhd->vh, "protocol-generic-sessions");
|
||||
```
|
||||
|
||||
The "real" protocol must also arrange generic-sessions per_session_data in his
|
||||
own per-session allocation. To allow keeping generic-sessions opaque, the
|
||||
real protocol must allocate that space at runtime, using the pss size
|
||||
the generic-sessions protocol struct exposes
|
||||
|
||||
```
|
||||
struct per_session_data__myapp {
|
||||
void *pss_gs;
|
||||
...
|
||||
|
||||
pss->pss_gs = malloc(vhd->gsp->per_session_data_size);
|
||||
```
|
||||
|
||||
The allocation reserved for generic-sessions is then used as user_space when
|
||||
the real protocol calls through to the generic-sessions callback
|
||||
|
||||
```
|
||||
vhd->gsp->callback(wsi, reason, &pss->pss_gs, in, len);
|
||||
```
|
||||
|
||||
In that way the "real" protocol can subclass generic-sessions functionality.
|
||||
|
||||
|
||||
To ease management of these secondary allocations, there are callbacks that
|
||||
occur when a wsi binds to a protocol and when the binding is dropped. These
|
||||
should be used to malloc and free and kind of per-connection
|
||||
secondary allocations.
|
||||
|
||||
|
||||
```
|
||||
case LWS_CALLBACK_HTTP_BIND_PROTOCOL:
|
||||
if (!pss || pss->pss_gs)
|
||||
break;
|
||||
|
||||
pss->pss_gs = malloc(vhd->gsp->per_session_data_size);
|
||||
if (!pss->pss_gs)
|
||||
return -1;
|
||||
|
||||
memset(pss->pss_gs, 0, vhd->gsp->per_session_data_size);
|
||||
break;
|
||||
|
||||
case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
|
||||
if (vhd->gsp->callback(wsi, reason, pss ? pss->pss_gs : NULL, in, len))
|
||||
return -1;
|
||||
|
||||
if (pss->pss_gs) {
|
||||
free(pss->pss_gs);
|
||||
pss->pss_gs = NULL;
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
|
||||
Getting session-specific information from another protocol
|
||||
----------------------------------------------------------
|
||||
|
||||
At least at the time when someone tries to upgrade an http(s) connection to
|
||||
ws(s) with your real protocol, it is necessary to confirm the cookie the http(s)
|
||||
connection has with generic-sessions and find out his username and other info.
|
||||
|
||||
Generic sessions lets another protocol check it again by calling his callback,
|
||||
and lws itself provides a generic session info struct to pass the related data
|
||||
|
||||
```
|
||||
struct lws_session_info {
|
||||
char username[32];
|
||||
char email[100];
|
||||
char ip[72];
|
||||
unsigned int mask;
|
||||
char session[42];
|
||||
};
|
||||
```
|
||||
|
||||
```
|
||||
struct lws_session_info sinfo;
|
||||
...
|
||||
vhd->gsp->callback(wsi, LWS_CALLBACK_SESSION_INFO,
|
||||
&pss->pss_gs, &sinfo, 0);
|
||||
```
|
||||
|
||||
After the call to generic-sessions, the results can be
|
||||
|
||||
- all the strings will be zero-length and .mask zero, there is no usable cookie
|
||||
|
||||
- only .ip and .session are set: the cookie is OK but no user logged in
|
||||
|
||||
- all the strings contain information about the logged-in user
|
||||
|
||||
the real protocol can use this to reject attempts to open ws connections from
|
||||
http connections that are not authenticated; afterwards there's no need to
|
||||
check the ws connection auth status again.
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
environment:
|
||||
matrix:
|
||||
- LWS_METHOD: lwsws
|
||||
CMAKE_ARGS: -DLWS_WITH_LWSWS=1 -DLIBUV_INCLUDE_DIRS=C:\assets\libuv\include -DLIBUV_LIBRARIES=C:\assets\libuv\libuv.lib
|
||||
CMAKE_ARGS: -DLWS_WITH_LWSWS=1 -DSQLITE3_INCLUDE_DIRS=C:\assets\sqlite3 -DSQLITE3_LIBRARIES=C:\assets\sqlite3\sqlite3.lib -DLIBUV_INCLUDE_DIRS=C:\assets\libuv\include -DLIBUV_LIBRARIES=C:\assets\libuv\libuv.lib
|
||||
|
||||
- LWS_METHOD: default
|
||||
|
||||
|
@ -26,6 +26,9 @@ install:
|
|||
- Win32OpenSSL-1_0_2h.exe /silent /verysilent /sp- /suppressmsgboxes
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/nsis-3.0rc1-setup.exe
|
||||
- cmd /c start /wait nsis-3.0rc1-setup.exe /S /D=C:\nsis
|
||||
- appveyor DownloadFile https://libwebsockets.org:444/sqlite-dll-win32-x86-3130000.zip
|
||||
- mkdir c:\assets\sqlite3
|
||||
- 7z x -oc:\assets\sqlite3 sqlite-dll-win32-x86-3130000.zip
|
||||
- SET PATH=C:\Program Files\NSIS\;C:\Program Files (x86)\NSIS\;c:\nsis;%PATH%
|
||||
build:
|
||||
|
||||
|
|
|
@ -67,8 +67,15 @@ lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols
|
|||
while (n < vhost->count_protocols && &vhost->protocols[n] != prot)
|
||||
n++;
|
||||
|
||||
if (n == vhost->count_protocols)
|
||||
return NULL;
|
||||
if (n == vhost->count_protocols) {
|
||||
n = 0;
|
||||
while (n < vhost->count_protocols &&
|
||||
strcmp(vhost->protocols[n].name, prot->name))
|
||||
n++;
|
||||
|
||||
if (n == vhost->count_protocols)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vhost->protocol_vh_privs[n] = lws_zalloc(size);
|
||||
return vhost->protocol_vh_privs[n];
|
||||
|
@ -86,8 +93,15 @@ lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *pr
|
|||
n++;
|
||||
|
||||
if (n == vhost->count_protocols) {
|
||||
lwsl_err("%s: unknown protocol %p\n", __func__, prot);
|
||||
return NULL;
|
||||
n = 0;
|
||||
while (n < vhost->count_protocols &&
|
||||
strcmp(vhost->protocols[n].name, prot->name))
|
||||
n++;
|
||||
|
||||
if (n == vhost->count_protocols) {
|
||||
lwsl_err("%s: unknown protocol %p\n", __func__, prot);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return vhost->protocol_vh_privs[n];
|
||||
|
@ -165,15 +179,17 @@ lws_protocol_init(struct lws_context *context)
|
|||
* NOTE the wsi is all zeros except for the context, vh and
|
||||
* protocol ptrs so lws_get_context(wsi) etc can work
|
||||
*/
|
||||
vh->protocols[n].callback(&wsi,
|
||||
if (vh->protocols[n].callback(&wsi,
|
||||
LWS_CALLBACK_PROTOCOL_INIT, NULL,
|
||||
(void *)pvo, 0);
|
||||
(void *)pvo, 0))
|
||||
return 1;
|
||||
}
|
||||
|
||||
vh = vh->vhost_next;
|
||||
}
|
||||
|
||||
context->protocol_init_done = 1;
|
||||
lws_finalize_startup(context);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -287,12 +303,14 @@ lws_create_vhost(struct lws_context *context,
|
|||
struct lws_vhost *vh = lws_zalloc(sizeof(*vh)),
|
||||
**vh1 = &context->vhost_list;
|
||||
const struct lws_http_mount *mounts;
|
||||
const struct lws_protocol_vhost_options *pvo;
|
||||
#ifdef LWS_WITH_PLUGINS
|
||||
struct lws_plugin *plugin = context->plugin_list;
|
||||
struct lws_protocols *lwsp;
|
||||
int m, n, f = !info->pvo;
|
||||
int m, f = !info->pvo;
|
||||
#endif
|
||||
char *p;
|
||||
int n;
|
||||
|
||||
if (!vh)
|
||||
return NULL;
|
||||
|
@ -381,6 +399,21 @@ lws_create_vhost(struct lws_context *context,
|
|||
lwsl_notice(" mounting %s%s to %s\n",
|
||||
mount_protocols[mounts->origin_protocol],
|
||||
mounts->origin, mounts->mountpoint);
|
||||
|
||||
/* convert interpreter protocol names to pointers */
|
||||
pvo = mounts->interpret;
|
||||
while (pvo) {
|
||||
for (n = 0; n < vh->count_protocols; n++)
|
||||
if (!strcmp(pvo->value, vh->protocols[n].name)) {
|
||||
((struct lws_protocol_vhost_options *)pvo)->value =
|
||||
(const char *)(long)n;
|
||||
break;
|
||||
}
|
||||
if (n == vh->count_protocols)
|
||||
lwsl_err("ignoring unknown interpret protocol %s\n", pvo->value);
|
||||
pvo = pvo->next;
|
||||
}
|
||||
|
||||
mounts = mounts->mount_next;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,9 @@ static const char * const paths_vhosts[] = {
|
|||
"vhosts[].access-log",
|
||||
"vhosts[].mounts[].mountpoint",
|
||||
"vhosts[].mounts[].origin",
|
||||
"vhosts[].mounts[].protocol",
|
||||
"vhosts[].mounts[].default",
|
||||
"vhosts[].mounts[].auth-mask",
|
||||
"vhosts[].mounts[].cgi-timeout",
|
||||
"vhosts[].mounts[].cgi-env[].*",
|
||||
"vhosts[].mounts[].cache-max-age",
|
||||
|
@ -67,6 +69,7 @@ static const char * const paths_vhosts[] = {
|
|||
"vhosts[].mounts[].cache-revalidate",
|
||||
"vhosts[].mounts[].cache-intermediaries",
|
||||
"vhosts[].mounts[].extra-mimetypes.*",
|
||||
"vhosts[].mounts[].interpret.*",
|
||||
"vhosts[].ws-protocols[].*.*",
|
||||
"vhosts[].ws-protocols[].*",
|
||||
"vhosts[].ws-protocols[]",
|
||||
|
@ -94,7 +97,9 @@ enum lejp_vhost_paths {
|
|||
LEJPVP_ACCESS_LOG,
|
||||
LEJPVP_MOUNTPOINT,
|
||||
LEJPVP_ORIGIN,
|
||||
LEJPVP_MOUNT_PROTOCOL,
|
||||
LEJPVP_DEFAULT,
|
||||
LEJPVP_DEFAULT_AUTH_MASK,
|
||||
LEJPVP_CGI_TIMEOUT,
|
||||
LEJPVP_CGI_ENV,
|
||||
LEJPVP_MOUNT_CACHE_MAX_AGE,
|
||||
|
@ -102,6 +107,7 @@ enum lejp_vhost_paths {
|
|||
LEJPVP_MOUNT_CACHE_REVALIDATE,
|
||||
LEJPVP_MOUNT_CACHE_INTERMEDIARIES,
|
||||
LEJPVP_MOUNT_EXTRA_MIMETYPES,
|
||||
LEJPVP_MOUNT_INTERPRET,
|
||||
LEJPVP_PROTOCOL_NAME_OPT,
|
||||
LEJPVP_PROTOCOL_NAME,
|
||||
LEJPVP_PROTOCOL,
|
||||
|
@ -154,6 +160,7 @@ struct jpargs {
|
|||
|
||||
struct lws_protocol_vhost_options *pvo;
|
||||
struct lws_protocol_vhost_options *pvo_em;
|
||||
struct lws_protocol_vhost_options *pvo_int;
|
||||
struct lws_http_mount m;
|
||||
const char **plugin_dirs;
|
||||
int count_plugin_dirs;
|
||||
|
@ -363,8 +370,10 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
for (n = 0; n < ARRAY_SIZE(mount_protocols); n++)
|
||||
if (!strncmp(a->m.origin, mount_protocols[n],
|
||||
strlen(mount_protocols[n]))) {
|
||||
lwsl_err("----%s\n", a->m.origin);
|
||||
m->origin_protocol = n;
|
||||
m->origin = a->m.origin + strlen(mount_protocols[n]);
|
||||
m->origin = a->m.origin +
|
||||
strlen(mount_protocols[n]);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -424,11 +433,18 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
a->m.mountpoint_len = (unsigned char)strlen(ctx->buf);
|
||||
break;
|
||||
case LEJPVP_ORIGIN:
|
||||
a->m.origin = a->p;
|
||||
if (!strncmp(ctx->buf, "callback://", 11))
|
||||
a->m.protocol = a->p + 11;
|
||||
|
||||
if (!a->m.origin)
|
||||
a->m.origin = a->p;
|
||||
break;
|
||||
case LEJPVP_DEFAULT:
|
||||
a->m.def = a->p;
|
||||
break;
|
||||
case LEJPVP_DEFAULT_AUTH_MASK:
|
||||
a->m.auth_mask = atoi(ctx->buf);
|
||||
return 0;
|
||||
case LEJPVP_MOUNT_CACHE_MAX_AGE:
|
||||
a->m.cache_max_age = atoi(ctx->buf);
|
||||
return 0;
|
||||
|
@ -505,6 +521,22 @@ lejp_vhosts_cb(struct lejp_ctx *ctx, char reason)
|
|||
a->pvo_em->options = NULL;
|
||||
break;
|
||||
|
||||
case LEJPVP_MOUNT_INTERPRET:
|
||||
a->pvo_int = lwsws_align(a);
|
||||
a->p += sizeof(*a->pvo_int);
|
||||
|
||||
n = lejp_get_wildcard(ctx, 0, a->p, a->end - a->p);
|
||||
/* ie, enable this protocol, no options yet */
|
||||
a->pvo_int->next = a->m.interpret;
|
||||
a->m.interpret = a->pvo_int;
|
||||
a->pvo_int->name = a->p;
|
||||
lwsl_notice(" adding interpret %s -> %s\n", a->p,
|
||||
ctx->buf);
|
||||
a->p += n;
|
||||
a->pvo_int->value = a->p;
|
||||
a->pvo_int->options = NULL;
|
||||
break;
|
||||
|
||||
case LEJPVP_ENABLE_CLIENT_SSL:
|
||||
a->enable_client_ssl = arg_to_bool(ctx->buf);
|
||||
return 0;
|
||||
|
|
|
@ -1735,39 +1735,6 @@ lws_socket_bind(struct lws_vhost *vhost, int sockfd, int port,
|
|||
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
|
||||
static int
|
||||
urlencode(const char *in, int inlen, char *out, int outlen)
|
||||
{
|
||||
char *start = out, *end = out + outlen;
|
||||
|
||||
while (inlen-- && out < end - 4) {
|
||||
if ((*in >= 'A' && *in <= 'Z') ||
|
||||
(*in >= 'a' && *in <= 'z') ||
|
||||
(*in >= '0' && *in <= '9') ||
|
||||
*in == '-' ||
|
||||
*in == '_' ||
|
||||
*in == '.' ||
|
||||
*in == '~') {
|
||||
*out++ = *in++;
|
||||
continue;
|
||||
}
|
||||
if (*in == ' ') {
|
||||
*out++ = '+';
|
||||
in++;
|
||||
continue;
|
||||
}
|
||||
*out++ = '%';
|
||||
*out++ = hex[(*in) >> 4];
|
||||
*out++ = hex[(*in++) & 15];
|
||||
}
|
||||
*out = '\0';
|
||||
|
||||
if (out >= end - 4)
|
||||
return -1;
|
||||
|
||||
return out - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* lws_sql_purify() - like strncpy but with escaping for sql quotes
|
||||
*
|
||||
|
@ -1787,7 +1754,7 @@ lws_sql_purify(char *escaped, const char *string, int len)
|
|||
|
||||
while (*p && len-- > 2) {
|
||||
if (*p == '\'') {
|
||||
*q++ = '\\';
|
||||
*q++ = '\'';
|
||||
*q++ = '\'';
|
||||
len --;
|
||||
p++;
|
||||
|
@ -1964,6 +1931,39 @@ lws_is_cgi(struct lws *wsi) {
|
|||
|
||||
#ifdef LWS_WITH_CGI
|
||||
|
||||
static int
|
||||
urlencode(const char *in, int inlen, char *out, int outlen)
|
||||
{
|
||||
char *start = out, *end = out + outlen;
|
||||
|
||||
while (inlen-- && out < end - 4) {
|
||||
if ((*in >= 'A' && *in <= 'Z') ||
|
||||
(*in >= 'a' && *in <= 'z') ||
|
||||
(*in >= '0' && *in <= '9') ||
|
||||
*in == '-' ||
|
||||
*in == '_' ||
|
||||
*in == '.' ||
|
||||
*in == '~') {
|
||||
*out++ = *in++;
|
||||
continue;
|
||||
}
|
||||
if (*in == ' ') {
|
||||
*out++ = '+';
|
||||
in++;
|
||||
continue;
|
||||
}
|
||||
*out++ = '%';
|
||||
*out++ = hex[(*in) >> 4];
|
||||
*out++ = hex[(*in++) & 15];
|
||||
}
|
||||
*out = '\0';
|
||||
|
||||
if (out >= end - 4)
|
||||
return -1;
|
||||
|
||||
return out - start;
|
||||
}
|
||||
|
||||
static struct lws *
|
||||
lws_create_basic_wsi(struct lws_context *context, int tsi)
|
||||
{
|
||||
|
|
|
@ -476,6 +476,12 @@ enum lws_callback_reasons {
|
|||
LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47,
|
||||
LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48,
|
||||
LWS_CALLBACK_HTTP_DROP_PROTOCOL = 49,
|
||||
LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 50,
|
||||
LWS_CALLBACK_PROCESS_HTML = 51,
|
||||
LWS_CALLBACK_ADD_HEADERS = 52,
|
||||
LWS_CALLBACK_SESSION_INFO = 53,
|
||||
|
||||
LWS_CALLBACK_GS_EVENT = 54,
|
||||
|
||||
/****** add new things just above ---^ ******/
|
||||
|
||||
|
@ -1338,6 +1344,14 @@ struct lws_protocols {
|
|||
* This is part of the ABI, don't needlessly break compatibility */
|
||||
};
|
||||
|
||||
struct lws_session_info {
|
||||
char username[32];
|
||||
char email[100];
|
||||
char ip[72];
|
||||
unsigned int mask;
|
||||
char session[42];
|
||||
};
|
||||
|
||||
struct lws_process_html_args {
|
||||
char *p;
|
||||
int len;
|
||||
|
@ -1362,6 +1376,33 @@ LWS_VISIBLE LWS_EXTERN int
|
|||
lws_chunked_html_process(struct lws_process_html_args *args,
|
||||
struct lws_process_html_state *s);
|
||||
|
||||
/* generic-sessions public api */
|
||||
|
||||
#define LWSGS_EMAIL_CONTENT_SIZE 16384
|
||||
|
||||
/* SHA-1 binary and hexified versions */
|
||||
typedef struct { unsigned char bin[20]; } lwsgw_hash_bin;
|
||||
typedef struct { char id[41]; } lwsgw_hash;
|
||||
|
||||
enum lwsgs_auth_bits {
|
||||
LWSGS_AUTH_LOGGED_IN = 1,
|
||||
LWSGS_AUTH_ADMIN = 2,
|
||||
LWSGS_AUTH_VERIFIED = 4,
|
||||
LWSGS_AUTH_FORGOT_FLOW = 8,
|
||||
};
|
||||
|
||||
enum lws_gs_event {
|
||||
LWSGSE_CREATED,
|
||||
LWSGSE_DELETED
|
||||
};
|
||||
|
||||
struct lws_gs_event_args {
|
||||
enum lws_gs_event event;
|
||||
const char *username;
|
||||
const char *email;
|
||||
};
|
||||
|
||||
|
||||
enum lws_ext_options_types {
|
||||
EXTARG_NONE,
|
||||
EXTARG_DEC,
|
||||
|
@ -1470,12 +1511,15 @@ struct lws_http_mount {
|
|||
const char *mountpoint; /* mountpoint in http pathspace, eg, "/" */
|
||||
const char *origin; /* path to be mounted, eg, "/var/www/warmcat.com" */
|
||||
const char *def; /* default target, eg, "index.html" */
|
||||
const char *protocol; /* "protocol-name" to handle mount */
|
||||
|
||||
const struct lws_protocol_vhost_options *cgienv;
|
||||
const struct lws_protocol_vhost_options *extra_mimetypes;
|
||||
const struct lws_protocol_vhost_options *interpret;
|
||||
|
||||
int cgi_timeout;
|
||||
int cache_max_age;
|
||||
unsigned int auth_mask;
|
||||
|
||||
unsigned int cache_reusable:1;
|
||||
unsigned int cache_revalidate:1;
|
||||
|
@ -1737,6 +1781,9 @@ LWS_VISIBLE LWS_EXTERN int
|
|||
lws_init_vhost_client_ssl(const struct lws_context_creation_info *info,
|
||||
struct lws_vhost *vhost);
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const struct lws_protocols *
|
||||
lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name);
|
||||
|
||||
/* deprecated: use lws_get_vhost() */
|
||||
LWS_VISIBLE LWS_EXTERN struct lws_vhost *
|
||||
lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED;
|
||||
|
|
49
lib/output.c
49
lib/output.c
|
@ -568,7 +568,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
{
|
||||
struct lws_context *context = wsi->context;
|
||||
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
|
||||
unsigned long amount;
|
||||
struct lws_process_html_args args;
|
||||
unsigned long amount, poss;
|
||||
unsigned char *p = pt->serv_buf;
|
||||
int n, m;
|
||||
|
||||
while (wsi->http2_substream || !lws_send_pipe_choked(wsi)) {
|
||||
|
@ -585,31 +587,58 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
|
|||
if (wsi->u.http.filepos == wsi->u.http.filelen)
|
||||
goto all_sent;
|
||||
|
||||
if (lws_plat_file_read(wsi, wsi->u.http.fd, &amount,
|
||||
pt->serv_buf,
|
||||
context->pt_serv_buf_size) < 0)
|
||||
poss = context->pt_serv_buf_size;
|
||||
|
||||
if (wsi->sending_chunked) {
|
||||
/* we need to drop the chunk size in here */
|
||||
p += 10;
|
||||
/* allow for the chunk to grow by 128 in translation */
|
||||
poss -= 10 + 128;
|
||||
}
|
||||
|
||||
if (lws_plat_file_read(wsi, wsi->u.http.fd, &amount, p, poss) < 0)
|
||||
return -1; /* caller will close */
|
||||
|
||||
n = (int)amount;
|
||||
if (n) {
|
||||
lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,
|
||||
context->timeout_secs);
|
||||
wsi->u.http.filepos += n;
|
||||
m = lws_write(wsi, pt->serv_buf, n,
|
||||
|
||||
if (wsi->sending_chunked) {
|
||||
args.p = (char *)p;
|
||||
args.len = n;
|
||||
args.max_len = poss + 128;
|
||||
args.final = wsi->u.http.filepos + n ==
|
||||
wsi->u.http.filelen;
|
||||
if (user_callback_handle_rxflow(
|
||||
wsi->vhost->protocols[(int)wsi->protocol_interpret_idx].callback, wsi,
|
||||
LWS_CALLBACK_PROCESS_HTML,
|
||||
wsi->user_space, &args, 0) < 0)
|
||||
return -1;
|
||||
n = args.len;
|
||||
p = (unsigned char *)args.p;
|
||||
}
|
||||
|
||||
m = lws_write(wsi, p, n,
|
||||
wsi->u.http.filepos == wsi->u.http.filelen ?
|
||||
LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);
|
||||
LWS_WRITE_HTTP_FINAL :
|
||||
LWS_WRITE_HTTP
|
||||
);
|
||||
if (m < 0)
|
||||
return -1;
|
||||
|
||||
if (m != n)
|
||||
wsi->u.http.filepos += amount;
|
||||
if (m != n) {
|
||||
/* adjust for what was not sent */
|
||||
if (lws_plat_file_seek_cur(wsi, wsi->u.http.fd,
|
||||
m - n) ==
|
||||
(unsigned long)-1)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
all_sent:
|
||||
if (!wsi->trunc_len && wsi->u.http.filepos == wsi->u.http.filelen) {
|
||||
if (!wsi->trunc_len &&
|
||||
wsi->u.http.filepos == wsi->u.http.filelen) {
|
||||
wsi->state = LWSS_HTTP;
|
||||
/* we might be in keepalive, so close it off here */
|
||||
lws_plat_file_close(wsi, wsi->u.http.fd);
|
||||
|
@ -622,11 +651,11 @@ all_sent:
|
|||
LWS_CALLBACK_HTTP_FILE_COMPLETION,
|
||||
wsi->user_space, NULL, 0) < 0)
|
||||
return -1;
|
||||
|
||||
return 1; /* >0 indicates completed */
|
||||
}
|
||||
}
|
||||
|
||||
lwsl_info("choked before able to send whole file (post)\n");
|
||||
lws_callback_on_writable(wsi);
|
||||
|
||||
return 0; /* indicates further processing must be done */
|
||||
|
|
|
@ -1257,6 +1257,7 @@ struct lws {
|
|||
unsigned int cache_revalidate:1;
|
||||
unsigned int cache_intermediaries:1;
|
||||
unsigned int favoured_pollin:1;
|
||||
unsigned int sending_chunked:1;
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
unsigned int access_log_pending:1;
|
||||
#endif
|
||||
|
@ -1295,6 +1296,7 @@ struct lws {
|
|||
char pending_timeout; /* enum pending_timeout */
|
||||
char pps; /* enum lws_pending_protocol_send */
|
||||
char tsi; /* thread service index we belong to */
|
||||
char protocol_interpret_idx;
|
||||
#ifdef LWS_WITH_CGI
|
||||
char cgi_channel; /* which of stdin/out/err */
|
||||
char hdr_state;
|
||||
|
|
193
lib/server.c
193
lib/server.c
|
@ -206,6 +206,27 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* lws_vhost_name_to_protocol() - get vhost's protocol object from its name
|
||||
*
|
||||
* @vh: vhost to search
|
||||
* @name: protocol name
|
||||
*
|
||||
* Returns NULL or a pointer to the vhost's protocol of the requested name
|
||||
*/
|
||||
|
||||
LWS_VISIBLE LWS_EXTERN const struct lws_protocols *
|
||||
lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < vh->count_protocols; n++)
|
||||
if (!strcmp(name, vh->protocols[n].name))
|
||||
return &vh->protocols[n];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *
|
||||
get_mimetype(const char *file, const struct lws_http_mount *m)
|
||||
{
|
||||
|
@ -271,11 +292,13 @@ static int
|
|||
lws_http_serve(struct lws *wsi, char *uri, const char *origin,
|
||||
const struct lws_http_mount *m)
|
||||
{
|
||||
const struct lws_protocol_vhost_options *pvo = m->interpret;
|
||||
struct lws_process_html_args args;
|
||||
const char *mimetype;
|
||||
#ifndef _WIN32_WCE
|
||||
struct stat st;
|
||||
#endif
|
||||
char path[256], sym[256];
|
||||
char path[256], sym[512];
|
||||
unsigned char *p = (unsigned char *)sym + 32 + LWS_PRE, *start = p;
|
||||
unsigned char *end = p + sizeof(sym) - 32 - LWS_PRE;
|
||||
#if !defined(WIN32)
|
||||
|
@ -342,7 +365,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
|
|||
return -1;
|
||||
|
||||
n = lws_write(wsi, start, p - start,
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
LWS_WRITE_HTTP_HEADERS);
|
||||
if (n != (p - start)) {
|
||||
lwsl_err("_write returned %d from %d\n", n, p - start);
|
||||
return -1;
|
||||
|
@ -363,6 +386,43 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
|
|||
goto bail;
|
||||
}
|
||||
|
||||
wsi->sending_chunked = 0;
|
||||
|
||||
/*
|
||||
* check if this is in the list of file suffixes to be interpreted by
|
||||
* a protocol
|
||||
*/
|
||||
while (pvo) {
|
||||
n = strlen(path);
|
||||
if (n > (int)strlen(pvo->name) &&
|
||||
!strcmp(&path[n - strlen(pvo->name)], pvo->name)) {
|
||||
wsi->sending_chunked = 1;
|
||||
wsi->protocol_interpret_idx = (char)(long)pvo->value;
|
||||
lwsl_info("want %s interpreted by %s\n", path,
|
||||
wsi->vhost->protocols[(int)(long)(pvo->value)].name);
|
||||
wsi->protocol = &wsi->vhost->protocols[(int)(long)(pvo->value)];
|
||||
if (lws_ensure_user_space(wsi))
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
pvo = pvo->next;
|
||||
}
|
||||
|
||||
if (m->protocol) {
|
||||
const struct lws_protocols *pp = lws_vhost_name_to_protocol(
|
||||
wsi->vhost, m->protocol);
|
||||
|
||||
wsi->protocol = pp;
|
||||
if (lws_ensure_user_space(wsi))
|
||||
return -1;
|
||||
args.p = (char *)p;
|
||||
args.max_len = end - p;
|
||||
if (pp->callback(wsi, LWS_CALLBACK_ADD_HEADERS,
|
||||
wsi->user_space, &args, 0))
|
||||
return -1;
|
||||
p = (unsigned char *)args.p;
|
||||
}
|
||||
|
||||
n = lws_serve_http_file(wsi, path, mimetype, (char *)start, p - start);
|
||||
|
||||
if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
|
||||
|
@ -381,6 +441,7 @@ lws_http_action(struct lws *wsi)
|
|||
enum http_connection_type connection_type;
|
||||
enum http_version request_version;
|
||||
char content_length_str[32];
|
||||
struct lws_process_html_args args;
|
||||
const struct lws_http_mount *hm, *hit = NULL;
|
||||
unsigned int n, count = 0;
|
||||
char http_version_str[10];
|
||||
|
@ -409,6 +470,9 @@ lws_http_action(struct lws *wsi)
|
|||
#endif
|
||||
};
|
||||
#endif
|
||||
static const char * const oprot[] = {
|
||||
"http://", "https://"
|
||||
};
|
||||
|
||||
/* it's not websocket.... shall we accept it as http? */
|
||||
|
||||
|
@ -614,7 +678,8 @@ lws_http_action(struct lws *wsi)
|
|||
) {
|
||||
if (hm->origin_protocol == LWSMPRO_CALLBACK ||
|
||||
((hm->origin_protocol == LWSMPRO_CGI ||
|
||||
lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) &&
|
||||
lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||
|
||||
hm->protocol) &&
|
||||
hm->mountpoint_len > best)) {
|
||||
best = hm->mountpoint_len;
|
||||
hit = hm;
|
||||
|
@ -628,6 +693,38 @@ lws_http_action(struct lws *wsi)
|
|||
lwsl_debug("*** hit %d %d %s\n", hit->mountpoint_len,
|
||||
hit->origin_protocol , hit->origin);
|
||||
|
||||
if (hit->protocol) {
|
||||
const struct lws_protocols *pp = lws_vhost_name_to_protocol(
|
||||
wsi->vhost, hit->protocol);
|
||||
|
||||
if (!pp) {
|
||||
lwsl_err("unknown protocol %s\n", hit->protocol);
|
||||
return 1;
|
||||
}
|
||||
|
||||
wsi->protocol = pp;
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_err("Unable to allocate user space\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
lwsl_info("wsi %s protocol '%s'\n", uri_ptr, wsi->protocol->name);
|
||||
|
||||
args.p = uri_ptr;
|
||||
args.len = uri_len;
|
||||
args.max_len = hit->auth_mask;
|
||||
args.final = 0; /* used to signal callback dealt with it */
|
||||
|
||||
n = wsi->protocol->callback(wsi, LWS_CALLBACK_CHECK_ACCESS_RIGHTS,
|
||||
wsi->user_space, &args, 0);
|
||||
if (n) {
|
||||
lws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,
|
||||
NULL);
|
||||
goto bail_nuke_ah;
|
||||
}
|
||||
if (args.final) /* callback completely handled it well */
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* if we have a mountpoint like https://xxx.com/yyy
|
||||
* there is an implied / at the end for our purposes since
|
||||
|
@ -649,12 +746,12 @@ lws_http_action(struct lws *wsi)
|
|||
(*s != '/' ||
|
||||
(hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
||||
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)) &&
|
||||
(hit->origin_protocol != LWSMPRO_CGI && hit->origin_protocol != LWSMPRO_CALLBACK)) {
|
||||
(hit->origin_protocol != LWSMPRO_CGI &&
|
||||
hit->origin_protocol != LWSMPRO_CALLBACK //&&
|
||||
//hit->protocol == NULL
|
||||
)) {
|
||||
unsigned char *start = pt->serv_buf + LWS_PRE,
|
||||
*p = start, *end = p + 512;
|
||||
static const char *oprot[] = {
|
||||
"http://", "https://"
|
||||
};
|
||||
|
||||
lwsl_debug("Doing 301 '%s' org %s\n", s, hit->origin);
|
||||
|
||||
|
@ -662,13 +759,14 @@ lws_http_action(struct lws *wsi)
|
|||
goto bail_nuke_ah;
|
||||
|
||||
/* > at start indicates deal with by redirect */
|
||||
if (hit->origin_protocol & 4)
|
||||
if (hit->origin_protocol == LWSMPRO_REDIR_HTTP ||
|
||||
hit->origin_protocol == LWSMPRO_REDIR_HTTPS)
|
||||
n = snprintf((char *)end, 256, "%s%s",
|
||||
oprot[hit->origin_protocol & 1],
|
||||
hit->origin);
|
||||
else
|
||||
n = snprintf((char *)end, 256,
|
||||
"https://%s/%s/",
|
||||
"%s%s%s/", oprot[lws_is_ssl(wsi)],
|
||||
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),
|
||||
uri_ptr);
|
||||
|
||||
|
@ -686,32 +784,36 @@ lws_http_action(struct lws *wsi)
|
|||
* For the duration of this http transaction, bind us to the
|
||||
* associated protocol
|
||||
*/
|
||||
if (hit->origin_protocol == LWSMPRO_CALLBACK) {
|
||||
|
||||
for (n = 0; n < (unsigned int)wsi->vhost->count_protocols; n++)
|
||||
if (!strcmp(wsi->vhost->protocols[n].name,
|
||||
hit->origin)) {
|
||||
if (hit->origin_protocol == LWSMPRO_CALLBACK ||
|
||||
(hit->protocol && lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))) {
|
||||
if (! hit->protocol) {
|
||||
for (n = 0; n < (unsigned int)wsi->vhost->count_protocols; n++)
|
||||
if (!strcmp(wsi->vhost->protocols[n].name,
|
||||
hit->origin)) {
|
||||
|
||||
if (wsi->protocol != &wsi->vhost->protocols[n])
|
||||
if (!wsi->user_space_externally_allocated)
|
||||
lws_free_set_NULL(wsi->user_space);
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_err("Unable to allocate user space\n");
|
||||
if (wsi->protocol != &wsi->vhost->protocols[n])
|
||||
if (!wsi->user_space_externally_allocated)
|
||||
lws_free_set_NULL(wsi->user_space);
|
||||
wsi->protocol = &wsi->vhost->protocols[n];
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_err("Unable to allocate user space\n");
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
if (n == wsi->vhost->count_protocols) {
|
||||
n = -1;
|
||||
lwsl_err("Unable to find plugin '%s'\n",
|
||||
hit->origin);
|
||||
}
|
||||
|
||||
if (n == wsi->vhost->count_protocols) {
|
||||
n = -1;
|
||||
lwsl_err("Unable to find plugin '%s'\n",
|
||||
hit->origin);
|
||||
}
|
||||
|
||||
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
||||
wsi->user_space, uri_ptr, uri_len);
|
||||
wsi->user_space,
|
||||
uri_ptr + hit->mountpoint_len,
|
||||
uri_len - hit->mountpoint_len);
|
||||
|
||||
goto after;
|
||||
}
|
||||
|
@ -765,17 +867,35 @@ lws_http_action(struct lws *wsi)
|
|||
wsi->cache_revalidate = hit->cache_revalidate;
|
||||
wsi->cache_intermediaries = hit->cache_intermediaries;
|
||||
|
||||
|
||||
n = lws_http_serve(wsi, s, hit->origin, hit);
|
||||
if (n) {
|
||||
/*
|
||||
* lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
|
||||
*/
|
||||
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
||||
if (hit->protocol) {
|
||||
const struct lws_protocols *pp = lws_vhost_name_to_protocol(
|
||||
wsi->vhost, hit->protocol);
|
||||
|
||||
wsi->protocol = pp;
|
||||
if (lws_ensure_user_space(wsi)) {
|
||||
lwsl_err("Unable to allocate user space\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
n = pp->callback(wsi, LWS_CALLBACK_HTTP,
|
||||
wsi->user_space,
|
||||
uri_ptr + hit->mountpoint_len,
|
||||
uri_len - hit->mountpoint_len);
|
||||
} else
|
||||
n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
|
||||
wsi->user_space, uri_ptr, uri_len);
|
||||
}
|
||||
} else {
|
||||
/* deferred cleanup and reset to protocols[0] */
|
||||
|
||||
lwsl_notice("no hit\n");
|
||||
|
||||
if (wsi->protocol != &wsi->vhost->protocols[0])
|
||||
if (!wsi->user_space_externally_allocated)
|
||||
lws_free_set_NULL(wsi->user_space);
|
||||
|
@ -1283,6 +1403,7 @@ lws_http_transaction_completed(struct lws *wsi)
|
|||
wsi->state = LWSS_HTTP;
|
||||
wsi->mode = LWSCM_HTTP_SERVING;
|
||||
wsi->u.http.content_length = 0;
|
||||
wsi->u.http.content_remain = 0;
|
||||
wsi->hdr_parsing_completed = 0;
|
||||
#ifdef LWS_WITH_ACCESS_LOG
|
||||
wsi->access_log.sent = 0;
|
||||
|
@ -1813,8 +1934,16 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
|
|||
(unsigned char *)content_type,
|
||||
strlen(content_type), &p, end))
|
||||
return -1;
|
||||
if (lws_add_http_header_content_length(wsi, wsi->u.http.filelen, &p, end))
|
||||
return -1;
|
||||
|
||||
if (!wsi->sending_chunked) {
|
||||
if (lws_add_http_header_content_length(wsi, wsi->u.http.filelen, &p, end))
|
||||
return -1;
|
||||
} else {
|
||||
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_TRANSFER_ENCODING,
|
||||
(unsigned char *)"chunked",
|
||||
7, &p, end))
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wsi->cache_secs && wsi->cache_reuse) {
|
||||
if (wsi->cache_revalidate) {
|
||||
|
@ -2277,8 +2406,6 @@ lws_urldecode_s_destroy(struct lws_urldecode_stateful *s)
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
lwsl_notice("%s\n", __func__);
|
||||
|
||||
if (s->state != US_IDLE)
|
||||
ret = -1;
|
||||
|
||||
|
|
5
plugins/generic-sessions/assets/admin-login.html
Normal file
5
plugins/generic-sessions/assets/admin-login.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
This is an example destination that will appear after successful Admin login.
|
||||
|
||||
This URL cannot be served if you're not logged in as admin.
|
||||
</html>
|
3
plugins/generic-sessions/assets/failed-login.html
Normal file
3
plugins/generic-sessions/assets/failed-login.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<html>
|
||||
This is an example destination that will appear after a failed login
|
||||
</html>
|
35
plugins/generic-sessions/assets/index.html
Normal file
35
plugins/generic-sessions/assets/index.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="lwsgs.js"></script>
|
||||
<style>
|
||||
.body { font-size: 12 }
|
||||
.gstitle { font-size: 18 }
|
||||
</style>
|
||||
</head>
|
||||
<body style="background-image:url(seats.jpg)">
|
||||
<table style="width:100%;transition: max-height 2s;">
|
||||
<tr>
|
||||
<td style="vertical-align:top;text-align:left;width=200px">
|
||||
<img src="lwsgs-logo.png">
|
||||
</td>
|
||||
<td style="vertical-align:top;float:right">
|
||||
<div id=lwsgs style="text-align:right;background-color: rgba(255, 255, 255, 0.8);"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td colspan=2>
|
||||
<div id="nolog" style="display:none">
|
||||
Register / Login to see the messages
|
||||
</div>
|
||||
<div id="logged" style="display:none">
|
||||
Logged in
|
||||
</div>
|
||||
</td></tr>
|
||||
</table>
|
||||
</form>
|
||||
<script>lwsgs_initial();
|
||||
document.getElementById("nolog").style.display = !!lwsgs_user ? "none" : "inline";
|
||||
document.getElementById("logged").style.display = !lwsgs_user ? "none" : "inline";
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
BIN
plugins/generic-sessions/assets/lwsgs-logo.png
Normal file
BIN
plugins/generic-sessions/assets/lwsgs-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
476
plugins/generic-sessions/assets/lwsgs.js
Normal file
476
plugins/generic-sessions/assets/lwsgs.js
Normal file
|
@ -0,0 +1,476 @@
|
|||
<!-- lwsgs rewrites the below $vars $v $v into the correct values on the fly -->
|
||||
|
||||
var lwsgs_user = "$lwsgs_user";
|
||||
var lwsgs_auth = "$lwsgs_auth";
|
||||
var lwsgs_email = "$lwsgs_email";
|
||||
|
||||
var lwsgs_html = '\
|
||||
<div id="dlogin" style="display:none"> \
|
||||
<form action="lwsgs-login" method="post"> \
|
||||
<input type="hidden" name="admin" value="needadmin/admin-login.html"> \
|
||||
<input type="hidden" name="good" value="index.html"> \
|
||||
<input type="hidden" name="bad" value="failed-login.html"> \
|
||||
<input type="hidden" name="forgot-good" value="sent-forgot-ok.html"> \
|
||||
<input type="hidden" name="forgot-bad" value="sent-forgot-fail.html">\
|
||||
<input type="hidden" name="forgot-post-good" value="post-forgot-ok.html">\
|
||||
<input type="hidden" name="forgot-post-bad" value="post-forgot-fail.html">\
|
||||
<table style="vertical-align:top;text-align:right"\
|
||||
<tr>\
|
||||
<td>User Name\
|
||||
<input type="text" size="10" id="username" name="username" oninput="lwsgs_update()" onchange="lwsgs_update()"></td>\
|
||||
<td>Password\
|
||||
<input type="password" id="password" size="10" name="password" oninput="lwsgs_update()" onchange="lwsgs_update()"><div id="pw1"></div></td>\
|
||||
</tr><tr>\
|
||||
<td colspan="2" style="text-align:center"><input type="submit" id="login" name="login" value="Login" style="margin: 4px; padding: 2px; font-weight=bold;">\
|
||||
<input type="submit" id="forgot" name="forgot" value="Forgot password" style="margin: 2px; padding: 2px">\
|
||||
<input type="button" onclick="lwsgs_open_registration()" value="Sign up" style="margin: 2px; padding: 2px"></td>\
|
||||
</tr>\
|
||||
</table>\
|
||||
</form>\
|
||||
</div>\
|
||||
\
|
||||
<div id="dlogout" style="display:none;text-align:right">\
|
||||
<form action="lwsgs-logout" method="post" style="text-align:right">\
|
||||
<input type="hidden" name="good" value="index.html">\
|
||||
<table style="vertical-align:top;text-align:right">\
|
||||
<tr><td><span id=grav></span></td>\
|
||||
<td style="text-align:center"><table><tr><td style="text-align:center">\
|
||||
<a href="#" onclick="lwsgs_select_change(); event.preventDefault();">\
|
||||
<span id="curuser"></span></a></td></tr><tr>\
|
||||
<td style="text-align:center"><input type="submit" name="logout" value="Logout" style="margin: 2px; padding: 2px"></td>\
|
||||
</tr></table></td></tr>\
|
||||
</table>\
|
||||
</form></div>\
|
||||
\
|
||||
<div id="dregister" style="display:none">\
|
||||
<form action="lwsgs-login" method="post">\
|
||||
<input type="hidden" name="admin" value="needadmin/admin-login.html">\
|
||||
<input type="hidden" name="good" value="successful-login.html">\
|
||||
<input type="hidden" name="bad" value="failed-login.html">\
|
||||
<input type="hidden" name="reg-good" value="post-register-ok.html">\
|
||||
<input type="hidden" name="reg-bad" value="post-register-fail.html">\
|
||||
<input type="hidden" name="forgot-good" value="sent-forgot-ok.html">\
|
||||
<input type="hidden" name="forgot-bad" value="sent-forgot-fail.html">\
|
||||
<input type="hidden" name="forgot-post-good" value="post-forgot-ok.html">\
|
||||
<input type="hidden" name="forgot-post-bad" value="post-forgot-fail.html">\
|
||||
<table style="vertical-align:top;text-align:left">\
|
||||
<tr>\
|
||||
<td colspan=2 align=center>\
|
||||
<span id="curuser"></span>\
|
||||
<script>\
|
||||
if (lwsgs_user)\
|
||||
document.getElementById("curuser").innerHTML = "currently logged in as " + lwsgs_san(lwsgs_user) + "</br>";\
|
||||
</script>\
|
||||
<b>Please enter your details to register</b>:\
|
||||
</td>\
|
||||
</tr>\
|
||||
<tr><td align=right>\
|
||||
User Name:</td>\
|
||||
<td><input type="text" size="10" id="rusername" name="username" oninput="lwsgs_rupdate(); lwsgs_check_user();"> <span id=uchk></span></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td align=right>Password:</td>\
|
||||
<td><input type="password" size="10" id="rpassword" name="password" oninput="lwsgs_rupdate()"> <span id="rpw1"></span></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td align=right><span id="pw2">Password (again):</span></td>\
|
||||
<td><input type="password" size="10" id="password2" name="password2" oninput="lwsgs_rupdate()"> <span id="match"></span></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td align=right>Email:</td>\
|
||||
<td><input type="email" size="10" id="email" name="email"\
|
||||
placeholder="me@example.com" oninput="lwsgs_rupdate(); lwsgs_check_email(\'email\')"> <span id=echk></span></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td colspan=2 align=center>\
|
||||
<input type="submit" id="register" name="register" value="Register" style="margin: 2px; padding: 2px">\
|
||||
<input type="submit" id="rforgot" name="forgot" value="Forgot Password" style="margin: 2px; padding: 2px;display: none">\
|
||||
<input type="button" id="cancel" name="cancel" value="Cancel" style="margin: 2px; padding: 2px;" onclick="lwsgs_cancel_registration()">\
|
||||
</td>\
|
||||
</tr>\
|
||||
</table>\
|
||||
</form>\
|
||||
</div>\
|
||||
\
|
||||
<div id="dchange" style="display:none">\
|
||||
<form action="lwsgs-change" method="post">\
|
||||
<input type="hidden" id="cusername" name="username">\
|
||||
<input type="hidden" name="admin" value="needadmin/admin-login.html">\
|
||||
<input type="hidden" name="good" value="index.html">\
|
||||
<input type="hidden" name="bad" value="failed-login.html">\
|
||||
<input type="hidden" name="reg-good" value="post-register-ok.html">\
|
||||
<input type="hidden" name="reg-bad" value="post-register-fail.html">\
|
||||
<input type="hidden" name="forgot-good" value="sent-forgot-ok.html">\
|
||||
<input type="hidden" name="forgot-bad" value="sent-forgot-fail.html">\
|
||||
<input type="hidden" name="forgot-post-good" value="post-forgot-ok.html">\
|
||||
<input type="hidden" name="forgot-post-bad" value="post-forgot-fail.html">\
|
||||
<table style="vertical-align:top;text-align:left">\
|
||||
<tr>\
|
||||
<td colspan=2 align=center>\
|
||||
<span id="ccuruser"></span>\
|
||||
<script>\
|
||||
if (lwsgs_user)\
|
||||
document.getElementById("ccuruser").innerHTML =\
|
||||
"<span class=\"gstitle\">Login settings for " +\
|
||||
lwsgs_san(lwsgs_user) + "</span></br>";\
|
||||
</script>\
|
||||
<b>Please enter your details to change</b>:\
|
||||
</td>\
|
||||
</tr>\
|
||||
<tr><td align=right id="ccurpw_name">\
|
||||
Current Password:</td>\
|
||||
<td><input type="password" size="10" id="ccurpw" name="curpw"\
|
||||
oninput="lwsgs_cupdate();"> <span id=cuchk></span></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td align=right>Password:</td>\
|
||||
<td><input type="password" size="10" id="cpassword" name="password"\
|
||||
oninput="lwsgs_cupdate()"> <span id="cpw1"></span></td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td align=right><span id="pw2">Password (again)</span></td>\
|
||||
<td><input type="password" size="10" id="cpassword2" name="password2"\
|
||||
oninput="lwsgs_cupdate()"> <span id="cmatch"></span></td>\
|
||||
</tr>\
|
||||
<!-- not supported yet\
|
||||
<tr>\
|
||||
<td align=right id="cemail_name">Email:</td>\
|
||||
<td><input type="email" size="10" id="cemail" name="email"\
|
||||
placeholder="?" oninput="lwsgs_cupdate(); lwsgs_check_email(\'cemail\')">\
|
||||
<span id=cechk></span></td>\
|
||||
</tr> -->\
|
||||
<tr>\
|
||||
<td colspan=2 align=center>\
|
||||
<input type="submit" id="change" name="change"\
|
||||
value="Change" style="margin: 6px; padding: 6px">\
|
||||
<input type="submit" id="cforgot" name="forgot"\
|
||||
value="Forgot Password" style="margin: 6px; padding: 6px;display: none">\
|
||||
<input type="button" id="cancel" name="cancel"\
|
||||
value="Cancel" style="margin: 6px; padding: 6px;"\
|
||||
onclick="lwsgs_cancel_registration()">\
|
||||
</td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td colspan=2>\
|
||||
<input type="checkbox" id="showdel" name="showdel"\
|
||||
onchange="lwsgs_cupdate();"> Show Delete \
|
||||
<input type="submit" id="delete" name="delete" \
|
||||
value="Delete Account" style="margin: 6px; padding: 6px;display: none">\
|
||||
</td>\
|
||||
</tr>\
|
||||
</table>\
|
||||
</form>\
|
||||
</div>\
|
||||
\
|
||||
<div id="dadmin" style="display:none">\
|
||||
Admin settings TBD\
|
||||
</div>\
|
||||
';
|
||||
|
||||
/*-- 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<<t|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<<r%32,n[(r+64>>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e<n.length;e+=16)i=l,a=g,h=v,d=m,l=o(l,g,v,m,n[e],7,-680876936),m=o(m,l,g,v,n[e+1],12,-389564586),v=o(v,m,l,g,n[e+2],17,606105819),g=o(g,v,m,l,n[e+3],22,-1044525330),l=o(l,g,v,m,n[e+4],7,-176418897),m=o(m,l,g,v,n[e+5],12,1200080426),v=o(v,m,l,g,n[e+6],17,-1473231341),g=o(g,v,m,l,n[e+7],22,-45705983),l=o(l,g,v,m,n[e+8],7,1770035416),m=o(m,l,g,v,n[e+9],12,-1958414417),v=o(v,m,l,g,n[e+10],17,-42063),g=o(g,v,m,l,n[e+11],22,-1990404162),l=o(l,g,v,m,n[e+12],7,1804603682),m=o(m,l,g,v,n[e+13],12,-40341101),v=o(v,m,l,g,n[e+14],17,-1502002290),g=o(g,v,m,l,n[e+15],22,1236535329),l=u(l,g,v,m,n[e+1],5,-165796510),m=u(m,l,g,v,n[e+6],9,-1069501632),v=u(v,m,l,g,n[e+11],14,643717713),g=u(g,v,m,l,n[e],20,-373897302),l=u(l,g,v,m,n[e+5],5,-701558691),m=u(m,l,g,v,n[e+10],9,38016083),v=u(v,m,l,g,n[e+15],14,-660478335),g=u(g,v,m,l,n[e+4],20,-405537848),l=u(l,g,v,m,n[e+9],5,568446438),m=u(m,l,g,v,n[e+14],9,-1019803690),v=u(v,m,l,g,n[e+3],14,-187363961),g=u(g,v,m,l,n[e+8],20,1163531501),l=u(l,g,v,m,n[e+13],5,-1444681467),m=u(m,l,g,v,n[e+2],9,-51403784),v=u(v,m,l,g,n[e+7],14,1735328473),g=u(g,v,m,l,n[e+12],20,-1926607734),l=c(l,g,v,m,n[e+5],4,-378558),m=c(m,l,g,v,n[e+8],11,-2022574463),v=c(v,m,l,g,n[e+11],16,1839030562),g=c(g,v,m,l,n[e+14],23,-35309556),l=c(l,g,v,m,n[e+1],4,-1530992060),m=c(m,l,g,v,n[e+4],11,1272893353),v=c(v,m,l,g,n[e+7],16,-155497632),g=c(g,v,m,l,n[e+10],23,-1094730640),l=c(l,g,v,m,n[e+13],4,681279174),m=c(m,l,g,v,n[e],11,-358537222),v=c(v,m,l,g,n[e+3],16,-722521979),g=c(g,v,m,l,n[e+6],23,76029189),l=c(l,g,v,m,n[e+9],4,-640364487),m=c(m,l,g,v,n[e+12],11,-421815835),v=c(v,m,l,g,n[e+15],16,530742520),g=c(g,v,m,l,n[e+2],23,-995338651),l=f(l,g,v,m,n[e],6,-198630844),m=f(m,l,g,v,n[e+7],10,1126891415),v=f(v,m,l,g,n[e+14],15,-1416354905),g=f(g,v,m,l,n[e+5],21,-57434055),l=f(l,g,v,m,n[e+12],6,1700485571),m=f(m,l,g,v,n[e+3],10,-1894986606),v=f(v,m,l,g,n[e+10],15,-1051523),g=f(g,v,m,l,n[e+1],21,-2054922799),l=f(l,g,v,m,n[e+8],6,1873313359),m=f(m,l,g,v,n[e+15],10,-30611744),v=f(v,m,l,g,n[e+6],15,-1560198380),g=f(g,v,m,l,n[e+13],21,1309151649),l=f(l,g,v,m,n[e+4],6,-145523070),m=f(m,l,g,v,n[e+11],10,-1120210379),v=f(v,m,l,g,n[e+2],15,718787259),g=f(g,v,m,l,n[e+9],21,-343485551),l=t(l,i),g=t(g,a),v=t(v,h),m=t(m,d);return[l,g,v,m]}function a(n){var t,r="";for(t=0;t<32*n.length;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;for(t=0;t<8*n.length;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function d(n){return a(i(h(n),8*n.length))}function l(n,t){var r,e,o=h(n),u=[],c=[];for(u[15]=c[15]=void 0,o.length>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<n.length;r+=1)t=n.charCodeAt(r),o+=e.charAt(t>>>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 =
|
||||
"<b style=\"color:green\">\u2713</b>";
|
||||
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 =
|
||||
"<span style=\"color: red\">\u2718 <b>Passwords do not match</b></span>";
|
||||
} else
|
||||
document.getElementById('match').innerHTML =
|
||||
"<span style=\"color: gray\">\u2718 Passwords do not match</span>";
|
||||
|
||||
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 = "<b style=\"color:green\">\u2713</b>";
|
||||
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 = "<b style=\"color:green\">\u2713</b>";
|
||||
else
|
||||
document.getElementById('uchk').innerHTML = "";
|
||||
} else {
|
||||
document.getElementById('uchk').innerHTML = "<b style=\"color:red\">\u2718 Already registered</b>";
|
||||
en_forgot = 1;
|
||||
}
|
||||
|
||||
if (lwsgs_email_check === '0') {
|
||||
if (document.getElementById('email').value)
|
||||
document.getElementById('echk').innerHTML = "<b style=\"color:green\">\u2713</b>";
|
||||
else
|
||||
document.getElementById('echk').innerHTML = "";
|
||||
} else {
|
||||
document.getElementById('echk').innerHTML = "<b style=\"color:red\">\u2718 Already registered</b>";
|
||||
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 = "<b style=\"color:red\">\u2718</b>";
|
||||
} 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 = "<b style=\"color:green\">\u2713</b>";
|
||||
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 =
|
||||
"<span style=\"color: red\">\u2718 <b>Passwords do not match</b></span>";
|
||||
} else
|
||||
document.getElementById('cmatch').innerHTML = "<span style=\"color: gray\">\u2718 Passwords do not match</span>";
|
||||
|
||||
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 = "<b style=\"color:green\">\u2713</b>";
|
||||
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 = "<b style=\"color:green\">\u2713</b>";
|
||||
else
|
||||
document.getElementById('cechk').innerHTML = "";
|
||||
} else {
|
||||
document.getElementById('cechk').innerHTML = "<b style=\"color:red\">\u2718 Already registered</b>";
|
||||
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 =
|
||||
"<img src=\"https://www.gravatar.com/avatar/" + md5(lwsgs_email) +
|
||||
"?d=identicon\">";
|
||||
//if (lwsgs_email)
|
||||
//document.getElementById('cemail').placeholder = lwsgs_email;
|
||||
document.getElementById('cusername').value = lwsgs_user;
|
||||
lwsgs_update();
|
||||
lwsgs_cupdate();
|
||||
}
|
2
plugins/generic-sessions/assets/md5.min.js
vendored
Normal file
2
plugins/generic-sessions/assets/md5.min.js
vendored
Normal file
|
@ -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<<t|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<<r%32,n[(r+64>>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e<n.length;e+=16)i=l,a=g,h=v,d=m,l=o(l,g,v,m,n[e],7,-680876936),m=o(m,l,g,v,n[e+1],12,-389564586),v=o(v,m,l,g,n[e+2],17,606105819),g=o(g,v,m,l,n[e+3],22,-1044525330),l=o(l,g,v,m,n[e+4],7,-176418897),m=o(m,l,g,v,n[e+5],12,1200080426),v=o(v,m,l,g,n[e+6],17,-1473231341),g=o(g,v,m,l,n[e+7],22,-45705983),l=o(l,g,v,m,n[e+8],7,1770035416),m=o(m,l,g,v,n[e+9],12,-1958414417),v=o(v,m,l,g,n[e+10],17,-42063),g=o(g,v,m,l,n[e+11],22,-1990404162),l=o(l,g,v,m,n[e+12],7,1804603682),m=o(m,l,g,v,n[e+13],12,-40341101),v=o(v,m,l,g,n[e+14],17,-1502002290),g=o(g,v,m,l,n[e+15],22,1236535329),l=u(l,g,v,m,n[e+1],5,-165796510),m=u(m,l,g,v,n[e+6],9,-1069501632),v=u(v,m,l,g,n[e+11],14,643717713),g=u(g,v,m,l,n[e],20,-373897302),l=u(l,g,v,m,n[e+5],5,-701558691),m=u(m,l,g,v,n[e+10],9,38016083),v=u(v,m,l,g,n[e+15],14,-660478335),g=u(g,v,m,l,n[e+4],20,-405537848),l=u(l,g,v,m,n[e+9],5,568446438),m=u(m,l,g,v,n[e+14],9,-1019803690),v=u(v,m,l,g,n[e+3],14,-187363961),g=u(g,v,m,l,n[e+8],20,1163531501),l=u(l,g,v,m,n[e+13],5,-1444681467),m=u(m,l,g,v,n[e+2],9,-51403784),v=u(v,m,l,g,n[e+7],14,1735328473),g=u(g,v,m,l,n[e+12],20,-1926607734),l=c(l,g,v,m,n[e+5],4,-378558),m=c(m,l,g,v,n[e+8],11,-2022574463),v=c(v,m,l,g,n[e+11],16,1839030562),g=c(g,v,m,l,n[e+14],23,-35309556),l=c(l,g,v,m,n[e+1],4,-1530992060),m=c(m,l,g,v,n[e+4],11,1272893353),v=c(v,m,l,g,n[e+7],16,-155497632),g=c(g,v,m,l,n[e+10],23,-1094730640),l=c(l,g,v,m,n[e+13],4,681279174),m=c(m,l,g,v,n[e],11,-358537222),v=c(v,m,l,g,n[e+3],16,-722521979),g=c(g,v,m,l,n[e+6],23,76029189),l=c(l,g,v,m,n[e+9],4,-640364487),m=c(m,l,g,v,n[e+12],11,-421815835),v=c(v,m,l,g,n[e+15],16,530742520),g=c(g,v,m,l,n[e+2],23,-995338651),l=f(l,g,v,m,n[e],6,-198630844),m=f(m,l,g,v,n[e+7],10,1126891415),v=f(v,m,l,g,n[e+14],15,-1416354905),g=f(g,v,m,l,n[e+5],21,-57434055),l=f(l,g,v,m,n[e+12],6,1700485571),m=f(m,l,g,v,n[e+3],10,-1894986606),v=f(v,m,l,g,n[e+10],15,-1051523),g=f(g,v,m,l,n[e+1],21,-2054922799),l=f(l,g,v,m,n[e+8],6,1873313359),m=f(m,l,g,v,n[e+15],10,-30611744),v=f(v,m,l,g,n[e+6],15,-1560198380),g=f(g,v,m,l,n[e+13],21,1309151649),l=f(l,g,v,m,n[e+4],6,-145523070),m=f(m,l,g,v,n[e+11],10,-1120210379),v=f(v,m,l,g,n[e+2],15,718787259),g=f(g,v,m,l,n[e+9],21,-343485551),l=t(l,i),g=t(g,a),v=t(v,h),m=t(m,d);return[l,g,v,m]}function a(n){var t,r="";for(t=0;t<32*n.length;t+=8)r+=String.fromCharCode(n[t>>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t<r.length;t+=1)r[t]=0;for(t=0;t<8*n.length;t+=8)r[t>>5]|=(255&n.charCodeAt(t/8))<<t%32;return r}function d(n){return a(i(h(n),8*n.length))}function l(n,t){var r,e,o=h(n),u=[],c=[];for(u[15]=c[15]=void 0,o.length>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<n.length;r+=1)t=n.charCodeAt(r),o+=e.charAt(t>>>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
|
5
plugins/generic-sessions/assets/post-forgot-fail.html
Normal file
5
plugins/generic-sessions/assets/post-forgot-fail.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
Sorry, something went wrong.
|
||||
|
||||
Click <a href="../">here</a> to continue.
|
||||
</html>
|
6
plugins/generic-sessions/assets/post-forgot-ok.html
Normal file
6
plugins/generic-sessions/assets/post-forgot-ok.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
This is a one-time password recovery login.
|
||||
|
||||
Please click <a href="./">here</a> and click your username at the top to reset your password.
|
||||
</html>
|
||||
|
1
plugins/generic-sessions/assets/post-register-fail.html
Normal file
1
plugins/generic-sessions/assets/post-register-fail.html
Normal file
|
@ -0,0 +1 @@
|
|||
Registration failed, sorry
|
27
plugins/generic-sessions/assets/post-register-ok.html
Normal file
27
plugins/generic-sessions/assets/post-register-ok.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="lwsgs.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td colspan=2 align=center>
|
||||
<img src="lwsgs-logo.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Your registration as <span id="u"></span> is accepted,<br>
|
||||
you will receive an email shortly with instructions<br>
|
||||
to verify and enable the account for normal use.<br><br>
|
||||
The link is only valid for an hour, after that if it has<br>
|
||||
not been verified your account will be deleted.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
<script>
|
||||
document.getElementById('u').innerHTML = "<b>" + lwsgs_san(lwsgs_user) + "</b>";
|
||||
</script>
|
||||
</html>
|
||||
|
20
plugins/generic-sessions/assets/post-verify-fail.html
Normal file
20
plugins/generic-sessions/assets/post-verify-fail.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="lwsgs.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td colspan=2 align=center>
|
||||
<img src="lwsws-logo.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Sorry, the link was invalid.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
25
plugins/generic-sessions/assets/post-verify-ok.html
Normal file
25
plugins/generic-sessions/assets/post-verify-ok.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<html>
|
||||
<head>
|
||||
<script src="lwsgs.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td colspan=2 align=center>
|
||||
<img src="lwsgs-logo.png">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Thanks for signing up, your registration as <span id="u"></span> is verified.<br>
|
||||
<br>
|
||||
Click <a href="/lwsgs">here</a> to continue.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
<script>
|
||||
document.getElementById('u').innerHTML = "<b>" + san(lwsgs_user) + "</b>";
|
||||
</script>
|
||||
</html>
|
||||
|
BIN
plugins/generic-sessions/assets/seats.jpg
Normal file
BIN
plugins/generic-sessions/assets/seats.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 120 KiB |
5
plugins/generic-sessions/assets/sent-forgot-fail.html
Normal file
5
plugins/generic-sessions/assets/sent-forgot-fail.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
Sorry, something went wrong.
|
||||
|
||||
Click <a href="../">here</a> to continue.
|
||||
</html>
|
4
plugins/generic-sessions/assets/sent-forgot-ok.html
Normal file
4
plugins/generic-sessions/assets/sent-forgot-ok.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
An email has been sent to your registered address.
|
||||
|
||||
Please follow the instructions to reset your password.
|
||||
|
4
plugins/generic-sessions/assets/successful-login.html
Normal file
4
plugins/generic-sessions/assets/successful-login.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<html>
|
||||
This is an example destination that will appear after successful non-Admin login
|
||||
</html>
|
||||
|
598
plugins/generic-sessions/handlers.c
Normal file
598
plugins/generic-sessions/handlers.c
Normal file
|
@ -0,0 +1,598 @@
|
|||
/*
|
||||
* 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';
|
||||
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;
|
||||
}
|
161
plugins/generic-sessions/private-lwsgs.h
Normal file
161
plugins/generic-sessions/private-lwsgs.h
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#define LWS_DLL
|
||||
#define LWS_INTERNAL
|
||||
#include "../lib/libwebsockets.h"
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
|
901
plugins/generic-sessions/protocol_generic_sessions.c
Normal file
901
plugins/generic-sessions/protocol_generic_sessions.c
Normal file
|
@ -0,0 +1,901 @@
|
|||
/*
|
||||
* ws protocol handler plugin for "generic sessions"
|
||||
*
|
||||
* Copyright (C) 2010-2016 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation:
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "private-lwsgs.h"
|
||||
|
||||
/* keep changes in sync with the enum in lwsgs.h */
|
||||
static const char * const param_names[] = {
|
||||
"username",
|
||||
"password",
|
||||
"password2",
|
||||
"email",
|
||||
"register",
|
||||
"good",
|
||||
"bad",
|
||||
"reg-good",
|
||||
"reg-bad",
|
||||
"admin",
|
||||
"forgot",
|
||||
"forgot-good",
|
||||
"forgot-bad",
|
||||
"forgot-post-good",
|
||||
"forgot-post-bad",
|
||||
"change",
|
||||
"curpw",
|
||||
"delete"
|
||||
};
|
||||
|
||||
struct lwsgs_fill_args {
|
||||
char *buf;
|
||||
int len;
|
||||
};
|
||||
|
||||
static const struct lws_protocols protocols[];
|
||||
|
||||
static int
|
||||
lwsgs_lookup_callback_email(void *priv, int cols, char **col_val,
|
||||
char **col_name)
|
||||
{
|
||||
struct lwsgs_fill_args *a = (struct lwsgs_fill_args *)priv;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < cols; n++) {
|
||||
if (!strcmp(col_name[n], "content")) {
|
||||
strncpy(a->buf, col_val[n], a->len - 1);
|
||||
a->buf[a->len - 1] = '\0';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
lwsgs_email_cb_get_body(struct lws_email *email, char *buf, int len)
|
||||
{
|
||||
struct per_vhost_data__gs *vhd = (struct per_vhost_data__gs *)email->data;
|
||||
struct lwsgs_fill_args a;
|
||||
char ss[150], esc[50];
|
||||
|
||||
a.buf = buf;
|
||||
a.len = len;
|
||||
|
||||
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;
|
||||
}
|
450
plugins/generic-sessions/utils.c
Normal file
450
plugins/generic-sessions/utils.c
Normal file
|
@ -0,0 +1,450 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
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;
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
#include <time.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#include <gettimeofday.h>
|
||||
#endif
|
||||
|
||||
|
|
|
@ -23,6 +23,13 @@
|
|||
#include "../lib/libwebsockets.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
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[] = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -85,7 +85,7 @@ struct per_session_data__http {
|
|||
|
||||
char filename[256];
|
||||
long file_length;
|
||||
int post_fd;
|
||||
lws_filefd_type post_fd;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Reference in a new issue