- update: app.js: add really long timeout for closing missed sockets
 - update: app.js: fix for minibrowser connectivity
 - update: app.js: enhance security
 - update: wtv-home:/home: fix wtv-needs-upgrade -> wtv-need-upgrade
 - update: wtv-head-waiter:/login-stage-two: do not send wtv-settings:/get to minibrowser
 - update: wtv-1800:/preregister: Disconnect and clean up all previous sockets for the connecting SSID when hitting preregister. Also re-create wtvsec_login.
 - update: add initial wtv-capability-flags decoding, as well as wtv-tricks:/info demonstration
 - update: rename classes
 - minor update: quirky 'Special Thanks' in each custom class.
 - minor update: notice about Initial Shared Key and multiple minisrvs
 - update: wtv-music:/demo/index: update wtv-star image paths
 - update: app.js: fix unencrypted post
 - update: app.js: improve buffering and cleanup in attempt to fix occasional 'double-up' bug
 - update: info.js: remove debug dump of capabilities
 - Update: add test.js, syntax-testing script for `npm test`
 - Update: wtv-chat:/home experimental nick change page thanks to MattMan (chat still giving issues on real boxes, works in Viewer)
 - Update: README.md: Add ways to support the project
This commit is contained in:
zefie
2021-07-24 09:41:00 -04:00
parent b0fd271dc3
commit e591d255b7
17 changed files with 661 additions and 73 deletions

View File

@@ -44,3 +44,9 @@ This open source server is in alpha status. Use at your own risk.
- Test with a WebTV Viewer or connect with a real box
- To connect with a real box, you will need to open ports in your firewall and have a way to connect your WebTV (and preferably reroute 10.0.0.1 to the server)
- See [ServiceVault.md](ServiceVault.md) for a brief introduction to how the service files work
### How to Support the Project
- [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)

View File

@@ -6,6 +6,27 @@ if (request_headers["wtv-ticket"]) {
gourl = "wtv-head-waiter:/login-stage-two?";
}
if (socket.ssid) {
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", socket.ssid);
}
}
if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
delete ssid_sessions[socket.ssid].data_store.wtvsec_login;
}
}
headers = `200 OK
Connection: Keep-Alive
wtv-expire-all: wtv-

View File

@@ -0,0 +1,123 @@
<html>
<head>
<title>
Chat Home
</title>
</head>
<body bgcolor="#101C1E" text="#A2ACB5" link="#CFC382" vlink="#E1EOE3" fontsize="medium" vspace=0 hspace=0>
<sidebar width=109>
<table cellspacing=0 cellpadding=0>
<tr>
<td width=104 height=420 bgcolor=#777896 valign=top>
<table cellspacing=0 cellpadding=0>
<tr>
<td height=7 colspan=3>
<spacer type=vertical size=7>
<tr>
<td width=7>
<spacer type=horizontal size=7>
<td width=87 href="wtv-home:/home">
<img src="images/htv_chat.gif" width=87 height=67>
<td width=10>
<spacer type=horizontal size=10>
</table>
<spacer type=vertical size=6>
<table cellspacing=0 cellpadding=0 border=0>
<tr> <td bgcolor=#2E3A54 height=2 width=104 colspan=3>
<tr>
<td width=10 height=26>
<td width=89 valign=middle>
<td width=5>
</table>
<td width=5 bgcolor=#2E3A54>
</table>
</sidebar>
<table cellspacing=0 cellpadding=0 border=0>
<tr>
<td width=451 colspan=2 align=center bgcolor=#2E3A54>
<spacer type=vertical size=13>
<tr>
<td><img src="wtv-chat:/images/top_corner_dark.jpg" width=8 height=8>
<td width=60>
<tr>
<td bgcolor=#101C1E width=13>
<spacer type=horizontal size=13>
<td bgcolor=#101C1E width=438 valign=top>
<table cellspacing=0 cellpadding=0>
<tr>
<td width=105 height=9><spacer type=vertical size=9>
<td>
</table>
<tr>
<td colspan=2>
<table cellspacing=0 cellpadding=0 border=0>
<tr>
<td width=375 height=25 bgcolor=#101C1E gradcolor=#3C4652 gradangle=90>
<table cellspacing=0 cellpadding=0 border=0>
<tr>
<td width=366 valign=middle>&nbsp;&nbsp;
<blackface><font color=#D6D6D6>
</font></blackface><td>
<table cellspacing=0 cellpadding=0 border=0 bgcolor=#3C4652 gradcolor=#2E3A54 gradangle=90>
<tr>
<td height=16>
<td width=3>
<td width=100>
<spacer type=vertical size=2><br>
<font size=2 color=#E7CE4A><b>Chat Home</b></font>
<td width=21>
<td width=34>
</table>
</table>
</table>
<spacer type=vertical size=12> <table cellspacing=0 cellpadding=0>
<tr>
<td colspan=3 height=12>
<spacer type=vertical size=12> <tr>
<td abswidth=14>
<td>
yo yo yo yo
enter your nick...
<td abswidth=20>
<tr>
<td height=10>
<td>
<td colspan=2 height=2>
<spacer>
<tr>
<td height=1>
<tr>
<td>
<td colspan=2 height=2>
<spacer>
<tr>
<td height=230><!--yo yo yo Change this if needed-->
</table>
<table cellspacing=0 cellpadding=0 width=100%>
<tr>
<form action="wtv-chat:/MakeChatPage?host=chat.irchat.tv&port=6667&channel=WebTV" ONSUBMIT="this.chatinput.focus()">
<td abswidth=14>
<td>
<input id="chatinput" name="nick" type="text" value="" size=32 bgcolor=262626 text=ffc342 cursor=cc9933 font=proportional selected autoactivate nohighlight>
<td align=right>
<font color=e7ce4a><shadow>
<input type=submit borderimage="file://ROM/Borders/ButtonBorder2.bif" value="Join" usestyle width=80>
<td abswidth=9>
</form>
<tr> <TD HEIGHT=8>
</table>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -96,7 +96,10 @@ wtv-demo-mode: 0
wtv-wink-deferrer-retries: 3
wtv-offline-mail-enable: false
wtv-name-server: 8.8.8.8
wtv-settings-url: wtv-setup:/get
wtv-visit: wtv-home:/splash?
`;
if (ssid_sessions[socket.ssid].get('wtv-need-upgrade') != 'true') {
headers += "wtv-settings-url: wtv-setup:/get\n";
}
headers += `wtv-visit: wtv-home:/splash?
Content-Type: text/html`;
}

