From d98101d1e30b436d3b9facfca7ea7c98c878cc26 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 27 Aug 2020 09:34:35 +0100 Subject: [PATCH] plugins: generalize and provide public api Move the common plugin scanning dir stuff to be based on lws_dir, which already builds for windows. Previously this was done via dirent for unix and libuv for windows. Reduce the dl plat stuff to just wrap instantiation and destruction of dynlibs, establish common code in lib/misc/dir.c for plugin scanning itself. Migrate the libuv windows dl stuff to windows-plugins.c, so that he's available even if later libuv loop support becomes and event lib plugin. Remove the existing api exports scheme for plugins, just export a const struct now which has a fixed header type but then whatever you want afterwards depending on the class / purpose of the plugin. Place a "class" string in the header so there can be different kinds of plugins implying different types exported. Make the plugin apis public and add support for filter by class string, and per instantation / destruction callbacks so the subclassed header type can do its thing for the plugin class. The user provides a linked-list base for his class of plugins, so he can manage them completely separately and in user code / user export types. Rip out some last hangers-on from generic sessions / tables. This is all aimed at making the plugins support general enough so it can provide event lib plugins later. --- READMEs/README.coding.md | 2 +- READMEs/README.generic-sessions.md | 373 ----- READMEs/README.generic-table.md | 219 --- include/libwebsockets.h | 1 - .../lws-plugin-generic-sessions.h | 75 - include/libwebsockets/lws-protocols-plugins.h | 103 +- lib/core-net/private-lib-core-net.h | 10 +- lib/core-net/vhost.c | 9 +- lib/core/private-lib-core.h | 1 + lib/event-libs/libuv/libuv.c | 164 --- lib/misc/dir.c | 73 + lib/plat/unix/unix-init.c | 21 +- lib/plat/unix/unix-plugins.c | 200 +-- lib/plat/windows/windows-init.c | 24 +- lib/plat/windows/windows-plugins.c | 108 +- lib/roles/ws/ops-ws.c | 9 +- .../minimal-ws-proxy/protocol_lws_minimal.c | 34 - .../protocol_lws_minimal_dbus_ws_proxy.c | 33 - .../CMakeLists.txt | 28 - .../README.md | 26 - .../localhost-100y.cert | 34 - .../localhost-100y.key | 52 - .../minimal-http-server-generic-sessions.c | 202 --- .../mount-origin/404.html | 11 - .../mount-origin/admin-login.html | 5 - .../mount-origin/example.js | 21 - .../mount-origin/failed-login.html | 3 - .../mount-origin/favicon.ico | Bin 1406 -> 0 bytes .../mount-origin/http2.png | Bin 7563 -> 0 bytes .../mount-origin/index.html | 57 - .../mount-origin/libwebsockets.org-logo.svg | 66 - .../mount-origin/lws-common.js | 125 -- .../mount-origin/lwsgs-logo.png | Bin 9729 -> 0 bytes .../mount-origin/lwsgs.css | 144 -- .../mount-origin/lwsgs.js | 634 -------- .../mount-origin/md5.min.js | 2 - .../mount-origin/needadmin/admin-login.html | 5 - .../needauth/successful-login.html | 4 - .../mount-origin/post-forgot-fail.html | 5 - .../mount-origin/post-forgot-ok.html | 6 - .../mount-origin/post-register-fail.html | 1 - .../mount-origin/post-register-ok.html | 27 - .../mount-origin/post-verify-fail.html | 20 - .../mount-origin/post-verify-ok.html | 25 - .../mount-origin/seats.jpg | Bin 122754 -> 0 bytes .../mount-origin/sent-forgot-fail.html | 5 - .../mount-origin/sent-forgot-ok.html | 4 - .../mount-origin/strict-csp.svg | 53 - .../mount-origin/successful-login.html | 4 - .../protocol_lws_minimal_client_echo.c | 33 - .../protocol_lws_minimal_pmd_bulk.c | 33 - .../minimal-ws-broker/protocol_lws_minimal.c | 33 - .../protocol_lws_minimal_server_echo.c | 33 - .../protocol_lws_minimal_pmd_bulk.c | 33 - .../protocol_lws_minimal.c | 33 - .../protocol_lws_minimal.c | 33 - .../protocol_lws_minimal.c | 33 - .../protocol_lws_minimal_threadpool.c | 33 - .../protocol_lws_minimal.c | 33 - .../protocol_lws_minimal.c | 33 - .../minimal-ws-server/protocol_lws_minimal.c | 33 - .../protocol_example_standalone.c | 33 +- .../acme-client/protocol_lws_acme_client.c | 33 +- plugins/deaddrop/protocol_lws_deaddrop.c | 33 +- plugins/protocol_client_loopback_test.c | 33 +- plugins/protocol_dumb_increment.c | 33 +- plugins/protocol_esp32_lws_group.c | 242 ---- plugins/protocol_esp32_lws_ota.c | 291 ---- .../protocol_esp32_lws_reboot_to_factory.c | 61 - plugins/protocol_esp32_lws_scan.c | 1276 ----------------- plugins/protocol_fulltext_demo.c | 33 +- plugins/protocol_lws_mirror.c | 32 +- plugins/protocol_lws_raw_test.c | 33 +- plugins/protocol_lws_server_status.c | 33 +- plugins/protocol_lws_sshd_demo.c | 33 +- plugins/protocol_lws_status.c | 34 +- plugins/protocol_post_demo.c | 33 +- plugins/raw-proxy/protocol_lws_raw_proxy.c | 33 +- plugins/ssh-base/sshd.c | 32 +- 79 files changed, 541 insertions(+), 5214 deletions(-) delete mode 100644 READMEs/README.generic-sessions.md delete mode 100644 READMEs/README.generic-table.md delete mode 100644 include/libwebsockets/lws-plugin-generic-sessions.h delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/README.md delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/favicon.ico delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/http2.png delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/index.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs-logo.png delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/seats.jpg delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg delete mode 100644 minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html delete mode 100644 plugins/protocol_esp32_lws_group.c delete mode 100644 plugins/protocol_esp32_lws_ota.c delete mode 100644 plugins/protocol_esp32_lws_reboot_to_factory.c delete mode 100644 plugins/protocol_esp32_lws_scan.c diff --git a/READMEs/README.coding.md b/READMEs/README.coding.md index 40aa50649..a14d1fa8d 100644 --- a/READMEs/README.coding.md +++ b/READMEs/README.coding.md @@ -1,7 +1,7 @@ Notes about coding with lws =========================== -@section era Old lws and lws v2.0 +@section era Old lws and lws v2.0+ Originally lws only supported the "manual" method of handling everything in the user callback found in test-server.c / test-server-http.c. diff --git a/READMEs/README.generic-sessions.md b/READMEs/README.generic-sessions.md deleted file mode 100644 index 376342a07..000000000 --- a/READMEs/README.generic-sessions.md +++ /dev/null @@ -1,373 +0,0 @@ -Notes about generic-sessions Plugin -=================================== - -@section gseb Enabling lwsgs for build - -Enable at CMake with -DLWS_WITH_GENERIC_SESSIONS=1 - -This also needs sqlite3 (libsqlite3-dev or similar package) - - -@section gsi lwsgs 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. - - -@section gsin Lwsgs 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 - -``` - - - - - - - - - - - -
- - -
-
- - - - - - -``` - -@section gsof Lwsgs 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
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. - - - -@section gsconf Lwsgs Configuration - -"auth-mask" defines the authorization 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) - - b3 is set if your session just did the forgot password flow successfully - -``` - { - # 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 ", - "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. - - -@section gspwc Lwsgs 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. - - -@section gsprep Lwsgs 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 -``` - -@section gsrmail Lwsgs 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. - - -@section gsap Lwsgs 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. - -We provide the "messageboard" plugin as an example of how to integrate with -your actual application protocol. - -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; -``` - - -#section gsapsib 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. - diff --git a/READMEs/README.generic-table.md b/READMEs/README.generic-table.md deleted file mode 100644 index 7a8580968..000000000 --- a/READMEs/README.generic-table.md +++ /dev/null @@ -1,219 +0,0 @@ -Notes about generic-table -========================= - -@section gtint What is generic-table? - -Generic-table is a JSON schema and client-side JS file that makes it easy to -display live, table structured HTML over a ws link. - -An example plugin and index.html using it are provided, but lwsgt itself doesn't -have its own plugin, it's just a JSON schema and client-side JS that other -plugins can use to simplify displaying live, table-based data without having -to reinvent the wheel each time. - -The ws protocol sends JSON describing the table, and then JSON updating the table -contents when it chooses, the brower table is updated automatically, live. - -\image html lwsgt-overview.png - - - Example protocol plugin (displays directory contents): https://github.com/warmcat/libwebsockets/tree/master/plugins/generic-table/protocol_table_dirlisting.c - - - Example HTML: https://github.com/warmcat/libwebsockets/tree/master/plugins/generic-table/assets/index.html - - - lwsgt.js (client-side table rendering / ws link management): https://github.com/warmcat/libwebsockets/tree/master/plugins/generic-table/assets/lwsgt.js - - -@section gteb Enabling for build - -Enable the demo plugin at CMake with -DLWS_WITH_PLUGINS=1 - - -@section gtinth Integrating with your html - - - In your HEAD section, include lwsgt.js - -``` - -``` - - - Also in your HEAD section, style the lwsgt CSS, eg - -``` - -``` - -You can skip this but the result will be less beautiful until some CSS is -provided. - - - In your body section, declare a div with an id (can be whatever you want) - -``` -
-``` - -lwsgt JS will put its content there. - - - Finally in a -``` - -In the callback, you can recover the ws object by `window[gt].lwsgt_ws`. - - -@section gtc Lwsgt constructor - -To instantiate the ws link and lwsgt instance, your HTML must call a lwsgt -constructor for each region on the page managed by lwsgt. - -`var myvar = new lwsgt_initial(title, ws_protocol, div_id, click_cb, myvar);` - -All of the arguments are strings. - -| Parameter | Description | -|-----------------|---------------------------------------------------------| -| title | Title string to go above the table | -| ws_protocol | Protocol name string to use when making ws connection | -| div_id | HTML id of div to fill with content | -| click_cb | Callback function name string to handle clickable links | -| myvar | Name of var used to hold this instantiation globally | - -Note "myvar" is needed so it can be passed to the click handling callback. - - -@section gtclick Lwsgt click handling function - -When a clickable link produced by lwsgt is clicked, the function named in the -click_cb parameter to lwsgt_initial is called. - -That function is expected to take four parameters, eg - -`function lwsgt_dir_click(gt, u, col, row)` - -| Parameter | Description | -|------- ---|-----------------------------------------------------------| -| gt | Name of global var holding this lwsgt context (ie, myvar) | -| u | Link "url" string | -| col | Table column number link is from | -| row | Table row number link is from | - - - -@section gtgj Generic-table JSON - -### Column layout - -When the ws connection is established, the protocol should send a JSON message -describing the table columns. For example - -``` - "cols": [ - { "name": "Date" }, - { "name": "Size", "align": "right" }, - { "name": "Icon" }, - { "name": "Name", "href": "uri"}, - { "name": "uri", "hide": "1" } - ] - } -``` - - - This describes 5 columns - - - Only four columns (not "uri") should be visible - - - "Name" should be presented as a clickable link using "uri" as the - destination, when a "uri" field is presented. - - - "Size" field should be presented aligned to the right - - ### Breadcrumbs - - When a view is hierarchical, it's useful to provide a "path" with links back - in the "path", known as "breadcrumbs". - - Elements before the last one should provide a "url" member as well as the - displayable name, which is used to create the link destination. - - The last element, being the current displayed page should not have a url - member and be displayed without link style. - - - ``` - "breadcrumbs":[{"name":"top", "url": "/" }, {"name":"mydir"}] - ``` - - ### Table data - - The actual file data consists of an array of rows, containing the columns - mentioned in the original "cols" section. - - ``` - "data":[ - { - "Icon":" ", - "Date":"2015-Feb-06 03:08:35 +0000", - "Size":"1406", - "uri":"./serve//favicon.ico", - "Name":"favicon.ico" - } - ] - - ``` - - @section gtdirl Setting up protocol-lws-table-dirlisting - - The example protocol needs two mounts, one to provide the index.html, js and - the protocol itself - - ``` - { - "mountpoint": "/dirtest", - "origin": "file:///usr/share/libwebsockets-test-server/generic-table", - "origin": "callback://protocol-lws-table-dirlisting", - "default": "index.html", - "pmo": [{ - "dir": "/usr/share/libwebsockets-test-server" - }] - }, -``` - -The protocol wants a per-mount option (PMO) to tell it the base directory it -is serving from, named "dir". - -The other mount is there to simply serve items that get clicked on from the -table in a secure way - -``` - { - "mountpoint": "/dirtest/serve", - "origin": "file:///usr/share/libwebsockets-test-server", - "default": "index.html" - }, -``` - -This last bit is not related to using lwsgt itself. diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 544a9db5d..f93bff3d6 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -546,7 +546,6 @@ struct lws; #include #include #include -#include #include diff --git a/include/libwebsockets/lws-plugin-generic-sessions.h b/include/libwebsockets/lws-plugin-generic-sessions.h deleted file mode 100644 index 2aa6e92a6..000000000 --- a/include/libwebsockets/lws-plugin-generic-sessions.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -/*! \defgroup generic-sessions plugin: generic-sessions - * \ingroup Protocols-and-Plugins - * - * ##Plugin Generic-sessions related - * - * generic-sessions plugin provides a reusable, generic session and login / - * register / forgot password framework including email verification. - */ -///@{ - -#define LWSGS_EMAIL_CONTENT_SIZE 16384 -/**< Maximum size of email we might send */ - -/* SHA-1 binary and hexified versions */ -/** typedef struct lwsgw_hash_bin */ -typedef struct { unsigned char bin[32]; /**< binary representation of hash */} lwsgw_hash_bin; -/** typedef struct lwsgw_hash */ -typedef struct { char id[65]; /**< ascii hex representation of hash */ } lwsgw_hash; - -/** enum lwsgs_auth_bits */ -enum lwsgs_auth_bits { - LWSGS_AUTH_LOGGED_IN = 1, /**< user is logged in as somebody */ - LWSGS_AUTH_ADMIN = 2, /**< logged in as the admin user */ - LWSGS_AUTH_VERIFIED = 4, /**< user has verified his email */ - LWSGS_AUTH_FORGOT_FLOW = 8, /**< just completed "forgot password" */ -}; - -/** struct lws_session_info - information about user session status */ -struct lws_session_info { - char username[32]; /**< username logged in as, or empty string */ - char email[100]; /**< email address associated with login, or empty string */ - char ip[72]; /**< ip address session was started from */ - unsigned int mask; /**< access rights mask associated with session - * see enum lwsgs_auth_bits */ - char session[42]; /**< session id string, usable as opaque uid when not logged in */ -}; - -/** enum lws_gs_event */ -enum lws_gs_event { - LWSGSE_CREATED, /**< a new user was created */ - LWSGSE_DELETED /**< an existing user was deleted */ -}; - -/** struct lws_gs_event_args */ -struct lws_gs_event_args { - enum lws_gs_event event; /**< which event happened */ - const char *username; /**< which username the event happened to */ - const char *email; /**< the email address of that user */ -}; - -///@} diff --git a/include/libwebsockets/lws-protocols-plugins.h b/include/libwebsockets/lws-protocols-plugins.h index 869c76f44..4df9a2228 100644 --- a/include/libwebsockets/lws-protocols-plugins.h +++ b/include/libwebsockets/lws-protocols-plugins.h @@ -196,34 +196,107 @@ lws_protocol_init(struct lws_context *context); #ifdef LWS_WITH_PLUGINS -/* PLUGINS implies LIBUV */ +#define LWS_PLUGIN_API_MAGIC 190 -#define LWS_PLUGIN_API_MAGIC 180 +/* + * Abstract plugin header for any kind of plugin class, always at top of + * actual class plugin export type. + * + * The export type object must be exported with the same name as the plugin + * file, eg, libmyplugin.so must export a const one of these as the symbol + * "myplugin". + * + * That is the only expected export from the plugin. + */ + +typedef struct lws_plugin_header { + const char *name; + const char *_class; + + unsigned int api_magic; + /* set to LWS_PLUGIN_API_MAGIC at plugin build time */ + + /* plugin-class specific superclass data follows */ +} lws_plugin_header_t; + +/* + * "lws_protocol_plugin" class export, for lws_protocol implementations done + * as plugins + */ +typedef struct lws_plugin_protocol { + lws_plugin_header_t hdr; -/** struct lws_plugin_capability - how a plugin introduces itself to lws */ -struct lws_plugin_capability { - unsigned int api_magic; /**< caller fills this in, plugin fills rest */ const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */ - int count_protocols; /**< how many protocols */ const struct lws_extension *extensions; /**< array of extensions provided by plugin */ + int count_protocols; /**< how many protocols */ int count_extensions; /**< how many extensions */ -}; +} lws_plugin_protocol_t; -typedef int (*lws_plugin_init_func)(struct lws_context *, - struct lws_plugin_capability *); -typedef int (*lws_plugin_destroy_func)(struct lws_context *); -/** struct lws_plugin */ +/* + * This is the dynamic, runtime created part of the plugin instantiation. + * These are kept in a linked-list and destroyed with the context. + */ + struct lws_plugin { struct lws_plugin *list; /**< linked list */ + + const lws_plugin_header_t *hdr; + + union { +#if defined(LWS_WITH_LIBUV) #if (UV_VERSION_MAJOR > 0) - uv_lib_t lib; /**< shared library pointer */ + uv_lib_t lib; /**< shared library pointer */ #endif - void *l; /**< so we can compile on ancient libuv */ - char name[64]; /**< name of the plugin */ - struct lws_plugin_capability caps; /**< plugin capabilities */ +#endif + void *l; /**< */ + } u; }; +typedef int (*each_plugin_cb_t)(struct lws_plugin *p, void *user); + +/** + * lws_plugins_init() - dynamically load plugins of matching class from dirs + * + * \param pplugin: pointer to linked-list for this kind of plugin + * \param d: array of directory paths to look in + * \param _class: class string that plugin must declare + * \param filter: NULL, or a string that must appear after the third char of the plugin filename + * \param each: NULL, or each_plugin_cb_t callback for each instantiated plugin + * \param each_user: pointer passed to each callback + * + * Allows you to instantiate a class of plugins to a specified linked-list. + * The each callback allows you to init each inistantiated callback and pass a + * pointer each_user to it. + * + * To take down the plugins, pass a pointer to the linked-list head to + * lws_plugins_destroy. + * + * This is used for lws protocol plugins but you can define your own plugin + * class name like "mypluginclass", declare it in your plugin headers, and load + * your own plugins to your own list using this api the same way. + */ +LWS_VISIBLE LWS_EXTERN int +lws_plugins_init(struct lws_plugin **pplugin, const char * const *d, + const char *_class, const char *filter, + each_plugin_cb_t each, void *each_user); + +/** + * lws_plugins_destroy() - dynamically unload list of plugins + * + * \param pplugin: pointer to linked-list for this kind of plugin + * \param each: NULL, or each_plugin_cb_t callback for each instantiated plugin + * \param each_user: pointer passed to each callback + * + * Allows you to destroy a class of plugins from a specified linked-list + * created by a call to lws_plugins_init(). + * + * The each callback allows you to deinit each inistantiated callback and pass a + * pointer each_user to it, just before its footprint is destroyed. + */ +LWS_VISIBLE LWS_EXTERN int +lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each, + void *each_user); #endif ///@} diff --git a/lib/core-net/private-lib-core-net.h b/lib/core-net/private-lib-core-net.h index 6b122568c..a23a44772 100644 --- a/lib/core-net/private-lib-core-net.h +++ b/lib/core-net/private-lib-core-net.h @@ -1158,11 +1158,13 @@ lws_libuv_closehandle(struct lws *wsi); int lws_libuv_check_watcher_active(struct lws *wsi); -LWS_VISIBLE LWS_EXTERN int -lws_plat_plugins_init(struct lws_context * context, const char * const *d); +const lws_plugin_header_t * +lws_plat_dlopen(struct lws_plugin **pplugin, const char *libpath, + const char *sofilename, const char *_class, + each_plugin_cb_t each, void *each_user); -LWS_VISIBLE LWS_EXTERN int -lws_plat_plugins_destroy(struct lws_context * context); +int +lws_plat_destroy_dl(struct lws_plugin *p); struct lws * lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); diff --git a/lib/core-net/vhost.c b/lib/core-net/vhost.c index c5c0fcd50..e66eb2f4a 100644 --- a/lib/core-net/vhost.c +++ b/lib/core-net/vhost.c @@ -685,15 +685,18 @@ lws_create_vhost(struct lws_context *context, #ifdef LWS_WITH_PLUGINS if (plugin) { while (plugin) { - for (n = 0; n < plugin->caps.count_protocols; n++) { + const lws_plugin_protocol_t *plpr = + (const lws_plugin_protocol_t *)plugin->hdr; + + for (n = 0; n < plpr->count_protocols; n++) { /* * for compatibility's sake, no pvo implies * allow all protocols */ if (f || lws_vhost_protocol_options(vh, - plugin->caps.protocols[n].name)) { + plpr->protocols[n].name)) { memcpy(&lwsp[m], - &plugin->caps.protocols[n], + &plpr->protocols[n], sizeof(struct lws_protocols)); m++; vh->count_protocols++; diff --git a/lib/core/private-lib-core.h b/lib/core/private-lib-core.h index e5e11f4bc..f38b07a12 100644 --- a/lib/core/private-lib-core.h +++ b/lib/core/private-lib-core.h @@ -111,6 +111,7 @@ #define strerror(x) "" #endif + /* * * ------ private platform defines ------ diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c index 26ca5046d..ff0e0cbac 100644 --- a/lib/event-libs/libuv/libuv.c +++ b/lib/event-libs/libuv/libuv.c @@ -312,170 +312,6 @@ lws_libuv_check_watcher_active(struct lws *wsi) return uv_is_active(h); } - -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - -int -lws_uv_plugins_init(struct lws_context *context, const char * const *d) -{ - struct lws_plugin_capability lcaps; - struct lws_plugin *plugin; - lws_plugin_init_func initfunc; - int m, ret = 0; - void *v; - uv_dirent_t dent; - uv_fs_t req; - char path[256]; - uv_lib_t lib; - int pofs = 0; - -#if defined(__MINGW32__) || !defined(WIN32) - pofs = 3; -#endif - - lib.errmsg = NULL; - lib.handle = NULL; - - uv_loop_init(&context->uv.loop); - - lwsl_notice(" Plugins:\n"); - - while (d && *d) { - - lwsl_notice(" Scanning %s\n", *d); - m =uv_fs_scandir(&context->uv.loop, &req, *d, 0, NULL); - if (m < 1) { - lwsl_err("Scandir on %s failed\n", *d); - d++; - continue; - } - - while (uv_fs_scandir_next(&req, &dent) != UV_EOF) { - if (strlen(dent.name) < 7) - continue; - - lwsl_notice(" %s\n", dent.name); - - lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, - dent.name); - if (uv_dlopen(path, &lib)) { - uv_dlerror(&lib); - lwsl_err("Error loading DSO: %s\n", lib.errmsg); - uv_dlclose(&lib); - goto bail; - } - - /* we could open it, can we get his init function? */ - -#if !defined(WIN32) && !defined(__MINGW32__) - m = lws_snprintf(path, sizeof(path) - 1, "init_%s", - dent.name + pofs /* snip lib... */); - path[m - 3] = '\0'; /* snip the .so */ -#else - m = lws_snprintf(path, sizeof(path) - 1, "init_%s", - dent.name + pofs); - path[m - 4] = '\0'; /* snip the .dll */ -#endif - if (uv_dlsym(&lib, path, &v)) { - uv_dlerror(&lib); - lwsl_err("%s: Failed to get '%s' on %s: %s\n", - __func__, path, dent.name, lib.errmsg); - uv_dlclose(&lib); - goto bail; - } - initfunc = (lws_plugin_init_func)v; - lcaps.api_magic = LWS_PLUGIN_API_MAGIC; - m = initfunc(context, &lcaps); - if (m) { - lwsl_err("Init %s failed %d\n", dent.name, m); - goto skip; - } - - plugin = lws_malloc(sizeof(*plugin), "plugin"); - if (!plugin) { - uv_dlclose(&lib); - lwsl_err("OOM\n"); - goto bail; - } - plugin->list = context->plugin_list; - context->plugin_list = plugin; - lws_strncpy(plugin->name, dent.name, sizeof(plugin->name)); - plugin->lib = lib; - plugin->caps = lcaps; - context->plugin_protocol_count += lcaps.count_protocols; - context->plugin_extension_count += lcaps.count_extensions; - - continue; - -skip: - uv_dlclose(&lib); - } -bail: - uv_fs_req_cleanup(&req); - d++; - } - - return ret; -} - -int -lws_uv_plugins_destroy(struct lws_context *context) -{ - struct lws_plugin *plugin = context->plugin_list, *p; - lws_plugin_destroy_func func; - char path[256]; - int pofs = 0; - void *v; - int m; - -#if defined(__MINGW32__) || !defined(WIN32) - pofs = 3; -#endif - - if (!plugin) - return 0; - - while (plugin) { - p = plugin; - -#if !defined(WIN32) && !defined(__MINGW32__) - m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", - plugin->name + pofs); - path[m - 3] = '\0'; -#else - m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", - plugin->name + pofs); - path[m - 4] = '\0'; -#endif - - if (uv_dlsym(&plugin->lib, path, &v)) { - uv_dlerror(&plugin->lib); - lwsl_err("Failed to get %s on %s: %s", path, - plugin->name, plugin->lib.errmsg); - } else { - func = (lws_plugin_destroy_func)v; - m = func(context); - if (m) - lwsl_err("Destroying %s failed %d\n", - plugin->name, m); - } - - uv_dlclose(&p->lib); - plugin = p->list; - p->list = NULL; - free(p); - } - - context->plugin_list = NULL; - - while (uv_loop_close(&context->uv.loop)) - ; - - return 0; -} - -#endif - static int elops_init_context_uv(struct lws_context *context, const struct lws_context_creation_info *info) diff --git a/lib/misc/dir.c b/lib/misc/dir.c index 971afa413..760c6953b 100644 --- a/lib/misc/dir.c +++ b/lib/misc/dir.c @@ -286,3 +286,76 @@ lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) #endif + +#if defined(LWS_WITH_PLUGINS) + +struct lws_plugins_args { + struct lws_plugin **pplugin; + const char *_class; + const char *filter; + each_plugin_cb_t each; + void *each_user; +}; + +static int +lws_plugins_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde) +{ + struct lws_plugins_args *pa = (struct lws_plugins_args *)user; + char path[256]; + + if (strlen(lde->name) < 7) + return 0; + + /* if he's given a filter, only match if name + 3 matches it */ + if (pa->filter && strncmp(lde->name + 3, pa->filter, strlen(pa->filter))) + return 0; + + lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name); + lwsl_notice(" %s\n", path); + + return !lws_plat_dlopen(pa->pplugin, path, lde->name + 3, pa->_class, + pa->each, pa->each_user); +} + +int +lws_plugins_init(struct lws_plugin **pplugin, const char * const *d, + const char *_class, const char *filter, + each_plugin_cb_t each, void *each_user) +{ + struct lws_plugins_args pa; + + pa.pplugin = pplugin; + pa._class = _class; + pa.each = each; + pa.each_user = each_user; + pa.filter = filter; + + while (d && *d) { + lws_dir(*d, &pa, lws_plugins_dir_cb); + d++; + } + + return 0; +} + +int +lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each, + void *each_user) +{ + struct lws_plugin *p = *pplugin, *p1; + + while (p) { + if (each) + each(p, each_user); + lws_plat_destroy_dl(p); + p1 = p->list; + p->list = NULL; + lws_free(p); + p = p1; + } + + *pplugin = NULL; + + return 0; +} +#endif diff --git a/lib/plat/unix/unix-init.c b/lib/plat/unix/unix-init.c index 0f78e1a0b..1a53a1c92 100644 --- a/lib/plat/unix/unix-init.c +++ b/lib/plat/unix/unix-init.c @@ -80,6 +80,19 @@ lws_sul_plat_unix(lws_sorted_usec_list_t *sul) } #endif +static int +protocol_plugin_cb(struct lws_plugin *pin, void *each_user) +{ + struct lws_context *context = (struct lws_context *)each_user; + const lws_plugin_protocol_t *plpr = + (const lws_plugin_protocol_t *)pin->hdr; + + context->plugin_protocol_count += plpr->count_protocols; + context->plugin_extension_count += plpr->count_extensions; + + return 0; +} + int lws_plat_init(struct lws_context *context, const struct lws_context_creation_info *info) @@ -134,7 +147,9 @@ lws_plat_init(struct lws_context *context, #if defined(LWS_WITH_PLUGINS) if (info->plugin_dirs) - lws_plat_plugins_init(context, info->plugin_dirs); + lws_plugins_init(&context->plugin_list, info->plugin_dirs, + "lws_protocol_plugin", NULL, + protocol_plugin_cb, context); #endif @@ -167,9 +182,9 @@ lws_plat_context_early_destroy(struct lws_context *context) void lws_plat_context_late_destroy(struct lws_context *context) { -#ifdef LWS_WITH_PLUGINS +#if defined(LWS_WITH_PLUGINS) if (context->plugin_list) - lws_plat_plugins_destroy(context); + lws_plugins_destroy(&context->plugin_list, NULL, NULL); #endif #if defined(LWS_WITH_NETWORK) if (context->lws_lookup) diff --git a/lib/plat/unix/unix-plugins.c b/lib/plat/unix/unix-plugins.c index 6875bac16..05fb79382 100644 --- a/lib/plat/unix/unix-plugins.c +++ b/lib/plat/unix/unix-plugins.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 - 2019 Andy Green + * Copyright (C) 2010 - 2020 Andy Green * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -35,149 +35,75 @@ #endif #include -static int filter(const struct dirent *ent) +const lws_plugin_header_t * +lws_plat_dlopen(struct lws_plugin **pplugin, const char *libpath, + const char *sofilename, const char *_class, + each_plugin_cb_t each, void *each_user) { - if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) - return 0; - - return 1; -} - -int -lws_plat_plugins_init(struct lws_context * context, const char * const *d) -{ - struct lws_plugin_capability lcaps; - struct lws_plugin *plugin; - lws_plugin_init_func initfunc; - struct dirent **namelist; - int n, i, m, ret = 0; - char path[256]; + const lws_plugin_header_t *hdr; + struct lws_plugin *pin; + char sym[96]; void *l; - -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_init(context, d); -#endif - - lwsl_notice(" Plugins:\n"); - - while (d && *d) { - n = scandir(*d, &namelist, filter, alphasort); - if (n < 0) { - lwsl_err("Scandir on %s failed\n", *d); - d++; - continue; - } - - for (i = 0; i < n; i++) { - if (strlen(namelist[i]->d_name) < 7) - goto inval; - - lwsl_notice(" %s\n", namelist[i]->d_name); - - lws_snprintf(path, sizeof(path) - 1, "%s/%s", *d, - namelist[i]->d_name); - l = dlopen(path, RTLD_NOW); - if (!l) { - lwsl_err("Error loading DSO: %s\n", dlerror()); - while (i++ < n) - free(namelist[i]); - goto bail; - } - /* we could open it, can we get his init function? */ - m = lws_snprintf(path, sizeof(path) - 1, "init_%s", - namelist[i]->d_name + 3 /* snip lib... */); - path[m - 3] = '\0'; /* snip the .so */ - initfunc = dlsym(l, path); - if (!initfunc) { - lwsl_err("%s: Failed to get init '%s' on %s: %s\n", - __func__, path, namelist[i]->d_name, dlerror()); - goto skip; - } - lcaps.api_magic = LWS_PLUGIN_API_MAGIC; - m = initfunc(context, &lcaps); - if (m) { - lwsl_err("Initializing %s failed %d\n", - namelist[i]->d_name, m); - goto skip; - } - - plugin = lws_malloc(sizeof(*plugin), "plugin"); - if (!plugin) { - dlclose(l); - lwsl_err("OOM\n"); - goto bail; - } - plugin->list = context->plugin_list; - context->plugin_list = plugin; - lws_strncpy(plugin->name, namelist[i]->d_name, - sizeof(plugin->name)); - plugin->l = l; - plugin->caps = lcaps; - context->plugin_protocol_count += lcaps.count_protocols; - context->plugin_extension_count += lcaps.count_extensions; - - free(namelist[i]); - continue; - - skip: - dlclose(l); - inval: - free(namelist[i]); - } - free(namelist); - d++; - } - - return 0; - -bail: - free(namelist); - - return ret; -} - -int -lws_plat_plugins_destroy(struct lws_context * context) -{ - struct lws_plugin *plugin = context->plugin_list, *p; - lws_plugin_destroy_func func; - char path[256]; int m; -#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_destroy(context); -#endif + if (strlen(sofilename) < 6) + /* [lib]...[.so] */ + return NULL; - if (!plugin) - return 0; + l = dlopen(libpath, RTLD_NOW); + if (!l) { + lwsl_err("%s: Error loading DSO: %s\n", __func__, dlerror()); - lwsl_notice("%s\n", __func__); - - while (plugin) { - p = plugin; - m = lws_snprintf(path, sizeof(path) - 1, "destroy_%s", - plugin->name + 3); - path[m - 3] = '\0'; - func = dlsym(plugin->l, path); - if (!func) { - lwsl_err("Failed to get destroy on %s: %s", - plugin->name, dlerror()); - goto next; - } - m = func(context); - if (m) - lwsl_err("Initializing %s failed %d\n", - plugin->name, m); -next: - dlclose(p->l); - plugin = p->list; - p->list = NULL; - free(p); + return NULL; } - context->plugin_list = NULL; + /* we could open it... can we get his export struct? */ + m = lws_snprintf(sym, sizeof(sym) - 1, "%s", sofilename); + if (m < 4) + goto bail; + if (!strcmp(&sym[m - 3], ".so")) + sym[m - 3] = '\0'; /* snip the .so */ - return 0; + hdr = (const lws_plugin_header_t *)dlsym(l, sym); + if (!hdr) { + lwsl_err("%s: Failed to get export '%s' from %s: %s\n", + __func__, sym, libpath, dlerror()); + goto bail; + } + + if (hdr->api_magic != LWS_PLUGIN_API_MAGIC) { + lwsl_err("%s: plugin %s has outdated api %d (vs %d)\n", + __func__, libpath, hdr->api_magic, + LWS_PLUGIN_API_MAGIC); + goto bail; + } + + if (strcmp(hdr->_class, _class)) + goto bail; + + pin = lws_malloc(sizeof(*pin), __func__); + if (!pin) + goto bail; + + pin->list = *pplugin; + *pplugin = pin; + + pin->u.l = l; + pin->hdr = hdr; + + if (each) + each(pin, each_user); + + return hdr; + +bail: + dlclose(l); + + return NULL; +} + +int +lws_plat_destroy_dl(struct lws_plugin *p) +{ + return dlclose(p->u.l); } diff --git a/lib/plat/windows/windows-init.c b/lib/plat/windows/windows-init.c index d6139f499..1f2b39f9f 100644 --- a/lib/plat/windows/windows-init.c +++ b/lib/plat/windows/windows-init.c @@ -55,6 +55,19 @@ lws_plat_context_early_init(void) return 1; } +static int +protocol_plugin_cb(struct lws_plugin *pin, void *each_user) +{ + struct lws_context *context = (struct lws_context *)each_user; + const lws_plugin_protocol_t *plpr = + (const lws_plugin_protocol_t *)pin->hdr; + + context->plugin_protocol_count += plpr->count_protocols; + context->plugin_extension_count += plpr->count_extensions; + + return 0; +} + int lws_plat_init(struct lws_context *context, const struct lws_context_creation_info *info) @@ -81,9 +94,11 @@ lws_plat_init(struct lws_context *context, context->fd_random = 0; -#ifdef LWS_WITH_PLUGINS +#if defined(LWS_WITH_PLUGINS) if (info->plugin_dirs) - lws_plat_plugins_init(context, info->plugin_dirs); + lws_plat_plugins_init(&context->plugin_list, info->plugin_dirs, + "lws_protocol_plugin", + protocol_plugin_cb, context); #endif return 0; @@ -107,6 +122,11 @@ lws_plat_context_late_destroy(struct lws_context *context) { int n; +#ifdef LWS_WITH_PLUGINS + if (context->plugin_list) + lws_plugins_destroy(&context->plugin_list); +#endif + for (n = 0; n < FD_HASHTABLE_MODULUS; n++) { if (context->fd_hashtable[n].wsi) lws_free(context->fd_hashtable[n].wsi); diff --git a/lib/plat/windows/windows-plugins.c b/lib/plat/windows/windows-plugins.c index c30bd83ec..e55f802d3 100644 --- a/lib/plat/windows/windows-plugins.c +++ b/lib/plat/windows/windows-plugins.c @@ -27,12 +27,106 @@ #endif #include "private-lib-core.h" +#if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) + +const lws_plugin_header_t * +lws_plat_dlopen(struct lws_plugin **pplugin, const char *libpath, + const char *sofilename, const char *_class, + each_plugin_cb_t each, void *each_user) +{ + const lws_plugin_header_t *hdr; + struct lws_plugin *pin; + char sym[96], *dot; + uv_lib_t lib; + void *v; + int m; + + lib.errmsg = NULL; + lib.handle = NULL; + + if (uv_dlopen(libpath, &lib)) { + uv_dlerror(&lib); + lwsl_err("Error loading DSO: %s\n", lib.errmsg); + uv_dlclose(&lib); + return NULL; + } + + /* we could open it... can we get his export struct? */ + m = lws_snprintf(sym, sizeof(sym) - 1, "%s", sofilename); + if (m < 4) + goto bail; + dot = strchr(sym, '.'); + if (dot) + *dot = '\0'; /* snip the .so or .lib or what-have-you*/ + + if (uv_dlsym(&lib, sym, &v)) { + uv_dlerror(&lib); + lwsl_err("%s: Failed to get '%s' on %s: %s\n", + __func__, path, dent.name, lib.errmsg); + goto bail; + } + + hdr = (const lws_plugin_header_t *)v; + if (hdr->api_magic != LWS_PLUGIN_API_MAGIC) { + lwsl_err("%s: plugin %s has outdated api %d (vs %d)\n", + __func__, libpath, hdr->api_magic, + LWS_PLUGIN_API_MAGIC); + goto bail; + } + + if (strcmp(hdr->_class, _class)) + goto bail; + + pin = lws_malloc(sizeof(*pin), __func__); + if (!pin) + goto bail; + + pin->list = *pplugin; + *pplugin = pin; + + pin->u.lib = lib; + pin->hdr = hdr; + + if (each) + each(pin, each_user); + + return hdr; + +bail: + uv_dlclose(&lib); + + return NULL; +} + int -lws_plat_plugins_init(struct lws_context * context, const char * const *d) +lws_plat_destroy_dl(struct lws_plugin *p) +{ + return uv_dlclose(&p->u.lib); +} + +static int +protocol_plugin_cb(struct lws_plugin *pin, void *each_user) +{ + struct lws_context *context = (struct lws_context *)each_user; + const lws_plugin_protocol_t *plpr = + (const lws_plugin_protocol_t *)pin->hdr; + + context->plugin_protocol_count += plpr->count_protocols; + context->plugin_extension_count += plpr->count_extensions; + + return 0; +} + +int +lws_plat_plugins_init(struct lws_context *context, const char * const *d) { #if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_init(context, d); + if (info->plugin_dirs) { + uv_loop_init(&context->uv.loop); + lws_plugins_init(&context->plugin_list, info->plugin_dirs, + "lws_protocol_plugin", NULL, + protocol_plugin_cb, context); + } #endif return 0; @@ -42,8 +136,12 @@ int lws_plat_plugins_destroy(struct lws_context * context) { #if defined(LWS_WITH_PLUGINS) && (UV_VERSION_MAJOR > 0) - if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) - return lws_uv_plugins_destroy(context); + if (lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV) && + context->plugin_list) { + lws_plugins_destroy(&context->plugin_list, NULL, NULL); + while (uv_loop_close(&context->uv.loop)) + ; + } #endif return 0; diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 17882edc7..d81e285d7 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -1967,11 +1967,14 @@ rops_init_vhost_ws(struct lws_vhost *vh, sizeof(struct lws_extension) * m); plugin = vh->context->plugin_list; while (plugin) { + const lws_plugin_protocol_t *plpr = + (const lws_plugin_protocol_t *)plugin->hdr; + memcpy((struct lws_extension *)&vh->ws.extensions[m], - plugin->caps.extensions, + plpr->extensions, sizeof(struct lws_extension) * - plugin->caps.count_extensions); - m += plugin->caps.count_extensions; + plpr->count_extensions); + m += plpr->count_extensions; plugin = plugin->list; } } else diff --git a/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c b/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c index 10e525c3f..428a27d71 100644 --- a/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c +++ b/minimal-examples/client-server/minimal-ws-proxy/protocol_lws_minimal.c @@ -238,37 +238,3 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 128, \ 0, NULL, 0 \ } - - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c b/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c index e3e01c3db..e621c8e2a 100644 --- a/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c +++ b/minimal-examples/dbus-server/minimal-dbus-ws-proxy/protocol_lws_minimal_dbus_ws_proxy.c @@ -793,36 +793,3 @@ callback_minimal_dbus_wsproxy(struct lws *wsi, enum lws_callback_reasons reason, 1024, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_DBUS_WSPROXY -}; - -int -init_protocol_minimal_dbus_wsproxy(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_dbus_wsproxy(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt deleted file mode 100644 index 6a24a7be8..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -project(lws-minimal-http-server-generic-sessions C) -cmake_minimum_required(VERSION 2.8) -find_package(libwebsockets CONFIG REQUIRED) -list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR}) -include(CheckCSourceCompiles) -include(LwsCheckRequirements) - -set(SAMP lws-minimal-http-server-generic-sessions) -set(SRCS minimal-http-server-generic-sessions.c) - -set(requirements 1) -require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_SERVER 1 requirements) -require_lws_config(LWS_WITH_TLS 1 requirements) -require_lws_config(LWS_WITH_GENERIC_SESSIONS 1 requirements) -require_lws_config(LWS_WITH_LIBUV 1 requirements) -require_lws_config(LWS_WITH_PLUGINS 1 requirements) - -if (requirements) - add_executable(${SAMP} ${SRCS}) - - if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS}) - add_dependencies(${SAMP} websockets_shared) - else() - target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS}) - endif() -endif() diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/README.md b/minimal-examples/http-server/minimal-http-server-generic-sessions/README.md deleted file mode 100644 index 976aea686..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# lws minimal http server with generic-sessions - -## build - -``` - $ cmake . && make -``` - -## usage - -``` - $ ./lws-minimal-http-server-tls -[2018/03/20 13:23:13:0131] USER: LWS minimal http server TLS | visit https://localhost:7681 -[2018/03/20 13:23:13:0142] NOTICE: Creating Vhost 'default' port 7681, 1 protocols, IPv6 off -[2018/03/20 13:23:13:0142] NOTICE: Using SSL mode -[2018/03/20 13:23:13:0146] NOTICE: SSL ECDH curve 'prime256v1' -[2018/03/20 13:23:13:0146] NOTICE: HTTP2 / ALPN enabled -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing cert filepath localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: Loaded client cert localhost-100y.cert -[2018/03/20 13:23:13:0195] NOTICE: lws_tls_client_create_vhost_context: doing private key filepath -[2018/03/20 13:23:13:0196] NOTICE: Loaded client cert private key localhost-100y.key -[2018/03/20 13:23:13:0196] NOTICE: created client ssl context for default -[2018/03/20 13:23:14:0207] NOTICE: vhost default: cert expiry: 730459d -``` - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert b/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert deleted file mode 100644 index 6f372db40..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.cert +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -e2Hl/7M0c/lbZ4FvXgARlex2rkgS0Ka06HE= ------END CERTIFICATE----- diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key b/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key deleted file mode 100644 index 148f8598e..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/localhost-100y.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -fBrpEY1IATtPq1taBZZogRqI3rOkkPk= ------END PRIVATE KEY----- diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c b/minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c deleted file mode 100644 index f4e11e645..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/minimal-http-server-generic-sessions.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * lws-minimal-http-server-generic-sessions - * - * Copyright (C) 2019 Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates setting up and using generic sessions - */ - -#include -#include -#include - -static int interrupted; -struct lws_context *context; - -static const struct lws_protocol_vhost_options - pvo_mm1 = { - NULL, NULL, "message-db", (void *)"/var/www/sessions/messageboard.sqlite3" -}, pvo_m1 = { - NULL, &pvo_mm1, "protocol-lws-messageboard", "" -}, - - pvo13 = { - NULL, NULL, "email-confirm-url-base", (void *)"https://localhost:7681/" -}, pvo12 = { - &pvo13, NULL, "urlroot", (void *)"https://127.0.0.1:7681/" -}, pvo11 = { - &pvo12, NULL, "email-contact-person", (void *)"andy@warmcat.com" -}, pvo10 = { - &pvo11, NULL, "email-helo", (void *)"warmcat.com" -}, pvo9 = { - &pvo10, NULL, "email-expire", (void *)"3600" -}, pvo8 = { - &pvo9, NULL, "email-smtp-ip", (void *)"127.0.0.1" -}, pvo7 = { - &pvo8, NULL, "email-from", (void *)"noreply@warmcat.com" -}, pvo6 = { - &pvo7, NULL, "confounder", (void *)"some kind of secret confounder" -}, pvo5 = { - &pvo6, NULL, "timeout-anon-idle-secs", (void *)"1200" -}, pvo4 = { - &pvo5, NULL, "timeout-idle-secs", (void *)"6000" -}, pvo3 = { - &pvo4, NULL, "session-db", (void *)"/var/www/sessions/lws.sqlite3" -}, pvo2 = { - &pvo3, NULL, "admin-password-sha256", - (void *)"25d08521d996bad92605f5a40fe71179dc968e70f669cb1db6190dcd53258200" /* pvo value */ -}, pvo1 = { - &pvo2, NULL, "admin-user", (void *)"admin" -}, pvo = { - &pvo_m1, &pvo1, "protocol-generic-sessions", "" -}, - - interpret1 = { - NULL, NULL, ".js", "protocol-lws-messageboard" -}, - - pvo_hsbph[] = {{ - NULL, NULL, "referrer-policy:", "no-referrer" -}, { - &pvo_hsbph[0], NULL, "x-xss-protection:", "1; mode=block" -}, { - &pvo_hsbph[1], NULL, "x-content-type-options:", "nosniff" -}, { - &pvo_hsbph[2], NULL, "content-security-policy:", - "default-src 'self'; " - "img-src https://www.gravatar.com 'self' data: ; " - "script-src 'self'; " - "font-src 'self'; " - "style-src 'self'; " - "connect-src 'self'; " - "frame-ancestors 'self'; " - "base-uri 'none'; " - "form-action 'self';" -}}; - - static const struct lws_http_mount mount2 = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/needadmin", /* mountpoint URL */ - /* .origin */ "./mount-origin/needadmin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ "protocol-lws-messageboard", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ &interpret1, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 7, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, - }; - - static const struct lws_http_mount mount1 = { - /* .mount_next */ &mount2, /* linked-list "next" */ - /* .mountpoint */ "/needauth", /* mountpoint URL */ - /* .origin */ "./mount-origin/needauth", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ "protocol-lws-messageboard", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ &interpret1, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 5, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, - }; - -static const struct lws_http_mount mount = { - /* .mount_next */ &mount1, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ "protocol-lws-messageboard", - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ &interpret1, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void sigint_handler(int sig) -{ - lws_context_destroy(context); - - interrupted = 1; -} - -int main(int argc, const char **argv) -{ - struct lws_context_creation_info info; - const char *p, *plugin_dirs[] = { - "/usr/local/share/libwebsockets-test-server/plugins", - NULL }; - int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server TLS | visit https://localhost:7681\n"); - - signal(SIGINT, sigint_handler); - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | - LWS_SERVER_OPTION_EXPLICIT_VHOSTS; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - info.plugin_dirs = plugin_dirs; - info.pvo = &pvo; - - if (lws_cmdline_option(argc, argv, "-h")) - info.options |= LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK; - - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return 1; - } - - info.headers = &pvo_hsbph[3]; - - if (!lws_create_vhost(context, &info)) { - lwsl_err("lws init failed\n"); - return 1; - } - - while (n >= 0 && !interrupted) - n = lws_service(context, 0); - - lws_context_destroy(context); - - return 0; -} diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html deleted file mode 100644 index 6fdd6bf33..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/404.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - -
- -

404

- Sorry, that file doesn't exist. - - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html deleted file mode 100644 index 113df9cd3..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/admin-login.html +++ /dev/null @@ -1,5 +0,0 @@ - -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. - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js deleted file mode 100644 index 1bde61e46..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/example.js +++ /dev/null @@ -1,21 +0,0 @@ -document.addEventListener("DOMContentLoaded", function() { - - var transport_protocol = ""; - - if ( performance && performance.timing.nextHopProtocol ) { - transport_protocol = performance.timing.nextHopProtocol; - } else if ( window.chrome && window.chrome.loadTimes ) { - transport_protocol = window.chrome.loadTimes().connectionInfo; - } else { - - var p = performance.getEntriesByType("resource"); - for (var i=0; i < p.length; i++) { - var value = "nextHopProtocol" in p[i]; - if (value) - transport_protocol = p[i].nextHopProtocol; - } - } - - if (transport_protocol === "h2") - document.getElementById("transport").innerHTML = ""; -}, false); \ No newline at end of file diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html deleted file mode 100644 index 9ab065b53..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/failed-login.html +++ /dev/null @@ -1,3 +0,0 @@ - -This is an example destination that will appear after a failed login - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/favicon.ico b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/favicon.ico deleted file mode 100644 index c0cc2e3dff34012ba3d4a7848a7ed17579788ec5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1406 zcmZQzU<5(|0R}M0U}azs1F|%L7$l?s#Ec9aKoZP=&`9i!<^REA8>%80(yxAC$j<-A zkb5S8;qL6446ipNFl>5#fuVR6L=8goC~GtXMnhmYga9MSfQgBTk&TUw5$JocUP63y z3phA97+G0a8QIy{!BT|y==xb$SQt4uIT@LmnZZ(o_~`mk`Tv1M8w?+DXJCL~kQj^& JqOtKoVgQl$ETjMc diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/http2.png b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/http2.png deleted file mode 100644 index 439bfa482fa00e69af2d562f17a6e89453eb98cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7563 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 z - - - - - - - - - - - - - - - - -
-
-
- -
- - This is a demo application for lws generic-sessions.

- It's a simple messageboard.

- What's interesting about it is there is no serverside scripting,
- instead client js makes a wss:// connection back to the server
- and then reacts to JSON from the ws protocol. Sessions stuff is
- handled by lws generic sessions, making the actual
- test application
very small.

- And because it's natively websocket, it's naturally connected
- for dynamic events and easy to maintain. -

- Register / Login at the top right to see and create new messages. -
- -
- - New message
-
- - -
-
-
- -
- -
-
- - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg deleted file mode 100644 index ef241b37c..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/libwebsockets.org-logo.svg +++ /dev/null @@ -1,66 +0,0 @@ - - - - - -image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js deleted file mode 100644 index 096909f6f..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lws-common.js +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This section around grayOut came from here: - * http://www.codingforums.com/archive/index.php/t-151720.html - * Assumed public domain - * - * Init like this in your main html script, this also reapplies the gray - * - * lws_gray_out(true,{'zindex':'499'}); - * - * To remove the gray - * - * lws_gray_out(false); - * - */ - -function gsize(ptype) -{ - var h = document.compatMode === "CSS1Compat" && - !window.opera ? - document.documentElement.clientHeight : - document.body.clientHeight; - var w = document.compatMode === "CSS1Compat" && - !window.opera ? - document.documentElement.clientWidth : - document.body.clientWidth; - var pageWidth, pageHeight, t; - - if (document.body && - (document.body.scrollWidth || document.body.scrollHeight)) { - t = document.body.scrollWidth; - pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); - t = document.body.scrollHeight; - pageHeight = (h > t) ? ("" + h + "px") : ("" + (t) + "px"); - } else if (document.body.offsetWidth) { - t = document.body.offsetWidth; - pageWidth = (w > t) ? ("" + w + "px") : ("" + (t) + "px"); - t = document.body.offsetHeight; - pageHeight =(h > t) ? ("" + h + "px") : ("" + (t) + "px"); - } else { - pageWidth = "100%"; - pageHeight = "100%"; - } - return (ptype === 1) ? pageWidth : pageHeight; -} - -function addEvent( obj, type, fn ) { - if ( obj.attachEvent ) { - obj["e" + type + fn] = fn; - obj[type+fn] = function() { obj["e" + type + fn]( window.event );}; - obj.attachEvent("on" + type, obj[type + fn]); - } else - obj.addEventListener(type, fn, false); -} - -function removeEvent( obj, type, fn ) { - if ( obj.detachEvent ) { - obj.detachEvent("on" + type, obj[type + fn]); - obj[type + fn] = null; - } else - obj.removeEventListener(type, fn, false); -} - -function lws_gray_out(vis, _options) { - - var options = _options || {}; - var zindex = options.zindex || 50; - var opacity = options.opacity || 70; - var opaque = (opacity / 100); - var bgcolor = options.bgcolor || "#000000"; - var dark = document.getElementById("darkenScreenObject"); - - if (!dark) { - var tbody = document.getElementsByTagName("body")[0]; - var tnode = document.createElement("div"); - tnode.style.position = "absolute"; - tnode.style.top = "0px"; - tnode.style.left = "0px"; - tnode.style.overflow = "hidden"; - tnode.style.display ="none"; - tnode.id = "darkenScreenObject"; - tbody.appendChild(tnode); - dark = document.getElementById("darkenScreenObject"); - } - if (vis) { - dark.style.opacity = opaque; - dark.style.MozOpacity = opaque; - // dark.style.filter ='alpha(opacity='+opacity+')'; - dark.style.zIndex = zindex; - dark.style.backgroundColor = bgcolor; - dark.style.width = gsize(1); - dark.style.height = gsize(0); - dark.style.display = "block"; - addEvent(window, "resize", - function() { - dark.style.height = gsize(0); - dark.style.width = gsize(1); - } - ); - } else { - dark.style.display = "none"; - removeEvent(window, "resize", - function() { - dark.style.height = gsize(0); - dark.style.width = gsize(1); - } - ); - } -} - -/* - * end of grayOut related stuff - */ - -function new_ws(urlpath, protocol) -{ - return new WebSocket(urlpath, protocol); -} - -function lws_san(s) -{ - if (s.search("<") !== -1) - return "invalid string"; - - return s; -} diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs-logo.png b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs-logo.png deleted file mode 100644 index 723a124431189c21c340de517bd9b82cb35374d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9729 zcmai)cQ}>*|HsckkrgsBvR7p9Egkb?CD~g<_KHLZA%rA*XJ%&aO=Zj8vNs_ze=pbd z`~T}A*Ev_mec$i<{d&G$&&Tr)QCF46!=}JSAP{(p3MdWu{T+VPux`S?MceN$;TMLB zjG`tMe0gC#3x?OX92In35D3Pk>mQ`9|6dV!liXES$MuPWrK`KCvjxK4-JR3g-p0k; z)X{>|!TEXOjyMGZL5omCJ=F9_+BSS;pt(GIh2MtThX2iW0mO2ir9plbnGm4fuE)DblKjH&_fjJn9Hy)Ra(?HtP zlu_7cyx+Z_d_4#`Q_Pe zxz|5uY&<+B78VvF>_7}(F$^?h2#OvXA3yum!OCFB9hTh6%F(q0b+(xDXFrI(m6Xhf z7jFx@u0;-Z`6TQu1kwE28Ea|rgX>EpUpc?hn>g(jrGTNW ztzBJL_lS`wy}q8R%3-dv(r!v!Umr(IOicW2t4uDrduyVs^yT)X<>swG8C)vifn0Mb zJM}9LL|R4$n)~joth)NZorK4mdpXPmSsEc1XUB`HsXn7850jFU3GdwtA0O8lpPZ!U z;0XWfzA=2?!tt|mLc?h+H=lto>=6H<@OHxu20U@DZS{kiEgC_|S6Vvy;zIQ=E@`)sWVa?MD z3c}pCo31Xnj9zaGgx4C@y_6j*GMwU{O+}*lA@=w8gCin*hMrj8!DWdocrZUeH$Rm1 zG6^4{txd~`@3BHIO+Tm`9;~jR@wT>Bl$97eoEJ97r`XuVf%He?b?#i9AMS;opL=O) zY02bi!Z(H8Pf8*CrR3x7DY9SLDmHFD$GdY~H7={do7z{G7lT>uv zk6BVuB5h@5RYqF&`gmvI{NDj97gtb4g#cXKSO$-ai%V2uVrX--G;HX*)2bs{M}y9; zt|)bN0#Ddf8IqHDHxCl(#WSw{(a;+k(?XP9>dZ)~`#=A@_lHD3JH z-xw`S)CwKRfBKd6{{0Zk{uGtb(NTWqC588)p_#tE2%-=azezKq*7Jx>F>6S$bpGAD zn?#`<^&$lZHQ%0kFE1|-;&9bVqxnfcdKUKnea6}G?!m_HR8<6fRaqHBje)BC2P!&a zV`C$IN=nMfa_i_(9v5e4tM*_*y{@niA4cyU#2UX@`1wdiMu9W-SAlL>_1~SH9TduS zz8Ps`e1}DK*t4l$>Z)Fg-MGqrhE+hI;%Wb29Cwnx>jp;HJsfc_tgXo#pJI@5=h%p{LR9m~qz)zEH*^rX=nL_Bz-&u5jUZ~q`Tr6_i82*uV#=*)e4F}nouF0$rmyo!~<9>Eml@<*-zm4}m zr@8T{%AqWF{4Oopix~cLtIE9%8gc(gwiQ8;&Vv;~ zW2n8xe=C$;=lflHXSyXOetkTkuy4-y<%OT7LKUvn#m_?Z7r=o zecRjHbc2~<)@`NcU5*Q_H+H6>L)5wN%6WJQf1nb1JD4f&!nJ){>O3u48j%K)qf_lj z_eqIK@?;);X=TOPB6j$&%h>0FBrh)y7CV#Scv~5R9>6W1;*HYU-JU`uvi^M_=QJJUMP6N6N~f;o}ovVK}7>MnnWa{cK8$q+_E z`u_fHR)|4FP2Jb<3^DLko6D%CR%AMoXXKuDM@NTVsv&fS&|q{#kzrk@SVU;(V(*#t zTc=w~Lrf-TFHPnoY5AhpN!b(_!zhVKkkKsYMn++*2^lup^I5Sv`ZY;TA8SSD$BdEb zk%p9lxF-y}LaeM<)YQ~^Br5bW(6OK?sq5-1H)@!6CNvgxnt7CN`nZQ@u%l{PEnXLKs*)R!d7uJ-hfn;cO5w#3(WC z>Jk#ZyG>*Ni3iW`^a4$}-BZt$3N1=$BKC66bbiP1@6c-xL}_XB;kvXrL*(b1RKf@< zv726(BJ*24KMf5ljwim7nOOf2?tF*WQ#&%y++Ov^x%nqHOm9A$b2K0>3TkRy6*go1 zJ&hZ}ZX3icV8IJHZR}Ia^f3rr;3+XTwJ*$ z-Rr23xaal%k#$P^-9if5p?Mw-sL&$jcW?J3cM~6{zA+o+hz|>S;j)6bI%mN!HNDGk zMF2x(R8*87w8MrcDvFAJ8hHWA%)cnGhcphiChnm5`O3$!sHH4neG!REOgvEj+Sk{o z)8JXrcl!DBXFEae;0`l*-=z~BhCUF-t3lnlpFfpyL>dIFPEFZ3IR*9RI^Q{Ap_tK~ zUC3D;6DrI#`qpO3Gn$QMt6}cUVcJid_&ky+KNUuBC%4Tc>z2NF5f}Vay}9!~heeWN zUO-iqP)25ERBUWep?(#buc|72jFQvEsT(6Rb8AlzE?2`LQ9(h0p7$m`K0X#MF3Qr9 z*`0%jhtQ$%Uv>*uOlT+$%K@maSx+3#cH^JHj*h^x7f&@cX*oHI49TMMGV>H-Vq%VT zI_>bn%r|YV5Jkq%Pm#^Udpk2UF{?|6rgO9;F-&1mUKwj!j2AZXSR+&ct@8Aw&DE-2 z;*@QZ6dMV}!gUUJ9XS%&^v zB@yj~JyT|_nd%nemsua3@Je;6j4@0uW@_5nOS@yqqpE9pZ+M@&rW_0m4icuPr=Oh7 zU48ie{ktVe)5gY&^S@ILI#u@fK2S^K^G&xBC?|`O{AM`M|k*(&Q-fBLi|yvnrg zX5n@a{=J`KS0{@R0!xQ552o;y7IHu90pZI0kSFsitBRbQT%0f>*m3{2Y-Yq1#oeF? z9E>gYVxs$uM1AR)2x0F8-Ad7y&wQR}#1ZW-ebKM~_~WkA)=ak-EeT0jNv$hVF5+hS z`0V!97Wa!`R;)l7==Vt?E;mU?NJMiu;LgG{5AVjs$I~%0%eC>p!1D6;=5gOK@H!gN zo))Mv_`vkvXOs1zEKqP6C#O=?juRLN9?Hm|QBYDM5zqUQ9u^xl5Rj2!>g($-Eiaqy z&Nk4}FRiXp%I0}5$7n+_qa5^7QuL*9vP*AH=$g)F+(Sg&=)YAk zIlC}?t76eB@nsd$f9<2hG?*-gV%H*=m>|4`QDE%nYqh4H;g1>ejJM&5sK*{{IE8=` z!;sb(4~(er+T<(ELpwW;Kec!*&PjcygYfpPQ=Y4!{Oim(!455<1A0m{4 zd7FCgCyWby>{n}<5x5jCu+t1EkF1Ok{B9eXh5eZ|R zLNs0V9R{3wW_ulPbYOM$eF%|@it0Hgr1;sYRIdAiS`uTl=tiX-`ky~>PkZ|C)nDm% z|BRzygpMa_Z{PE28iPIx3uMXB$?2iDw}i8^^Wo&Bgt+(~?Cr`QU)f1Rc`SSGws&^6 z_VwX|Ne5p97qNGABntz^N`GpEfX%2CBNy~?kA*-o0%*bU;T#Vh%)_+}45-)tTS-(< zRz`ujsS%V(CrC+2xgLby;ZeSWd6a>hJ8~?ip+Q1h5_Zt+#Vri9FbV-||C@Md&0fMF z4n{^sSU5Os`VI|&Fz#GWbM-t8_4TeF@*M0pXfQ^`VK!SC;?vdBqo<`sfa%n!coEY= zQ}r_N@kykn(x+6lax22ev7D&UQB=Q#{Nb+8otV!=_lbLt>0--jjo&B|6ScMrq(^=q ziCMci4ym*=&(JfWy;IR{meJxLB4|hd;#;o#Jqrtqa-XX=6ui%ZM=Lh-Gacp{%U^Em zWs2k9xfJEb$0Wo?zJA?F0s<$?E)*O1Sy-6c_+7;j17X4aCFf3&m%BF`&+x0=LJbcN zG33HbR9l)Z!jCf5xd{u(KOGqx_MX0UyHVbL1FoWVWP}8crtsf?iv>z53+wA4joxRH zBTf&Q#oyc=qZ}!uUau>rlia~cWHATWrjIg&izWVP^_Aq_hxxNQhva&rjxeQ**P6Ub; z7EDlyk#TXkrIQ3EetzbiVb|(7Ju@@qEoq0mAoTP(*)t6Y3|trk2jRn#U1^L;st^dB z_r7rZ+nOTLEi+4k-N_W}o9eUD<%sO5q9fj5#wY3N(J9}pb_!E?a~Z$Rb#$aR)-8qq zn-v@%$b|h&ZC05o$mD3TaYWXCv6W|DlkWkKP)T~-ML?I+HK=y<)Bk*Sbz_^7_XXSs z^44$Bti;ONCl~IdqG;Tu`Hh!RebN|TvcKgT%FAc>2cu5Au&~Wk6*WmRMxm$aCJ>ifg2e<@>5Bd!ySvLzB16E|&8YodQYA9a(5`d}Psy z;4q!qR-t|R^eOc3F`8eu+QG$mY*GVz8Z6WoQjX(ZP&4N)si^2!|CQskHb4g+xpi#p zSW#`gba9^`SQ-&V-q!eMdD++V%gTI9Po0qtR;~eWR+e&2@mKW5^N&u^E5!OVG^Ccs z+Q;9q(%$`Ljs&FQXzgs-mz3vTkx|NhVQwF zCUKcoV35=hji9Z`&zG-6_^!~JpQrL%$+_To2FS=!=^056eGD9oQ&$_^drpw5%#9l% zR9BbDz}Ld5+e+n?UtNHIFD_2Nj)LK%#A_WLFH<@krSgg!mi?CNZxa|kvPe@QJZvht z3~K{^M6*=Y7JvLmlqBUtot2ffcX)_8-kBME?Cl*+3QqL*?~c&i++1JqGid1Ogj7@o zBVUwJ)n5O2hkpGU93RJmyK%TPQ`e!GGXkbGGASuc%za1t0V${^LBIHca;Wm8JGkE% zc5QV%J!}UD2iTbF%k2UJ0t%|CouG`gq@jbrI>e8sT{>0hhN<`2m9jo=Y;NXNSI6b& z=jQ_aA(o|KUBsMQQ1E59 z@$3Vz!ATC%ukhI^hW5$H1YSNq{b~89Pf2p3XJSiz%f-L5vnTfUMJ;xe z6cntyydP~G0BT-dUW%K&TF;8*pS&J4*vUdWmKGNu5rqWQRoaYk7TKGdGq~+c$Fl=~ zqNkyu;S~~E#VRc+i5&BB!T1JbdDi9d@NlW2dSzt=Oz@XMRasi_JDFf_(4=M3BfY%5 zJYI{}*3>+fmAz5C4d&gWeQ9Gusi>%E`X8VRyf-_&=_(i*>Z|~vKZ7l~&%+Zn>wRoU z!DEKuwmlgG4!HUjPN2-U($e(ENP+}0cg|#Cr?=FSUJAV4p(!7uLyWv*IKB~uj1v;0 z%5BLX>*|KC=<4b!&b>LatGlBBcNi}G;LSA9xVyuDL%AB~CmRm>#Hgw~PWI?zQ7A`? zST5t0$xoj@J9GU6{`sm-7+TpNIMB|i0!?qf5rFGmb{DkKKUkTV0<{Ws(s{VNF`Qu! ze5e+6-%$qWMo0hG2;kp4h8@*p(Z`P-p%)jvm)7h8_FMhBC_g{rai*M;#pYP?YDIWN z1Pg*OO2LCFJ3s$D5c2G3n~Ixu^pQ|+lMSzh!l@+^3JMEoCP!~X#8bYW+ReW2TFDCNFGCDaFHo0yz< zI@VZSJ)U+pC{N30*)#uU4xrYHPh3Xzag+`aO4ReZp}o2|8WWhbw6Jhm1|GZ^lP9cd zYg_Q}c!0Ega@GUX-|O<_6iVGKJE?ccwDhC|BO)VHU)fpYRIqnP*G9QCk@;|?U{vz zb-}_qWJmyZ9M4ajK*3YaJjyZklxbnP!9uh{{Y9pxI+SSZ&=NSdr=5B(16rIAh0H0m zEPxUl-~$FGruolL_eGn2-xGBm_&zpFs-vsB&_(0Z4z>u~(# z@X!@u>kyTYKoLG&(af-F)}S|?02Umglc4RXs>SIVm*DsBTS2?r5&}|vE?=Ge`**#> zU$qKlU~<{2q3;o)ig6sjG87NKP^` zGD@5l<$xYm2D+Qj>d{BsC&p%DXV1*dWi9O1zDGs%eQYeY)^%NGc6RpJ^XFanqu3;PQ8t@bYs0jqy?j_olMv{VOq>lR0WrgPLTp(isp=6is(_ zzPm=s$)f#%IAk6-KfzueTxbh&>obhS|Mt9}x+ntldCgV|98i%FFm!jrEPr89(G6R% zH|HK`(vZrGu%7L;-G0??F`WBYQ(Zl+tavm-R#X8r_0OL_k@4|Ayq6)2>*?)Xn4kA8 zDdAR5mAvP&+Sjr4BYLpdSZaJ~%Eg&=eE=Qsx<8PR-;gB9fwu@nRZ5}1_4jvzdu*RG z0RzjQO8X~H0_o~l^78Tx@=uvDJMi|ZikEDSfqtO zsvPbD$n|2ip8{hB^s9jc0UK5+sni!XHhD!wAAr5Bftr-Nu4`-z=S8dTq>Dlm{5~}F zFX+M7)ZGJ$mxp&V}rC@$3XVD zHa0eW)3&(fN{ZT7n@dXpu*la|4UA*;n+s81US23`EC|1p6ZmhzhHuVRNxbG9$;S5~ zUN}AV+?uMo4GRBiOAKFEU;jFwC~iD`QOEHtLD-3Y)?*P5;YDubR?L=FRS_H-+BVGG!RNUgiaB#vrAA{LG%Pg1 ziEjT%JUuxvnQQbuIX~#Xj%Ogp{2eYd%T2*zP3V`4x_@M)3CJzr-MiM_UOYIj7C7I? zgaqsw1N`UDpTm@j1Y;fmG*6us1zC@gmXW6B@CsR$06}O+wa=9#RFGwF!VS3K%;#cN zRaHUZ;ds}W^mhbXSbbw7HK=J4>nH{6;@^;LJ%nof+1#?K5@mPA5ALWDtlO&ezWZ{BFN>&0;{b0N;ARY7OS7 z(O_Y`&9OJE)MS%|tVhscHVg)=s&OwnEGU1G2}6nM(&zmt(^olasrCoLA3l5tzHHO} z(m0a984wiY@|o+=26~Sg-KjZGViCp|s(o92+)!f5N9>_QcVqhTAQfmN%tC7;U&&xJ zLzWaQv$+)OJdlH8;W2B+g26nkw3HC`?e`x){I10g5QnU^$_l-&}cPrquB+lev6a0l|yY<@u{{-gxDAARxaRj%U%EtGm_)Gi#i=xVWy11aXk>^XCT(g*hn+iBlqa z9t3g%m6|tw0Ym+U|A%Nka9mwpw%)SGK|oMAeN$4!y>lc;F`5}^LRzR>X1OoP43m_B zrf*CPNoNF&gJVy?__P)QUFhv6L{c^DNH)fz-OPy+I(o>&bdBUPA8vcZW8*vxnm1+m%p0APnf zM-KLs?caC^c|Y3hE-%YMI7CcCqxR?>E*)eYDHW;`ty+O$VYo)bwvLY64Hti>_OTYyJB`#Eaqqnr%)?2WVy6p0mqv_*Kivna9E=l0-Pn3l zrBY!pwB4YZoS3L!P0(hJ-j`W@eYp1UV5K)xXx76YTzJi~h$Yoa#?Q}>kr?~O>}<;U#l?L9 zPw@93*?kW)hZQK^rmr+>H4+Hc8>(Gi8P3Jb@5<}{pq5XH#I zxYycKanIh~o??@4*y;4p`p@3pz=R|}|0n1zZ6m2tK8<~~FeWx$p4u2)frt$2&J7Mm zUBhFZoR!v@-I+Rh@6*FUks#=WFYLzj&}v>CWY2PN$UJ)V>T$1Kpl_~QYl21|%F|Qy zdgg;b6vh#!QQQy|8dy9O3T1M#w}6F>Jygq~XJkZuonAsmlN1oc76=Rsya85{2f8Kz zdvwrd(Lx@WjKGPLUB@{9Q9uWsCx$TbC;}k+v2-8h;Nt26Pv*2UEh6r9LI7j!+H8ZE znVDI6r#L+5viLoUq59PUGvq@i*482LJdPR6H1)O{2$w@ZO$!jvUB^X+hWDV@_p6#N z@L-DlRm&c$6mgvrM$$bh@6+XsHSbT65cWFZrWW@=z*7r{YlG<&Ahji7-8~vBA*CzsyA2K0{%3e+t zk3Uv?@JLdtF}Y95IUjS}9>)KCQ0ad>+jLHI=TVqeB|~x|Jg$XMlvPC)$(X$TKVRg% ARR910 diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css deleted file mode 100644 index 907851f0e..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.css +++ /dev/null @@ -1,144 +0,0 @@ -.body { font-size: 12px } -.gstitle { font-size: 18px } - -.group1 { - vertical-align:middle; - text-align:center; - background:#f0f0e0; - padding:12px; - border-radius:10px; -} -.group2 { - display:block; - vertical-align:middle; - font-size: 22px; - text-align:center; - margin:auto; - align:center; - background-color: rgba(255, 255, 255, 0.8); - padding:12px; - border-radius:10px; -} - -body { - background-color: rgba(205, 205, 205, 1); -} - -div.lwsgs { - z-index: 3; - text-align:right; - background-color: rgba(255, 255, 255, 0.8); -} - -table.lwsgs { - width:100%; - height:100%; - transition: max-height 2s; -} -table.c100 { - text-align:center; - width:100%; -} - -table.r { - vertical-align:top; - text-align:right; -} - -table.l { - vertical-align:top; - text-align:left; -} - -table.fixed { - table-layout: fixed; -} - -td.logo { - vertical-align:top; - text-align:left; - width:200px -} - -td.rlogo { - vertical-align:top; - text-align:right -} - -td.lwsgs { - vertical-align:top; - float:right; -} - -td.h99 { - height:99%; - vertical-align:middle; -} - -td.c { - margin:auto; - align:center -} - -td.tac { - text-align:center -} - -td.ava { - display:inline-block; - vertical-align:top; - word-wrap:break-word; -} - -iframe.hidden { - display:none; -} - -div.hidden { - display:none; -} - -div.hiddenr { - display:none; - text-align:right; -} - -input { - margin: 2px; - padding: 2px; -} - -input.em { - margin: 4px; - font-weight:bold; -} - -input.wide { - margin: 6px; - padding: 6px; -} - -input.hidden { - display: none; -} - -form.r { - text-align:right; -} - -span.bad { - color: red; -} - -span.small { - font-size:8pt; -} - -img.av { - width: 64px; - height: 64px; -} - -.green { - color: green; -} diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js deleted file mode 100644 index e1204ccc4..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/lwsgs.js +++ /dev/null @@ -1,634 +0,0 @@ - - -var lwsgs_user = "$lwsgs_user"; -var lwsgs_auth = "$lwsgs_auth"; -var lwsgs_email = "$lwsgs_email"; - -var lwsgs_html = '\ - \ -\ -
\ -
\ - \ - \ - \ - \ -
\ - \ -
\ - \ -
\ -\ - \ - \ - \ - \ - \ -'; - -/*-- this came from - -- https://raw.githubusercontent.com/blueimp/JavaScript-MD5/master/js/md5.min.js - -- under MIT license */ -!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t),e=(n>>16)+(t>>16)+(r>>16);return e<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;16>r;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="0123456789abcdef",o="";for(r=0;r>>4&15)+e.charAt(15&t);return o}function v(n){return unescape(encodeURIComponent(n))}function m(n){return d(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this); - -if (lwsgs_user.substring(0, 1) == "$") { - alert("lwsgs.js: lws generic sessions misconfigured and not providing vars"); -} -function lwsgs_san(s) -{ - if (s.search("<") != -1) - return "invalid string"; - - return s; -} - -function lwsgs_update() -{ - var en_login = 1, en_forgot = 1; - - if (document.getElementById('password').value.length && - document.getElementById('password').value.length < 8) - en_login = 0; - - if (!document.getElementById('username').value || - !document.getElementById('password').value) - en_login = 0; - - if (!document.getElementById('username').value || - document.getElementById('password').value) - en_forgot = 0; - - document.getElementById('login').disabled = !en_login; - document.getElementById('forgot').disabled = !en_forgot; - - if (lwsgs_user) - document.getElementById("curuser").innerHTML = lwsgs_san(lwsgs_user); - - if (lwsgs_user === "") - document.getElementById("dlogin").style.display = "inline"; - else - document.getElementById("dlogout").style.display = "inline"; - } - -function lwsgs_open_registration() -{ - document.getElementById("dadmin").style.display = "none"; - document.getElementById("dlogin").style.display = "none"; - document.getElementById("dlogout").style.display = "none"; - document.getElementById("dchange").style.display = "none"; - document.getElementById("dregister").style.display = "inline"; -} - -function lwsgs_cancel_registration() -{ - document.getElementById("dadmin").style.display = "none"; - document.getElementById("dregister").style.display = "none"; - document.getElementById("dchange").style.display = "none"; - - if (lwsgs_user === "") - document.getElementById("dlogin").style.display = "inline"; - else - document.getElementById("dlogout").style.display = "inline"; -} - -function lwsgs_select_change() -{ - document.getElementById("dlogin").style.display = "none"; - document.getElementById("dlogout").style.display = "none"; - document.getElementById("dregister").style.display = "none"; - if (lwsgs_auth & 2) { - document.getElementById("dadmin").style.display = "inline"; - document.getElementById("dchange").style.display = "none"; - } else { - document.getElementById("dadmin").style.display = "none"; - document.getElementById("dchange").style.display = "inline"; - } - - event.preventDefault() -} - -var lwsgs_user_check = '0'; -var lwsgs_email_check = '0'; - -function lwsgs_rupdate() -{ - var en_register = 1, en_forgot = 0, op; - - if (document.getElementById('rpassword').value == - document.getElementById('password2').value) { - if (document.getElementById('rpassword').value.length) - document.getElementById('match').innerHTML = - "\u2713"; - else - document.getElementById('match').innerHTML = ""; - document.getElementById('pw2').style = ""; - } else { - if (document.getElementById('password2').value || - document.getElementById('email').value) { // ie, he is filling in "register" path and cares - document.getElementById('match').innerHTML = - "\u2718 Passwords do not match"; - } else - document.getElementById('match').innerHTML = - "\u2718 Passwords do not match"; - - en_register = 0; - } - - if (document.getElementById('rpassword').value.length && - document.getElementById('rpassword').value.length < 8) { - en_register = 0; - document.getElementById('rpw1').innerHTML = "Need 8 chars"; - } else - if (document.getElementById('rpassword').value.length) - document.getElementById('rpw1').innerHTML = "\u2713"; - else - document.getElementById('rpw1').innerHTML = ""; - - if (!document.getElementById('rpassword').value || - !document.getElementById('password2').value || - !document.getElementById('rusername').value || - !document.getElementById('email').value || - lwsgs_email_check === '1'|| - lwsgs_user_check === '1') - en_register = 0; - - document.getElementById('register').disabled = !en_register; - document.getElementById('rpassword').disabled = lwsgs_user_check === '1'; - document.getElementById('password2').disabled = lwsgs_user_check === '1'; - document.getElementById('email').disabled = lwsgs_user_check === '1'; - - if (lwsgs_user_check === '0') { - var uc = document.getElementById('uchk'); - - if (uc) { - if (document.getElementById('rusername').value) - uc.innerHTML = "\u2713"; - else - uc.innerHTML = ""; - } - } else { - if (document.getElementById('uchk')) - ocument.getElementById('uchk').innerHTML = "\u2718 Already registered"; - en_forgot = 1; - } - - if (lwsgs_email_check === '0') { - var ec = document.getElementById('echk'); - - if (ec) { - if (document.getElementById('email').value) - ec.innerHTML = "\u2713"; - else - ec.innerHTML = ""; - } - } else { - if (document.getElementById('echk')) - document.getElementById('echk').innerHTML = "\u2718 Already registered"; - en_forgot = 1; - } - - if (en_forgot) - document.getElementById('rforgot').style.display = "inline"; - else - document.getElementById('rforgot').style.display = "none"; - - if (lwsgs_user_check === '1') - op = '0.5'; - else - op = '1.0'; - document.getElementById('rpassword').style.opacity = op; - document.getElementById('password2').style.opacity = op; - document.getElementById('email').style.opacity = op; - } - -function lwsgs_cupdate() -{ - var en_change = 1, en_forgot = 1, pwok = 1, op; - - if (lwsgs_auth & 8) { - document.getElementById('ccurpw').style.display = "none"; - document.getElementById('ccurpw_name').style.display = "none"; - } else { - if (!document.getElementById('ccurpw').value || - document.getElementById('ccurpw').value.length < 8) { - en_change = 0; - pwok = 0; - document.getElementById('cuchk').innerHTML = "\u2718"; - } else { - en_forgot = 0; - document.getElementById('cuchk').innerHTML = ""; - } - document.getElementById('ccurpw').style.display = "inline"; - document.getElementById('ccurpw_name').style.display = "inline"; - } - - if (document.getElementById('cpassword').value == - document.getElementById('cpassword2').value) { - if (document.getElementById('cpassword').value.length) - document.getElementById('cmatch').innerHTML = "\u2713"; - else - document.getElementById('cmatch').innerHTML = ""; - document.getElementById('pw2').style = ""; - } else { - if (document.getElementById('cpassword2').value //|| - //document.getElementById('cemail').value - ) { // ie, he is filling in "register" path and cares - document.getElementById('cmatch').innerHTML = - "\u2718 Passwords do not match"; - } else - document.getElementById('cmatch').innerHTML = "\u2718 Passwords do not match"; - - en_change = 0; - } - - if (document.getElementById('cpassword').value.length && - document.getElementById('cpassword').value.length < 8) { - en_change = 0; - document.getElementById('cpw1').innerHTML = "Need 8 chars"; - } else { - var cpw = document.getElementById('cpw1'); - - if (cpw) { - if (document.getElementById('cpassword').value.length) - cpw.innerHTML = "\u2713"; - else - cpw.innerHTML = ""; - } - } - - if (!document.getElementById('cpassword').value || - !document.getElementById('cpassword2').value || - pwok === 0) - en_change = 0; - - if (document.getElementById('showdel').checked) - document.getElementById('delete').style.display = "inline"; - else - document.getElementById('delete').style.display = "none"; - - document.getElementById('change').disabled = !en_change; - document.getElementById('cpassword').disabled = pwok === 0; - document.getElementById('cpassword2').disabled = pwok === 0; - document.getElementById('showdel').disabled = pwok === 0; - document.getElementById('delete').disabled = pwok === 0; - //document.getElementById('cemail').disabled = pwok === 0; - - /* - if (lwsgs_auth & 8) { - document.getElementById('cemail').style.display = "none"; - document.getElementById('cemail_name').style.display = "none"; - } else { - document.getElementById('cemail').style.display = "inline"; - document.getElementById('cemail_name').style.display = "inline"; - if (lwsgs_email_check === '0' && - document.getElementById('cemail').value != lwsgs_email) { - if (document.getElementById('cemail').value) - document.getElementById('cechk').innerHTML = "\u2713"; - else - document.getElementById('cechk').innerHTML = ""; - } else { - document.getElementById('cechk').innerHTML = "\u2718 Already registered"; - en_forgot = 1; - } - } */ - - if (lwsgs_auth & 8) - en_forgot = 0; - - if (en_forgot) - document.getElementById('cforgot').style.display = "inline"; - else - document.getElementById('cforgot').style.display = "none"; - - if (pwok === 0) - op = '0.5'; - else - op = '1.0'; - document.getElementById('cpassword').style.opacity = op; - document.getElementById('cpassword2').style.opacity = op; - // document.getElementById('cemail').style.opacity = op; - } - -function lwsgs_check_user() -{ - var xmlHttp = new XMLHttpRequest(); - xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { - lwsgs_user_check = xmlHttp.responseText; - lwsgs_rupdate(); - } - } - xmlHttp.open("GET", "lwsgs-check/username="+document.getElementById('rusername').value, true); - xmlHttp.send(null); -} - -function lwsgs_check_email(id) -{ - var xmlHttp = new XMLHttpRequest(); - xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { - lwsgs_email_check = xmlHttp.responseText; - lwsgs_rupdate(); - } - } - xmlHttp.open("GET", "lwsgs-check/email="+document.getElementById(id).value, true); - xmlHttp.send(null); -} - -function rupdate_user() -{ - lwsgs_rupdate(); - lwsgs_check_user(); -} - -function rupdate_email() -{ - lwsgs_rupdate(); - lwsgs_check_email('email'); -} - -function cupdate_email() -{ - lwsgs_cupdate(); - lwsgs_check_email('cemail'); -} - - -function lwsgs_initial() -{ - document.getElementById('lwsgs').innerHTML = lwsgs_html; - - if (lwsgs_user) { - document.getElementById("curuser").innerHTML = - "currently logged in as " + lwsgs_san(lwsgs_user) + "
"; - - document.getElementById("ccuruser").innerHTML = - "Login settings for " + - lwsgs_san(lwsgs_user) + "
"; - } - - document.getElementById('username').oninput = lwsgs_update; - document.getElementById('username').onchange = lwsgs_update; - document.getElementById('password').oninput = lwsgs_update; - document.getElementById('password').onchange = lwsgs_update; - document.getElementById('doreg').onclick = lwsgs_open_registration; - document.getElementById('clink').onclick = lwsgs_select_change; - document.getElementById('cancel').onclick =lwsgs_cancel_registration; - document.getElementById('cancel2').onclick =lwsgs_cancel_registration; - document.getElementById('rpassword').oninput = lwsgs_rupdate; - document.getElementById('password2').oninput = lwsgs_rupdate; - document.getElementById('rusername').oninput = rupdate_user; - document.getElementById('email').oninput = rupdate_email; - document.getElementById('ccurpw').oninput = lwsgs_cupdate; - document.getElementById('cpassword').oninput = lwsgs_cupdate; - document.getElementById('cpassword2').oninput = lwsgs_cupdate; - - document.getElementById('showdel').onchange = lwsgs_cupdate; - - if (lwsgs_email) - document.getElementById('grav').innerHTML = - ""; - //if (lwsgs_email) - //document.getElementById('cemail').placeholder = lwsgs_email; - document.getElementById('cusername').value = lwsgs_user; - lwsgs_update(); - lwsgs_cupdate(); -} - -window.addEventListener("load", function() { - lwsgs_initial(); - document.getElementById("nolog").style.display = !!lwsgs_user ? "none" : "inline-block"; - document.getElementById("logged").style.display = !lwsgs_user ? "none" : "inline-block"; - - document.getElementById("msg").onkeyup = mupd; - document.getElementById("msg").onchange = mupd; - - var ws; - - function mb_format(s) - { - var r = "", n, wos = 0; - - for (n = 0; n < s.length; n++) { - if (s[n] == ' ') - wos = 0; - else { - wos++; - if (wos === 40) { - wos = 0; - r = r + ' '; - } - } - if (s[n] == '<') { - r = r + "<"; - continue; - } - if (s[n] == '\n') { - r = r + "
"; - continue; - } - - r = r + s[n]; - } - - return r; - } - - function add_div(n, m) - { - var q = document.getElementById(n); - var d = new Date(m.time * 1000), s = d.toTimeString(), t; - - t = s.indexOf('('); - if (t) - s = s.substring(0, t); - - q.innerHTML = "
" + - "
" + - "" + lwsgs_san(m.username) + "
" + - "" + d.toDateString() + - "
" + s + "

" + - "IP: " + lwsgs_san(m.ip) + - "
" + - mb_format(m.content) + - "

" + q.innerHTML; - } - - function get_appropriate_ws_url() - { - var pcol; - var u = document.URL; - - if (u.substring(0, 5) == "https") { - pcol = "wss://"; - u = u.substr(8); - } else { - pcol = "ws://"; - if (u.substring(0, 4) == "http") - u = u.substr(7); - } - u = u.split('/'); - - return pcol + u[0] + "/xxx"; - } - - if (lwsgs_user) { - - ws = new WebSocket(get_appropriate_ws_url(), - "protocol-lws-messageboard"); - - try { - ws.onopen = function() { - document.getElementById("debug").textContent = "ws opened"; - } - ws.onmessage =function got_packet(msg) { - add_div("messages", JSON.parse(msg.data)); - } - ws.onclose = function(){ - } - } catch(exception) { - alert('

Error' + exception); - } - } - - function mupd() - { - document.getElementById("send").disabled = !document.getElementById("msg").value; - } -}, false); diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js deleted file mode 100644 index 4bd9de1e9..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/md5.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(n){"use strict";function t(n,t){var r=(65535&n)+(65535&t),e=(n>>16)+(t>>16)+(r>>16);return e<<16|65535&r}function r(n,t){return n<>>32-t}function e(n,e,o,u,c,f){return t(r(t(t(e,n),t(u,f)),c),o)}function o(n,t,r,o,u,c,f){return e(t&r|~t&o,n,t,u,c,f)}function u(n,t,r,o,u,c,f){return e(t&o|r&~o,n,t,u,c,f)}function c(n,t,r,o,u,c,f){return e(t^r^o,n,t,u,c,f)}function f(n,t,r,o,u,c,f){return e(r^(t|~o),n,t,u,c,f)}function i(n,r){n[r>>5]|=128<>>9<<4)+14]=r;var e,i,a,h,d,l=1732584193,g=-271733879,v=-1732584194,m=271733878;for(e=0;e>5]>>>t%32&255);return r}function h(n){var t,r=[];for(r[(n.length>>2)-1]=void 0,t=0;t>5]|=(255&n.charCodeAt(t/8))<16&&(o=i(o,8*n.length)),r=0;16>r;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}function g(n){var t,r,e="0123456789abcdef",o="";for(r=0;r>>4&15)+e.charAt(15&t);return o}function v(n){return unescape(encodeURIComponent(n))}function m(n){return d(v(n))}function p(n){return g(m(n))}function s(n,t){return l(v(n),v(t))}function C(n,t){return g(s(n,t))}function A(n,t,r){return t?r?s(t,n):C(t,n):r?m(n):p(n)}"function"==typeof define&&define.amd?define(function(){return A}):"object"==typeof module&&module.exports?module.exports=A:n.md5=A}(this); -//# sourceMappingURL=md5.min.js.map \ No newline at end of file diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html deleted file mode 100644 index 113df9cd3..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needadmin/admin-login.html +++ /dev/null @@ -1,5 +0,0 @@ - -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. - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html deleted file mode 100644 index dfc25cf74..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/needauth/successful-login.html +++ /dev/null @@ -1,4 +0,0 @@ - -This is an example destination that will appear after successful non-Admin login - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html deleted file mode 100644 index ead3d13ec..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-fail.html +++ /dev/null @@ -1,5 +0,0 @@ - -Sorry, something went wrong. - -Click here to continue. - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html deleted file mode 100644 index 3e8e9cf59..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-forgot-ok.html +++ /dev/null @@ -1,6 +0,0 @@ - -This is a one-time password recovery login. - -Please click here and click your username at the top to reset your password. - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html deleted file mode 100644 index 063c3c50f..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-fail.html +++ /dev/null @@ -1 +0,0 @@ -Registration failed, sorry diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html deleted file mode 100644 index c00c3f3d2..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-register-ok.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - -
- -
- Your registration as is accepted,
- you will receive an email shortly with instructions
- to verify and enable the account for normal use.

- The link is only valid for an hour, after that if it has
- not been verified your account will be deleted. -
- - - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html deleted file mode 100644 index d1d89ca56..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-fail.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - -
- -
- Sorry, the link was invalid. -
- - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html deleted file mode 100644 index ae647fc5c..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/post-verify-ok.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - -
- -
- Thanks for signing up, your registration as is verified.
-
- Click here to continue. -
- - - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/seats.jpg b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/seats.jpg deleted file mode 100644 index 5bed40d919872359f2fcc00a8430e7dcfabaab4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 122754 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 diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html deleted file mode 100644 index ead3d13ec..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-fail.html +++ /dev/null @@ -1,5 +0,0 @@ - -Sorry, something went wrong. - -Click here to continue. - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html deleted file mode 100644 index 83df7510a..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/sent-forgot-ok.html +++ /dev/null @@ -1,4 +0,0 @@ -An email has been sent to your registered address. - -Please follow the instructions to reset your password. - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg deleted file mode 100644 index cd128f1d2..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/strict-csp.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html b/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html deleted file mode 100644 index dfc25cf74..000000000 --- a/minimal-examples/http-server/minimal-http-server-generic-sessions/mount-origin/successful-login.html +++ /dev/null @@ -1,4 +0,0 @@ - -This is an example destination that will appear after successful non-Admin login - - diff --git a/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c b/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c index 2e34b7782..60014951d 100644 --- a/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c +++ b/minimal-examples/ws-client/minimal-ws-client-echo/protocol_lws_minimal_client_echo.c @@ -281,36 +281,3 @@ callback_minimal_client_echo(struct lws *wsi, enum lws_callback_reasons reason, 1024, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_CLIENT_ECHO -}; - -int -init_protocol_minimal_client_echo(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_client_echo(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c b/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c index 5212c0fd9..c7564ac50 100644 --- a/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c +++ b/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/protocol_lws_minimal_pmd_bulk.c @@ -280,36 +280,3 @@ callback_minimal_pmd_bulk(struct lws *wsi, enum lws_callback_reasons reason, 4096, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK -}; - -int -init_protocol_minimal_pmd_bulk(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_pmd_bulk(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c index b13d4be0a..0ed24e6eb 100644 --- a/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-broker/protocol_lws_minimal.c @@ -215,36 +215,3 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 128, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c b/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c index d6075fb07..5b5683373 100644 --- a/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c +++ b/minimal-examples/ws-server/minimal-ws-server-echo/protocol_lws_minimal_server_echo.c @@ -230,36 +230,3 @@ callback_minimal_server_echo(struct lws *wsi, enum lws_callback_reasons reason, 1024, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_SERVER_ECHO -}; - -int -init_protocol_minimal_server_echo(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_server_echo(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c b/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c index 7c49f0ee4..301996dae 100644 --- a/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c +++ b/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/protocol_lws_minimal_pmd_bulk.c @@ -221,36 +221,3 @@ callback_minimal_pmd_bulk(struct lws *wsi, enum lws_callback_reasons reason, 4096, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL_PMD_BULK -}; - -int -init_protocol_minimal_pmd_bulk(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal_pmd_bulk(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c index 9f11af505..90ab25d66 100644 --- a/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-server-pmd-corner/protocol_lws_minimal.c @@ -269,36 +269,3 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 2048, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c index db07511f5..00287d723 100644 --- a/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-server-pmd/protocol_lws_minimal.c @@ -158,36 +158,3 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 128, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c index 5370d53e8..c809c5e72 100644 --- a/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-server-ring/protocol_lws_minimal.c @@ -279,36 +279,3 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 0, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c b/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c index ba8c22781..67b5049f1 100644 --- a/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c +++ b/minimal-examples/ws-server/minimal-ws-server-threadpool/protocol_lws_minimal_threadpool.c @@ -322,36 +322,3 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 128, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c index 1ee7ce705..2bc3284b6 100644 --- a/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-server-threads-smp/protocol_lws_minimal.c @@ -300,36 +300,3 @@ init_fail: 128, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c index 8f230bbf3..2b9c09a51 100644 --- a/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-server-threads/protocol_lws_minimal.c @@ -295,36 +295,3 @@ init_fail: 128, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c b/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c index c1dd3f759..6e0ed9483 100644 --- a/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c +++ b/minimal-examples/ws-server/minimal-ws-server/protocol_lws_minimal.c @@ -152,36 +152,3 @@ callback_minimal(struct lws *wsi, enum lws_callback_reasons reason, 128, \ 0, NULL, 0 \ } - -#if !defined (LWS_PLUGIN_STATIC) - -/* boilerplate needed if we are built as a dynamic plugin */ - -static const struct lws_protocols protocols[] = { - LWS_PLUGIN_PROTOCOL_MINIMAL -}; - -int -init_protocol_minimal(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -int -destroy_protocol_minimal(struct lws_context *context) -{ - return 0; -} -#endif diff --git a/plugin-standalone/protocol_example_standalone.c b/plugin-standalone/protocol_example_standalone.c index c33f683e8..b8ec2ef54 100644 --- a/plugin-standalone/protocol_example_standalone.c +++ b/plugin-standalone/protocol_example_standalone.c @@ -127,26 +127,15 @@ static const struct lws_protocols protocols[] = { }, }; -LWS_VISIBLE int -init_protocol_example_standalone(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_example_standalone = { + .hdr = { + "standalone", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_example_standalone(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; diff --git a/plugins/acme-client/protocol_lws_acme_client.c b/plugins/acme-client/protocol_lws_acme_client.c index 73c9c6728..12b204778 100644 --- a/plugins/acme-client/protocol_lws_acme_client.c +++ b/plugins/acme-client/protocol_lws_acme_client.c @@ -1630,28 +1630,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_LWS_ACME_CLIENT }; -LWS_VISIBLE int -init_protocol_lws_acme_client(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_acme_client = { + .hdr = { + "acme client", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_acme_client(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/deaddrop/protocol_lws_deaddrop.c b/plugins/deaddrop/protocol_lws_deaddrop.c index b36532743..85d777a96 100644 --- a/plugins/deaddrop/protocol_lws_deaddrop.c +++ b/plugins/deaddrop/protocol_lws_deaddrop.c @@ -689,28 +689,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_DEADDROP }; -LWS_VISIBLE int -init_protocol_deaddrop(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_deaddrop = { + .hdr = { + "deaddrop", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_deaddrop(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/protocol_client_loopback_test.c b/plugins/protocol_client_loopback_test.c index 2ccaacdb5..49f73d865 100644 --- a/plugins/protocol_client_loopback_test.c +++ b/plugins/protocol_client_loopback_test.c @@ -173,26 +173,15 @@ static const struct lws_protocols protocols[] = { }, }; -LWS_VISIBLE int -init_protocol_client_loopback_test(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_client_loopback_test = { + .hdr = { + "client loopback test", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_client_loopback_test(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; diff --git a/plugins/protocol_dumb_increment.c b/plugins/protocol_dumb_increment.c index 357ba9f51..1c3d785c6 100644 --- a/plugins/protocol_dumb_increment.c +++ b/plugins/protocol_dumb_increment.c @@ -121,28 +121,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT }; -LWS_VISIBLE int -init_protocol_dumb_increment(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_dumb_increment = { + .hdr = { + "dumb increment", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_dumb_increment(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/protocol_esp32_lws_group.c b/plugins/protocol_esp32_lws_group.c deleted file mode 100644 index 68adc88c1..000000000 --- a/plugins/protocol_esp32_lws_group.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include -#include - -typedef enum { - GROUP_STATE_NONE, - GROUP_STATE_INITIAL, - GROUP_STATE_MEMBERS, - GROUP_STATE_FINAL -} group_state; - -struct per_session_data__lws_group { - struct per_session_data__lws_group *next; - group_state group_state; - - struct lws_group_member *member; - - unsigned char subsequent:1; - unsigned char changed_partway:1; -}; - -struct per_vhost_data__lws_group { - struct per_session_data__lws_group *live_pss_list; - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - int count_live_pss; -}; - -static void render_ip4(char *dest, int len, uint8_t *ip) -{ - snprintf(dest, len, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); -} - - - -static int -callback_lws_group(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__lws_group *pss = - (struct per_session_data__lws_group *)user; - struct per_vhost_data__lws_group *vhd = - (struct per_vhost_data__lws_group *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - char buffer[1024 + LWS_PRE], ipv4[20]; - char *start = buffer + LWS_PRE - 1, *p = start, - *end = buffer + sizeof(buffer) - 1; - struct lws_group_member *mbr; - int n, m; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__lws_group)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - if (!vhd) - break; - break; - - case LWS_CALLBACK_ESTABLISHED: - lwsl_notice("%s: ESTABLISHED\n", __func__); - vhd->count_live_pss++; - pss->next = vhd->live_pss_list; - vhd->live_pss_list = pss; - pss->group_state = GROUP_STATE_INITIAL; - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - - switch (pss->group_state) { - - case GROUP_STATE_NONE: - /* fallthru */ - - case GROUP_STATE_INITIAL: - - p += snprintf((char *)p, end - p, - "{\n" - " \"group\":\"%s\"," - " \"members\":[\n", - lws_esp32.group); - - n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN; - pss->group_state = GROUP_STATE_MEMBERS; - pss->subsequent = 0; - pss->changed_partway = 0; - pss->member = lws_esp32.first; - break; - - case GROUP_STATE_MEMBERS: - - /* confirm pss->member is still in the list... */ - - mbr = lws_esp32.first; - while (mbr && mbr != pss->member) - mbr = mbr->next; - - if (!mbr) { /* no longer exists... */ - if (lws_esp32.first || pss->member) - pss->changed_partway = 1; - *p++ = ' '; - pss->member = NULL; - - /* - * finish the list where we got to, then - * immediately reissue it - */ - } - - while (end - p > 100 && pss->member) { - - if (pss->subsequent) - *p++ = ','; - - pss->subsequent = 1; - render_ip4(ipv4, sizeof(ipv4), (uint8_t *)&pss->member->addr); - - p += snprintf((char *)p, end - p, - " {\n" - " \"mac\":\"%s\",\n" - " \"model\":\"%s\",\n" - " \"role\":\"%s\",\n" - " \"width\":\"%d\",\n" - " \"height\":\"%d\",\n" - " \"ipv4\":\"%s\"\n" - " }\n", - pss->member->mac, - pss->member->model, - pss->member->role, - pss->member->width, - pss->member->height, - ipv4 - ); - pss->member = pss->member->next; - } - - lwsl_notice("%s\n", p); - - n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN; - if (!pss->member) - pss->group_state = GROUP_STATE_FINAL; - break; - - case GROUP_STATE_FINAL: - n = LWS_WRITE_CONTINUATION; - p += sprintf((char *)p, "],\n \"discard\":\"%d\"}\n", - pss->changed_partway); - if (pss->changed_partway) - pss->group_state = GROUP_STATE_INITIAL; - else - pss->group_state = GROUP_STATE_NONE; - break; - default: - return 0; - } -// lwsl_notice("issue: %d (%d)\n", p - start, n); - m = lws_write(wsi, (unsigned char *)start, p - start, n); - if (m < 0) { - lwsl_err("ERROR %d writing to di socket\n", m); - return -1; - } - - if (pss->group_state != GROUP_STATE_NONE) - lws_callback_on_writable(wsi); - - break; - - case LWS_CALLBACK_RECEIVE: - { - break; - } - - case LWS_CALLBACK_CLOSED: - { - struct per_session_data__lws_group **p = &vhd->live_pss_list; - - while (*p) { - if ((*p) == pss) { - *p = pss->next; - continue; - } - - p = &((*p)->next); - } - - vhd->count_live_pss--; - } - break; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - /* called when our wsi user_space is going to be destroyed */ - break; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_LWS_GROUP \ - { \ - "lws-group", \ - callback_lws_group, \ - sizeof(struct per_session_data__lws_group), \ - 1024, 0, NULL, 900 \ - } - diff --git a/plugins/protocol_esp32_lws_ota.c b/plugins/protocol_esp32_lws_ota.c deleted file mode 100644 index f66e229a1..000000000 --- a/plugins/protocol_esp32_lws_ota.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include -#include -#include - -struct per_session_data__esplws_ota { - struct lws_spa *spa; - char filename[32]; - char result[LWS_PRE + 512]; - int result_len; - int filename_length; - esp_ota_handle_t otahandle; - const esp_partition_t *part; - long file_length; - long last_rep; - nvs_handle nvh; - TimerHandle_t reboot_timer; -}; - -struct per_vhost_data__esplws_ota { - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; -}; - -static const char * const ota_param_names[] = { - "upload", -}; - -enum enum_ota_param_names { - EPN_UPLOAD, -}; - -static void ota_reboot_timer_cb(TimerHandle_t t) -{ - esp_restart(); -} - -const esp_partition_t * -ota_choose_part(void) -{ - const esp_partition_t *bootpart, *part = NULL; - esp_partition_iterator_t i; - - bootpart = lws_esp_ota_get_boot_partition(); - i = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL); - while (i) { - part = esp_partition_get(i); - - /* cannot update ourselves */ - if (part == bootpart) - goto next; - - /* OTA Partition numbering is from _OTA_MIN to less than _OTA_MAX */ - if (part->subtype < ESP_PARTITION_SUBTYPE_APP_OTA_MIN || - part->subtype >= ESP_PARTITION_SUBTYPE_APP_OTA_MAX) - goto next; - - break; - -next: - i = esp_partition_next(i); - } - - if (!i) { - lwsl_err("Can't find good OTA part\n"); - return NULL; - } - lwsl_notice("Directing OTA to part type %d/%d start 0x%x\n", - part->type, part->subtype, - (uint32_t)part->address); - - return part; -} - -static int -ota_file_upload_cb(void *data, const char *name, const char *filename, - char *buf, int len, enum lws_spa_fileupload_states state) -{ - struct per_session_data__esplws_ota *pss = - (struct per_session_data__esplws_ota *)data; - - switch (state) { - case LWS_UFS_OPEN: - lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename); - lws_strncpy(pss->filename, filename, sizeof(pss->filename)); - if (strcmp(name, "ota")) - return 1; - - pss->part = ota_choose_part(); - if (!pss->part) - return 1; - - if (esp_ota_begin(pss->part, OTA_SIZE_UNKNOWN, &pss->otahandle) != ESP_OK) { - lwsl_err("OTA: Failed to begin\n"); - return 1; - } - - pss->file_length = 0; - pss->last_rep = -1; - break; - - case LWS_UFS_FINAL_CONTENT: - case LWS_UFS_CONTENT: - if (pss->file_length + len > pss->part->size) { - lwsl_err("OTA: incoming file too large\n"); - return 1; - } - - if ((pss->file_length & ~0xffff) != (pss->last_rep & ~0xffff)) { - lwsl_notice("writing 0x%lx...\n", - pss->part->address + pss->file_length); - pss->last_rep = pss->file_length; - } - if (esp_ota_write(pss->otahandle, buf, len) != ESP_OK) { - lwsl_err("OTA: Failed to write\n"); - return 1; - } - pss->file_length += len; - - if (state == LWS_UFS_CONTENT) - break; - - lwsl_notice("LWS_UFS_FINAL_CONTENT\n"); - if (esp_ota_end(pss->otahandle) != ESP_OK) { - lwsl_err("OTA: end failed\n"); - return 1; - } - - if (esp_ota_set_boot_partition(pss->part) != ESP_OK) { - lwsl_err("OTA: set boot part failed\n"); - return 1; - } - - pss->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, NULL, - ota_reboot_timer_cb); - xTimerStart(pss->reboot_timer, 0); - break; - } - - return 0; -} - -static int -callback_esplws_ota(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__esplws_ota *pss = - (struct per_session_data__esplws_ota *)user; - struct per_vhost_data__esplws_ota *vhd = - (struct per_vhost_data__esplws_ota *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - unsigned char buf[LWS_PRE + 384], *start = buf + LWS_PRE - 1, *p = start, - *end = buf + sizeof(buf) - 1; - int n; - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__esplws_ota)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - if (!vhd) - break; - break; - - /* OTA POST handling */ - - case LWS_CALLBACK_HTTP_BODY: - /* create the POST argument parser if not already existing */ - // lwsl_notice("LWS_CALLBACK_HTTP_BODY (ota) %d %d %p\n", (int)pss->file_length, (int)len, pss->spa); - lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 30); - if (!pss->spa) { - pss->spa = lws_spa_create(wsi, ota_param_names, - LWS_ARRAY_SIZE(ota_param_names), 4096, - ota_file_upload_cb, pss); - if (!pss->spa) - return -1; - - pss->filename[0] = '\0'; - pss->file_length = 0; - } - lws_esp32.upload = 1; - - /* let it parse the POST data */ - if (lws_spa_process(pss->spa, in, len)) - return -1; - break; - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION (ota)\n"); - /* call to inform no more payload data coming */ - lws_spa_finalize(pss->spa); - - pss->result_len = snprintf(pss->result + LWS_PRE, sizeof(pss->result) - LWS_PRE - 1, - "Rebooting after OTA update"); - - if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) - goto bail; - - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)"text/html", 9, &p, end)) - goto bail; - if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end)) - goto bail; - if (lws_finalize_http_header(wsi, &p, end)) - goto bail; - - n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS | LWS_WRITE_H2_STREAM_END); - if (n < 0) - goto bail; - - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_HTTP_WRITEABLE: - if (!pss->result_len) - break; - lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n", - pss->result_len); - n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE, - pss->result_len, LWS_WRITE_HTTP); - if (n < 0) - return 1; - - if (lws_http_transaction_completed(wsi)) - return 1; - - /* stop further service so we don't serve the probe GET to see if we rebooted */ - while (1); - - break; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - /* called when our wsi user_space is going to be destroyed */ - if (pss->spa) { - lws_spa_destroy(pss->spa); - pss->spa = NULL; - } - lws_esp32.upload = 0; - break; - - default: - break; - } - - return 0; - -bail: - return 1; -} - -#define LWS_PLUGIN_PROTOCOL_ESPLWS_OTA \ - { \ - "esplws-ota", \ - callback_esplws_ota, \ - sizeof(struct per_session_data__esplws_ota), \ - 4096, 0, NULL, 900 \ - } - diff --git a/plugins/protocol_esp32_lws_reboot_to_factory.c b/plugins/protocol_esp32_lws_reboot_to_factory.c deleted file mode 100644 index d6cd7f5ee..000000000 --- a/plugins/protocol_esp32_lws_reboot_to_factory.c +++ /dev/null @@ -1,61 +0,0 @@ - /* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - * - * This is intended to be mounted somewhere in your ESP32 user app... if the - * client touched the mount, the plugin hangs up and reboots into the - * factory mode one second later. - * - * The factory mode will reassociate with the same IP with the same MAC - * shortly afterwards and be accessible by the same IP / mDNS name. - */ -#include -#include -#include -#include - -static int -callback_esplws_rtf(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - switch (reason) { - - case LWS_CALLBACK_HTTP: - - lws_esp32_restart_guided(LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY); - return 1; - - default: - break; - } - - return 0; -} - -#define LWS_PLUGIN_PROTOCOL_ESPLWS_RTF \ - { \ - "esplws-rtf", \ - callback_esplws_rtf, \ - 0, \ - 10, 0, NULL, 0 \ - } - diff --git a/plugins/protocol_esp32_lws_scan.c b/plugins/protocol_esp32_lws_scan.c deleted file mode 100644 index 5fcc8b4b5..000000000 --- a/plugins/protocol_esp32_lws_scan.c +++ /dev/null @@ -1,1276 +0,0 @@ -/* - * libwebsockets - small server side websockets and web server implementation - * - * Copyright (C) 2010 - 2019 Andy Green - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include -#include - -typedef enum { - SCAN_STATE_NONE, - SCAN_STATE_INITIAL, - SCAN_STATE_INITIAL_MANIFEST, - SCAN_STATE_KNOWN, - SCAN_STATE_LIST, - SCAN_STATE_FINAL -} scan_state; - -struct store_json { - const char *j; - const char *nvs; -}; - -struct per_session_data__esplws_scan { - struct per_session_data__esplws_scan *next; - scan_state scan_state; - struct timeval last_send; - - struct lws_spa *spa; - char filename[32]; - char result[LWS_PRE + 512]; - unsigned char buffer[4096]; - int result_len; - int filename_length; - long file_length; - nvs_handle nvh; - - char ap_record; - unsigned char subsequent:1; - unsigned char changed_partway:1; -}; - -#define max_aps 12 - -struct per_vhost_data__esplws_scan { - wifi_ap_record_t ap_records[10]; - TimerHandle_t timer, reboot_timer; - struct per_session_data__esplws_scan *live_pss_list; - struct lws_context *context; - struct lws_vhost *vhost; - const struct lws_protocols *protocol; - struct lws_wifi_scan *known_aps_list; - - const esp_partition_t *part; - esp_ota_handle_t otahandle; - long file_length; - long content_length; - - int cert_remaining_days; - - struct lws *cwsi; - char json[2048]; - int json_len; - - int acme_state; - char acme_msg[256]; - - uint16_t count_ap_records; - char count_live_pss; - unsigned char scan_ongoing:1; - unsigned char completed_any_scan:1; - unsigned char reboot:1; - unsigned char changed_settings:1; - unsigned char checked_updates:1; - unsigned char autonomous_update:1; - unsigned char autonomous_update_sampled:1; -}; - -static const struct store_json store_json[] = { - { "\"ssid0\":\"", "0ssid" }, - { ",\"pw0\":\"", "0password" }, - { "\"ssid1\":\"", "1ssid" }, - { ",\"pw1\":\"", "1password" }, - { "\"ssid2\":\"", "2ssid" }, - { ",\"pw2\":\"", "2password" }, - { "\"ssid3\":\"", "3ssid" }, - { ",\"pw3\":\"", "3password" }, - { ",\"access_pw\":\"", "access_pw" }, - { "{\"group\":\"", "group" }, - { "{\"role\":\"", "role" }, - { ",\"region\":\"", "region" }, -}; - -static wifi_scan_config_t scan_config = { - .ssid = 0, - .bssid = 0, - .channel = 0, - .show_hidden = true -}; - -const esp_partition_t * -ota_choose_part(void); - -static const char * const param_names[] = { - "text", - "pub", - "pri", - "serial", - "opts", - "group", - "role", - "updsettings", -}; - -enum enum_param_names { - EPN_TEXT, - EPN_PUB, - EPN_PRI, - EPN_SERIAL, - EPN_OPTS, - EPN_GROUP, - EPN_ROLE, - EPN_UPDSETTINGS, -}; - - -static void -scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v); - -static int -esplws_simple_arg(char *dest, int len, const char *in, const char *match) -{ - const char *p = strstr(in, match); - int n = 0; - - if (!p) - return 1; - - p += strlen(match); - while (*p && *p != '\"' && n < len - 1) - dest[n++] = *p++; - dest[n] = '\0'; - - return 0; -} - -static void -scan_start(struct per_vhost_data__esplws_scan *vhd) -{ - int n; - - if (vhd->reboot) - esp_restart(); - - if (vhd->scan_ongoing) - return; - - if (lws_esp32.acme) - return; - - if (lws_esp32.upload) - return; - - vhd->scan_ongoing = 1; - lws_esp32.scan_consumer = scan_finished; - lws_esp32.scan_consumer_arg = vhd; - n = esp_wifi_scan_start(&scan_config, false); - if (n != ESP_OK) - lwsl_err("scan start failed %d\n", n); -} - -static int scan_defer; - -static void timer_cb(TimerHandle_t t) -{ - struct per_vhost_data__esplws_scan *vhd = pvTimerGetTimerID(t); - -// if (!lws_esp32.inet && ((scan_defer++) & 1)) -/* - * AP mode + scan does not work well on ESP32... if we didn't connect to an AP - * ourselves, just scan once at boot. Then leave us on the AP channel. - * - * Do the callback for everyone to keep the heartbeat alive. - */ - if (!lws_esp32.inet && scan_defer++) { - lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol); - - return; - } - - scan_start(vhd); -} - -static void reboot_timer_cb(TimerHandle_t t) -{ - esp_restart(); -} - -static int -client_connection(struct per_vhost_data__esplws_scan *vhd, const char *file) -{ -#if defined(CONFIG_LWS_IS_FACTORY_APPLICATION) && defined(CONFIG_LWS_OTA_SERVER_BASE_URL) && \ - defined(CONFIG_LWS_OTA_SERVER_FQDN) - static struct lws_client_connect_info i; - char path[256]; - - memset(&i, 0, sizeof i); - - snprintf(path, sizeof(path) - 1, CONFIG_LWS_OTA_SERVER_BASE_URL "/" CONFIG_LWS_MODEL_NAME "/%s", file); - - lwsl_notice("Fetching %s\n", path); - - i.port = 443; - i.context = vhd->context; - i.address = CONFIG_LWS_OTA_SERVER_FQDN; - i.ssl_connection = 1; - i.host = i.address; - i.origin = i.host; - i.vhost = vhd->vhost; - i.method = "GET"; - i.path = path; - i.protocol = "esplws-scan"; - i.pwsi = &vhd->cwsi; - - vhd->cwsi = lws_client_connect_via_info(&i); - if (!vhd->cwsi) { - lwsl_notice("NULL return\n"); - return 1; /* fail */ - } -#endif - return 0; /* ongoing */ -} - -static int -lws_wifi_scan_rssi(struct lws_wifi_scan *p) -{ - if (!p->count) - return -127; - - return p->rssi / p->count; -} - -/* - * Insert new lws_wifi_scan into linkedlist in rssi-sorted order, trimming the - * list if needed to keep it at or below max_aps entries. - */ - -static int -lws_wifi_scan_insert_trim(struct lws_wifi_scan **list, struct lws_wifi_scan *ns) -{ - int count = 0, ins = 1, worst; - struct lws_wifi_scan *newlist, **pworst, *pp1; - - lws_start_foreach_llp(struct lws_wifi_scan **, pp, *list) { - /* try to find existing match */ - if (!strcmp((*pp)->ssid, ns->ssid) && - !memcmp((*pp)->bssid, ns->bssid, 6)) { - if ((*pp)->count > 127) { - (*pp)->count /= 2; - (*pp)->rssi /= 2; - } - (*pp)->rssi += ns->rssi; - (*pp)->count++; - ins = 0; - break; - } - } lws_end_foreach_llp(pp, next); - - if (ins) { - lws_start_foreach_llp(struct lws_wifi_scan **, pp, *list) { - /* trim any excess guys */ - if (count++ >= max_aps - 1) { - pp1 = *pp; - *pp = (*pp)->next; - free(pp1); - continue; /* stay where we are */ - } - } lws_end_foreach_llp(pp, next); - - /* we are inserting... so alloc a copy of him */ - pp1 = malloc(sizeof(*pp1)); - if (!pp1) - return -1; - - memcpy(pp1, ns, sizeof(*pp1)); - pp1->next = *list; - *list = pp1; - } - - /* sort the list ... worst first, but added at the newlist head */ - - newlist = NULL; - - /* while anybody left on the old list */ - while (*list) { - worst = 0; - pworst = NULL; - - /* who is the worst guy still left on the old list? */ - lws_start_foreach_llp(struct lws_wifi_scan **, pp, *list) { - if (lws_wifi_scan_rssi(*pp) <= worst) { - worst = lws_wifi_scan_rssi(*pp); - pworst = pp; - } - } lws_end_foreach_llp(pp, next); - - if (pworst) { - /* move the worst to the head of the new list */ - pp1 = *pworst; - *pworst = (*pworst)->next; - pp1->next = newlist; - newlist = pp1; - } - } - - *list = newlist; - - return 0; -} - -static void -scan_finished(uint16_t count, wifi_ap_record_t *recs, void *v) -{ - struct per_vhost_data__esplws_scan *vhd = v; - struct per_session_data__esplws_scan *p = vhd->live_pss_list; - struct lws_wifi_scan lws; - wifi_ap_record_t *r; - int m; - - lwsl_notice("%s: count %d\n", __func__, count); - - vhd->scan_ongoing = 0; - - if (count < LWS_ARRAY_SIZE(vhd->ap_records)) - vhd->count_ap_records = count; - else - vhd->count_ap_records = LWS_ARRAY_SIZE(vhd->ap_records); - - memcpy(vhd->ap_records, recs, vhd->count_ap_records * sizeof(*recs)); - - while (p) { - if (p->scan_state != SCAN_STATE_INITIAL && - p->scan_state != SCAN_STATE_NONE) - p->changed_partway = 1; - else - p->scan_state = SCAN_STATE_INITIAL; - p = p->next; - } - - /* convert to generic, cumulative scan results */ - - for (m = 0; m < vhd->count_ap_records; m++) { - - r = &vhd->ap_records[m]; - - lws.authmode = r->authmode; - lws.channel = r->primary; - lws.rssi = r->rssi; - lws.count = 1; - memcpy(&lws.bssid, r->bssid, 6); - lws_strncpy(lws.ssid, (const char *)r->ssid, sizeof(lws.ssid)); - - lws_wifi_scan_insert_trim(&vhd->known_aps_list, &lws); - } - - lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol); - - if (lws_esp32.inet && !vhd->cwsi && !vhd->checked_updates) - client_connection(vhd, "manifest.json"); - - if (vhd->changed_settings) { - lws_esp32_wlan_nvs_get(1); - vhd->changed_settings = 0; - } else - esp_wifi_connect(); -} - -static const char *ssl_names[] = { "ap-cert.pem", "ap-key.pem" }; - -static int -file_upload_cb(void *data, const char *name, const char *filename, - char *buf, int len, enum lws_spa_fileupload_states state) -{ - struct per_session_data__esplws_scan *pss = - (struct per_session_data__esplws_scan *)data; - int n; - - switch (state) { - case LWS_UFS_OPEN: - if (lws_esp32_get_reboot_type() != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) - return -1; - - lwsl_notice("LWS_UFS_OPEN Filename %s\n", filename); - lws_strncpy(pss->filename, filename, sizeof(pss->filename)); - if (!strcmp(name, "pub") || !strcmp(name, "pri")) { - if (nvs_open("lws-station", NVS_READWRITE, &pss->nvh)) - return 1; - } else - return 1; - pss->file_length = 0; - break; - - case LWS_UFS_FINAL_CONTENT: - case LWS_UFS_CONTENT: - if (len) { - /* if the file length is too big, drop it */ - if (pss->file_length + len > sizeof(pss->buffer)) - return 1; - - memcpy(pss->buffer + pss->file_length, buf, len); - } - pss->file_length += len; - - if (state == LWS_UFS_CONTENT) - break; - - lwsl_notice("LWS_UFS_FINAL_CONTENT\n"); - n = 0; - if (!strcmp(name, "pri")) - n = 1; - lwsl_notice("writing %s\n", ssl_names[n]); - n = nvs_set_blob(pss->nvh, ssl_names[n], pss->buffer, pss->file_length); - if (n == ESP_OK) - nvs_commit(pss->nvh); - nvs_close(pss->nvh); - if (n != ESP_OK) - return 1; - break; - } - - return 0; -} - -static int -callback_esplws_scan(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - struct per_session_data__esplws_scan *pss = - (struct per_session_data__esplws_scan *)user; - struct per_vhost_data__esplws_scan *vhd = - (struct per_vhost_data__esplws_scan *) - lws_protocol_vh_priv_get(lws_get_vhost(wsi), - lws_get_protocol(wsi)); - unsigned char *start = pss->buffer + LWS_PRE - 1, *p = start, - *end = pss->buffer + sizeof(pss->buffer) - 1; - union lws_tls_cert_info_results ir; - struct lws_wifi_scan *lwscan; - char subject[64]; - int n, m; - nvs_handle nvh; - size_t s; - - - switch (reason) { - - case LWS_CALLBACK_PROTOCOL_INIT: - vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi), - lws_get_protocol(wsi), - sizeof(struct per_vhost_data__esplws_scan)); - vhd->context = lws_get_context(wsi); - vhd->protocol = lws_get_protocol(wsi); - vhd->vhost = lws_get_vhost(wsi); - vhd->timer = xTimerCreate("x", pdMS_TO_TICKS(10000), 1, vhd, - (TimerCallbackFunction_t)timer_cb); - vhd->scan_ongoing = 0; - strcpy(vhd->json, " { }"); - // scan_start(vhd); - break; - - case LWS_CALLBACK_PROTOCOL_DESTROY: - if (!vhd) - break; - xTimerStop(vhd->timer, 0); - xTimerDelete(vhd->timer, 0); - break; - - case LWS_CALLBACK_ESTABLISHED: - lwsl_notice("%s: ESTABLISHED\n", __func__); - if (!vhd->live_pss_list) { - // scan_start(vhd); - xTimerStart(vhd->timer, 0); - } - vhd->count_live_pss++; - pss->next = vhd->live_pss_list; - vhd->live_pss_list = pss; - /* if we have scan results, update them. Otherwise wait */ -// if (vhd->count_ap_records) { - pss->scan_state = SCAN_STATE_INITIAL; - lws_callback_on_writable(wsi); -// } - break; - - case LWS_CALLBACK_SERVER_WRITEABLE: - if (vhd->autonomous_update_sampled) { - p += snprintf((char *)p, end - p, - " {\n \"auton\":\"1\",\n \"pos\": \"%ld\",\n" - " \"len\":\"%ld\"\n}\n", - vhd->file_length, - vhd->content_length); - - n = LWS_WRITE_TEXT; - goto issue; - } - - switch (pss->scan_state) { - struct timeval t; - uint8_t mac[6]; - struct lws_esp32_image i; - char img_factory[384], img_ota[384], group[16], role[16]; - int grt; - - case SCAN_STATE_NONE: - - /* fallthru */ - - case SCAN_STATE_INITIAL: - - gettimeofday(&t, NULL); - // if (t.tv_sec - pss->last_send.tv_sec < 10) - // return 0; - - pss->last_send = t; - - if (nvs_open("lws-station", NVS_READWRITE, &nvh)) { - lwsl_err("unable to open nvs\n"); - return -1; - } - n = 0; - if (nvs_get_blob(nvh, "ap-cert.pem", NULL, &s) == ESP_OK) - n = 1; - if (nvs_get_blob(nvh, "ap-key.pem", NULL, &s) == ESP_OK) - n |= 2; - s = sizeof(group) - 1; - group[0] = '\0'; - role[0] = '\0'; - nvs_get_str(nvh, "group", group, &s); - nvs_get_str(nvh, "role", role, &s); - - nvs_close(nvh); - - ir.ns.name[0] = '\0'; - subject[0] = '\0'; - - if (t.tv_sec > 1464083026 && - !lws_tls_vhost_cert_info(vhd->vhost, - LWS_TLS_CERT_INFO_VALIDITY_TO, &ir, 0)) { - vhd->cert_remaining_days = - (ir.time - t.tv_sec) / (24 * 3600); - ir.ns.name[0] = '\0'; - lws_tls_vhost_cert_info(vhd->vhost, - LWS_TLS_CERT_INFO_COMMON_NAME, &ir, - sizeof(ir.ns.name)); - lws_strncpy(subject, ir.ns.name, sizeof(subject)); - - ir.ns.name[0] = '\0'; - lws_tls_vhost_cert_info(vhd->vhost, - LWS_TLS_CERT_INFO_ISSUER_NAME, &ir, - sizeof(ir.ns.name)); - } - - /* - * this value in the JSON is just - * used for UI indication. Each conditional feature confirms - * it itself before it allows itself to be used. - */ - - grt = lws_esp32_get_reboot_type(); - - esp_efuse_mac_get_default(mac); - strcpy(img_factory, " { \"date\": \"Empty\" }"); - strcpy(img_ota, " { \"date\": \"Empty\" }"); - - // if (grt != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) { - lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP, - ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL), &i, - img_factory, sizeof(img_factory) - 1); - img_factory[sizeof(img_factory) - 1] = '\0'; - if (img_factory[0] == 0xff || strlen(img_factory) < 8) - strcpy(img_factory, " { \"date\": \"Empty\" }"); - - lws_esp32_get_image_info(esp_partition_find_first(ESP_PARTITION_TYPE_APP, - ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL), &i, - img_ota, sizeof(img_ota) - 1); - img_ota[sizeof(img_ota) - 1] = '\0'; - if (img_ota[0] == 0xff || strlen(img_ota) < 8) - strcpy(img_ota, " { \"date\": \"Empty\" }"); - // } - - p += snprintf((char *)p, end - p, - "{ \"model\":\"%s\",\n" - " \"forced_button\":\"%d\",\n" - " \"serial\":\"%s\",\n" - " \"opts\":\"%s\",\n" - " \"host\":\"%s-%s\",\n" - " \"region\":\"%d\",\n" - " \"ssl_pub\":\"%d\",\n" - " \"ssl_pri\":\"%d\",\n" - " \"mac\":\"%02X%02X%02X%02X%02X%02X\",\n" - " \"ssid\":\"%s\",\n" - " \"conn_ip\":\"%s\",\n" - " \"conn_mask\":\"%s\",\n" - " \"conn_gw\":\"%s\",\n" - " \"certdays\":\"%d\",\n" - " \"unixtime\":\"%llu\",\n" - " \"certissuer\":\"%s\",\n" - " \"certsubject\":\"%s\",\n" - " \"le_dns\":\"%s\",\n" - " \"le_email\":\"%s\",\n" - " \"acme_state\":\"%d\",\n" - " \"acme_msg\":\"%s\",\n" - " \"button\":\"%d\",\n" - " \"group\":\"%s\",\n" - " \"role\":\"%s\",\n", - lws_esp32.model, - grt == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON, - lws_esp32.serial, - lws_esp32.opts, - lws_esp32.model, lws_esp32.serial, - lws_esp32.region, - n & 1, (n >> 1) & 1, - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] | 1, - lws_esp32.active_ssid, - lws_esp32.sta_ip, - lws_esp32.sta_mask, - lws_esp32.sta_gw, - vhd->cert_remaining_days, - (unsigned long long)t.tv_sec, - ir.ns.name, subject, - lws_esp32.le_dns, - lws_esp32.le_email, - vhd->acme_state, - vhd->acme_msg, - ((volatile struct lws_esp32 *)(&lws_esp32))->button_is_down, - group, role); - p += snprintf((char *)p, end - p, - " \"img_factory\": %s,\n" - " \"img_ota\": %s,\n", - img_factory, - img_ota - ); - - - n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN; - pss->scan_state = SCAN_STATE_INITIAL_MANIFEST; - pss->ap_record = 0; - pss->subsequent = 0; - break; - - case SCAN_STATE_INITIAL_MANIFEST: - p += snprintf((char *)p, end - p, - " \"latest\": %s,\n" - " \"inet\":\"%d\",\n", - vhd->json, - lws_esp32.inet - ); - - p += snprintf((char *)p, end - p, - " \"known\":[\n"); - - n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN; - pss->scan_state = SCAN_STATE_KNOWN; - break; - - case SCAN_STATE_KNOWN: - if (nvs_open("lws-station", NVS_READONLY, &nvh)) { - lwsl_notice("unable to open nvh\n"); - return -1; - } - - for (m = 0; m < 4; m++) { - char name[10], ssid[65]; - unsigned int pp = 0, use = 0; - - if (m) - *p++ = ','; - - s = sizeof(ssid) - 1; - ssid[0] = '\0'; - lws_snprintf(name, sizeof(name) - 1, "%dssid", m); - nvs_get_str(nvh, name, ssid, &s); - lws_snprintf(name, sizeof(name) - 1, "%dpassword", m); - s = 10; - nvs_get_str(nvh, name, NULL, &s); - pp = !!s; - lws_snprintf(name, sizeof(name) - 1, "%duse", m); - nvs_get_u32(nvh, name, &use); - - p += snprintf((char *)p, end - p, - "{\"ssid\":\"%s\",\n" - " \"pp\":\"%u\",\n" - "\"use\":\"%u\"}\n", - ssid, pp, use); - } - nvs_close(nvh); - pss->ap_record = 0; - - p += snprintf((char *)p, end - p, - "], \"aps\":[\n"); - - n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN; - pss->scan_state = SCAN_STATE_LIST; - break; - - case SCAN_STATE_LIST: - lwscan = vhd->known_aps_list; - - n = pss->ap_record; - while (lwscan && n--) - lwscan = lwscan->next; - - for (m = 0; m < 6; m++) { - n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN; - if (!lwscan) - goto scan_state_final; - - if (pss->subsequent) - *p++ = ','; - pss->subsequent = 1; - pss->ap_record++; - - p += snprintf((char *)p, end - p, - "{\"ssid\":\"%s\",\n" - "\"bssid\":\"%02X:%02X:%02X:%02X:%02X:%02X\",\n" - "\"rssi\":\"%d\",\n" - "\"chan\":\"%d\",\n" - "\"auth\":\"%d\"}\n", - lwscan->ssid, - lwscan->bssid[0], lwscan->bssid[1], lwscan->bssid[2], - lwscan->bssid[3], lwscan->bssid[4], lwscan->bssid[5], - lws_wifi_scan_rssi(lwscan), - lwscan->channel, lwscan->authmode); - - lwscan = lwscan->next; - if (!lwscan) - pss->scan_state = SCAN_STATE_FINAL; - } - break; - - case SCAN_STATE_FINAL: -scan_state_final: - n = LWS_WRITE_CONTINUATION; - p += sprintf((char *)p, "]\n}\n"); - if (pss->changed_partway) { - pss->changed_partway = 0; - pss->subsequent = 0; - pss->scan_state = SCAN_STATE_INITIAL; - } else { - pss->scan_state = SCAN_STATE_NONE; - vhd->autonomous_update_sampled = vhd->autonomous_update; - } - break; - default: - return 0; - } -issue: - m = lws_write(wsi, (unsigned char *)start, p - start, n); - if (m < 0) { - lwsl_err("ERROR %d writing to di socket\n", m); - return -1; - } - - if (pss->scan_state != SCAN_STATE_NONE) - lws_callback_on_writable(wsi); - - break; - - case LWS_CALLBACK_VHOST_CERT_UPDATE: - lwsl_notice("LWS_CALLBACK_VHOST_CERT_UPDATE: %d\n", (int)len); - vhd->acme_state = (int)len; - if (in) { - lws_strncpy(vhd->acme_msg, in, sizeof(vhd->acme_msg)); - lwsl_notice("acme_msg: %s\n", (char *)in); - } - lws_callback_on_writable_all_protocol_vhost(vhd->vhost, vhd->protocol); - break; - - case LWS_CALLBACK_RECEIVE: - { - const char *sect = "\"app\": {", *b; - nvs_handle nvh; - char p[64], use[6]; - int n, si = -1; - - if (strstr((const char *)in, "identify")) { - lws_esp32_identify_physical_device(); - break; - } - - if (vhd->json_len && strstr((const char *)in, "update-factory")) { - sect = "\"factory\": {"; - goto auton; - } - if (vhd->json_len && strstr((const char *)in, "update-ota")) - goto auton; - - if (strstr((const char *)in, "\"reset\"")) - goto sched_reset; - - if (!strncmp((const char *)in, "{\"job\":\"start-le\"", 17)) - goto start_le; - - - if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) { - lwsl_err("Unable to open nvs\n"); - break; - } - - if (!esplws_simple_arg(p, sizeof(p), in, ",\"slot\":\"")) - si = atoi(p); - - lwsl_notice("si %d\n", si); - - for (n = 0; n < LWS_ARRAY_SIZE(store_json); n++) { - if (esplws_simple_arg(p, sizeof(p), in, store_json[n].j)) - continue; - - /* only change access password if he has physical access to device */ - if (n == 8 && lws_esp32_get_reboot_type() != LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) - continue; - - if (lws_nvs_set_str(nvh, store_json[n].nvs, p) != ESP_OK) { - lwsl_err("Unable to store %s in nvm\n", store_json[n].nvs); - goto bail_nvs; - } - - if (si != -1 && n < 8) { - if (!(n & 1)) { - lws_strncpy(lws_esp32.ssid[(n >> 1) & 3], p, - sizeof(lws_esp32.ssid[0])); - lws_snprintf(use, sizeof(use) - 1, "%duse", si); - lwsl_notice("resetting %s to 0\n", use); - nvs_set_u32(nvh, use, 0); - - } else - lws_strncpy(lws_esp32.password[(n >> 1) & 3], p, - sizeof(lws_esp32.password[0])); - } - - } - - nvs_commit(nvh); - nvs_close(nvh); - - if (strstr((const char *)in, "\"factory-reset\"")) { - if (lws_esp32_get_reboot_type() == - LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) { - - lwsl_notice("Doing factory reset\n"); - ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh)); - n = nvs_erase_all(nvh); - if (n) - lwsl_notice("erase_all failed %d\n", n); - nvs_commit(nvh); - nvs_close(nvh); - - goto sched_reset; - } else - lwsl_notice("failed on factory button boot\n"); - } - - if (vhd->scan_ongoing) - vhd->changed_settings = 1; - else - lws_esp32_wlan_nvs_get(1); - - lwsl_notice("set Join AP info\n"); - break; - -bail_nvs: - nvs_close(nvh); - - return 1; - -sched_reset: - vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, vhd, - (TimerCallbackFunction_t)reboot_timer_cb); - xTimerStart(vhd->reboot_timer, 0); - - return 1; - -auton: - lwsl_notice("Autonomous upload\n"); - b = strstr(vhd->json, sect); - if (!b) { - lwsl_notice("Can't find %s in JSON\n", sect); - return 1; - } - b = strstr(b, "\"file\": \""); - if (!b) { - lwsl_notice("Can't find \"file\": JSON\n"); - return 1; - } - vhd->autonomous_update = 1; - if (pss->scan_state == SCAN_STATE_NONE) - vhd->autonomous_update_sampled = 1; - b += 9; - n = 0; - while ((*b != '\"') && n < sizeof(p) - 1) - p[n++] = *b++; - - p[n] = '\0'; - - vhd->part = ota_choose_part(); - if (!vhd->part) - return 1; - - if (client_connection(vhd, p)) - vhd->autonomous_update = 0; - - break; - -start_le: - lws_esp32.acme = 1; /* hold off scanning */ - puts(in); - /* - * {"job":"start-le","cn":"home.warmcat.com", - * "email":"andy@warmcat.com", "staging":"true"} - */ - - if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) { - lwsl_err("Unable to open nvs\n"); - break; - } - - n = 0; - b = strstr(in, ",\"cn\":\""); - if (b) { - b += 7; - while (*b && *b != '\"' && n < sizeof(lws_esp32.le_dns) - 1) - lws_esp32.le_dns[n++] = *b++; - } - lws_esp32.le_dns[n] = '\0'; - - lws_nvs_set_str(nvh, "acme-cn", lws_esp32.le_dns); - n = 0; - b = strstr(in, ",\"email\":\""); - if (b) { - b += 10; - while (*b && *b != '\"' && n < sizeof(lws_esp32.le_email) - 1) - lws_esp32.le_email[n++] = *b++; - } - lws_esp32.le_email[n] = '\0'; - lws_nvs_set_str(nvh, "acme-email", lws_esp32.le_email); - nvs_commit(nvh); - - nvs_close(nvh); - - n = 1; - b = strstr(in, ",\"staging\":\""); - if (b) - lwsl_notice("staging: %s\n", b); - if (b && b[12] == 'f') - n = 0; - - lwsl_notice("cn: %s, email: %s, staging: %d\n", lws_esp32.le_dns, lws_esp32.le_email, n); - - { - struct lws_acme_cert_aging_args caa; - - memset(&caa, 0, sizeof(caa)); - caa.vh = vhd->vhost; - - caa.element_overrides[LWS_TLS_REQ_ELEMENT_COMMON_NAME] = lws_esp32.le_dns; - caa.element_overrides[LWS_TLS_REQ_ELEMENT_EMAIL] = lws_esp32.le_email; - - if (n) - caa.element_overrides[LWS_TLS_SET_DIR_URL] = - "https://acme-staging.api.letsencrypt.org/directory"; /* staging */ - else - caa.element_overrides[LWS_TLS_SET_DIR_URL] = - "https://acme-v01.api.letsencrypt.org/directory"; /* real */ - - lws_callback_vhost_protocols_vhost(vhd->vhost, - LWS_CALLBACK_VHOST_CERT_AGING, - (void *)&caa, 0); - } - - break; - - } - - case LWS_CALLBACK_CLOSED: - { - struct per_session_data__esplws_scan **p = &vhd->live_pss_list; - - while (*p) { - if ((*p) == pss) { - *p = pss->next; - continue; - } - - p = &((*p)->next); - } - - vhd->count_live_pss--; - } - if (!vhd->live_pss_list) - xTimerStop(vhd->timer, 0); - break; - - /* "factory" POST handling */ - - case LWS_CALLBACK_HTTP_BODY: - /* create the POST argument parser if not already existing */ - if (!pss->spa) { - pss->spa = lws_spa_create(wsi, param_names, - LWS_ARRAY_SIZE(param_names), 1024, - file_upload_cb, pss); - if (!pss->spa) - return -1; - - pss->filename[0] = '\0'; - pss->file_length = 0; - } - //puts((const char *)in); - /* let it parse the POST data */ - if (lws_spa_process(pss->spa, in, len)) - return -1; - break; - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION (scan)\n"); - /* call to inform no more payload data coming */ - lws_spa_finalize(pss->spa); - - for (n = 0; n < LWS_ARRAY_SIZE(param_names); n++) - if (lws_spa_get_string(pss->spa, n)) - lwsl_notice(" Param %s: %s\n", param_names[n], - lws_spa_get_string(pss->spa, n)); - else - lwsl_notice(" Param %s: (none)\n", - param_names[n]); - - if (nvs_open("lws-station", NVS_READWRITE, &nvh) != ESP_OK) { - lwsl_err("Unable to open nvs\n"); - break; - } - - if (lws_esp32_get_reboot_type() == LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON) { - - if (lws_spa_get_string(pss->spa, EPN_SERIAL)) { - if (lws_nvs_set_str(nvh, "serial", lws_spa_get_string(pss->spa, EPN_SERIAL)) != ESP_OK) { - lwsl_err("Unable to store serial in nvm\n"); - goto bail_nvs; - } - - nvs_commit(nvh); - } - - if (lws_spa_get_string(pss->spa, EPN_OPTS)) { - if (lws_nvs_set_str(nvh, "opts", lws_spa_get_string(pss->spa, EPN_OPTS)) != ESP_OK) { - lwsl_err("Unable to store options in nvm\n"); - goto bail_nvs; - } - - nvs_commit(nvh); - } - } - - if (lws_spa_get_string(pss->spa, EPN_GROUP)) { - if (lws_nvs_set_str(nvh, "group", lws_spa_get_string(pss->spa, EPN_GROUP)) != ESP_OK) { - lwsl_err("Unable to store group in nvm\n"); - goto bail_nvs; - } - - nvs_commit(nvh); - } - - if (lws_spa_get_string(pss->spa, EPN_ROLE)) { - if (lws_nvs_set_str(nvh, "role", lws_spa_get_string(pss->spa, EPN_ROLE)) != ESP_OK) { - lwsl_err("Unable to store group in nvm\n"); - goto bail_nvs; - } - - nvs_commit(nvh); - } - - nvs_close(nvh); - - pss->result_len = snprintf(pss->result + LWS_PRE, sizeof(pss->result) - LWS_PRE - 1, - "OK"); - - if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) - goto bail; - - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, - (unsigned char *)"text/html", 9, &p, end)) - goto bail; - if (lws_add_http_header_content_length(wsi, pss->result_len, &p, end)) - goto bail; - if (lws_finalize_http_header(wsi, &p, end)) - goto bail; - - n = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS); - goto bail; - - case LWS_CALLBACK_HTTP_WRITEABLE: - lwsl_debug("LWS_CALLBACK_HTTP_WRITEABLE: sending %d\n", - pss->result_len); - if (!pss->result_len) - break; - n = lws_write(wsi, (unsigned char *)pss->result + LWS_PRE, - pss->result_len, LWS_WRITE_HTTP); - if (n < 0) - return 1; - - vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(3000), 0, vhd, - (TimerCallbackFunction_t)reboot_timer_cb); - xTimerStart(vhd->reboot_timer, 0); - - return 1; // hang up since we will reset - - /* ----- client handling ----- */ - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - lwsl_notice("Client connection error %s\n", (char *)in); - vhd->cwsi = NULL; - break; - - case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: - if (!vhd->autonomous_update) - break; - - { - char pp[20]; - - if (lws_hdr_copy(wsi, pp, sizeof(pp) - 1, WSI_TOKEN_HTTP_CONTENT_LENGTH) < 0) - return -1; - - vhd->content_length = atoi(pp); - if (vhd->content_length <= 0 || - vhd->content_length > vhd->part->size) - return -1; - - if (esp_ota_begin(vhd->part, (long)-1, &vhd->otahandle) != ESP_OK) { - lwsl_err("OTA: Failed to begin\n"); - return 1; - } - - vhd->file_length = 0; - break; - } - break; - - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: - //lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: %ld\n", - // (long)len); - - if (!vhd->autonomous_update) { - if (sizeof(vhd->json) - vhd->json_len - 1 < len) - len = sizeof(vhd->json) - vhd->json_len - 1; - memcpy(vhd->json + vhd->json_len, in, len); - vhd->json_len += len; - vhd->json[vhd->json_len] = '\0'; - break; - } - - /* autonomous download */ - - - if (vhd->file_length + len > vhd->part->size) { - lwsl_err("OTA: incoming file too large\n"); - goto abort_ota; - } - - lwsl_debug("writing 0x%lx... 0x%lx\n", - vhd->part->address + vhd->file_length, - vhd->part->address + vhd->file_length + len); - if (esp_ota_write(vhd->otahandle, in, len) != ESP_OK) { - lwsl_err("OTA: Failed to write\n"); - goto abort_ota; - } - vhd->file_length += len; - - lws_callback_on_writable_all_protocol(vhd->context, vhd->protocol); - break; - -abort_ota: - esp_ota_end(vhd->otahandle); - vhd->otahandle = 0; - vhd->autonomous_update = 0; - - return 1; - - case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: - { - char *px = (char *)pss->buffer + LWS_PRE; - int lenx = sizeof(pss->buffer) - LWS_PRE - 1; - - //lwsl_notice("LWS_CALLBACK_RECEIVE_CLIENT_HTTP: %d\n", len); - - if (lws_http_client_read(wsi, &px, &lenx) < 0) - return -1; - } - break; - - case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_notice("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); - vhd->cwsi = NULL; - if (!vhd->autonomous_update) { - - vhd->checked_updates = 1; - puts(vhd->json); - return -1; - } - - /* autonomous download */ - - lwsl_notice("auton complete\n"); - - if (esp_ota_end(vhd->otahandle) != ESP_OK) { - lwsl_err("OTA: end failed\n"); - return 1; - } - - if (esp_ota_set_boot_partition(vhd->part) != ESP_OK) { - lwsl_err("OTA: set boot part failed\n"); - return 1; - } - vhd->otahandle = 0; - vhd->autonomous_update = 0; - - vhd->reboot_timer = xTimerCreate("x", pdMS_TO_TICKS(250), 0, vhd, - (TimerCallbackFunction_t)reboot_timer_cb); - xTimerStart(vhd->reboot_timer, 0); - return -1; - - case LWS_CALLBACK_CLOSED_CLIENT_HTTP: - lwsl_notice("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n"); - break; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - /* called when our wsi user_space is going to be destroyed */ - if (pss->spa) { - lws_spa_destroy(pss->spa); - pss->spa = NULL; - } - break; - - default: - break; - } - - return 0; - -bail: - return 1; -} - -#define LWS_PLUGIN_PROTOCOL_ESPLWS_SCAN \ - { \ - "esplws-scan", \ - callback_esplws_scan, \ - sizeof(struct per_session_data__esplws_scan), \ - 1024, 0, NULL, 900 \ - } - diff --git a/plugins/protocol_fulltext_demo.c b/plugins/protocol_fulltext_demo.c index be581bfa0..b15148236 100644 --- a/plugins/protocol_fulltext_demo.c +++ b/plugins/protocol_fulltext_demo.c @@ -266,28 +266,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_FULLTEXT_DEMO }; -LWS_VISIBLE int -init_protocol_fulltext_demo(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_fulltext_demo = { + .hdr = { + "fulltext demo", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_fulltext_demo(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/protocol_lws_mirror.c b/plugins/protocol_lws_mirror.c index 380d6c2e9..d5cc8e69e 100644 --- a/plugins/protocol_lws_mirror.c +++ b/plugins/protocol_lws_mirror.c @@ -482,27 +482,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_MIRROR }; -LWS_VISIBLE int -init_protocol_lws_mirror(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_mirror = { + .hdr = { + "lws mirror", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_mirror(struct lws_context *context) -{ - return 0; -} #endif diff --git a/plugins/protocol_lws_raw_test.c b/plugins/protocol_lws_raw_test.c index 255de8c3a..abe29bbd6 100644 --- a/plugins/protocol_lws_raw_test.c +++ b/plugins/protocol_lws_raw_test.c @@ -278,28 +278,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_RAW_TEST }; -LWS_VISIBLE int -init_protocol_lws_raw_test(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_raw_test = { + .hdr = { + "lws raw test", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_raw_test(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/protocol_lws_server_status.c b/plugins/protocol_lws_server_status.c index ab9a7978d..61f81ddd9 100644 --- a/plugins/protocol_lws_server_status.c +++ b/plugins/protocol_lws_server_status.c @@ -204,26 +204,15 @@ static const struct lws_protocols protocols[] = { }, }; -LWS_VISIBLE int -init_protocol_lws_server_status(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_server_status = { + .hdr = { + "lws server status", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_server_status(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; diff --git a/plugins/protocol_lws_sshd_demo.c b/plugins/protocol_lws_sshd_demo.c index e933de1b5..cddf0c16d 100644 --- a/plugins/protocol_lws_sshd_demo.c +++ b/plugins/protocol_lws_sshd_demo.c @@ -455,28 +455,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_LWS_SSHD_DEMO }; -LWS_VISIBLE int -init_protocol_lws_sshd_demo(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_sshd_demo = { + .hdr = { + "lws sshd demo", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_sshd_demo(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/protocol_lws_status.c b/plugins/protocol_lws_status.c index 35a6d155d..bf8b45f5b 100644 --- a/plugins/protocol_lws_status.c +++ b/plugins/protocol_lws_status.c @@ -245,29 +245,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_LWS_STATUS }; +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_status = { + .hdr = { + "lws status", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, -LWS_VISIBLE int -init_protocol_lws_status(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 = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_status(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c index 86157c29d..a9cf83546 100644 --- a/plugins/protocol_post_demo.c +++ b/plugins/protocol_post_demo.c @@ -287,28 +287,17 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_POST_DEMO }; -LWS_VISIBLE int -init_protocol_post_demo(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_post_demo = { + .hdr = { + "post demo", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_post_demo(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/raw-proxy/protocol_lws_raw_proxy.c b/plugins/raw-proxy/protocol_lws_raw_proxy.c index 6c15f3241..515da3df6 100644 --- a/plugins/raw-proxy/protocol_lws_raw_proxy.c +++ b/plugins/raw-proxy/protocol_lws_raw_proxy.c @@ -562,29 +562,18 @@ static const struct lws_protocols protocols[] = { LWS_PLUGIN_PROTOCOL_RAW_PROXY }; -LWS_VISIBLE int -init_protocol_lws_raw_proxy(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_raw_proxy = { + .hdr = { + "raw proxy", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols; - c->count_protocols = LWS_ARRAY_SIZE(protocols); - c->extensions = NULL; - c->count_extensions = 0; - - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_raw_proxy(struct lws_context *context) -{ - return 0; -} + .protocols = protocols, + .count_protocols = LWS_ARRAY_SIZE(protocols), + .extensions = NULL, + .count_extensions = 0, +}; #endif diff --git a/plugins/ssh-base/sshd.c b/plugins/ssh-base/sshd.c index 1b78019d8..4ff303dd0 100644 --- a/plugins/ssh-base/sshd.c +++ b/plugins/ssh-base/sshd.c @@ -2565,27 +2565,17 @@ const struct lws_protocols protocols_sshd[] = { #if !defined (LWS_PLUGIN_STATIC) -LWS_VISIBLE int -init_protocol_lws_ssh_base(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; - } +LWS_VISIBLE const lws_plugin_protocol_t protocol_lws_ssh_base = { + .hdr = { + "ssh base", + "lws_protocol_plugin", + LWS_PLUGIN_API_MAGIC + }, - c->protocols = protocols_sshd; - c->count_protocols = LWS_ARRAY_SIZE(protocols_sshd); - c->extensions = NULL; - c->count_extensions = 0; + .protocols = protocols_sshd, + .count_protocols = LWS_ARRAY_SIZE(protocols_sshd), + .extensions = NULL, + .count_extensions = 0, +}; - return 0; -} - -LWS_VISIBLE int -destroy_protocol_lws_ssh_base(struct lws_context *context) -{ - return 0; -} #endif