1
0
Fork 0
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:
Andy Green 2016-05-19 15:28:31 +08:00
parent 920daf10a1
commit 7a2fc442b6
34 changed files with 3596 additions and 102 deletions

View file

@ -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
View 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.

View file

@ -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:

View file

@ -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;
}

View file

@ -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;

View file

@ -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)
{

View file

@ -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;

View file

@ -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 */

View file

@ -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;

View file

@ -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;

View 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>

View file

@ -0,0 +1,3 @@
<html>
This is an example destination that will appear after a failed login
</html>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View 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;">\
&nbsp;<input type="submit" id="forgot" name="forgot" value="Forgot password" style="margin: 2px; padding: 2px">\
&nbsp;<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();">&nbsp;<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()">&nbsp;<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()">&nbsp;<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\')">&nbsp;<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();">&nbsp;<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()">&nbsp;<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()">&nbsp;<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\')">\
&nbsp;<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&nbsp;\
<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();
}

View 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

View file

@ -0,0 +1,5 @@
<html>
Sorry, something went wrong.
Click <a href="../">here</a> to continue.
</html>

View 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>

View file

@ -0,0 +1 @@
Registration failed, sorry

View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

View file

@ -0,0 +1,5 @@
<html>
Sorry, something went wrong.
Click <a href="../">here</a> to continue.
</html>

View file

@ -0,0 +1,4 @@
An email has been sent to your registered address.
Please follow the instructions to reset your password.

View file

@ -0,0 +1,4 @@
<html>
This is an example destination that will appear after successful non-Admin login
</html>

View 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;
}

View 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);

View 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;
}

View 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;
}

View file

@ -25,6 +25,7 @@
#include <time.h>
#include <string.h>
#ifdef WIN32
#include <io.h>
#include <gettimeofday.h>
#endif

View file

@ -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[] = {

View file

@ -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,

View file

@ -85,7 +85,7 @@ struct per_session_data__http {
char filename[256];
long file_length;
int post_fd;
lws_filefd_type post_fd;
};
/*