View File

@@ -28,14 +28,13 @@ function go() {
<p>
<form name=access onsubmit="go()">
<ul>
<li><a href="client:relog">client:relog (direct)</a></li>
<li><a href="wtv-tricks:/blastcache?return_to=wtv-home:/home">Clear Cache</a></li>
<li><a href="wtv-flashrom:/willie" selected>Ultra Willies</a></li>
<li><a href="client:relog">client:relog (direct)</a> ~ <a href="wtv-tricks:/blastcache?return_to=wtv-home:/home">Clear Cache</a></li>
<li><a href="client:diskhax">DiskHax</a> ~ <a href="client:vfathax">VFatHax</a></li>
<li><a href="wtv-flashrom:/willie" selected>Ultra Willies</a> ~ <a href="wtv-tricks:/info">Tricks Info</a></li>
<li><a href="wtv-music:/demo/index">MIDI Music Demo</a></li>
<li><a href="client:diskhax">DiskHax</a> - <a href="client:vfathax">VFatHax</a></li>
<li>Old MSNTV DealerDemo: <a href="wtv-update:/DealerDemo">Download</a> ~ <a href="file://Disk/Demo/index.html">Access (after Download)</a></li>
<li><a href="http://duckduckgo.com/lite/">DuckDuckGo Lite</a></li>`
if (ssid_sessions[socket.ssid].get('wtv-needs-upgrade') != 'true') {
if (ssid_sessions[socket.ssid].get('wtv-need-upgrade') != 'true') {
data += `<li><input name=url `;
if (request_headers.query.url) {

View File

@@ -22,7 +22,7 @@ WebTV Music Index
<tr>
<td width=10 height=26>
<td width=105 valign=middle>
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/images/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/ROMCache/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<tr>
<td height=1>
<tr>
@@ -35,7 +35,7 @@ WebTV Music Index
<tr>
<td width=10 height=26>
<td width=105 valign=middle>
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/images/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/ROMCache/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<tr>
<td height=1>
<tr>
@@ -48,7 +48,7 @@ WebTV Music Index
<tr>
<td width=10 height=26>
<td width=105 valign=middle>
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/images/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/ROMCache/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<tr>
<td height=1>
<tr>
@@ -74,7 +74,7 @@ WebTV Music Index
<tr>
<td width=10 height=26>
<td width=105 valign=middle>
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/images/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/ROMCache/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<tr>
<td height=1>
<tr>
@@ -87,7 +87,7 @@ WebTV Music Index
<tr>
<td width=10 height=26>
<td width=105 valign=middle>
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/images/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<table cellspacing=0 cellpadding=0 href="client:showalert?message=%3Cfont%20size%3D%22small%22%3EPlease%20download%20HackTV%20from%3Cbr%3E%3Cbr%3E%3Ccenter%3Ehttp%3A%2F%2Fturdinc.kicks-ass.net%2FMsntv%2F%3C%2Fcenter%3E%3Cbr%3E%3Cbr%3Efor%20the%20complete%20WebTV%20Music%20Experience%21%3C%2Ffont%3E&image=wtv-star:/ROMCache/HackTVLogoJewel.gif&buttonlabel1=Okay&buttonaction1=client:donothing">
<tr>
<td height=1>
<tr>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,148 @@
var client_caps = null;
if (socket.ssid != null) {
if (ssid_sessions[socket.ssid].data_store.capabilities) {
client_caps = ssid_sessions[socket.ssid].data_store.capabilities;
}
}
if (client_caps) {
headers = `200 OK
Content-Type: text/html`
var client_label = "TODO";
var boot_client_label = "TODO";
var wtv_system_sysconfig_str = "TODO";
var wtv_system_version = ssid_sessions[socket.ssid].get("wtv-system-version");
var wtv_client_bootrom_version = ssid_sessions[socket.ssid].get("wtv-client-bootrom-version");
var wtv_client_serial_number = filterSSID(ssid_sessions[socket.ssid].get("wtv-client-serial-number"));
var wtv_client_rom_type = ssid_sessions[socket.ssid].get("wtv-client-rom-type");
var wtv_system_chipversion_str = ssid_sessions[socket.ssid].get("wtv-system-chipversion");
var wtv_system_sysconfig_hex = parseInt(ssid_sessions[socket.ssid].get("wtv-system-sysconfig")).toString(16);
var capabilities_table = new WTVClientCapabilities().capabilities_table;
data = `<html>
<!--- *=* Copyright 1996, 1997 WebTV Networks, Inc. All rights reserved. --->
<display nosave nosend skipback>
<title>${minisrv_config.config.service_name} Info</title>
<sidebar width=20%>
<img src="wtv-tricks:/images/About_bg.jpg">
</sidebar>
<body bgcolor="#191919" text="#44cc55" link="36d5ff" vlink="36d5ff" vspace=0>
<br>
<br>
<br>
<h1>${minisrv_config.config.service_name} Info</h1>
<table>
<tr>
<td height=20>
<tr>
<td valign=top align=right><shadow>Connected to:</shadow>
<td width=10>
<td valign=top>Mini Service
<tr>
<td valign=top align=right width=150><shadow>Service:</shadow>
<td width=10>
<td valign=top>${z_title}
<tr>
<td valign=top align=right><shadow>Client:</shadow>
<td width=10>
<td valign=top>&vers; (Build ${wtv_system_version} [${client_label}])
<tr>
<td valign=top align=right><shadow>Boot:</shadow>
<td width=10>
<td valign=top>&wtv-bootvers; (Build ${wtv_client_bootrom_version} [${boot_client_label}])
<tr>
<td height=20)
<tr>
<td valign=top align=right><shadow>Silicon serial ID:</shadow>
<td width=10>
<td valign=top>${wtv_client_serial_number}
<tr>
<td valign=top align=right><shadow>Connected at:</shadow>
<td width=10>
<td valign=top>&rate;
<tr>
<td valign=top align=right><shadow>Client IP number:</shadow>
<td width=10>
<td valign=top>${socket.remoteAddress}
<tr>
<td height=20>
<tr>
<td valign=top align=right><shadow>ROM type:</shadow>
<td width=10>
<td valign=top>${wtv_client_rom_type}
<tr>
<td valign=top align=right><shadow>Modem f/w (when available):</shadow>
<td width=10>
<td valign=top>&modem;
`;
if (ssid_sessions[socket.ssid].get("wtv-need-upgrade")) {
data += `<tr>
<td valign=top align=right><shadow>Mini-browser:</shadow>
<td width=10>
<td valign=top>Yes
`;
}
data += `
<tr>
<td valign=top align=right><shadow>Chip version:</shadow>
<td width=10>
<td valign=top>${wtv_system_chipversion_str} (TODO)
<tr>
<td valign=top align=right><shadow>SysConfig:</shadow>
<td width=10>
<td valign=top>0x${wtv_system_sysconfig_hex.toUpperCase()}
</table>
<table>
<tr>
<td height=20>
<tr>
<td valign=top align=right width=175><shadow>Client capabilities:</shadow>
<td width=10>
<td valign=top>
</table>
<table>
`;
// start loop
Object.keys(capabilities_table).forEach(function (k) {
data += `<tr>
<td valign=top align=right>${capabilities_table[k][1]}
<td width=10>
`;
if (client_caps[capabilities_table[k][0]]) data += "<td valign=top>True\n";
else data += "<td valign=top>False\n";
});
// end loop
data += `
</table>
<pre>
${wtv_system_sysconfig_str}
</pre>
<br>
</body> </html>`;
} else {
var errpage = doErrorPage(400);
headers = errpage[0];
data = errpage[1];
}

View File

@@ -0,0 +1,150 @@
class WTVClientCapabilities {
/***********************************\
|* Special Thanks to: *|
|* Outatyme *|
|* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *|
|* For the binary information *|
|* about capability flags *|
\***********************************/
capabilities = null;
capabilities_table = null;
constructor(wtv_capability_flags = null) {
// [ flag_name, friendly_flag_name ]
// so far we assume the reversed bit order = the order on wtv-tricks:/info (production service)
// also speculation that `client-has-relogin-function` is forced true on the service side
// (this script does not do that, also note that LC2 MiniBrowser does not support client:relog)
// None of this is 100% for certain yet (except the bitfield stuff), do not trust as verbatim, more testing needed
var capabilities_table = [
["client-can-do-muzac", "Can Do Muzac"],
["client-can-do-chat", "Can Chat"],
["client-can-do-openISP", "Can do OpenISP"],
["client-can-receive-compressed-data", "Can receive compressed data"],
["client-can-display-spotads1", "Can show Spotads1"],
["client-can-print", "Can Print"],
["client-can-do-macromedia-flash1", "Can do Macromedia Flash1"],
["client-can-do-javascript", "Can do JavaScript"],
["client-can-do-videoflash", "Can do VideoFlash"],
["client-can-do-videoads", "Can do VideoAds"],
["client-has-disk", "Has Disk"],
["client-supports-classical-service", "Supports Classical"],
["client-open-isp-settings-valid", "OISP settings valid"],
["client-can-tell-valid-open-isp", "Can tell OISP settings valid"],
["client-has-tuner", "Has Tuner"],
["client-can-data-download", "Can data download"],
["client-supports-approx-content-len", "Supports approximate content length"],
["client-has-built-in-printer-port", "Has built-in printer port"],
["client-has-tv-experience", "Has TV experience"],
["client-can-handle-proxy-bypass", "Can handle proxy bypass"],
["client-can-handle-download-v2", "Can handle Download protocol 2"],
["client-has-relogin-function", "Has Relogin function"],
["client-can-display-spotads2", "Can display spotads2"],
["client-can-display-30-sec-video-ads", "Can display 30 second video ads"],
["client-supports-etude-service", "Supports Etude"],
["client-can-do-av-capture", "Can do AV capture"],
["client-can-do-disconnected-email", "Can do disconnected email"],
["client-can-do-macromedia-flash2", "Can do Macromedia Flash2"],
["client-has-memory-size-bit1-set", "Memory size bit1 set"],
["client-has-memory-size-bit2-set", "Memory size bit2 set"],
["client-has-memory-size-bit3-set", "Memory size bit3 set"],
["client-can-do-rmf", "Can do RMF"],
["client-can-do-png", "Can do PNG"],
["client-does-broadband-data-dowload", "Supports broadband download"],
["client-has-softmodem", "Has Softmodem"],
["client-can-do-preparsed-epg", "Can do pre-parsed EPG"],
["client-supports-funk-e-service", "Supports Funk-e"],
["client-wants-dial-script", "Wants dial script"],
["client-upgrade-visits-not-needed", "Upgrade visits not needed"],
["client-uses-flexible-videoad-paths", "Uses flexible videoad paths"],
["client-non-production-build", "Non-production build"],
["client-can-download-printer-drivers", "Can download printer drivers"],
["client-supports-hiphop-service", "Supports HipHop"],
["client-can-use-messenger", "Can use MSN Messenger"],
["client-uses-third-party-billing", "Uses 3rd-party billing"],
["client-can-do-offlineads", "Can do offline ads"],
["client-has-no-dialin-support", "Has no dialin support"],
["client-has-ssl-support-for-wtvp", "Has SSL support for WTVP"],
["client-can-do-audio-capture", "Can do audio capture"],
["client-can-do-metered-pricing", "Can do Metered Pricing"],
["client-negotiates-user-agent", "Can Negotiate User-Agent"],
["client-can-do-element-logging", "Can do Unsupported Element Logging"],
["client-supports-jazz-security", "Supports Jazz security"],
["client-supports-MSN-service", "Supports MSN service"],
["client-supports-notify-port-header", "Supports notify port header"],
["client-supports-messenger-update-light", "Supports MSN Messenger update light"],
["client-supports-MSN-chat", "Supports MSN Chat"],
["client-supports-MSN-chat-findu", "Supports MSN Chat FindU"],
["client-supports-MSN-messenger-CVR", "Supports MSN Messenger CVR"],
["client-supports-MSN-messenger-MSNP8", "Supports MSN Messenger MSNP8"],
["client-supports-MSN-chat-R9C", "Supports MSN Chat R9C"]
];
this.capabilities_table = capabilities_table;
var capabilities = new Array();
// might want to pass without a flag to get the table
if (wtv_capability_flags != null) {
// define function to convert hex string to binary string (0s & 1s)
var hex2bin = function (hex) {
var binary = "";
var remainingSize = hex.length;
for (var p = 0; p < hex.length / 8; p++) {
//In case remaining hex length (or initial) is not multiple of 8
var blockSize = remainingSize < 8 ? remainingSize : 8;
binary += parseInt(hex.substr(p * 8, blockSize), 16).toString(2);
remainingSize -= blockSize;
}
return binary;
}
// Add .reverse() to strings for ease of processing
if (!String.prototype.reverse) {
String.prototype.reverse = function () {
var splitString = this.split("");
var reverseArray = splitString.reverse();
var joinArray = reverseArray.join("");
return joinArray;
}
}
// convert wtv_capability_flags to binary string, reverse the string, and split into array containing each character;
var bitfield = hex2bin(wtv_capability_flags).reverse().split("");
// only add to the capabilities array if the result is true
var add = function (flag_name, flag) {
if (flag) capabilities[flag_name] = flag;
}
// process bitfield and set capabilities
Object.keys(bitfield).forEach(function (k) {
// Convert binary to boolean, 0 to false, 1 to true
var bitfield_result = (bitfield[k] == "1")
// set flags based on position of bit
add(capabilities_table[k][0], bitfield_result);
});
this.capabilities = capabilities;
return capabilities;
}
}
get(key = null) {
if (typeof (this.capabilities) === 'undefined') return null;
else if (key === null) return this.capabilities;
else if (this.capabilities[key]) return this.capabilities[key];
else return null;
}
}
module.exports = WTVClientCapabilities;

View File

@@ -1,4 +1,13 @@
class ClientSessionData {
class WTVClientSessionData {
/***********************************\
|* Special Thanks to: *|
|* No one *|
|* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *|
|* There is literally nothing *|
|* special about this class *|
\***********************************/
data_store = null;
constructor() {
@@ -25,4 +34,4 @@ class ClientSessionData {
}
module.exports = ClientSessionData;
module.exports = WTVClientSessionData;

View File

@@ -2,9 +2,21 @@ 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 *|
\***********************************/
class WTVSec {
//initial_shared_key = CryptoJS.lib.WordArray.random(8);
initial_shared_key_b64 = "CC5rWmRUE0o="; // You can change this but it doesn't mean much for security. Just make sure its static. 8 bytes base64 encoded.
// Initial Shared Key, in Base64 Format
// You can change this but it doesn't mean much for security. Just make sure its static. 8 bytes base64 encoded.
// If you intend to link multiple minisrv's together, they must all share the same Initial Shared Key.
initial_shared_key_b64 = "CC5rWmRUE0o=";
initial_shared_key = null;
current_shared_key = null;
challenge_key = null;

View File

@@ -1,6 +1,7 @@
'use strict';
const fs = require('fs');
const path = require('path');
const http = require('http');
const https = require('https');
const strftime = require('strftime'); // used externally by service scripts
@@ -8,8 +9,9 @@ const net = require('net');
const CryptoJS = require('crypto-js');
const mime = require('mime-types');
const { crc16 } = require('easy-crc');
var WTVSec = require('./wtvsec.js');
var ClientSessionData = require('./session_data.js');
var WTVSec = require('./WTVSec.js');
var WTVClientCapabilities = require('./WTVClientCapabilities.js');
var WTVClientSessionData = require('./WTVClientSessionData.js');
// Where we store our session information
var ssid_sessions = new Array();
@@ -19,11 +21,13 @@ var ports = [];
// add .reverse() feature to all JavaScript Strings in this application
// works for service vault scripts too.
String.prototype.reverse = function () {
var splitString = this.split("");
var reverseArray = splitString.reverse();
var joinArray = reverseArray.join("");
return joinArray;
if (!String.prototype.reverse) {
String.prototype.reverse = function () {
var splitString = this.split("");
var reverseArray = splitString.reverse();
var joinArray = reverseArray.join("");
return joinArray;
}
}
function getServiceString(service) {
@@ -89,7 +93,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
try {
service_vaults.forEach(function (service_vault_dir) {
if (service_vault_found) return;
service_vault_file_path = service_vault_dir + "/" + service_path.replace(/\\/g, "/");
service_vault_file_path = makeSafePath(service_vault_dir,service_path);
if (fs.existsSync(service_vault_file_path)) {
@@ -139,7 +143,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
service_vault_found = true;
if (!zquiet) console.log(" * Found " + service_vault_file_path + ".js to handle request (JS Interpreter mode) [Socket " + socket.id + "]");
// expose var service_dir for script path to the root of the wtv-service
var service_dir = service_vault_dir.replace(/\\/g, "/") + "/" + service_name;
var service_dir = service_vault_dir + path.sep + service_name;
socket_sessions[socket.id].starttime = Math.floor(new Date().getTime() / 1000);
var jscript_eval = fs.readFileSync(service_vault_file_path + ".js").toString();
eval(jscript_eval);
@@ -166,7 +170,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
}
if (!request_is_async) {
if (!service_vault_found) {
console.log(" * Could not find a Service Vault for", service_path);
console.log(" * Could not find a Service Vault for " + service_name + ":/" + service_path.replace(service_name + path.sep, ""));
var errpage = doErrorPage(404);
headers = errpage[0];
data = errpage[1];
@@ -209,6 +213,12 @@ function filterSSID(obj) {
}
}
function makeSafePath(base, target) {
target.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, "");
var targetPath = path.posix.normalize(target)
return base + path.sep + targetPath;
}
async function processURL(socket, request_headers) {
if (request_headers === null) {
return;
@@ -251,7 +261,7 @@ async function processURL(socket, request_headers) {
}
// assume webtv since there is a :/ in the GET
var service_name = shortURL.split(':/')[0];
var urlToPath = service_name + "/" + shortURL.split(':/')[1];
var urlToPath = service_name + path.sep + shortURL.split(':/')[1];
if (zshowheaders) console.log(" * Incoming headers on socket ID", socket.id, (await filterSSID(request_headers)));
processPath(socket, urlToPath, request_headers, service_name);
} else if (shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0) {
@@ -502,7 +512,16 @@ async function sendToClient(socket, headers_obj, data) {
}
if (zquiet) console.log(" * Sent" + verbosity_mod + " " + headers_obj.http_response + " to client (Content-Type:", headers_obj['Content-Type'], "~", headers_obj['Content-Length'], "bytes)");
}
socket_sessions[socket.id].buffer = null;
if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data;
if (socket_sessions[socket.id].header_buffer) delete socket_sessions[socket.id].header_buffer;
if (socket_sessions[socket.id].secure_buffer) delete socket_sessions[socket.id].secure_buffer;
if (socket_sessions[socket.id].buffer) delete socket_sessions[socket.id].buffer;
if (socket_sessions[socket.id].headers) delete socket_sessions[socket.id].headers;
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") {
@@ -550,11 +569,11 @@ function headersAreStandard(string, verbose = false) {
return /^([A-Za-z0-9\+\/\=\-\.\,\ \"\;\:\?\&\r\n\(\)\%\<\>\_]{8,})$/.test(string);
}
async function processRequest(socket, data_hex, returnHeadersBeforeSecure = false, encryptedRequest = false) {
async function processRequest(socket, data_hex, skipSecure = false, encryptedRequest = false) {
// TODO: clean up this function (how much is even used anymore?)
// This function sucks and needs to be rewritten
var headers = null;
var headers = new Array();
if (socket_sessions[socket.id]) {
if (socket_sessions[socket.id].headers) {
headers = socket_sessions[socket.id].headers;
@@ -570,7 +589,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
data = data.split("\n\n")[0];
}
if (headersAreStandard(data)) {
if (headers != null) {
if (headers.length != 0) {
var new_header_obj = headerStringToObj(data);
Object.keys(new_header_obj).forEach(function (k, v) {
headers[k] = new_header_obj[k];
@@ -579,7 +598,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
} else {
headers = headerStringToObj(data);
}
} else if (!returnHeadersBeforeSecure) {
} 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
@@ -614,13 +633,15 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
}
if (!headers) return;
if (headers["wtv-client-serial-number"] != null) {
socket.ssid = headers["wtv-client-serial-number"];
if (!ssid_sessions[socket.ssid]) {
ssid_sessions[socket.ssid] = new ClientSessionData();
ssid_sessions[socket.ssid] = new WTVClientSessionData();
}
if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Array();
ssid_sessions[socket.ssid].data_store.sockets.push(socket.id);
if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Set();
ssid_sessions[socket.ssid].data_store.sockets.add(socket);
}
var ip2long = function (ip) {
@@ -702,6 +723,15 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
}
// Passed Security
if (headers["wtv-capability-flags"] != null) {
if (!ssid_sessions[socket.ssid]) {
ssid_sessions[socket.ssid] = new WTVClientSessionData();
}
if (!ssid_sessions[socket.ssid].data_store.capabilities) ssid_sessions[socket.ssid].data_store.capabilities = new WTVClientCapabilities(headers["wtv-capability-flags"]);
}
// log all client wtv- headers to the SessionData for that SSID
// this way we can pull up client info such as wtv-client-rom-type or wtv-system-sysconfig
@@ -734,11 +764,8 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
}
if (returnHeadersBeforeSecure) {
return headers;
}
if (headers.secure === true || headers.encrypted === true) {
if ((headers.secure === true || headers.encrypted === true) && !skipSecure) {
if (!socket_sessions[socket.id].wtvsec) {
if (!zquiet) console.log(" * Starting new WTVSec instance on socket", socket.id);
if (ssid_sessions[socket.ssid].get("wtv-incarnation")) {
@@ -766,7 +793,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
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), (!returnHeadersBeforeSecure && !encryptedRequest))) {
if (headersAreStandard(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
@@ -778,8 +805,23 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
// SECURE ON and detected encrypted data
ssid_sessions[socket.ssid].set("box-does-psuedo-encryption", false);
var dec_data = CryptoJS.lib.WordArray.create(socket_sessions[socket.id].wtvsec.Decrypt(0, enc_data))
var secure_headers = await processRequest(socket, dec_data.toString(CryptoJS.enc.Hex), true, true);
if (!socket_sessions[socket.id].secure_buffer) socket_sessions[socket.id].secure_buffer = "";
socket_sessions[socket.id].secure_buffer += dec_data.toString(CryptoJS.enc.Hex);
var secure_headers = null;
if (headers['request']) {
if (headers['request'] == "GET") {
if (socket_sessions[socket.id].secure_buffer.indexOf("0d0a0d0a") || socket_sessions[socket.id].secure_buffer.indexOf("0a0a")) {
secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
}
} else {
secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
}
} else {
secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
}
if (!secure_headers) return;
delete socket_sessions[socket.id].secure_buffer;
if (zdebug) console.log(" # Encrypted Request (SECURE ON)", "on", socket.id);
if (zshowheaders) console.log(secure_headers);
if (!secure_headers.request) {
@@ -792,11 +834,28 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
}
// Merge new headers into existing headers object
Object.keys(secure_headers).forEach(function (k, v) {
Object.keys(secure_headers).forEach(function (k) {
headers[k] = secure_headers[k];
});
} else {
socket_sessions[socket.id].headers = headers;
return;
}
}
} else if (skipSecure) {
if (headers) {
if (headers['request']) {
if (headers['request'].substring(0, 4) == "POST") {
if (socket_sessions[socket.id].secure_buffer) delete socket_sessions[socket.id].secure_buffer;
} else {
return headers;
}
} else {
return headers;
}
} else {
return;
}
}
// handle POST
@@ -818,11 +877,14 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
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)) {
@@ -841,7 +903,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
delete socket_sessions[socket.id].post_data_length;
processURL(socket, headers);
return;
}
}
} else {
socket_sessions[socket.id].headers = headers;
}
@@ -870,7 +932,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
console.log(" * ", Math.floor(new Date().getTime() / 1000), "Receiving", post_string, "data on", socket.id, "[", socket_sessions[socket.id].post_data.length / 2, "of", socket_sessions[socket.id].post_data_length, "bytes ]");
} else {
// calculate and display percentage of data received
var getPercentage = function(partialValue, totalValue) {
var getPercentage = function (partialValue, totalValue) {
return Math.floor((100 * partialValue) / totalValue);
}
var postPercent = getPercentage(socket_sessions[socket.id].post_data.length, (socket_sessions[socket.id].post_data_length * 2));
@@ -888,6 +950,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
}
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;
headers.post_data = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data);
if (socket_sessions[socket.id].secure == true) {
if (zdebug) console.log(" # Encrypted POST Content (SECURE ON)", "on", socket.id, "[", headers.post_data.sigBytes, "bytes ]");
@@ -901,6 +964,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
return;
}
if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) {
if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data;
// got too much data ? ... should not ever reach this code
var errpage = doErrorPage(400, "Received too much data in POST request<br>Got " + (socket_sessions[socket.id].post_data.length / 2) + ", expected " + socket_sessions[socket.id].post_data_length);
headers = errpage[0];
@@ -909,7 +973,7 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
return;
}
} else if (!returnHeadersBeforeSecure) {
} else if (!skipSecure) {
if (!encryptedRequest) {
if (socket_sessions[socket.id].secure != true) {
socket_sessions[socket.id].wtvsec = new WTVSec(1, zdebug);
@@ -933,23 +997,41 @@ async function processRequest(socket, data_hex, returnHeadersBeforeSecure = fals
} else {
var dec_data = CryptoJS.lib.WordArray.create(socket_sessions[socket.id].wtvsec.Decrypt(0, enc_data));
}
var secure_headers = await processRequest(socket, dec_data.toString(CryptoJS.enc.Hex), false, true);
if (!socket_sessions[socket.id].secure_buffer) socket_sessions[socket.id].secure_buffer = "";
socket_sessions[socket.id].secure_buffer += dec_data.toString(CryptoJS.enc.Hex);
var secure_headers = null;
if (headers['request']) {
if (headers['request'] == "GET") {
if (socket_sessions[socket.id].secure_buffer.indexOf("0d0a0d0a") || socket_sessions[socket.id].secure_buffer.indexOf("0a0a")) {
secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
}
} else {
var secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
}
} else {
var secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
}
if (secure_headers) {
delete socket_sessions[socket.id].secure_buffer;
if (!headers) headers = new Array();
headers.encrypted = true;
Object.keys(secure_headers).forEach(function (k, v) {
headers[k] = secure_headers[k];
});
if (headers['request']) {
if (headers['request'].substring(0, 4) == "POST" && !socket_sessions[socket.id].post_data) {
socket_sessions[socket.id].post_data_length = headers['Content-length'] || headers['Content-Length'] || 0;
socket_sessions[socket.id].post_data = "";
if (headers['request'].substring(0, 4) == "POST") {
if (!socket_sessions[socket.id].post_data) {
socket_sessions[socket.id].post_data_length = headers['Content-length'] || headers['Content-Length'] || 0;
socket_sessions[socket.id].post_data = "";
}
processRequest(socket, dec_data.toString(CryptoJS.enc.Hex));
} else {
processURL(socket, headers);
}
processRequest(socket, dec_data.toString(CryptoJS.enc.Hex));
}
}
}
}
}
}
}
}
}
@@ -962,12 +1044,9 @@ async function cleanupSocket(socket) {
delete socket_sessions[socket.id];
}
if (socket.ssid) {
var socket_array_index = ssid_sessions[socket.ssid].data_store.sockets.findIndex(element => element == socket.id);
if (socket_array_index != -1) {
ssid_sessions[socket.ssid].data_store.sockets.splice(socket_array_index,1);
}
ssid_sessions[socket.ssid].data_store.sockets.delete(socket);
if (ssid_sessions[socket.ssid].data_store.sockets.length === 0 && ssid_sessions[socket.ssid].data_store.wtvsec_login) {
if (ssid_sessions[socket.ssid].data_store.sockets.size === 0 && ssid_sessions[socket.ssid].data_store.wtvsec_login) {
// if last socket for SSID disconnected, destroy login session
if (!zquiet) console.log(" * Last socket from WebTV SSID", filterSSID(socket.ssid),"disconnected, cleaning up primary WTVSec instance for this SSID");
ssid_sessions[socket.ssid].delete("wtvsec_login");
@@ -989,20 +1068,20 @@ async function handleSocket(socket) {
socket.id = parseInt(crc16('CCITT-FALSE', Buffer.from(String(socket.remoteAddress) + String(socket.remotePort), "utf8")).toString(16), 16);
socket_sessions[socket.id] = [];
socket.setEncoding('hex'); //set data encoding (Text: 'ascii', 'utf8' ~ Binary: 'hex', 'base64' (do not trust 'binary' encoding))
socket.setTimeout(600000);
socket.on('data', function (data_hex) {
if (!socket_sessions[socket.id].secure) {
if (!socket_sessions[socket.id].secure && !socket_sessions[socket.id].expecting_post_data) {
// buffer unencrypted data until we see the classic double-newline, or get blank
if (!socket_sessions[socket.id].header_buffer) socket_sessions[socket.id].header_buffer = "";
socket_sessions[socket.id].header_buffer += data_hex;
var header_buffer_text = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].header_buffer).toString(CryptoJS.enc.Latin1);
if (header_buffer_text.indexOf("\r\n\r\n") != -1 || header_buffer_text.indexOf("\n\n") != -1 || header_buffer_text == "") {
if (socket_sessions[socket.id].header_buffer.indexOf("0d0a0d0a") != -1 || socket_sessions[socket.id].header_buffer.indexOf("0a0a") != -1) {
data_hex = socket_sessions[socket.id].header_buffer;
delete socket_sessions[socket.id].header_buffer;
processRequest(this, data_hex);
}
} else {
// stream encrypted requests through the processor
if (socket_sessions[socket.id].header_buffer) delete socket_sessions[socket.id].header_buffer;
processRequest(this, data_hex);
}
});
@@ -1041,25 +1120,24 @@ function integrateConfig(main, user) {
return main;
}
function returnAbsolsutePath(path) {
if (path.substring(0, 1) != "/" && path.substring(1, 1) != ":") {
function returnAbsolsutePath(check_path) {
if (check_path.substring(0, 1) != path.sep && check_path.substring(1, 1) != ":") {
// non-absolute path, so use current directory as base
path = (__dirname + "/" + path).replace(/\\/g, "/");
check_path = (__dirname + path.sep + check_path);
} else {
// already absolute path
path = path.replace(/\\/g, "/");
}
return path;
return check_path;
}
function getGitRevision() {
try {
const rev = fs.readFileSync(__dirname.replace(/\\/g, "/") + '/../.git/HEAD').toString().trim();
const rev = fs.readFileSync(__dirname + path.sep + ".." + path.sep + ".git" + path.sep + "HEAD").toString().trim();
if (rev.indexOf(':') === -1) {
return rev;
} else {
return fs.readFileSync(__dirname.replace(/\\/g, "/") + '/../.git/' + rev.substring(5)).toString().trim();
return fs.readFileSync(__dirname + path.sep + ".." + path.sep + ".git" + path.sep + rev.substring(5)).toString().trim();
}
} catch (e) {
return null;
@@ -1214,4 +1292,3 @@ initstring = initstring.substring(0, initstring.length - 2);
console.log(" * Started server on ports " + initstring + "...")
var listening_ip_string = (minisrv_config.config.bind_ip != "0.0.0.0") ? "IP: " + minisrv_config.config.bind_ip : "all interfaces";
console.log(" * Listening on", listening_ip_string,"~","Service IP:", service_ip);

View File

@@ -1,10 +1,13 @@
{
"name": "zefie_wtvp_minisrv",
"version": "0.9.5",
"version": "0.9.6",
"description": "WebTV Service (WTVP) Emulation Server",
"main": "app.js",
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv",
"license": "GPL3",
"scripts": {
"test": "node test.js"
},
"author": {
"name": "zefie",
"email": "zefie@zefie.net",

View File

@@ -0,0 +1,33 @@
const { promisify } = require('util');
const { resolve } = require('path');
const fs = require('fs');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const { exec } = require("child_process");
var path = require('path');
async function getFiles(dir) {
const subdirs = await readdir(dir);
const files = await Promise.all(subdirs.map(async (subdir) => {
const res = resolve(dir, subdir);
return (await stat(res)).isDirectory() ? getFiles(res) : res;
}));
return files.reduce((a, f) => a.concat(f), []);
}
getFiles(__dirname)
.then(files => {
files.forEach(function (file) {
if (path.extname(file) == ".js" && file.indexOf("node_modules") == -1) {
console.log(" * Checking syntax of", file.replace(__dirname + path.sep, "." + path.sep));
exec("node --check \"" + file + "\"", (error, stdout, stderr) => {
if (stderr.length > 0) {
console.log(`${stderr}`);
return;
}
});
}
});
})
.catch(e => console.error(e));

View File

@@ -202,12 +202,16 @@
<Content Include="ServiceVault\wtv-head-waiter\login.js" />
<Content Include="ServiceVault\wtv-home\splash.js" />
<Content Include="ServiceVault\wtv-log\log.js" />
<Content Include="session_data.js">
<Content Include="test.js" />
<Content Include="WTVClientSessionData.js">
<SubType>Code</SubType>
</Content>
<Content Include="user_config.example.json" />
<Content Include="user_config.json" />
<Content Include="wtvsec.js">
<Content Include="WTVClientCapabilities.js">
<SubType>Code</SubType>
</Content>
<Content Include="WTVSec.js">
<SubType>Code</SubType>
</Content>
<Content Include="package.json" />