From df472ab91fe8a74cdf54172b98c2ab388b990bd4 Mon Sep 17 00:00:00 2001 From: zefie Date: Sat, 7 Aug 2021 17:04:17 -0400 Subject: [PATCH] v0.9.13 - wtv-cookie support - further development will be in dev branch (may rebase alot) - compression not yet ready, leave it disabled - update: do not delete WTVSec on last socket, instead recreate on prereg - update: clean up SSID session data only if client is not seen for 3 minutes - update: add shouldWeCompress() function - update: tweak lzpf (still corrupted) - update: rename wtv-setup:/get to wtv-setup:/get-settings - update: add additional headers to wtv-setup:/get-settings - update: add initial blank wtv-music:/get-playlist - update wtv-tricks system - Info now shows Guest Mode or Subscriber Info - Implemented wtv-tricks:/unregister - Implemented wtv-tricks:/register - Show correct link in wtv-tricks:/tricks based on Guest Mode status - config.json: enable compression by default - WTVP does not use \r, so swapping the internal header's usage for now. May remove internal header in future update - lzpf: this doesn't fix anything but doesn't break it more either :) - renamed some functions - fixed some param documentation - added ConvertToBuffer function - WTVSec Updates - optimize WordArray to Buffer functions - update documentation in WTVSec - update WTVSec barrowed function in WTVLzpf - removed NewRC4Session, was a pointless alias to SecureOn --- README.md | 16 +- docker-compose/minisrv/Dockerfile | 5 +- user_config_README.md | 6 +- .../ServiceVault/wtv-1800/preregister.js | 104 +++---- .../ServiceVault/wtv-cookie/add.js | 2 +- .../ServiceVault/wtv-cookie/get.js | 3 +- .../ServiceVault/wtv-cookie/reset.js | 25 ++ .../wtv-flashrom/current-noflash.js | 4 +- .../ServiceVault/wtv-flashrom/get-by-path.js | 4 +- .../ServiceVault/wtv-flashrom/get-lc2-page.js | 4 +- .../ServiceVault/wtv-flashrom/noflash.js | 4 +- .../wtv-head-waiter/login-stage-two.js | 76 +++-- .../ServiceVault/wtv-head-waiter/login.js | 9 +- .../ServiceVault/wtv-home/home.js | 9 +- .../ServiceVault/wtv-home/splash.js | 4 +- .../ServiceVault/wtv-log/log.js | 4 +- .../ServiceVault/wtv-music/get-playlist.js | 4 +- .../ServiceVault/wtv-register/BeMyGuest.js | 26 ++ .../wtv-register/ValidateAccountInfo.js | 22 +- .../wtv-register/ValidateReviewAccountInfo.js | 8 +- .../wtv-register/ValidateWelcome.js | 11 +- .../ServiceVault/wtv-register/register.js | 8 +- .../ServiceVault/wtv-register/splash.js | 4 +- .../wtv-setup/{get.js => get-settings.js} | 0 .../ServiceVault/wtv-tricks/access.js | 2 +- .../ServiceVault/wtv-tricks/blastbacklist.js | 2 +- .../ServiceVault/wtv-tricks/info.js | 29 +- .../ServiceVault/wtv-tricks/register.js | 39 +++ .../ServiceVault/wtv-tricks/tricks.js | 6 +- .../ServiceVault/wtv-tricks/unregister.js | 42 +++ zefie_wtvp_minisrv/WTVClientSessionData.js | 42 ++- zefie_wtvp_minisrv/WTVFlashrom.js | 28 +- zefie_wtvp_minisrv/WTVLzpf.js | 194 +++++++++---- zefie_wtvp_minisrv/WTVRegister.js | 1 + zefie_wtvp_minisrv/WTVSec.js | 152 ++++++---- zefie_wtvp_minisrv/app.js | 260 ++++++++++-------- zefie_wtvp_minisrv/config.json | 5 +- zefie_wtvp_minisrv/package.json | 2 +- zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj | 15 +- 39 files changed, 794 insertions(+), 387 deletions(-) create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-cookie/reset.js create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-register/BeMyGuest.js rename zefie_wtvp_minisrv/ServiceVault/wtv-setup/{get.js => get-settings.js} (100%) create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-tricks/register.js create mode 100644 zefie_wtvp_minisrv/ServiceVault/wtv-tricks/unregister.js diff --git a/README.md b/README.md index 6a35a2f6..9d146267 100644 --- a/README.md +++ b/README.md @@ -11,21 +11,27 @@ This open source server is in alpha status. Use at your own risk. - Suports `.js` service files with synchronous or asynchronous requests - Supports multiple simultaneous users - WebTV-compatible HTTP(S) Proxy (via minisrv, or using an external proxy for enhanced features (such as [WebOne](https://github.com/atauenis/webone)) +- WebTV Cookie (wtv-cookie) support for HTTP(s) - Flashrom flashing support for all known units (including bf0app 'Old Classic') - Can flash anything on [Ultra Willies](https://wtv.zefie.com/willie.php) with optional `use_zefie_server` flag set on `wtv-flashrom` service. - `wtv-update:/sync` for Download-o-Rama style file downloading +- Custom Tellyscripts *(not yet customizable though)* +- Flat file client session store and registration system ### Current issues: -- Satellite Receiver units reportedly cannot surf with http or https proxy. +- wtv-cookie implementation is still partial +- wtv-lzpf compression support is not yet reliable - Mis-configuring wtv-update:/sync DiskMaps may cause units to delete contents of partitions (need more info) +- Satellite Receiver units reportedly cannot surf with http or https proxy. *(May be fixed by current partial wtv-cookie implementation)* ### Won't fix: - wtv-encryption stream breaks when two different sessions have the same SSID (eg spoofing, won't fix (production did it too)) -- No intentions to support user accounts, registration, or any form of database system +- ~~No intentions to support user accounts, registration, or any form of database system~~ *(I guess this was a lie, but we still don't use a database!)* ### Feature Todo: -- wtv-lzpf support -- TellyScript generation and/or manipulation (needed for Name Server configuration) +- wtv-lzpf support *(Milestone v1.0)* +- TellyScript generation and/or manipulation without external dependancies +- wtv-cookie full support ***Done [v0.9.13](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.13)*** - ~~Flashrom flashing for bf0app old classic~~ ***Done [v0.9.9](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.9)*** - ~~SSID/IP black/whitelisting (including tying SSID to an IP or multiple IPs)~~ ***Done [v0.9.4](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.9.4)*** - ~~Flashrom flashing functionality (at least for LC2 and higher)~~ ***Done [v0.8.0](https://github.com/zefie/zefie_wtvp_minisrv/releases/tag/v0.8.0)*** @@ -49,4 +55,4 @@ This open source server is in alpha status. Use at your own risk. - [Report Bugs](https://github.com/zefie/zefie_wtvp_minisrv/issues) - [Add a Feature and send a Pull Request](https://github.com/zefie/zefie_wtvp_minisrv/pulls) - Write and submit better documentation than I created (see Pull Request above) -- [Support financially on Patreon](https://www.patreon.com/zefie) \ No newline at end of file +- [Support financially on Patreon](https://www.patreon.com/zefie) diff --git a/docker-compose/minisrv/Dockerfile b/docker-compose/minisrv/Dockerfile index 57c7e1a4..447186f9 100644 --- a/docker-compose/minisrv/Dockerfile +++ b/docker-compose/minisrv/Dockerfile @@ -1,7 +1,8 @@ -FROM alpine:latest +FROM node:current-alpine -RUN apk add git nodejs npm +RUN apk add git RUN cd /opt && git clone --depth=1 https://github.com/zefie/zefie_wtvp_minisrv.git minisrv +RUN cd /opt/minisrv && git config pull.ff only RUN cd /opt/minisrv/zefie_wtvp_minisrv && npm install COPY ./run.sh /opt/minisrv/zefie_wtvp_minisrv/run.sh diff --git a/user_config_README.md b/user_config_README.md index 2db88a4f..392e5398 100644 --- a/user_config_README.md +++ b/user_config_README.md @@ -17,7 +17,11 @@ If an absolute path (`wtv-url:/`, `file://` url, or `http(s)://` url) is not pas ``` "post_debug": true ``` -If you would like to see debug information about realtime bytes received from a client POST request, set `post_debug` to true. +If you would like to see debug information about realtime bytes received from a client POST request, set `post_debug` to `true`. +``` + "allow_guests": false +``` +If you would like to require registration, disabling guest mode, you can set `allow_guests` to `false`. Default is `true`; ``` "post_percentages": [ 0, 25, 50, 100] ``` diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-1800/preregister.js b/zefie_wtvp_minisrv/ServiceVault/wtv-1800/preregister.js index ccbdee86..49ae3d46 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-1800/preregister.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-1800/preregister.js @@ -1,45 +1,45 @@ -var gourl = "wtv-head-waiter:/login?"; + var gourl = "wtv-head-waiter:/login?"; -if (socket.ssid) { - if (ssid_sessions[socket.ssid].loadSessionData() == true) { - console.log(" * Loaded session data from disk for", filterSSID(socket.ssid)) - ssid_sessions[socket.ssid].setSessionData("registered", (ssid_sessions[socket.ssid].getSessionData("registered") == true) ? true : false); + if (socket.ssid) { + if (ssid_sessions[socket.ssid].loadSessionData() == true) { + console.log(" * Loaded session data from disk for", filterSSID(socket.ssid)) + ssid_sessions[socket.ssid].setSessionData("registered", (ssid_sessions[socket.ssid].getSessionData("registered") == true) ? true : false); + } else { + ssid_sessions[socket.ssid].session_data = {}; + ssid_sessions[socket.ssid].setSessionData("registered", false); + } + if (ssid_sessions[socket.ssid].data_store) { + if (ssid_sessions[socket.ssid].data_store.sockets) { + var i = 0; + ssid_sessions[socket.ssid].data_store.sockets.forEach(function (k) { + if (typeof k != "undefined") { + if (k != socket) { + k.destroy(); + ssid_sessions[socket.ssid].data_store.sockets.delete(k); + i++; + } + } + }); + if (i > 0 && zdebug) console.log(" # Closed", i, "previous sockets for", filterSSID(socket.ssid)); + } + } + if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { + if (zdebug) console.log(" # Recreating primary WTVSec login instance for", filterSSID(socket.ssid)); + delete ssid_sessions[socket.ssid].data_store.wtvsec_login; + } + + ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(); + ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge(); + ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"] || 1); } else { - ssid_sessions[socket.ssid].session_data = {}; - ssid_sessions[socket.ssid].setSessionData("registered", false); + console.log(" * Something bad happened (we don't know the client ssid???)"); + var errpage = doErrorPage(400) + headers = errpage[0]; + data = errpage[1]; } - if (ssid_sessions[socket.ssid].data_store) { - if (ssid_sessions[socket.ssid].data_store.sockets) { - var i = 0; - ssid_sessions[socket.ssid].data_store.sockets.forEach(function (k) { - if (typeof k != "undefined") { - if (k != socket) { - k.destroy(); - ssid_sessions[socket.ssid].data_store.sockets.delete(k); - i++; - } - } - }); - if (i > 0 && zdebug) console.log(" # Closed", i, "previous sockets for", filterSSID(socket.ssid)); - } - } - if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { - if (zdebug) console.log(" # Recreating primary WTVSec login instance for", filterSSID(socket.ssid)); - delete ssid_sessions[socket.ssid].data_store.wtvsec_login; - } - ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(); - ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge(); - ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"] || 1); -} else { - console.log(" * Something bad happened (we don't know the client ssid???)"); - var errpage = doErrorPage(400) - headers = errpage[0]; - data = errpage[1]; -} - -if (request_headers.query.relogin && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "relogin=true"; -if (request_headers.query.reconnect && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "reconnect=true"; + if (request_headers.query.relogin && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "relogin=true"; + if (request_headers.query.reconnect && ssid_sessions[socket.ssid].getSessionData("registered")) gourl += "reconnect=true"; if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { var prereg_contype = "text/html"; @@ -53,11 +53,11 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { var file_path = null; var bf0app_update = false; var romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type"); - var send_tellyscripts = (minisrv_config.services[service_name].send_tellyscripts && !request_headers.query.relogin); + var send_tellyscript = (minisrv_config.services[service_name].send_tellyscripts && !request_headers.query.relogin); var wtv_script_id = parseInt(ssid_sessions[socket.ssid].get("wtv-script-id")); - var bootrom = ssid_sessions[socket.ssid].get("wtv-client-bootrom-version"); - if ((request_headers.query.reconnect || request_headers.query.relogin) && wtv_script_id != 0) send_tellyscripts = false; - if (send_tellyscripts) { + var bootrom = ssid_sessions[socket.ssid].get("wtv-client-bootrom-version"); + if ((request_headers.query.reconnect || request_headers.query.relogin) && wtv_script_id != 0) send_tellyscript = false; + if (send_tellyscript) { if (minisrv_config.services[service_name].send_tellyscript_ssid_whitelist) { var send_telly_to_ssid = (minisrv_config.services[service_name].send_tellyscript_ssid_whitelist.findIndex(element => element == socket.ssid) != -1) if (send_telly_to_ssid) { @@ -101,7 +101,7 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { } } - + if (!request_headers['wtv-client-rom-type'] && bootrom == "105") { // assume old classic in flash mode, override user setting and send tellyscript // because it is required to proceed in flash mode @@ -110,17 +110,23 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { var bf0app_update = true; ssid_sessions[socket.ssid].set("bf0app_update", bf0app_update); } - + if (request_headers["wtv-ticket"] && !request_headers.query.reconnect) { gourl = "wtv-head-waiter:/login-stage-two?relogin=true"; } if (request_headers.query.reconnect) { gourl = null; - } + } if (!file_path != null && !zquiet) console.log(" * Sending TellyScript", file_path, "on socket", socket.id); + if (request_headers.query.guest_login) { + send_tellyscript = false; + gourl += "&guest_login=true" + if (request_headers.query.skip_splash) gourl += "&skip_splash=true"; + } + headers = "200 OK\n" if (bf0app_update) headers += "minisrv-use-carriage-return: false\n"; headers += "Connection: Keep-Alive\n"; @@ -134,10 +140,14 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { if (bf0app_update) headers += getServiceString('wtv-star', { "no_star_word": true }) + "\n"; else headers += getServiceString('wtv-star') + "\n"; - if (request_headers.query.reconnect && !ssid_sessions[socket.ssid].session_data.registered) headers += getServiceString('wtv-register') + "\n"; + if (request_headers.query.reconnect && !ssid_sessions[socket.ssid].getSessionData("registered")) headers += getServiceString('wtv-register') + "\n"; headers += getServiceString('wtv-flashrom') + "\n"; if (bf0app_update) headers += "wtv-boot-url: " + gourl + "\n"; - else headers += "wtv-boot-url: wtv-1800:/preregister?relogin=true\n"; + else { + headers += "wtv-boot-url: wtv-1800:/preregister?relogin=true"; + if (request_headers.query.guest_login) headers += "&guest_login=true"; + headers += "\n"; + } if (gourl != null) headers += "wtv-visit: " + gourl + "\n"; if (!bf0app_update && ssid_sessions[socket.ssid].get("wtv-open-access")) headers += "wtv-open-isp-disabled: false\n"; headers += "wtv-client-time-zone: GMT -0000\n"; diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/add.js b/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/add.js index 36a46124..80975afb 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/add.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/add.js @@ -1,7 +1,7 @@ if (socket.ssid) { if (request_headers.post_data) { if (ssid_sessions[socket.ssid]) { - ssid_sessions[socket.ssid].addCookie(cookie_data); + ssid_sessions[socket.ssid].addCookie(request_headers.query.domain,request_headers.query.path,request_headers.query.expires,request_headers.query.cookie); headers = "200 OK\n"; headers += "Content-Type: text/html"; } diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/get.js b/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/get.js index 8a9c38ff..90f5ad98 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/get.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/get.js @@ -2,8 +2,7 @@ if (request_headers.post_data) { if (request_headers.query.domain && request_headers.query.path) { if (socket.ssid) { if (ssid_sessions[socket.ssid]) { - - data = ssid_sessions[socket.ssid].getCookieString(unescape(request_headers.query.domain), unescape(request_headers.query.path)); + data = ssid_sessions[socket.ssid].getCookieString(request_headers.query.domain, request_headers.query.path); headers = "200 OK\n"; headers += "Content-Type: text/plain"; } diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/reset.js b/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/reset.js new file mode 100644 index 00000000..6a1801dc --- /dev/null +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-cookie/reset.js @@ -0,0 +1,25 @@ +if (socket.ssid) { + if (ssid_sessions[socket.ssid]) { + ssid_sessions[socket.ssid].resetCookies(); + headers = "200 OK\n"; + headers += "Content-Type: text/html"; + data = ` + + +Cookies cleared! + + + +

+Your cookies have successfully been cleared!
+Redirecting shortly... Go Back + +`; + } +} + +if (!headers) { + var errpage = doErrorPage(400) + headers = errpage[0]; + data = errpage[1]; +} \ No newline at end of file diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/current-noflash.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/current-noflash.js index aa7d71b0..642803cf 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/current-noflash.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/current-noflash.js @@ -11,7 +11,7 @@ var default_build_to_send = minisrv_config.services[service_name].bf0app_default var request_path = ""; var bf0app_update = true; -if (request_headers.query.path) request_path = unescape(request_headers.query.path); +if (request_headers.query.path) request_path = request_headers.query.path; else request_path = default_build_to_send; if (ssid_sessions[socket.ssid].get("wtv-client-rom-type") == "bf0app" && ssid_sessions[socket.ssid].get("wtv-client-bootrom-version") == "105") { @@ -22,7 +22,7 @@ if (ssid_sessions[socket.ssid].get("wtv-client-rom-type") == "bf0app" && ssid_se } if (!ssid_sessions[socket.ssid].data_store.WTVFlashrom) { - ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update); + ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug); } ssid_sessions[socket.ssid].data_store.WTVFlashrom.getFlashRom(request_path, function (data, headers) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js index e25528a0..33f19f0e 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-by-path.js @@ -2,7 +2,7 @@ const WTVFlashrom = require("./WTVFlashrom.js"); request_is_async = true; var bf0app_update = false; -var request_path = unescape(request_headers.query.path); +var request_path = request_headers.query.path; var romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type"); var bootver = ssid_sessions[socket.ssid].get("wtv-client-bootrom-version") @@ -15,7 +15,7 @@ if ((romtype == "bf0app" || !romtype) && (bootver == "105" || !bootver)) { if (request_headers.query.raw || bf0app_update) { if (!ssid_sessions[socket.ssid].data_store.WTVFlashrom) { - ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update); + ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug); } ssid_sessions[socket.ssid].data_store.WTVFlashrom.getFlashRom(request_path, function (data, headers) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js index 1b189537..25a2368b 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/get-lc2-page.js @@ -8,8 +8,8 @@ if (!request_headers.query.path) { headers = errpage[0]; data = errpage[1]; } else { - var wtvflashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server); - var request_path = unescape(request_headers.query.path); + var wtvflashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, minisrv_config.services[service_name].debug); + var request_path = request_headers.query.path; // read flashrom header info into array using WTVFlashrom class wtvflashrom.getFlashromMeta(request_path, function (data) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/noflash.js b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/noflash.js index 440a6e64..03c990e9 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/noflash.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-flashrom/noflash.js @@ -11,7 +11,7 @@ var default_build_to_send = minisrv_config.services[service_name].bf0app_default var request_path = ""; var bf0app_update = true; -if (request_headers.query.path) request_path = unescape(request_headers.query.path); +if (request_headers.query.path) request_path = request_headers.query.path; else request_path = default_build_to_send; if (ssid_sessions[socket.ssid].get("wtv-client-rom-type") == "bf0app" && ssid_sessions[socket.ssid].get("wtv-client-bootrom-version") == "105") { @@ -22,7 +22,7 @@ if (ssid_sessions[socket.ssid].get("wtv-client-rom-type") == "bf0app" && ssid_se } if (!ssid_sessions[socket.ssid].data_store.WTVFlashrom) { - ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, 0, minisrv_config.services[service_name].use_zefie_server, bf0app_update); + ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, 0, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug); } ssid_sessions[socket.ssid].data_store.WTVFlashrom.getFlashRom(request_path, function (data, headers) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js b/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js index f64e9c35..415f3b64 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login-stage-two.js @@ -12,39 +12,33 @@ if (socket.ssid != null && !ssid_sessions[socket.ssid].get("wtvsec_login")) { if (socket.ssid !== null) { if (wtvsec_login.ticket_b64 == null) { - if (request_headers["wtv-ticket"]) { - if (request_headers["wtv-ticket"].length > 8) { - wtvsec_login.DecodeTicket(request_headers["wtv-ticket"]); - wtvsec_login.ticket_b64 = request_headers["wtv-ticket"]; + challenge_response = wtvsec_login.challenge_response; + var client_challenge_response = request_headers["wtv-challenge-response"] || null; + if (challenge_response && client_challenge_response) { + if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) { + console.log(" * wtv-challenge-response success for " + filterSSID(socket.ssid)); + wtvsec_login.PrepareTicket(); + + } else { + console.log(" * wtv-challenge-response FAILED for " + filterSSID(socket.ssid)); + if (zdebug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64)); + if (zdebug) console.log("Response Received:", client_challenge_response) + gourl = "wtv-head-waiter:/login?reissue_challenge=true"; } } else { - challenge_response = wtvsec_login.challenge_response; - var client_challenge_response = request_headers["wtv-challenge-response"] || null; - if (challenge_response && client_challenge_response) { - if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) { - console.log(" * wtv-challenge-response success for " + filterSSID(socket.ssid)); - wtvsec_login.PrepareTicket(); - if (!ssid_sessions[socket.ssid].getSessionData("registered")) gourl = "wtv-register:/splash"; - - } else { - console.log(" * wtv-challenge-response FAILED for " + filterSSID(socket.ssid)); - if (zdebug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64)); - if (zdebug) console.log("Response Received:", client_challenge_response) - gourl = "wtv-head-waiter:/login?reissue_challenge=true"; - } - } else { - gourl = "wtv-head-waiter:/login?no_response=true"; - } + gourl = "wtv-head-waiter:/login?no_response=true"; } } } +if (!ssid_sessions[socket.ssid].getSessionData("registered") && (!request_headers.query.guest_login || !minisrv_config.config.allow_guests)) gourl = "wtv-register:/splash"; + if (gourl) { headers = `200 OK Connection: Close wtv-open-isp-disabled: false `; - if (!ssid_sessions[socket.ssid].session_data.registered && !request_headers.query.guest_mode) { + if (!ssid_sessions[socket.ssid].getSessionData("registered") && (!request_headers.query.guest_login || !minisrv_config.config.allow_guests)) { headers += `wtv-encrypted: true wtv-ticket: ${wtvsec_login.ticket_b64} ${getServiceString('wtv-register')} @@ -58,14 +52,15 @@ Content-type: text/html`; data = ''; } else { - if (request_headers.query.guest_mode) { + if (request_headers.query.guest_login && minisrv_config.config.allow_guests) { var namerand = Math.floor(Math.random() * 100000); var nickname = (minisrv_config.config.service_name + '_' + namerand) var human_name = nickname; var userid = '1' + Math.floor(Math.random() * 1000000000000000000); var messenger_enabled = 0; var messenger_authorized = 0; - var home_url = "wtv-home:/home?"; + if (request_headers.query.skip_splash) var home_url = "wtv-home:/home?"; + else var home_url = "wtv-home:/splash?"; } else if (!ssid_sessions[socket.ssid].getSessionData("registered")) { var errpage = doErrorPage(400); headers = errpage[0]; @@ -99,7 +94,7 @@ wtv-messenger-authorized: ${messenger_authorized} wtv-messenger-enable: ${messenger_enabled} wtv-noback-all: wtv- wtv-service: reset -`+ getServiceString('all', { "exceptions": ["wtv-register"] } ) + ` +`+ getServiceString('all', { "exceptions": ["wtv-register"] }) + ` user-id: ${userid} wtv-human-name: ${human_name} ${ssid_sessions[socket.ssid].setIRCNick(nickname)} @@ -111,15 +106,36 @@ wtv-input-timeout: 14400 wtv-connection-timeout: 90 wtv-fader-timeout: 900 wtv-smartcard-inserted-message: Contacting service +user-id: ${userid} +wtv-transition-override: off +wtv-allow-dsc: true +wtv-messenger-enable: 0 +wtv-noback-all: wtv- +wtv-service: reset +`+ getServiceString('all') + ` +wtv-boot-url: wtv-1800:/preregister?relogin=true +wtv-human-name: ${nickname} +${ssid_sessions[socket.ssid].setIRCNick(nickname)} +wtv-home-url: wtv-home:/home?` + if (ssid_sessions[socket.ssid].get('wtv-need-upgrade') != 'true' && !request_headers.query.reconnect) { + headers += "\nwtv-settings-url: wtv-setup:/get-settings"; + } +headers += ` +wtv-domain: wtv.zefie.com wtv-inactive-timeout: 0 wtv-connection-timeout: 90 wtv-show-time-enabled: true wtv-fader-timeout: 900 -wtv-tourist-enabled: true -wtv-boot-url: wtv-1800:/preregister?relogin=true -wtv-allow-dsc: true -wtv-home-url: wtv-home:/home? -` +wtv-tourist-enabled: true` + headers += "\nwtv-relogin-url: wtv-1800:/preregister?relogin=true"; + if (request_headers.query.guest_login) headers += "&guest_login=true"; + headers += "\nwtv-reconnect-url: wtv-1800:/preregister?reconnect=true"; + if (request_headers.query.guest_login) headers += "&guest_login=true"; + headers += "\nwtv-boot-url: wtv-1800:/preregister?relogin=true"; + if (request_headers.query.guest_login) headers += "&guest_login=true"; + headers += "\nwtv-allow-dsc: true"; + headers += "\nwtv-home-url: wtv-home:/home?"; + if (ssid_sessions[socket.ssid].get('wtv-need-upgrade') != 'true' && !request_headers.query.reconnect) { headers += "\nwtv-settings-url: wtv-setup:/get"; } diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login.js b/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login.js index 9cd4cfd0..ae847962 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-head-waiter/login.js @@ -2,7 +2,14 @@ var challenge_response, challenge_header = ""; var gourl = "wtv-head-waiter:/login-stage-two?"; if (request_headers.query.relogin) gourl += "relogin=true"; -if (request_headers.query.reconnect) gourl += "reconnect=true"; +else if (request_headers.query.reconnect) gourl += "reconnect=true"; + +if (request_headers.query.guest_login) { + if (request_headers.query.relogin || request_headers.query.reconnect) gourl += "&"; + gourl += "guest_login=true"; + if (request_headers.query.skip_splash) gourl += "&skip_splash=true"; +} + var send_to_relogin = true; if (socket.ssid) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js b/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js index 8de6389e..214d201b 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-home/home.js @@ -4,7 +4,7 @@ wtv-expire-all: wtv-home:/splash wtv-expire-all: wtv-flashrom: Content-type: text/html` -if (request_headers.query.url) headers += "\nwtv-visit: " + unescape(request_headers.query.url); +if (request_headers.query.url) headers += "\nwtv-visit: " + request_headers.query.url; if (ssid_sessions[socket.ssid].get('box-does-psuedo-encryption')) { var cryptstatus = "Psuedo-encrypted"; @@ -24,7 +24,10 @@ function go() { location.href=document.access.url.value; } -

Welcome to `+ z_title + `

+Welcome to `+ z_title + `
+`; +if (minisrv_config.config.git_commit) data += "" + "  ".repeat(32) + "git revision " + minisrv_config.config.git_commit + "
"; +data += ` Encryption Status: ${cryptstatus}
Connection Speed: &rate;

@@ -53,7 +56,7 @@ if (ssid_sessions[socket.ssid].hasCap("client-can-do-javascript")) { data += `

  • diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js b/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js index fe5894e9..52aa1306 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-home/splash.js @@ -24,9 +24,7 @@ data = ` Mini service zefie minisrv v${minisrv_config.version}`; -if (getGitRevision()) { - data += " (git " + getGitRevision().substring(0, 8) + ")"; -} +if (minisrv_config.config.git_commit) data += " (git " + minisrv_config.config.git_commit + ")"; data += ` &rate; diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-log/log.js b/zefie_wtvp_minisrv/ServiceVault/wtv-log/log.js index 288c81a6..47fa7299 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-log/log.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-log/log.js @@ -15,7 +15,7 @@ Content-length: 0`; var logdata_outstring = ''; Object.keys(request_headers.query).forEach(function (k) { - logdata_outstring += k + "=" + unescape(request_headers.query[k].toString()) + "\r\n"; + logdata_outstring += k + "=" + request_headers.query[k].toString() + "\r\n"; }); logdata_outstring += "\r\n"; var logdata_outstring_hex = Buffer.from(logdata_outstring, 'utf8').toString('hex'); @@ -36,7 +36,7 @@ Content-length: 0`; var logdata_outstring = ''; Object.keys(request_headers.query).forEach(function (k) { - logdata_outstring += k + "=" + unescape(request_headers.query[k].toString()) + "\r\n"; + logdata_outstring += k + "=" + request_headers.query[k].toString() + "\r\n"; }); var logdata_outstring_hex = Buffer.from(logdata_outstring, 'utf8').toString('hex'); if (minisrv_config.services[service_name].write_logs_to_disk) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-music/get-playlist.js b/zefie_wtvp_minisrv/ServiceVault/wtv-music/get-playlist.js index 0f1575a6..b4dbee53 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-music/get-playlist.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-music/get-playlist.js @@ -31,4 +31,6 @@ wtv-backgroundmusic-add: wtv-music:/MusicCache/headspace/RMF/underground/renegad wtv-backgroundmusic-add: wtv-music:/MusicCache/headspace/RMF/moods/affectionate.rmf wtv-backgroundmusic-add: wtv-music:/MusicCache/headspace/RMF/moods/sociable.rmf`; -*/ \ No newline at end of file +*/ + +data = ''; diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-register/BeMyGuest.js b/zefie_wtvp_minisrv/ServiceVault/wtv-register/BeMyGuest.js new file mode 100644 index 00000000..dffe9ebf --- /dev/null +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-register/BeMyGuest.js @@ -0,0 +1,26 @@ +if (minisrv_config.config.allow_guests) { + headers = `300 Moved +Connection: Close +wtv-noback-all: wtv-register: +wtv-expire-all: wtv-`; + if (socket.ssid) { + if (ssid_sessions[socket.ssid]) { + if (ssid_sessions[socket.ssid].data_store) { + if (ssid_sessions[socket.ssid].data_store.wtvsec_login) { + headers += "\nwtv-ticket: " + ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64; + } + } + } + } + headers += ` +wtv-service: reset +${getServiceString('wtv-1800')} +wtv-relogin-url: wtv-1800:/preregister?guest_login=true&skip_splash=true +wtv-reconnect-url: wtv-1800:/preregister?guest_login=true&reconnect=true +wtv-boot-url: wtv-1800:/preregister?guest_login=true +Location: client:relogin`; +} else { + var errpage = doErrorPage(400, "Guest mode is not enabled on this service."); + headers = errpage[0]; + data = errpage[1]; +} \ No newline at end of file diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateAccountInfo.js b/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateAccountInfo.js index 40a92ecb..82009301 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateAccountInfo.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateAccountInfo.js @@ -10,9 +10,9 @@ if (!request_headers.query.registering) { if (!request_headers.query.registering) errpage = doErrorPage(400); else if (!request_headers.query.subscriber_name) errpage = doErrorPage(400, "Please enter your name. This can be your real name, or your well-known online alias."); else if (!request_headers.query.subscriber_username) errpage = doErrorPage(400, "Please enter a username."); - else if (unescape(request_headers.query.subscriber_username).length < 5) errpage = doErrorPage(400, "Please choose a username with 5 or more characters."); - else if (unescape(request_headers.query.subscriber_username).length > 16) errpage = doErrorPage(400, "Please choose a username with 16 or less characters."); - else if (!WTVRegister.checkUsernameSanity(unescape(request_headers.query.subscriber_username))) errpage = doErrorPage(400, "The username you have chosen contains invalid characters. Please choose a username with only letters, numbers, _ or -. Also, please be sure your username begins with a letter."); + else if (request_headers.query.subscriber_username.length < 5) errpage = doErrorPage(400, "Please choose a username with 5 or more characters."); + else if (request_headers.query.subscriber_username.length > 16) errpage = doErrorPage(400, "Please choose a username with 16 or less characters."); + else if (!WTVRegister.checkUsernameSanity(request_headers.query.subscriber_username)) errpage = doErrorPage(400, "The username you have chosen contains invalid characters. Please choose a username with only letters, numbers, _ or -. Also, please be sure your username begins with a letter."); else if (!WTVRegister.checkUsernameAvailable(request_headers.query.subscriber_username, ssid_sessions)) errpage = doErrorPage(400, "The username you have selected is already in use. Please select another username."); else if (!request_headers.query.subscriber_contact) errpage = doErrorPage(400, "Please enter your contact information."); else if (request_headers.query.subscriber_contact_method == "") errpage = doErrorPage(400, "Please select the type of contact information you provided."); @@ -64,10 +64,10 @@ Review account info
    - - - - + + + + Here is your account information. If you need to
    correct an item, press Back. @@ -76,24 +76,24 @@ correct an item, press Back.   NAME
    - ${unescape(request_headers.query.subscriber_name)} + ${request_headers.query.subscriber_name}

      CONTACT
    - ${unescape(request_headers.query.subscriber_contact)} + ${request_headers.query.subscriber_contact}
      USERNAME
    -${unescape(request_headers.query.subscriber_name)} +${request_headers.query.subscriber_name}

      CONTACT TYPE
    -${unescape(request_headers.query.subscriber_contact_method)} +${request_headers.query.subscriber_contact_method}

     

      diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateReviewAccountInfo.js b/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateReviewAccountInfo.js index 8d7e4d9d..32315cbf 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateReviewAccountInfo.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateReviewAccountInfo.js @@ -11,10 +11,10 @@ if (!request_headers.query.registering || headers = errpage[0]; data = errpage[1]; } else { - ssid_sessions[socket.ssid].setSessionData("subscriber_name", unescape(request_headers.query.subscriber_name)); - ssid_sessions[socket.ssid].setSessionData("subscriber_username", unescape(request_headers.query.subscriber_username)); - ssid_sessions[socket.ssid].setSessionData("subscriber_contact", unescape(request_headers.query.subscriber_contact)); - ssid_sessions[socket.ssid].setSessionData("subscriber_contact_method", unescape(request_headers.query.subscriber_contact_method)); + ssid_sessions[socket.ssid].setSessionData("subscriber_name", request_headers.query.subscriber_name); + ssid_sessions[socket.ssid].setSessionData("subscriber_username", request_headers.query.subscriber_username); + ssid_sessions[socket.ssid].setSessionData("subscriber_contact", request_headers.query.subscriber_contact); + ssid_sessions[socket.ssid].setSessionData("subscriber_contact_method", request_headers.query.subscriber_contact_method); ssid_sessions[socket.ssid].setSessionData("subscriber_userid", '1' + Math.floor(Math.random() * 1000000000000000000)); ssid_sessions[socket.ssid].setSessionData("registered", true); if (!ssid_sessions[socket.ssid].storeSessionData()) { diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateWelcome.js b/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateWelcome.js index d2d058e2..db85437c 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateWelcome.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-register/ValidateWelcome.js @@ -1,13 +1,4 @@ -if (request_headers.query.noreg) { - headers = `302 Moved -Connection: Close -wtv-noback-all: wtv-register: -wtv-expire-all: wtv- -wtv-open-isp-disabled: false -wtv-visit: wtv-head-waiter:/login-stage-two?guest_mode=true -Location: wtv-head-waiter:/login-stage-two?guest_mode=true -Content-type: text/html`; -} else if (!request_headers.query.registering) { +if (!request_headers.query.registering) { var errpage = doErrorPage(400); headers = errpage[0]; data = errpage[1]; diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-register/register.js b/zefie_wtvp_minisrv/ServiceVault/wtv-register/register.js index 7517c23b..aecdd722 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-register/register.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-register/register.js @@ -59,11 +59,13 @@ The next screens will lead you through a quick setup process for using this serv var namerand = Math.floor(Math.random() * 100000); var nickname = (minisrv_config.config.service_name + '_' + namerand) var human_name = nickname; - data += ` +data += ` - - +`; +if (minisrv_config.config.allow_guests) data += ``; + +data += `

    diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-register/splash.js b/zefie_wtvp_minisrv/ServiceVault/wtv-register/splash.js index ed4370f6..e2cca5fe 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-register/splash.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-register/splash.js @@ -24,9 +24,7 @@ data = ` Mini service zefie minisrv v${minisrv_config.version}`; -if (getGitRevision()) { - data += " (git " + getGitRevision().substring(0, 8) + ")"; -} +if (minisrv_config.config.git_commit) data += " (git " + minisrv_config.config.git_commit + ")"; data += ` &rate; diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-setup/get.js b/zefie_wtvp_minisrv/ServiceVault/wtv-setup/get-settings.js similarity index 100% rename from zefie_wtvp_minisrv/ServiceVault/wtv-setup/get.js rename to zefie_wtvp_minisrv/ServiceVault/wtv-setup/get-settings.js diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/access.js b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/access.js index 3281696d..28423f37 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/access.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/access.js @@ -1,6 +1,6 @@ // Allow URL access outside our trusted minisrv -if (request_headers.query.url) var url = unescape(request_headers.query.url); +if (request_headers.query.url) var url = request_headers.query.url; else var url = "client:showalert?message=Please%20provide%20a%20%3Furl%3D%20with%20the%20url%20you%20would%20like%20to%20access.&buttonlabel1=Okay&buttonacction1=client:donothing" headers = `200 OK diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/blastbacklist.js b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/blastbacklist.js index 8fff8d16..6e352b5c 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/blastbacklist.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/blastbacklist.js @@ -6,7 +6,7 @@ Content-type: text/html` var visit_url = null; if (request_headers.Referer) visit_url = request_headers.Referer; -else if (request_headers.query.return_to) visit_url = unescape(request_headers.query.return_to); +else if (request_headers.query.return_to) visit_url = request_headers.query.return_to; else visit_url = "client:goback"; data = ` diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/info.js b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/info.js index 87f93ade..06550461 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/info.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/info.js @@ -50,6 +50,10 @@ Content-Type: text/html` Connected to: Mini Service + + Host/Port: + + ${service_ip}/${minisrv_config.services[service_name].port} Service: @@ -76,11 +80,28 @@ Content-Type: text/html` Client IP number: ${socket.remoteAddress} - - Service IP number: +`; + if (ssid_sessions[socket.ssid].getSessionData("registered")) { + data += ` + Subscriber Name: - ${service_ip} + ${ssid_sessions[socket.ssid].getSessionData("subscriber_name")} + Subscriber Username: + + ${ssid_sessions[socket.ssid].getSessionData("subscriber_username")} + + Subscriber Contact: + + ${ssid_sessions[socket.ssid].getSessionData("subscriber_contact")} (${ssid_sessions[socket.ssid].getSessionData("subscriber_contact_method")})`; + } else { + data += ` + Unregistered Guest: + + Yes`; + } + + data += ` ROM type: @@ -106,7 +127,7 @@ Content-Type: text/html` SysConfig: - 0x${wtv_system_sysconfig_hex.toUpperCase()} + 0x${wtv_system_sysconfig_hex} diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/register.js b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/register.js new file mode 100644 index 00000000..6c7393f7 --- /dev/null +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/register.js @@ -0,0 +1,39 @@ +headers = `200 OK +Content-Type: text/html`; + +if (ssid_sessions[socket.ssid].getSessionData("registered")) { + var redirect = [10, "client:goback?"]; + var message = "Error: Your box is already registered. If you would like to re-register, you must first unregister."; +} else if (request_headers.query.confirm_register) { + headers += ` +wtv-noback-all: wtv- +wtv-expire-all: wtv- +wtv-relogin-url: wtv-1800:/preregister?relogin=true +wtv-reconnect-url: wtv-1800:/preregister?reconnect=true +wtv-boot-url: wtv-1800:/preregister?relogin=true`; + var redirect = [3, "client:relog?"]; + var message = "You will now be be redirected to registration.

    "; + message += `Click here if you are not automatically redirected.`; +} else { + message = `Are you ready to register your box with ${minisrv_config.config.service_name}? +

    + + + +`; +} + +data = ` + + +Comfy Zone! +`; +if (redirect) data += ``; + +data += ` + +

    +${message} + + +`; \ No newline at end of file diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/tricks.js b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/tricks.js index a57d53a4..f994ee0f 100644 --- a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/tricks.js +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/tricks.js @@ -38,7 +38,11 @@ data = `
    diff --git a/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/unregister.js b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/unregister.js new file mode 100644 index 00000000..7f40113f --- /dev/null +++ b/zefie_wtvp_minisrv/ServiceVault/wtv-tricks/unregister.js @@ -0,0 +1,42 @@ +headers = `200 OK +Content-Type: text/html`; + +if (!ssid_sessions[socket.ssid].getSessionData("registered")) { + var redirect = [10, "client:goback?"]; + var message = "Error: Your box is not registered. You are accessing " + minisrv_config.config.service_name + " in Guest Mode. There is nothing to delete!"; +} else if (request_headers.query.confirm_unregister) { + if (ssid_sessions[socket.ssid].unregisterBox()) { + headers += "\nwtv-noback-all: wtv-"; + headers += "\nwtv-expire-all: wtv-"; + var redirect = [3, "client:relog?"]; + var message = "Your account data has been successfully removed. You will now be be redirected to registration.

    "; + message += `Click here if you are not automatically redirected.`; + } else { + var redirect = [10, "client:goback?"]; + var message = "There was an error deleting your account data. Please try again later. If the problem persists, please contact " + minisrv_config.config.service_owner + " to request manual deletion."; + message += "SSID verifcation may be required to perform a manual deletion.< br >
    Returning from whence you came...

    "; + message += `Click here if you are not automatically redirected.`; + } +} else { + message = `Are you sure you wish to unregister your account? Session Data deleted by this tool is unrecoverable, even by ${minisrv_config.config.service_owner}. +Please be absolutely sure this is what you want to do!

    + + + +`; +} + +data = ` + + +Danger Zone! +`; +if (redirect) data += ``; + +data += ` + +

    +${message} + + +`; \ No newline at end of file diff --git a/zefie_wtvp_minisrv/WTVClientSessionData.js b/zefie_wtvp_minisrv/WTVClientSessionData.js index baf9cdde..bf1099dd 100644 --- a/zefie_wtvp_minisrv/WTVClientSessionData.js +++ b/zefie_wtvp_minisrv/WTVClientSessionData.js @@ -77,9 +77,23 @@ class WTVClientSessionData { } else { return false; } - } - this.session_store.cookies[this.countCookies()] = Object.assign({}, cookie_data); - this.storeSessionData(); + } + + var self = this; + var cookie_index = -1; + // see if we have a cookie for this domain/path + Object.keys(this.session_store.cookies).forEach(function (k) { + if (cookie_index >= 0) return; + if (domain == self.session_store.cookies[k].domain && path == self.session_store.cookies[k].path) cookie_index = k; + }); + // otherwise add a new one + if (cookie_index == -1) cookie_index = this.countCookies(); + + this.session_store.cookies[cookie_index] = Object.assign({}, cookie_data); + + // do not write file if user is not registered + if (getSessionData('registered')) this.storeSessionData(); + return true; } @@ -92,8 +106,8 @@ class WTVClientSessionData { if (self.session_store['cookies'][k].domain == domain && self.session_store['cookies'][k].path == path) { - var current_epoch_utc = Math.floor(Date.toUTCString().getTime() / 1000); - var cookie_expires_epoch_utc = Math.floor(Date.Parse(self.session_store['cookies'][k].expires).toUTCString().getTime()); + var current_epoch_utc = Date.parse((new Date()).toUTCString()); + var cookie_expires_epoch_utc = Date.parse(new Date(Date.parse(self.session_store['cookies'][k].expires)).toUTCString()); if (cookie_expires_epoch_utc <= current_epoch_utc) self.deleteCookie(self.session_store['cookies'][k]); else result = self.session_store['cookies'][k]; } @@ -103,11 +117,14 @@ class WTVClientSessionData { getCookieString(domain, path) { var cookie_data = this.getCookie(domain, path); - var outstring = ""; + /* + var outstring = ""; Object.keys(cookie_data).forEach(function (k) { outstring += k + "=" + escape(cookie_data[k]) + "&"; }); return outstring.substring(0, outstring.length - 1); + */ + return cookie_data.cookie; } deleteCookie(domain, path = null) { @@ -197,6 +214,19 @@ class WTVClientSessionData { } + unregisterBox() { + try { + if (this.fs.lstatSync(this.session_storage + this.path.sep + this.ssid + ".json")) { + return this.fs.unlinkSync(this.session_storage + this.path.sep + this.ssid + ".json"); + this.session_store = {}; + } + } catch (e) { + // Don't log error 'file not found', it just means the client isn't registered yet + if (e.code != "ENOENT") console.error(" # Error deleting session data for", this.filterSSID(this.ssid), e); + return false; + } + } + hasCap(cap) { if (this.capabilities) { return this.capabilities[cap] || false; diff --git a/zefie_wtvp_minisrv/WTVFlashrom.js b/zefie_wtvp_minisrv/WTVFlashrom.js index b426bca1..478e5629 100644 --- a/zefie_wtvp_minisrv/WTVFlashrom.js +++ b/zefie_wtvp_minisrv/WTVFlashrom.js @@ -14,9 +14,35 @@ class WTVFlashrom { this.service_name = service_name; this.use_zefie_server = use_zefie_server; this.bf0app_update = bf0app_update; - this.zdebug = debug; + this.zdebug = true; } + + doErrorPage(code, data = null) { + var headers = null; + switch (code) { + case 404: + if (data === null) data = "The service could not find the requested page."; + headers = "404 " + data + "\r\n"; + headers += "Content-Type: text/html\r\n"; + break; + case 400: + if (data === null) data = "HackTV ran into a technical problem."; + headers = "400 " + data + "\r\n"; + headers += "Content-Type: text/html\r\n"; + break; + default: + // what we send when we did not detect a wtv-url. + // e.g. when a pc browser connects + headers = "HTTP/1.1 200 OK\r\n"; + headers += "Content-Type: text/html\r\n"; + break; + } + console.error("doErrorPage Called:", code, data); + return new Array(headers, data); + } + + async doLocalFlashROM(flashrom_file_path, callback, info_only = false) { // use local flashrom files; console.log(info_only); diff --git a/zefie_wtvp_minisrv/WTVLzpf.js b/zefie_wtvp_minisrv/WTVLzpf.js index 1c95cd51..27b09cf8 100644 --- a/zefie_wtvp_minisrv/WTVLzpf.js +++ b/zefie_wtvp_minisrv/WTVLzpf.js @@ -1,3 +1,5 @@ +var EventEmitter = require('events').EventEmitter; + /** * Pure-JS implementation of WebTV's LZPF compression * @@ -5,15 +7,22 @@ * Originally reverse engineered from the box * * By: Eric MacDonald (eMac) +* Modified By: zefie */ class WTVLzpf { - // Note: currentlty doesn't offer streaming support but this is good enough to meet perf demands at the scale we're at. + // Note: currentlty doesn't offer optimal streaming support but this is good enough to meet perf demands at the scale we're at. - current_length = 0 - current_literal = 0 + current_length = 0; + current_literal = 0; + flag = 0xFFFF; + working_data = 0; + match_index = 0; + type_index = 0; + checksum = 0; flag_table = new Uint16Array(0x1000) - compressed_data = [] + ring_buffer = new Uint8Array(0x2000) + encoded_data = []; nomatchEncode = [ [0x0000, 0x10], [0x0001, 0x10], [0x0002, 0x10], @@ -274,8 +283,25 @@ class WTVLzpf { clear() { this.current_length = 0; this.current_literal = 0; + this.flag = 0xFFFF; + this.working_data = 0; + this.match_index = 0; + this.type_index = 0; + this.checksum = 0; + this.ring_buffer.fill(0x00, 0, 0x2000) this.flag_table.fill(0xFFFF, 0, 0x1000); - this.compressed_data = []; + this.encoded_data = []; + } + + /** + * Appends a byte to the end of the compressed byte array. Re-allocates as needed + * + * @param byte {Number} char code of the byte to be added. + * + * @returns {undefined} + */ + AddByte(byte) { + this.encoded_data.push(byte); } /** @@ -293,8 +319,7 @@ class WTVLzpf { this.current_length += code_length; while (this.current_length > 7) { - //console.log("add", this.current_literal >>> 0, code >>> 0, byte, type) - this.compressed_data.push((this.current_literal >>> 0x18) & 0xFF); + this.AddByte((this.current_literal >>> 0x18) & 0xFF); this.current_length -= 8; this.current_literal = (this.current_literal << 8) & 0xFFFFFFFF; @@ -302,74 +327,73 @@ class WTVLzpf { } /** - * Compress data using WebTV's Lzpf compression algorithm and adds the footer to the end. + * Starts a compression stream * - * @param uncompressed_data {String} data to compress - * - * @returns {Buffer} Lzpf compression data + * @returns {undefined} Lzpf compression data */ - Compress(uncompressed_data) { + Begin() { this.clear(); + } - if (uncompressed_data.words) { - uncompressed_data = new Buffer.from(this.wordArrayToUint8Array(uncompressed_data)); - } else { - uncompressed_data = new Buffer.from(uncompressed_data); - } - - var uncompressed_len = uncompressed_data.length; + /** + * Encode a block of data. Used for streamed chunks. + * + * @param unencoded_data {Buffer} data to encode + * @param compress_data {Boolean} compress data + * + * @returns {Buffer} Lzpf encoded data + */ + EncodeBlock(unencoded_data, compress_data) { + this.encoded_data = []; + var uncompressed_len = unencoded_data.byteLength; var i = 0; - var sum = 0; - var working_data = 0; - var flag = 0xFFFF; var flags_index = 0; - var match_index = 0; - var type_index = 0; - while(i < uncompressed_len) { + while (i < uncompressed_len) { var code_length = -1; var code = -1; - var byte = uncompressed_data.readUInt8(i); + var byte = unencoded_data.readUInt8(i); + this.ring_buffer[i & 0x1FFF] = byte; - if(match_index > 0) { - if (byte != uncompressed_data.readUInt8(flag) || match_index > 0x0127) { - code_length = this.matchEncode[match_index][1]; - code = this.matchEncode[match_index][0]; - match_index = 0; - type_index = 3; + if (this.match_index > 0) { + if (byte != this.ring_buffer[this.flag] || this.match_index > 0x0127) { + code_length = this.matchEncode[this.match_index][1]; + code = this.matchEncode[this.match_index][0]; + this.match_index = 0; + this.type_index = 3; } else { - match_index++; - flag++; - sum = (sum + byte) & 0xFFFF; - working_data = ((working_data * 0x0100) + byte) & 0xFFFFFFFF; + this.match_index = (this.match_index + 1) & 0x1FFF; + this.flag = (this.flag + 1) & 0x1FFF; + this.checksum = (this.checksum + byte) & 0xFFFF; + this.working_data = ((this.working_data * 0x0100) + byte) & 0xFFFFFFFF; i++; } } else { - flag = 0xFFFF; + this.flag = 0xFFFF; if (i >= 3) { - flags_index = (working_data >>> 0x0B ^ working_data) & 0x0FFF; - flag = this.flag_table[flags_index]; - this.flag_table[flags_index] = i; + flags_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF; + this.flag = this.flag_table[flags_index]; + this.flag_table[flags_index] = i & 0x1FFF; } else { - type_index++; + this.type_index++; } - if (flag == 0xFFFF) { + if (this.flag == 0xFFFF) { code_length = this.nomatchEncode[byte][1]; code = this.nomatchEncode[byte][0] << 0x10; - } else if (byte == uncompressed_data.readUInt8(flag)) { - match_index = 1; - flag++; - type_index = 4; + } else if (byte == this.ring_buffer[this.flag] && compress_data) { + this.match_index = 1; + this.flag = (this.flag + 1) & 0x1FFF; + this.type_index = 4; } else { code_length = this.nomatchEncode[byte][1] + 1; code = this.nomatchEncode[byte][0] << 0x0F; } - sum = (sum + byte) & 0xFFFF; - working_data = ((working_data * 0x0100) + byte) & 0xFFFFFFFF; + this.checksum = (this.checksum + byte) & 0xFFFF; + this.working_data = ((this.working_data * 0x0100) + byte) & 0xFFFFFFFF; i++; } @@ -378,18 +402,32 @@ class WTVLzpf { } } - // Finish up. This would normally be in an Lzpf_Finish method. - if(type_index == 2) { + return Buffer.from(this.encoded_data); + } + + /** + * Ends a compression stream. + * + * @param type_index {Number} the end type used to finalize + * + * @returns {Buffer} Lzpf compression data + */ + Finish() { + var code_length = -1 + var code = -1 + + if (this.type_index == 2) { this.EncodeLiteral(0x10, 0x00990000); - } else if(type_index >= 3) { - if(type_index == 4) { - code_length = this.matchEncode[match_index][1]; - code = this.matchEncode[match_index][0]; + } else if (this.type_index >= 3) { + if (this.type_index == 4) { + code_length = this.matchEncode[this.match_index][1]; + code = this.matchEncode[this.match_index][0]; this.EncodeLiteral(code_length, code); } - flags_index = (working_data >>> 0x0B ^ working_data) & 0x0FFF; - if (flags_index == 0xFFFF) { + var flags_index = (this.working_data >>> 0x0B ^ this.working_data) & 0x0FFF; + var flag = this.flag_table[flags_index]; + if (flag == 0xFFFF) { this.EncodeLiteral(0x10, 0x00990000); } else { this.EncodeLiteral(0x11, 0x004c8000); @@ -399,14 +437,48 @@ class WTVLzpf { // Below is just metadata. The compressed block is complete. // Encode checksum - this.EncodeLiteral(0x08, (sum << 0x10) & 0xFFFFFFFF); - this.EncodeLiteral(0x08, (sum << 0x18) & 0xFFFFFFFF); + this.EncodeLiteral(0x08, (this.checksum << 0x10) & 0xFFFFFFFF); + this.EncodeLiteral(0x08, (this.checksum << 0x18) & 0xFFFFFFFF); // End - this.compressed_data.push(this.current_literal >>> 0x18); - this.compressed_data.push(0x20); + this.AddByte((this.current_literal >>> 0x18) & 0xFF); + this.AddByte(0x20); + } - return new Buffer.from(this.compressed_data); + /** + * Converts the data to a Javascript Buffer object + * + * @param data {String|Buffer|CryptoJS.lib.WordArray} Data to convert + * + * @returns {Buffer} Javascript Buffer object + */ + ConvertToBuffer(data) { + if (data.words) { + var WTVSec = require("./WTVSec.js"); + wtvsec = new WTVSec(1); + data = wtvsec.wordArrayToBuffer(data); + WTVSec, wtvsec = null; + } else if (!data.byteLength) { + // otherwise if its not already a Buffer, convert it to one + data = new Buffer.from(data); + } + return data; + } + + /** + * Compress data using WebTV's Lzpf compression algorithm and adds the footer to the end. + * + * @param uncompressed_data {String|Buffer|CryptoJS.lib.WordArray} data to compress + * + * @returns {Buffer} Lzpf compression data + */ + Compress(uncompressed_data) { + uncompressed_data = this.ConvertToBuffer(uncompressed_data); + this.Begin(); + this.EncodeBlock(uncompressed_data, true); + this.Finish(); + + return Buffer.from(this.encoded_data); } } diff --git a/zefie_wtvp_minisrv/WTVRegister.js b/zefie_wtvp_minisrv/WTVRegister.js index 37f19d66..33a0afee 100644 --- a/zefie_wtvp_minisrv/WTVRegister.js +++ b/zefie_wtvp_minisrv/WTVRegister.js @@ -29,6 +29,7 @@ class WTVRegister { checkUsernameAvailable(username, ssid_sessions) { var username_match = false; this.fs.readdirSync(this.session_store_dir).forEach(file => { + if (!file.match(/.*\.json/ig)) return; if (username_match) return; try { var temp_session_data_file = this.fs.readFileSync(this.session_store_dir + this.path.sep + file, 'Utf8'); diff --git a/zefie_wtvp_minisrv/WTVSec.js b/zefie_wtvp_minisrv/WTVSec.js index 99206228..cbdfe98a 100644 --- a/zefie_wtvp_minisrv/WTVSec.js +++ b/zefie_wtvp_minisrv/WTVSec.js @@ -2,13 +2,14 @@ const CryptoJS = require('crypto-js'); const endianness = require('endianness'); var crypto = require('crypto'); -/***********************************\ -|* Special Thanks to: *| -|* eMac (Eric MacDonald) *| -|* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *| -|* For the encryption/decryption *| -|* information and process *| -\***********************************/ +/** + * Javascript implementation of WTVP Security + * + * Special Thanks to eMac (Eric MacDonald) + * For the encryption/decryption information and process + * + * By: zefie + */ class WTVSec { // Initial Shared Key, in Base64 Format @@ -32,6 +33,15 @@ class WTVSec { RC4Session = new Array(); zdebug = false; + + /** + * + * Initialize the WTVSec class. + * + * @param {Number} wtv_incarnation Sets the wtv-incarnation for this instance + * @param {Boolean} zdebug Enable debugging + * + */ constructor(wtv_incarnation = 1, zdebug = false) { this.zdebug = zdebug; this.initial_shared_key = CryptoJS.enc.Base64.parse(this.initial_shared_key_b64); @@ -44,6 +54,11 @@ class WTVSec { } } + /** + * Set the wtv-incarnation for this instance + * + * @param {Number} wtv_incarnation + */ set_incarnation(wtv_incarnation) { if (this.incarnation != wtv_incarnation) { this.incarnation = wtv_incarnation; @@ -51,14 +66,26 @@ class WTVSec { } } + /** + * Increments the wtv-incaration for this instance by 1 + */ increment_incarnation() { this.set_incarnation(parseInt(this.incarnation) + 1); } + /** + * Clones a WordArray to allow modification without referencing its original + * @param {CryptoJS.lib.WordArray} wa + * + * @returns {CryptoJS.lib.WordArray} + */ DuplicateWordArray(wa) { - return CryptoJS.lib.WordArray.create(this.wordArrayToUint8Array(wa).buffer); + return CryptoJS.lib.WordArray.create(this.wordArrayToBuffer(wa)); } + /** + * Prepares the wtv-ticket for this instance + */ PrepareTicket() { // store last challenge response in ticket var ticket_data = this.challenge_raw; @@ -77,6 +104,11 @@ class WTVSec { return this.ticket_b64; } + /** + * Decodes a wtv-ticket to set up this instance + * + * @param {Base64} ticket_b64 + */ DecodeTicket(ticket_b64) { var ticket_hex = CryptoJS.enc.Base64.parse(ticket_b64).toString(CryptoJS.enc.Hex); var challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(0, 16)); @@ -95,6 +127,13 @@ class WTVSec { console.log(" * Decoded session from wtv-ticket"); } + /** + * Processes a wtv-challenge to get the expected response + * @param {Base64} wtv_challenge + * @param {any} key + * + * @returns {CryptoJS.lib.WordArray} wtv-challenge-response (or blank if failed) + */ ProcessChallenge(wtv_challenge, key = this.current_shared_key) { var challenge_raw = CryptoJS.enc.Base64.parse(wtv_challenge); @@ -139,7 +178,6 @@ class WTVSec { var challenge_response = CryptoJS.enc.Hex.parse(challenge_raw_hex.substr(0, (8 * 2))).concat(echo_encrypted.ciphertext); return challenge_response; } else { - throw ("Couldn't solve challenge"); return ""; } } else { @@ -147,6 +185,11 @@ class WTVSec { } } + /** + * Generates a wtv-challenge for this instance + * + * @returns {Base64} wtv-challenge + */ IssueChallenge() { /* * bytes 0-8: Random id? Just echoed in the response @@ -188,41 +231,21 @@ class WTVSec { return challenge_b64; } - wordToByteArray(word, length) { - var ba = [], - i, - xFF = 0xFF; - if (length > 0) - ba.push(word >>> 24); - if (length > 1) - ba.push((word >>> 16) & xFF); - if (length > 2) - ba.push((word >>> 8) & xFF); - if (length > 3) - ba.push(word & xFF); - - return ba; + /** + * convert a CryptoJS.lib.WordArray to a Javascript Buffer + * @param {CryptoJS.lib.WordArray} wordArray + * + * #returns {Buffer} JS Buffer object + */ + wordArrayToBuffer(wordArray) { + return new Buffer.from(wordArray.toString(CryptoJS.enc.Hex), 'hex'); } - wordArrayToUint8Array(wordArray, length = 0) { - if (wordArray.hasOwnProperty("sigBytes") && wordArray.hasOwnProperty("words")) { - length = wordArray.sigBytes; - wordArray = wordArray.words; - } - - var result = [], - bytes, - i = 0; - while (length > 0) { - bytes = this.wordToByteArray(wordArray[i], Math.min(4, length)); - length -= bytes.length; - result.push(bytes); - i++; - } - return new Uint8Array([].concat.apply([], result)); - } - - + /** + * Starts an encryption session + * @param {Number} rc4session Session Type (0 = enc k1, 1 = dec k1, 3 = enc k2, 4 = dec k2, default: all) + * + */ SecureOn(rc4session = null) { if (this.zdebug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation); @@ -230,33 +253,37 @@ class WTVSec { endianness(buf, 4); this.hRC4_Key1 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key1).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key1)))); this.hRC4_Key2 = CryptoJS.MD5(this.DuplicateWordArray(this.session_key2).concat(CryptoJS.lib.WordArray.create(buf).concat(this.DuplicateWordArray(this.session_key2)))); + var key1 = this.wordArrayToBuffer(this.hRC4_Key1); + var key2 = this.wordArrayToBuffer(this.hRC4_Key2); switch (rc4session) { case 0: - this.RC4Session[0] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)),''); + this.RC4Session[0] = crypto.createCipheriv('rc4', key1,''); break; case 1: - this.RC4Session[1] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)),''); + this.RC4Session[1] = crypto.createDecipheriv('rc4', key1,''); break; case 2: - this.RC4Session[2] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)),''); + this.RC4Session[2] = crypto.createCipheriv('rc4', key2,''); break; case 3: - this.RC4Session[3] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)),''); + this.RC4Session[3] = crypto.createDecipheriv('rc4', key2,''); break; default: - this.RC4Session[0] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)), ''); - this.RC4Session[1] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)), ''); - this.RC4Session[2] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)), ''); - this.RC4Session[3] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)), ''); + this.RC4Session[0] = crypto.createCipheriv('rc4', key1, ''); + this.RC4Session[1] = crypto.createDecipheriv('rc4', key1, ''); + this.RC4Session[2] = crypto.createCipheriv('rc4', key2, ''); + this.RC4Session[3] = crypto.createDecipheriv('rc4', key2, ''); break; } } - - NewRC4Session(num) { - this.SecureOn(num); - } - + /** + * RC4 Encrypt data + * @param {Number} keynum Which key to use (0 = k1, 1 = k2) + * @param {CryptoJS.lib.WordArray|ArrayBuffer|Buffer} data Data to encrypt + * + * @returns {ArrayBuffer} Encrypted data + */ Encrypt(keynum, data) { var session_id; switch (keynum) { @@ -271,16 +298,23 @@ class WTVSec { break; } if (!this.RC4Session[session_id]) { - this.NewRC4Session(session_id); + this.SecureOn(session_id); } if (data.words) { - data = new Buffer.from(this.wordArrayToUint8Array(data)); + data = this.wordArrayToBuffer(data); } else if (data.constructor === ArrayBuffer) { data = new Buffer.from(data); } return this.RC4Session[session_id].update(data); } + /** + * RC4 Decrypt data + * @param {Number} keynum Which key to use (0 = k1, 1 = k2) + * @param {CryptoJS.lib.WordArray|ArrayBuffer|Buffer} data Data to decrypt + * + * @returns {ArrayBuffer} Decrypted data + */ Decrypt(keynum, data) { var session_id; switch (keynum) { @@ -295,10 +329,10 @@ class WTVSec { break; } if (!this.RC4Session[session_id]) { - this.NewRC4Session(session_id); + this.SecureOn(session_id); } if (data.words) { - data = new Buffer.from(this.wordArrayToUint8Array(data)); + data = this.wordArrayToBuffer(data); } else if (data.constructor === ArrayBuffer) { data = new Buffer.from(data); } diff --git a/zefie_wtvp_minisrv/app.js b/zefie_wtvp_minisrv/app.js index 5fda73c6..95752d97 100644 --- a/zefie_wtvp_minisrv/app.js +++ b/zefie_wtvp_minisrv/app.js @@ -99,12 +99,68 @@ function doErrorPage(code, data = null) { function getConType(path) { - // custom contype for flashrom - if (path.indexOf("wtv-flashrom") && (getFileExt(path).toLowerCase() == "rom" || getFileExt(path).toLowerCase() == "brom")) { - return "binary/x-wtv-flashblock"; - } else if (getFileExt(path).toLowerCase() == "rmf") { - return "audio/x-rmf"; + var file_ext = getFileExt(path).toLowerCase(); + // process WebTV overrides, fall back to generic mime lookup + switch (file_ext) { + case "aif": + return "audio/x-aif"; + case "aifc": + return "audio/x-aifc"; + case "aiff": + return "audio/x-aiff"; + case "ani": + return "x-wtv-animation"; + case "brom": + return "binary/x-wtv-bootrom"; + case "cdf": + return "application/netcdf"; + case "dat": + return "binary/cache-data"; + case "dl": + return "wtv/download-list"; + case "gsm": + return "audio/x-gsm"; + case "gz": + return "application/gzip"; + case "ini": + return "wtv/jack-configuration"; + case "mips-code": + return "code/x-wtv-code-mips"; + case "o": + return "binary/x-wtv-approm"; + case "ram": + return "audio/x-pn-realaudio"; + case "rom": + return "binary/x-wtv-flashblock"; + case "rsp": + return "wtv/jack-response"; + case "swa": + case "swf": + return "application/x-shockwave-flash"; + case "srf": + case "spl": + return "wtv/jack-data"; + case "ttf": + return "wtv/jack-fonts"; + case "tvch": + return "wtv/tv-channels"; + case "tvl": + return "wtv/tv-listings"; + case "tvsl": + return "wtv/tv-smartlinks"; + case "wad": + return "binary/doom-data"; + case "mp2": + case "hsb": + case "rmf": + case "s3m": + case "mod": + case "xm": + return "application/Music"; } + + // if we reach here, its not a WebTV specific override + // or we are not yet aware of said override return mime.lookup(path); } @@ -260,9 +316,10 @@ async function processURL(socket, request_headers) { if (qraw.length > 0) { qraw = qraw.split("&"); for (let i = 0; i < qraw.length; i++) { - var k = qraw[i].split("=")[0]; - if (k) { - request_headers.query[k] = qraw[i].split("=")[1]; + var qraw_split = qraw[i].split("="); + if (qraw_split.length == 2) { + var k = qraw_split[0]; + request_headers.query[k] = unescape(qraw[i].split("=")[1].replace(/\+/g,"%20")); } } } @@ -271,23 +328,25 @@ async function processURL(socket, request_headers) { } if (request_headers.post_data) { - if (headersAreStandard(request_headers.post_data.toString(CryptoJS.enc.Utf8))) { - if (request_headers.post_data.toString(CryptoJS.enc.Utf8).indexOf('=')) { - if (request_headers.post_data.toString(CryptoJS.enc.Utf8).indexOf('&')) { - var qraw = request_headers.post_data.toString(CryptoJS.enc.Utf8).split('&'); + var post_data_string = request_headers.post_data.toString(CryptoJS.enc.Utf8).replace("\0", ""); + if (isUnencryptedString(post_data_string)) { + if (post_data_string.indexOf('=')) { + if (post_data_string.indexOf('&')) { + var qraw = post_data_string.split('&'); if (qraw.length > 0) { for (let i = 0; i < qraw.length; i++) { - var k = qraw[i].split("=")[0]; - if (k) { - request_headers.query[k] = qraw[i].split("=")[1]; + var qraw_split = qraw[i].split("="); + if (qraw_split.length == 2) { + var k = qraw_split[0]; + request_headers.query[k] = unescape(qraw[i].split("=")[1].replace(/\+/g, "%20")); } } } } else { - var qraw = request_headers.post_data.toString(CryptoJS.enc.Utf8); - var k = qraw[i].split("=")[0]; - if (k) { - request_headers.query[k] = qraw[i].split("=")[1]; + var qraw_split = post_data_string.split("="); + if (qraw_split.length == 2) { + var k = qraw_split[0]; + request_headers.query[k] = unescape(qraw_split[1].replace(/\+/g, "%20")); } } } @@ -495,10 +554,10 @@ function headerStringToObj(headers, response = false) { return headers_obj; } -async function sendToClient(socket, headers_obj, data, compress_data = false) { - var headers = ""; - var wni_style_content_length = false; +async function sendToClient(socket, headers_obj, data) { var compress_data = false; + var headers = ""; + var content_length = 0; if (typeof (data) === 'undefined') data = ''; if (typeof (headers_obj) === 'string') { // string to header object @@ -517,72 +576,44 @@ async function sendToClient(socket, headers_obj, data, compress_data = false) { headers_obj = moveObjectElement('Connection', 'http_response', headers_obj); } - if (headers_obj['minisrv-already-compressed'] && wni_style_content_length) { - content_length = headers_obj["Content-length"]; - } else { - var content_length = 0; - if (typeof data.length !== 'undefined') { - content_length = data.length; - } else if (typeof data.byteLength !== 'undefined') { - content_length = data.byteLength; - } + var clen = 0; + if (typeof data.length !== 'undefined') { + clen = data.length; + } else if (typeof data.byteLength !== 'undefined') { + clen = data.byteLength; } - - // fix captialization of Content-Type header. May be unnecessary. + // fix captialization if (headers_obj["Content-type"]) { headers_obj["Content-Type"] = headers_obj["Content-type"]; delete headers_obj["Content-type"]; } - /* - // check if client reports it supports compressed data + + // if box can do compression, see if its worth enabling if (ssid_sessions[socket.ssid].capabilities) { - if (ssid_sessions[socket.ssid].capabilities['client-can-receive-compressed-data']) { - // if the client reports it supports compression, check the Content-Type - // of the file we are sending to see if its worth compressing + if (ssid_sessions[socket.ssid].capabilities['client-can-receive-compressed-data'] && minisrv_config.config.enable_lzpf_compression) { compress_data = shouldWeCompress(headers_obj["Content-Type"]); } } - */ - // compress if needed, and if not already compressed - if (compress_data && content_length > 0 && !headers_obj['minisrv-already-compressed']) { - if (zdebug) console.log(" # Uncompressed data length:", content_length); + // compress if needed + if (compress_data && clen > 0) { + content_length = clen; + headers_obj["wtv-lzpf"] = 0; + var wtvcomp = new WTVLzpf(); - // we expect the compressed data to be smaller or at most equal to the source size - // so we set our initial buffer size to the source size - var compressed_data = new Buffer.alloc(content_length); - wtvcomp.on('data', (data, length, offset, complete) => { - // put data received into buffer - data.copy(compressed_data, offset, 0, length); - if (complete !== false) { - if (zdebug) console.log(" # Compressed data length:", complete); - // now that we have all of the compressed data, copy it to a new buffer - // of the correct length, and clean up the original buffer. - data = new Buffer.alloc(complete); - compressed_data.copy(data, 0, 0, compressed_data.byteLength); - compressed_data, wtvcomp = null; - // internal header to tell ourselves to not compress again - headers_obj['minisrv-already-compressed'] = true; - if (wni_style_content_length) headers_obj["Content-length"] = content_length; - sendToClient(socket, headers_obj, data); - } - }); - wtvcomp.Compress(data); - return; + data = wtvcomp.Compress(data); + + wtvcomp = null; // Makes the garbage gods happy so it cleans up our mess } - // clean up internal header for compression - if (headers_obj['minisrv-already-compressed']) delete headers_obj['minisrv-already-compressed']; - - // encrypt if needed if (socket_sessions[socket.id].secure == true) { headers_obj["wtv-encrypted"] = 'true'; headers_obj = moveObjectElement('wtv-encrypted', 'Connection', headers_obj); - if (content_length > 0 && socket_sessions[socket.id].wtvsec) { + if (clen > 0 && socket_sessions[socket.id].wtvsec) { if (!zquiet) console.log(" * Encrypting response to client ...") var enc_data = socket_sessions[socket.id].wtvsec.Encrypt(1, data); data = enc_data; @@ -594,8 +625,14 @@ async function sendToClient(socket, headers_obj, data, compress_data = false) { if (headers_obj["Content-Length"]) delete headers_obj["Content-Length"]; if (headers_obj["Content-length"]) delete headers_obj["Content-length"]; - // On the WNI server this is the length before compression but we're using the length after compression. - // It matches the HTTP spec anyway so leaving. + if (content_length == 0) { + if (typeof data.length !== 'undefined') { + content_length = data.length; + } else if (typeof data.byteLength !== 'undefined') { + content_length = data.byteLength; + } + } + headers_obj["Content-length"] = content_length; if (ssid_sessions[socket.ssid]) { @@ -610,15 +647,13 @@ async function sendToClient(socket, headers_obj, data, compress_data = false) { } } - // internal header to determine EOL type. bf0app upgrader does not like \r, while the rest of the WebTV world does. - // set header 'minisrv-use-carriage-return' to true to disable \r for this specific transfer. var end_of_line = "\n"; - if (!headers_obj['minisrv-use-carriage-return'] || headers_obj['minisrv-use-carriage-return'] != "false") end_of_line = "\r\n"; + if (headers_obj['minisrv-use-carriage-return'] == "true") end_of_line = "\r\n"; if (headers_obj['minisrv-use-carriage-return']) delete headers_obj['minisrv-use-carriage-return']; - if (end_of_line == "\n" && zdebug) console.log(" * Script requested to send headers without carriage return (bf0app hack)"); + if (end_of_line == "\r\n" && zdebug) console.log(" * Script requested to send headers with carriage return (out of WTVP Spec)"); - // convert header object back to string + // header object to string if (zshowheaders) console.log(" * Outgoing headers on socket ID", socket.id, (await filterSSID(headers_obj))); Object.keys(headers_obj).forEach(function (k) { if (k == "http_response") { @@ -643,7 +678,7 @@ async function sendToClient(socket, headers_obj, data, compress_data = false) { if (zquiet) var verbosity_mod = (headers_obj["wtv-encrypted"] == 'true') ? " encrypted response" : ""; if (socket_sessions[socket.id].secure_headers == true) { // encrypt headers - if (zquiet)verbosity_mod += " with encrypted headers"; + if (zquiet) verbosity_mod += " with encrypted headers"; var enc_headers = socket_sessions[socket.id].wtvsec.Encrypt(1, headers + end_of_line); socket.write(new Uint8Array(concatArrayBuffer(enc_headers, data))); } else { @@ -660,7 +695,7 @@ async function sendToClient(socket, headers_obj, data, compress_data = false) { if (socket_sessions[socket.id].post_data) delete socket_sessions[socket.id].post_data; if (socket_sessions[socket.id].post_data_length) delete socket_sessions[socket.id].post_data_length; if (socket_sessions[socket.id].post_data_percents_shown) delete socket_sessions[socket.id].post_data_percents_shown; - + if (socket_sessions[socket.id].close_me) socket.end(); if (headers_obj["Connection"]) { if (headers_obj["Connection"].toLowerCase() == "close" || wtv_connection_close == "true") { @@ -712,7 +747,7 @@ function moveObjectElement(currentKey, afterKey, obj) { if (next !== -1) return result; else return obj; } -function headersAreStandard(string, verbose = false) { +function isUnencryptedString(string, verbose = false) { // a generic "isAscii" check is not sufficient, as the test will see the binary // compressed / encrypted data as ASCII. This function checks for characters expected // in unencrypted headers, and returns true only if every character in the string matches @@ -745,7 +780,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } else { data = data.split("\n\n")[0]; } - if (headersAreStandard(data)) { + if (isUnencryptedString(data)) { if (headers.length != 0) { var new_header_obj = headerStringToObj(data); Object.keys(new_header_obj).forEach(function (k, v) { @@ -758,7 +793,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } else if (!skipSecure) { // if its a POST request, assume its a binary blob and not encrypted (dangerous) if (!encryptedRequest) { - // its not a POST and it failed the headersAreStandard test, so we think this is an encrypted blob + // its not a POST and it failed the isUnencryptedString test, so we think this is an encrypted blob if (socket_sessions[socket.id].secure != true) { // first time so reroll sessions if (zdebug) console.log(" # [ UNEXPECTED BINARY BLOCK ] First sign of encryption, re-creating RC4 sessions for socket id", socket.id); @@ -917,6 +952,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq if (zdebug) console.log(" # New ticket from client"); ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 = headers["wtv-ticket"]; ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64); + ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(headers["wtv-incarnation"]); } } } @@ -951,7 +987,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq } var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2)); if (enc_data.sigBytes > 0) { - if (headersAreStandard(enc_data.toString(CryptoJS.enc.Latin1), (!skipSecure && !encryptedRequest))) { + if (isUnencryptedString(enc_data.toString(CryptoJS.enc.Latin1), (!skipSecure && !encryptedRequest))) { // some builds (like our targeted 3833), send SECURE ON but then unencrypted headers if (zdebug) console.log(" # Psuedo-encrypted Request (SECURE ON)", "on", socket.id); // don't actually encrypt output @@ -1028,33 +1064,34 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq var post_string = "POST"; if (socket_sessions[socket.id].secure == true) { post_string = "Encrypted " + post_string; - } else { - // if the request is not encrypted, the client may have just sent the data with the primary headers, so lets look for that. - if (data_hex.indexOf("0d0a0d0a") != -1) socket_sessions[socket.id].post_data = data_hex.substring(data_hex.indexOf("0d0a0d0a") + 8); - if (data_hex.indexOf("0a0a") != -1) socket_sessions[socket.id].post_data = data_hex.substring(data_hex.indexOf("0a0a") + 4); - } - if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) { - // got all expected data - if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data; - console.log(" * Incoming", post_string, "request on", socket.id, "from", filterSSID(socket.ssid), "to", headers['request_url'], "(got all expected", socket_sessions[socket.id].post_data_length, "bytes of data from client already)"); - headers.post_data = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data); - if (socket_sessions[socket.id].headers) delete socket_sessions[socket.id].headers; - processURL(socket, headers); - } else { - // expecting more data (see below) - socket_sessions[socket.id].expecting_post_data = true; - console.log(" * Incoming", post_string, "request on", socket.id, "from", filterSSID(socket.ssid), "to", headers['request_url'], "(expecting", socket_sessions[socket.id].post_data_length, "bytes of data from client...)"); - } - if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) { - // got too much data ? ... should not ever reach this code - var errpage = doErrorPage(400, "Received too much data in POST request
    Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length); - headers = errpage[0]; - data = errpage[1]; - sendToClient(socket, headers, data); - return; } + + // the client may have just sent the data with the primary headers, so lets look for that. + if (data_hex.indexOf("0d0a0d0a") != -1) socket_sessions[socket.id].post_data = data_hex.substring(data_hex.indexOf("0d0a0d0a") + 8); + if (data_hex.indexOf("0a0a") != -1) socket_sessions[socket.id].post_data = data_hex.substring(data_hex.indexOf("0a0a") + 4); + + } + if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) { + // got all expected data + if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data; + console.log(" * Incoming", post_string, "request on", socket.id, "from", filterSSID(socket.ssid), "to", headers['request_url'], "(got all expected", socket_sessions[socket.id].post_data_length, "bytes of data from client already)"); + headers.post_data = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data); + if (socket_sessions[socket.id].headers) delete socket_sessions[socket.id].headers; + processURL(socket, headers); + } else { + // expecting more data (see below) + socket_sessions[socket.id].expecting_post_data = true; + console.log(" * Incoming", post_string, "request on", socket.id, "from", filterSSID(socket.ssid), "to", headers['request_url'], "(expecting", socket_sessions[socket.id].post_data_length, "bytes of data from client...)"); + } + if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) { + // got too much data ? ... should not ever reach this code + var errpage = doErrorPage(400, "Received too much data in POST request
    Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length); + headers = errpage[0]; + data = errpage[1]; + sendToClient(socket, headers, data); return; } + return; } else { delete socket_sessions[socket.id].headers; delete socket_sessions[socket.id].post_data; @@ -1150,7 +1187,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq return; } var str_test = enc_data.toString(CryptoJS.enc.Latin1); - if (headersAreStandard(str_test)) { + if (isUnencryptedString(str_test)) { var dec_data = enc_data; } else { var dec_data = CryptoJS.lib.WordArray.create(socket_sessions[socket.id].wtvsec.Decrypt(0, enc_data)); @@ -1303,7 +1340,7 @@ function getGitRevision() { if (rev.indexOf(':') === -1) { return rev; } else { - return fs.readFileSync(__dirname + path.sep + ".." + path.sep + ".git" + path.sep + rev.substring(5)).toString().trim(); + return fs.readFileSync(__dirname + path.sep + ".." + path.sep + ".git" + path.sep + rev.substring(5)).toString().trim().substring(0,8) + "-" + rev.split('/').pop(); } } catch (e) { return null; @@ -1312,12 +1349,9 @@ function getGitRevision() { // SERVER START var git_commit = getGitRevision() -if (git_commit) { - var z_title = "zefie's wtv minisrv v" + require('./package.json').version + " (git " + git_commit.substring(0,8) + ")"; -} else { - var z_title = "zefie's wtv minisrv v" + require('./package.json').version; -} -console.log("**** Welcome to " + z_title + " ****"); +var z_title = "zefie's wtv minisrv v" + require('./package.json').version; +if (git_commit) console.log("**** Welcome to " + z_title + " (git " + git_commit + ") ****"); +else console.log("**** Welcome to " + z_title + " ****"); console.log(" *** Reading global configuration..."); try { var minisrv_config = JSON.parse(fs.readFileSync(__dirname + path.sep + "config.json")); diff --git a/zefie_wtvp_minisrv/config.json b/zefie_wtvp_minisrv/config.json index b76f1529..91767a6f 100644 --- a/zefie_wtvp_minisrv/config.json +++ b/zefie_wtvp_minisrv/config.json @@ -13,7 +13,9 @@ "hide_ssid_in_logs": true, "post_percentages": [ 0, 25, 50, 100 ], "verbosity": 2, - "error_log_file": "errors.log" + "error_log_file": "errors.log", + "allow_guests": true, + "enable_lzpf_compression": true }, "services": { "wtv-head-waiter": { @@ -48,6 +50,7 @@ "wtv-flashrom": { "port": 1618, "flags": "0x00000040", + "debug": false, "use_zefie_server": true, "bf0app_default_rom": "content/artemis-webtv-000/build7181/daily-nondebug/bf0app-part000.rom" }, diff --git a/zefie_wtvp_minisrv/package.json b/zefie_wtvp_minisrv/package.json index 43ffdd8b..6ae035e2 100644 --- a/zefie_wtvp_minisrv/package.json +++ b/zefie_wtvp_minisrv/package.json @@ -1,6 +1,6 @@ { "name": "zefie_wtvp_minisrv", - "version": "0.9.12", + "version": "0.9.13", "description": "WebTV Service (WTVP) Emulation Server", "main": "app.js", "homepage": "https://github.com/zefie/zefie_wtvp_minisrv", diff --git a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj index ec002c46..1e623a83 100644 --- a/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj +++ b/zefie_wtvp_minisrv/zefie_wtvp_minisrv.njsproj @@ -47,6 +47,9 @@ Code + + Code + Code @@ -196,6 +199,9 @@ Code + + Code + Code @@ -220,7 +226,8 @@ Code - + + Code @@ -231,9 +238,15 @@ Code + + Code + Code + + Code + Code
    Show Services -Unregister This Box +`; +if (ssid_sessions[socket.ssid].getSessionData("registered")) data += `Unregister This Box`; +else data += `Register This Box` + +data += `