- Rename project
- Add GPL3 License (RETROACTIVE)
- Fill out package.json
- Switch crc16 module to hopefully improve Windows end-user experience
- Update README
This commit is contained in:
zefie
2021-07-17 17:28:22 -04:00
parent 627433b2c1
commit bd1ef1415c
49 changed files with 741 additions and 58 deletions

370
zefie_wtvp_minisrv/.gitignore vendored Normal file
View File

@@ -0,0 +1,370 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# ServiceLogPost Posted Logs
ServiceLogPost/*_*
# Large files not pertaining to the service code
ServiceVault/wtv-flashrom/content/*
ServiceVault/wtv-music/content/*
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/hacktv_updsrv/ServiceLogPost/1626307222_warning_812bf30600b002bb

View File

@@ -0,0 +1,49 @@
if (socket_session_data[socket.id].ssid != null && !getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login')) {
var wtvsec_login = new WTVSec();
wtvsec_login.IssueChallenge();
wtvsec_login.set_incarnation(request_headers['wtv-incarnation']);
setSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login', wtvsec_login)
} else {
var wtvsec_login = getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login')
}
var contype = "text/tellyscript";
var skip_tellyscript = false;
// if relogin, skip tellyscript
if (query['relogin']) {
contype = "text/html"; // skip tellyscript
wtvsec_login.ticket_b64 = null; // clear old ticket
}
headers = `200 OK
Connection: Keep-Alive
wtv-initial-key: ` + wtvsec_login.challenge_key.toString(CryptoJS.enc.Base64) + `
Content-Type: `+ contype + `
wtv-service: reset
` + getServiceString('wtv-1800') + `
` + getServiceString('wtv-star') + `
` + getServiceString('wtv-head-waiter') + `
` + getServiceString('wtv-flashrom') + `
wtv-boot-url: wtv-1800:/preregister?relogin=true
wtv-visit: wtv-head-waiter:/login?
wtv-client-time-zone: GMT -0000
wtv-client-time-dst-rule: GMT
wtv-client-date: `+strftime("%a, %d %b %Y %H:%M:%S", new Date(new Date().toUTCString()))+` GMT`;
// if relogin, skip tellyscript
var romtype = null;
if (!query['relogin'] && skip_tellyscript == false) {
var romtype = getSessionData(socket_session_data[socket.id].ssid, 'wtv-client-rom-type');
}
switch (romtype) {
case "US-LC2-disk-0MB-8MB":
data = getFile("LC2/LC2_OISP_5555732_56k.tok", true);
break;
default:
data = '';
break;
}

View File

@@ -0,0 +1,14 @@
var gourl = "wtv-1800:/finish-prereg?";
if (query['relogin']) gourl += "relogin=true";
if (request_headers['wtv-ticket']) {
gourl = "wtv-head-waiter:/login-stage-two?";
}
headers = `200 OK
Connection: Keep-Alive
wtv-expire-all: wtv-
wtv-open-isp-disabled: false
wtv-visit: `+ gourl + `
Content-type: text/html`;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -0,0 +1,112 @@
// todo, actual file logic
// - ready query param to get flashrom path, check for its existance
// - handle last part to redirect to lc2-download-complete
// - handle failures
headers = `200 OK
Content-type: text/html`
data = `<html>
<head>
<title>
Updating
</title>
<display switchtowebmode transition=none nostatus nooptions skipback clearback>
</head>
<body noscroll bgcolor="#191919" text="#42CC55" link="36d5ff"
hspace=0 vspace=0 fontsize="large">
<table cellspacing=0 cellpadding=0>
<tr>
<td width=104 height=74 valign=middle align=center bgcolor="3B3A4D">
<td width=20 valign=top align=left bgcolor="3B3A4D">
<td colspan=10 width=436 valign=middle align=left bgcolor="3B3A4D">
<font color="D6DFD0" size="+2">
<blackface>
<shadow>
<br>
Updating now...
</shadow>
</blackface>
</font>
<tr>
<td colspan=12 width=560 height=10 valign=top align=left>
<tr>
<td width=104 height=10 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=68 valign=top align=left>
<td width=20 valign=top align=left>
<form action="client:poweroff">
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 width=100 height=258 valign=top align=left>
<font size=+1>
Your Internet Receiver is being<br>updated automatically.
<p> <font size=+1>
This will take a while, and then<br> your unit will reboot.<br><br>
`;
if (flashrom_isboot && flashrom_part == 16) {
data += `<p>
The system will pause for about 30 seconds at the end of this
update. Please <strong>do not</strong> interrupt the system
during this time.
`
}
data += `</font>
</table>
<table width="100%">
<tr>
<td align="left"><font size="-1" color="#D6DFD0"><small>&nbsp; &nbsp; Receiving part ${flashrom_part} of ${flashrom_total_parts}</small></font></td>
<td align="right"><font size="-1" color="#D6DFD0"><small>v${flashrom_version} (${flashrom_type}) &nbsp; &nbsp;</small></font></td>
</tr>
</table>
<center>
<upgradeblock width=520 height=15
${nextrompath}
errorurl="wtv-flashrom:/lc2-download-failed"
blockurl="wtv-flashrom:/${flashrom_rompath}
lastblock=${flashrom_lastpart}
curblock="${flashrom_part}"
totalblocks="${flashrom_total_parts}"></center>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=10 height=2 valign=middle align=center bgcolor="#191919">
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 height=1 valign=top align=left>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=10 height=2 valign=top align=left bgcolor="#191919">
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 height=4 valign=top align=left>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 width=416 valign=top align=left>
<table cellspacing=0 cellpadding=0>
<tr>
<td width=306 valign=top align=left>
<font size="-1"><i>
</i></font><td width=112 valign=top align=right>
<font size="-1" color="#191919">
</font>
</form>
</table>
<td width=20 valign=middle align=center>
</table>
</body>
</html>`

View File

@@ -0,0 +1,107 @@
headers = `200 OK
Content-type: text/html`
data = `<html>
<head>
<title>
Updating
</title>
<display switchtowebmode nostatus nooptions skipback clearback>
<meta http-equiv=Refresh content="270; url=client:poweroff?invalRAMImage">
</head>
<body onLoad="document.forms.invalram.submit()";
noscroll bgcolor="#191919" text="#42CC55" link="36d5ff"
hspace=0 vspace=0 fontsize="large">
<form name ="invalram" action="client:invalramimage">
</form>
<table cellspacing=0 cellpadding=0>
<tr>
<td width=104 height=74 valign=middle align=center bgcolor="3B3A4D">
<img src="wtv-flashrom:/ROMCache/MSNLogo.gif" width=87 height=67>
<td width=20 valign=top align=left bgcolor="3B3A4D">
<img src="wtv-flashrom:/ROMCache/Spacer.gif" width=1 height=1>
<td colspan=10 width=436 valign=middle align=left bgcolor="3B3A4D">
<font color="D6DFD0" size="+2">
<blackface>
<shadow>
<img src="wtv-flashrom:/ROMCache/Spacer.gif" width=1 height=4>
<br>
Updating complete
</shadow>
</blackface>
</font>
<tr>
<td colspan=12 width=560 height=10 valign=top align=left>
<img src="wtv-flashrom:/ROMCache/S40H1.gif" width=560 height=6>
<tr>
<td width=104 height=10 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=68 valign=top align=left>
<td width=20 valign=top align=left>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 width=100 height=258 valign=top align=left>
<font size=+1>
The update is complete.<br>
<p><font size=+1>Choose <b>Connect Now</b> if you<br>want to connect to ${services_configured.config.service_name}.
<p><font size=+1>Press the <b>power</b> button to switch<br>off your unit.
</font>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=10 height=2 valign=middle align=center bgcolor="2B2B2B">
<img src="wtv-flashrom:/ROMCache/Spacer.gif" width=436 height=1>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 height=1 valign=top align=left>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=10 height=2 valign=top align=left bgcolor="0D0D0D">
<img src="wtv-flashrom:/ROMCache/Spacer.gif" width=436 height=1>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 height=4 valign=top align=left>
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 width=416 valign=top align=left>
<table cellspacing=0 cellpadding=0>
<tr>
<td width=306 valign=top align=left>
<font size="-1"><i>
</i></font><td width=112 valign=top align=right>
<font size="-1" color="#E7CE4A">
<shadow>
<form action="client:poweroff">
<input selected type=submit value="Connect Now" name="UseUpgradeNow"
borderimage="file://ROM/Borders/ButtonBorder2.bif" usestyle width=170>
<input type="hidden" name="invalRAMImage">
<input type="hidden" name="autoPowerOn">
</shadow>
</font>
</form>
</table>
<td width=20 valign=middle align=center>
</table>
</body>
</html>`

View File

@@ -0,0 +1,50 @@
<html>
<head>
<title>
Updating failed
</title>
<display switchtowebmode transition=none nostatus nooptions skipback clearback>
</head>
<body noscroll bgcolor="#191919" text="#42CC55" link="36d5ff"
hspace=0 vspace=0 fontsize="large">
<table cellspacing=0 cellpadding=0>
<tr>
<td width=104 height=74 valign=middle align=center bgcolor="3B3A4D">
<img src="wtv-flashrom:/ROMCache/MSNLogo.gif" width=87 height=67>
<td width=20 valign=top align=left bgcolor="3B3A4D">
<img src="wtv-flashrom:/ROMCache/Spacer.gif" width=1 height=1>
<td colspan=10 width=436 valign=middle align=left bgcolor="3B3A4D">
<font color="D6DFD0" size="+2">
<blackface>
<shadow>
<img src="wtv-flashrom:/ROMCache/Spacer.gif" width=1 height=4>
<br>
Updated failed
</shadow>
</blackface>
</font>
<tr>
<td colspan=12 width=560 height=10 valign=top align=left>
<img src="wtv-flashrom:/ROMCache/S40H1.gif" width=560 height=6>
<tr>
<td width=104 height=10 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=67 valign=top align=left>
<td width=20 valign=top align=left>
<td width=68 valign=top align=left>
<td width=20 valign=top align=left>
<form action="client:poweroff">
<tr>
<td width=104 valign=middle align=center>
<td width=20 valign=middle align=center>
<td colspan=9 width=100 height=258 valign=top align=left>
<font size=+1>
Update failed, gomennasai.
</body>
</html>

View File

@@ -0,0 +1,35 @@
// willie is just a graphical frontend to a list of ROMs
// the rest of the scripts should work if you manually link to a ROM, and actually have it.
var proxy_query = '';
if (query['flash']) delete query['flash'];
if (query['vflash']) delete query['vflash'];
for (const [key, value] of Object.entries(query)) {
proxy_query += "&" + key + "=" + value;
}
console.log(proxy_query);
var options = {
host: "wtv.zefie.com",
path: "/willie.php?pflash=" + getSessionData(socket_session_data[socket.id].ssid, 'wtv-client-rom-type') + proxy_query,
method: 'GET'
}
headers = "200 OK\nContent-type: text/html";
const req = http.request(options, function (res) {
data = '';
console.log(` * Upstream HTTP StatusCode: ${res.statusCode}`)
res.on('data', d => {
data += d;
})
res.on('end', function () {
sendToClient(socket, headers, data);
});
});
req.end();

View File

@@ -0,0 +1,24 @@
var challenge_response, challenge_header = '';
var gourl;
if (socket_session_data[socket.id].ssid !== null) {
if (request_headers['wtv-ticket']) {
if (request_headers['wtv-ticket'].length > 8) {
DecodeTicket(request_headers['wtv-ticket']);
socket_session_data[socket.id].wtvsec.ticket_b64 = request_headers['wtv-ticket'];
//socket_session_data[socket.id].secure == true;
}
} else if (socket_session_data[socket.id].wtvsec.ticket_b64 == null) {
// TODO: client should have a ticket and send it back by now, if not we should handle this correctly
}
}
headers = `200 OK
Connection: Keep-Alive
wtv-encrypted: true
wtv-ticket: `+socket_session_data[socket.id].wtvsec.ticket_b64+`
wtv-expire-all: htv-
wtv-home-url: wtv-home:/home?
wtv-visit: wtv-home:/splash?
Content-Type: text/html
`;

View File

@@ -0,0 +1,107 @@
var challenge_response, challenge_header = '';
var gourl;
if (socket_session_data[socket.id].ssid != null && !getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login')) {
var wtvsec_login = new WTVSec();
wtvsec_login = new WTVSec();
wtvsec_login.IssueChallenge();
wtvsec_login.set_incarnation(request_headers['wtv-incarnation']);
setSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login', wtvsec_login)
} else {
var wtvsec_login = getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login')
}
if (socket_session_data[socket.id].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'];
//socket_session_data[socket.id].secure = 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).substring(0,85) == client_challenge_response.substring(0,85)) {
if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) {
console.log(" * wtv-challenge-response success for "+socket_session_data[socket.id].ssid);
wtvsec_login.PrepareTicket();
//socket_session_data[socket.id].secure = true;
} else {
console.log(" * wtv-challenge-response FAILED for " + socket_session_data[socket.id].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";
}
}
}
}
if (gourl) {
headers = `200 OK
Connection: Keep-Alive
wtv-open-isp-disabled: false
wtv-visit: `+ gourl + `
Content-type: text/html`;
data = '';
}
else {
var namerand = Math.floor(Math.random() * 100000);
var nickname = 'HackTVUsr_' + namerand;
var userid = '1'+ Math.floor(Math.random() * 1000000000000000000);
var offline_user_list = CryptoJS.enc.Latin1.parse("<user-list>\n\t<user userid=\"" + userid + " user-name=\"" + nickname + "\" first-name=\"HackTV\" last-name=\"User \"" + namerand + "\" password=\"\" mail-enabled=\"true\" />\n</user-list>").toString(CryptoJS.enc.Base64);
headers = `200 OK
Connection: Keep-Alive
wtv-encrypted: true
wtv-client-time-zone: GMT -0000
wtv-client-date: `+ strftime("%a, %d %b %Y %H:%M:%S", new Date(new Date().toUTCString())) + ` GMT
wtv-country: US
wtv-language-header: en-US,en
wtv-visit: client:closeallpanels
wtv-expire-all: client:closeallpanels
wtv-offline-user-list: `+offline_user_list+`
wtv-bypass-proxy: true
wtv-ticket: `+ wtvsec_login.ticket_b64 + `
wtv-messagewatch-checktimeoffset: off
wtv-input-timeout: 14400
wtv-connection-timeout: 90
wtv-fader-timeout: 900
wtv-ssl-log-url: wtv-log:/log
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-ssl-certs-download-url: wtv-head-waiter:/ssl-cert.der
wtv-ssl-certs-checksum: 473926DC1B11F635A6B920953FDCDE6A
wtv-user-name: `+ nickname + `
wtv-human-name: `+ nickname + `
wtv-irc-nick: `+ nickname + `
wtv-home-url: wtv-home:/home?
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-connection-timeout: 180
wtv-ssl-timeout: 240
wtv-login-timeout: 7200
wtv-open-isp-disabled: false
wtv-log-url: wtv-log:/log
wtv-demo-mode: 0
wtv-wink-deferrer-retries: 3
wtv-offline-mail-enable: false
wtv-name-server: 8.8.8.8
wtv-visit: wtv-home:/splash?
Content-Type: text/html`;
}

View File

@@ -0,0 +1,74 @@
var challenge_response, challenge_header = '';
if (socket_session_data[socket.id].ssid !== null) {
var wtvsec_login = getSessionData(socket_session_data[socket.id].ssid, 'wtvsec_login');
if (request_headers['wtv-ticket']) {
if (wtvsec_login.ticket_b64 == null) {
if (request_headers['wtv-ticket'].length > 8) {
wtvsec_login.DecodeTicket(request_headers['wtv-ticket']);
wtvsec_login.ticket_b64 = request_headers['wtv-ticket'];
}
}
} else {
if (wtvsec_login) {
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).substring(0, 85) == client_challenge_response.substring(0, 85)) {
console.log(" * wtv-challenge-response success for " + socket_session_data[socket.id].ssid);
wtvsec_login.PrepareTicket();
} else {
challenge_header = "wtv-challenge: " + wtvsec_login.IssueChallenge();
}
} else {
challenge_header = "wtv-challenge: " + wtvsec_login.IssueChallenge();
}
} else {
wtvsec_login = new WTVSec();
}
}
}
/*
if (request_headers) {
var cookiedata = {};
Object.keys(request_headers).forEach(function (k) {
switch (k) {
case "wtv-capability-flags":
case "wtv-system-version":
case "wtv-client-rom-type":
case "wtv-client-bootrom-version":
case "wtv-system-chipversion":
case "wtv-system-sysconfig":
case "wtv-system-cpuspeed":
cookiedata[k] = request_headers[k];
break;
}
});
}
*/
if (challenge_header != '') {
headers = `200 OK
Connection: Keep-Alive
Expires: Wed, 09 Oct 1991 22:00:00 GMT
wtv-expire-all: wtv-head-waiter:
`+ getServiceString('wtv-log') + `
wtv-log-url: wtv-log:/log
`+ challenge_header + `
wtv-relogin-url: wtv-1800:/preregister?relogin=true
wtv-reconnect-url: wtv-1800:/preregister?reconnect=true
wtv-visit: wtv-head-waiter:/login-stage-two?
Content-type: text/html`;
data = '';
} else {
headers = `200 OK
Connection: Keep-Alive
Expires: Wed, 09 Oct 1991 22:00:00 GMT
wtv-expire-all: wtv-head-waiter:
wtv-expire-all: wtv-1800:
wtv-visit: wtv-1800:/preregister?relogin=true
Content-type: text/html`;
}

View File

@@ -0,0 +1,49 @@
headers =`200 OK
Connection: Keep-Alive
wtv-expire-all: wtv-home:/splash
Content-type: text/html`
if (getSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption')) {
var cryptstatus = "<a href='client:showalert?message=Your%20WebTV%20Unit%20sent%20us%20a%20request%20for%20SECURE%20ON%2C%20but%20did%20not%20encrypt%20any%20data%2C%20nor%20will%20accept%20it.%20However%2C%20we%20send%20the%20wtv-encryption%20flag%20to%20roll%20with%20it%2C%20enabling%20%27psuedo-encryption%27.%20Nothing%20is%20encrypted%2C%20but%20the%20box%20trusts%20us.%20This%20will%20probably%20go%20away%20if%20you%20reload%20or%20change%20pages.&buttonaction1=client:donothing&buttonlabel1=Oh%2C%20okay...'>Psuedo-encrypted</a>";
} else {
var cryptstatus = ((socket_session_data[socket.id].secure === true) ? "Encrypted" : "Not Encrypted")
}
data =`<html>
<head>
<title>Home for minisrv</title>
<DISPLAY showwhencomplete options showoptions noscroll>
</head>
<body bgcolor="black" link="gold" vlink="gold" alink="gold" text="gold">
<script>
function go() {
location.href=document.access.url.value;
}
</script>
<h2>Welcome to `+ z_title + `</h2>
<h3>Encryption Status: `+cryptstatus+`</h3>`
data += `<h4>Working stuff</h4>
<a href="client:relog">client:relog (direct)</a><br>
<a href="wtv-tricks:/blastcache?return_to=wtv-home:/home">Clear Cache</a><br>
<form name=access onsubmit="go()">
<input name=url `;
if (query['url']) {
data += "value='"+unescape(query['url'])+"'";
}
data += `width=250 bgcolor=#444444 text=#ffdd33 cursor=#cc9933 selected>
<input type=submit value="Access URL" borderstyle="ButtonBorder2.bif">
</form>
<h4>zefie's server only</h4>
<a href="wtv-home:/zefie">zefie's stuff and things</a>
<h4>Test Stuff (probably broken)</h4>
<a href="wtv-update:/update?">HackTV Updater Test</a><br>
<a href="wtv-flashrom:/willie">Ultra Willies</a><br>
<a href="client:showalert?message=If%20you%20choose%20to%20disconnect%20and%20return%20to%20HackTV%20home%2C%20you%20may%20not%20be%20able%20to%20reconnect%20to%20the%20update%20server%20until%20you%20power%20cycle%20your%20box.%3Cbr%3E%3Cbr%3EAre%20you%20sure%20you%20would%20like%20to%20go%20offline%3F&buttonlabel1=No&buttonaction1=client:donothing&buttonlabel2=Yes&buttonaction2=wtv-tricks%3A%2Fgo-offline%3Ftitle%3DHackTV%2520Home">Disconnect and go to HackTV Home</a><br>
</body>
</html>`

View File

@@ -0,0 +1,25 @@
200 OK
Connection: Keep-Alive
wtv-expire-all: wtv-
Content-type: text/html
<html>
<title>Engaging zefie...</title>
<head>
<display nooptions showwhencomplete nostatus skipback clearback fontsize=medium>
<meta
http-equiv=refresh
content="5; url=wtv-home:/home?"
>
</head>
<body bgcolor="#000000" text="#449944">
<bgsound src="file://ROM/Sounds/Splash.mid">
<center>
<img src="wtv-star:/images/HackTVLogo.gif">
<br>
<spacer type=block height=98 width=21>
<br>
</center>
</body>
</html>

View File

@@ -0,0 +1,18 @@
<html>
<head>
<title>zefie's playground</title>
<DISPLAY showwhencomplete options showoptions noscroll>
</head>
<body bgcolor="black" link="gold" vlink="gold" alink="gold" text="gold">
<h3>zefie's playgrounf</h3>
Moved these off the main page to clean it up :)<br>
The stuff on this page is probably useless to you so why not move it off the main page.
<ul>
<li><a href="wtv-music:/content/index.html">Music Collection</a></li>
<li><a href="wtv-music:/midi/index.html">Matt Test</a></li>
<ul>
</body>
</html>

View File

@@ -0,0 +1,18 @@
// write posted log data to disk. should be decrypted by this point (if it was encrypted) if the crypto stream didn't break
if (request_headers['post_data']) {
var fullpath = __dirname + "/ServiceLogPost/" + Math.floor(new Date().getTime() / 1000) + "_" + query['type'];
if (socket_session_data[socket.id].ssid) fullpath += "_" + socket_session_data[socket.id].ssid;
fullpath = fullpath.replace(/\\/g, "/");
fs.writeFileSync(fullpath, request_headers['post_data'].toString(CryptoJS.enc.Hex), "Hex");
console.log("Wrote POST log data from", socket_session_data[socket.id].ssid, "to", fullpath, "on", socket.id);
}
headers = `200 OK
Connection: Keep-Alive
Content-length: 0`;
data = '';

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -0,0 +1,11 @@
// Allow URL access outside our trusted minisrv
if (query['url']) var url = unescape(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
wtv-visit: `+url+`
Content-type: text/html`
data = '';

View File

@@ -0,0 +1,23 @@
headers = `200 OK
wtv-expire-all: wtv-
wtv-expire-all: http
Content-type: text/html`
var visit_url = null;
if (request_headers['Referer']) visit_url = request_headers['Referer'];
else if (query['return_to']) visit_url = query['return_to'];
else visit_url = "client:goback";
data = `<html>
<head>
<meta
http-equiv=refresh
content="1; url=`+ visit_url +`"
>
<body bgcolor="black" text="gold" link="gold" vlink="gold" alink="gold">
Successfully expired service URL cache<br>
Any previously cached pages should be reloaded from the network.<br><br>
<a href="`+visit_url+`">Not loading? Click here.</a>
</body>
</html>`;

View File

@@ -0,0 +1,42 @@
headers = `200 OK
wtv-noback-all: wtv-
wtv-expire-all: wtv-
Content-type: text/html
wtv-service: reset
`+getServiceString('wtv-1800');
// HackTV Homepage is default
var url="file://Disk/Browser/Games/Games.html";
if (query['url']) {
url = query['url'];
}
data = `<html>
<head>
<title>Going offline...</title>
<DISPLAY notvaudio allowoffline hideoptions switchtowebmode>
<body bgcolor="black" text="gold" onload="onLoad">
<script type="text/javascript">
function disconnect() {
location.href = "client:HangUpPhone?allow-reconnect=no";
}
function go(url) {
location.href = url;
}
if (window.location) {
setTimeout('disconnect()',100);
setTimeout('go("`+url+`")',200);
}
</script>
<br><br>
`
if (query['title']) {
data += "Going offline and loading "+decodeURI(query['title'])+", please wait!";
} else {
data += "Please wait a moment.";
}
data += "</body>\n</html>";

View File

@@ -0,0 +1,10 @@
GROUP name=Browser version=!VERS! root=file://Disk/Browser/Games/
display Updating HackTV Files...
sync Games.html content/htvupdate/Games/Games.html
sync cSetup.html content/htvupdate/Games/cSetup.html
END-GROUP
GROUP name=Browser version=!VERS! root=file://Disk/Browser/MattMan/
display Updating HackTV Files...
sync Tricks/tricks.html content/htvupdate/MattMan/Tricks/tricks.html
END-GROUP

View File

@@ -0,0 +1,122 @@
<html>
<head>
<title>HackTV Home</title>
<DISPLAY notvaudio allowoffline options hideoptions switchtowebmode>
<script>
if (document.images) {
pic1 = new Image();
pic1.src = 'mame.jpg';
pic2 = new Image();
pic2.src = 'doom.jpg';
pic3 = new Image();
pic3.src = 'checkers.jpg';
pic4 = new Image();
pic4.src = 'music.jpg';
pic5 = new Image();
pic5.src = 'file1.jpg';
pic6 = new Image();
pic6.src = 'tricks.jpg';
pic7 = new Image();
pic7.src = 'wtvcity2.gif';
pic8 = new Image();
pic8.src = 'tic.jpg';
pic9 = new Image();
pic9.src = 'html.jpg';
pic10 = new Image();
pic10.src = 'cSetup.jpg';
pic11 = new Image();
pic11.src = 'msn.jpg';
pic12 = new Image();
pic12.src = 'wtvcity.gif';
pic13 = new Image();
pic13.src = 'tvh.jpg';
pic14 = new Image();
pic14.src = 'cross.jpg';
pic15 = new Image();
pic15.src = 'ydkj.jpg';
} else {
pic1 = pic2 = pic3 = pic4 = pic5 = pic6 = pic7 = pic8 = pic9 = pic10 = pic11 = pic12 = pic13 = pic14 = document.r1 = "";
}
</script>
</head>
<body bgcolor="#281f26" background="Pattern_Games.gif" text="#cbcbcb" alink="#4489a8" vlink="#4489a8" link="#4489a8" hspace="0" vspace="0" fontsize="large">
<table cellspacing="0" cellpadding="0" cellborder="0">
<tr>
<td background="GamesShadowLogo.gif" width="104" height="80" valign="top" align="left"><spacer type="block" WIDTH="11" HEIGHT="11"><br>
<spacer type="block" WIDTH="10" HEIGHT="1"> <a href="client:showalert?message=Credits:<br><br>eMac<br>MattMan69<br>Outa<br><br>Oct.%202014" onmouseover="document.r1.src=pic7.src;"><img src="file://disk/browser/Games/hacktv4.gif" width="90" height="69"></a></td>
<td width="456" height="80" valign="top" align="center"><img src="GamesBanner.gif" width="456" height="50"><br>
<img src="file://rom/tvimages/Shadow_Horizontal.gif" width="456" height="6"></td></tr></table>
<table cellspacing="0" cellpadding="0">
<tr>
<td colspan="3" height="6"></td>
</tr>
<tr>
<td abswidth="40">&nbsp;</td>
<td>
<table>
<td abswidth="200"><font size="1" color="#ffcf69"><shadow>Play Games</shadow></font></td>
<tr>
<td width="150" height="40" valign="top" align="left"><font size="1" color="#989898">
<li><a href="file://disk/browser/Games/mame.html" onmouseover="document.r1.src=pic1.src;">M.A.M.E.</a>
<li><a href="file://disk/browser/Games/doom.html" onmouseover="document.r1.src=pic2.src;">Doom</a>
<!--li><a href="client:boota?partition=JackROM&size=5242880&source=file://disk/Doom/jackWebTVplus.o">YDKJ</a-->
<li><a href="file://disk/browser/Games/Checkers/index.html" onmouseover="document.r1.src=pic3.src;">Checkers</a>
<li><a href="file://disk/browser/Games/crossword/general/index.html" onmouseover="document.r1.src=pic14.src;">Crosswords</a>
<li><a href="file://disk/browser/Games/tictac/index.html" onmouseover="document.r1.src=pic8.src;">TicTacToe</a></td></tr>
<tr>
<td><font size="1" color="#ffcf69"><shadow>Play Music</shadow></font></td>
<tr>
<td width="200" height="35" valign="top" align="left"><font size="1" color="#989898">
<li><a href="file://disk/Music/index.html" onmouseover="document.r1.src=pic4.src;">WebTV Music</a>
<li><a href="file://disk/Music/Rock/index.html" onmouseover="document.r1.src=pic4.src;">Rock Music</a></td></tr>
<tr>
<td><font size="1" color="#ffcf69"><shadow>Tech Area</shadow></font></td>
<tr>
<td width="150" height="60" valign="top" align="left"><font size="1" color="#989898">
<li><a href="file://disk/Browser/MattMan/techinfo.html" onmouseover="document.r1.src=pic5.src;">Tech Info</a>
<li><a href="file://disk/Browser/MattMan/html/index.html" onmouseover="document.r1.src=pic9.src;">HTML Viewer</a>
<li><a href="file://disk/Browser/MattMan/Tricks/tricks.html" onmouseover="document.r1.src=pic6.src;">HackTV Tricks</a>
<li><a href="file://disk/Browser/Games/cSetup.html" onmouseover="document.r1.src=pic10.src;">Connect Setup</a>
<li><font size="-1"><sup>NEW!</sup</font><a href="client:ConfirmConnectSetup?machine=192.168.11.8&port=1615&ServiceType=custom&useEncryption=true&useDirectConnection=true&Connect=Connect"> Check for Updates</a>
<!--li>Boot <a href="client:boota?NA" onmouseover="document.r1.src=pic11.src;">2.9</a> - <a href="client:boota?NA" onmouseover="document.r1.src=pic12.src;" onmouseout="document.r1.src=pic13.src;">2.1</a-->
</td></tr>
</table>
</td>
<td valign="top">
<table>
<td valign="top">
<center><font size="2" color="#ffcf69"><shadow>
<a href="file://rom/InternalOnly/SetClock.html" onmouseover="document.r1.src=pic7.src;">&date;</a></shadow></font></center><br>
<img src="wtvcity2.gif"
width="260" height="193"
border="2" name="r1"
alt="wtv" />
</td></tr>
</td>
</tr>
</table>
<table>
<tr>
<td align="right" width="252">
<FORM action="client:gototvhome">
<FONT COLOR="#E7CE4A" SIZE=-1><SHADOW>
<INPUT TYPE=BUTTON onclick="location.href='client:activ'" BORDERIMAGE="file://ROM/tvimages/TVButtonBorder.bif" VALUE="Connect" USESTYLE WIDTH=120 onmouseover="document.r1.src=pic7.src;">
</SHADOW></FONT>
<FONT COLOR="#E7CE4A" SIZE=-1><SHADOW>
<INPUT TYPE=SUBMIT BORDERIMAGE="file://ROM/tvimages/TVButtonBorder.bif" VALUE="TV Home" USESTYLE onmouseover="document.r1.src=pic13.src;" WIDTH=120>
</SHADOW></FONT></FORM>
</span>
</td>
</tr>
</table>
</td>
</table>
</body>
</html>

View File

@@ -0,0 +1,143 @@
<html>
<head>
<title>Connect Setup v2.1</title>
<DISPLAY noscroll allowoffline hideoptions notvaudio switchtowebmode>
</head>
<body bgcolor="#281f26" background="Pattern_Games.gif" text="#ffcf69" link="#4489a8"
hspace="0" vspace="0" fontsize="large" noscroll hideoptions>
<table cellspacing="0" cellpadding="0" cellborder="0">
<tr>
<td background="cSetupShadowLogo.gif" width="104" height="80" valign="top" align="left"><spacer type="block" WIDTH="11" HEIGHT="11"><br>
<spacer type="block" WIDTH="10" HEIGHT="1"> <a href="file://disk/Browser/Games/Games.html"><img src="hacktv4.gif" width="87"
height="67"></a> </td>
<td width="456" height="80" valign="top" align="center"><img src="cSetup.GIF"
width="456" height="50"><br>
<img src="file://rom/tvimages/Shadow_Horizontal.gif" width="456" height="6"> </td>
</tr>
</table>
<h2>
&nbsp;Connection Setup v2.1
<hr>
</h2>
<script>
function updateService() {
srv = document.connect.preset[document.connect.preset.selectedIndex].value;
switch (srv) {
case "hacktv":
document.connect.machine.value="192.168.1.3"
break;
case "wni-prod":
document.forms[0].machine.value="10.0.0.1"
break;
case "wni-int":
document.forms[0].machine.value="10.0.128.1"
break;
case "zefie":
document.forms[0].machine.value="home.zef.pw"
break;
case "mattman":
document.forms[0].machine.value="turdinc.kicks-ass.net"
break;
}
}
</script>
<form name="connect" action="client:ConfirmConnectSetup">
<table width=100% cellspacing=1 cellpadding=0>
<tr>
<td colspan=3>
&nbsp;<font color=#4489a8>Presets:</font>
</td>
<td>
<select name="preset" onchange="updateService()" selected>
<option value="hacktv">HackTV Default</option>
<option value="wni-prod">WNI ANI Production Default</option>
<option value="wni-int">WNI ANI Internal Default</option>
<option value="zefie">zefie's Server</option>
<option value="mattman">MattMan's Server</option>
</select>
<tr>
<td colspan=3>
&nbsp;<font color=#4489a8>Service:</font>
<tr>
<td width=15></td>
<td height=2>
<tr>
<td height=2>
<tr>
<td>
<td>
<input name=serviceType type=radio bgcolor=#444444 value=custom checked>
<td>
&nbsp;Custom:&nbsp;&nbsp;&nbsp;
<td>
Address:
<input size=16 ASCIIONLY name=machine bgcolor=#444444 text=#4489a8 cursor=#cc9933 value="192.168.1.3">
&nbsp;&nbsp;&nbsp;Port:
<input size=5 ASCIIONLY NUMBERS name=port bgcolor=#444444 text=#4489a8 cursor=#cc9933 value="1615">
</table>
<table cellspacing=1 cellpadding=0>
<tr>
<td height=6>
<tr>
<td colspan=3>
&nbsp;<font color=#4489a8>Service options:</font><br><br>
<tr>
<td width=15>
<td>
<input type=checkbox name=forceSignup value=true>
<td>
&nbsp;Force signup
<td width=390 align=right>
<form action="client:GoToPhoneSetup">
<font color="#E7CE4A" size=-2><shadow>&nbsp;
<input
type=submit
borderimage="file://ROM/tvimages/TVButtonBorder.bif"
value="Phone Settings"
name="Phone Settings"
usestyle
width=170> &nbsp;
</shadow></font>
</form>
<tr>
<td height=2>
<tr>
<td width=15>
<td>
<input type=checkbox name=useEncryption value=true checked>
<td>
&nbsp;Use encryption
<tr>
<td height=2>
<tr>
<td width=15>
<td>
<input type=checkbox name=useDirectConnection value=true>
<td width=300>
&nbsp;Use direct connection
<td width=390 align=right>
<font color="#E7CE4A" size=-2><shadow>&nbsp;
<input
type=submit
borderimage="file://ROM/tvimages/TVButtonBorder.bif"
value="Connect"
name="Connect"
usestyle
width=170> &nbsp;
</shadow></font>
</table>
<br><br>
<table width=100%>
<tr>
<td align=left width=100% height=70>
<td align=right width=100% height=70>
</table>
</form>
</body>
</html>

View File

@@ -0,0 +1,52 @@
<html>
<head>
<title>HackTV Tricks</title>
<SCRIPT language="JavaScript">
function checkWord(e) {
//e.preventDefault();
var readersEntry = document.pform.pword.value;
if (readersEntry.toLowerCase() == "seqret1") {
window.location.href="file://disk/Browser/MattMan/Tricks/tricks2.html" ;
} else {
document.pform.pword.value="";
alert ("Bad password, access denied!!!");
}
return false;
}
</SCRIPT>
</head>
<body bgcolor="#191919" text="#44cc55" link="36d5ff" vlink="36d5ff" vspace=0>
<sidebar width=20%>
<img src="tricksside.jpg">
</sidebar>
<br>
<br>
<br>
<h1>HackTV Tricks</h1>
<br>
<br>
Hello, citizen! You have reached the "tricks" page. This is meant
for use by 1337 hackers of WebTV networks. If you remember the
right "tricks" you know the password and may enter the access password now.
Otherwise hit the "back" button on your keyboard / remote now!
<p>
Pages beyond this area could cause bodily harm or may render your WebTV useless!
<p>
Remember, the computer is your friend.
<p>
<FORM name="pform" onsubmit="checkWord(e);">
<input name="pword" id="password" bgcolor=#444444 text=#ffdd33 cursor=#cc9933 type="password" size="16">&nbsp;
<input name="button1" value="Enter" onclick="checkWord()" type="button">
</FORM>
</body>
</html>

View File

@@ -0,0 +1,39 @@
var content_dir = service_dir + '/content/';
var diskmap_dir = content_dir + '/diskmaps/';
if (initial_headers['post_data']) {
console.log(initial_headers['post_data'].toString('CryptoJS.enc.Latin1'))
}
if (query['diskmap']) {
if (fs.lstatSync(diskmap_dir + query['diskmap'] + ".txt")) {
var diskmap_data = fs.readFileSync(diskmap_dir + query['diskmap'] + ".txt").toString();
// try to parse diskmap and get an accurate timestamp for webtv versioning
// check all files in the diskmap and return the timestamp of the most recently modified
data = '';
var latest_file_ts = 0;
diskmap_data.split("\n").forEach(function (v) {
if (v.indexOf(" sync ") != -1) {
v = v.trim();
var vcon = v.substring(v.indexOf("content/"));
vcon = vcon.replace("content/", content_dir)
var vconstat = Math.floor(fs.lstatSync(vcon).mtimeMs / 1000);
if (vconstat > latest_file_ts) {
latest_file_ts = vconstat
}
// todo read client post and only give whats needed
// instead of all that is available
// vconstat has the mtime of each file, we need to parse the post_data
data += v + "\n";
} else {
data += v + "\n";
}
});
//data = diskmap_data.replace("!VERS!", latest_file_ts);
}
}
headers = `200 OK
Content-type: text/download-list`

View File

@@ -0,0 +1,48 @@
<html>
<head>
<meta
http-equiv=refresh
content="0;url=client:Fetch?group=Browser&source=wtv-update:/sync%3Fdiskmap%3Dhtvupdate&message=Downloading%20updates..."
>
<display downloadsuccess="client:ShowAlert?message=HackTV%20Update%20was%20successful%21&buttonlabel2=Go%20to%20HackTV&action2=wtv-tricks:/go-offline%3Ftitle%3DHackTV%20Home&buttonlabel1=Okay&buttonaction1=client:goback&image=file://disk/browser/Games/hacktv2.gif&noback=true" downloadfail="client:ShowAlert?message=HackTV%20Update%20failed...&buttonlabel1=Okay...&buttonaction1=client:goback&image=file://disk/browser/Games/hacktv2.gif&noback=true">
<title>HackTV Updater</title>
</head>
<body bgcolor=#0 text=#42CC55 fontsize=large hspace=0 vspace=0>
<table cellspacing=0 cellpadding=0>
<tr>
<td width=104 height=74 valign=middle align=center bgcolor=3B3A4D>
<img src="file://Disk/Browser/Games/hacktv4.gif" width=86 height=64>
<td width=20 valign=top align=left bgcolor=3B3A4D>
<spacer>
<td colspan=2 width=436 valign=middle align=left bgcolor=3B3A4D>
<font color=D6DFD0 size=+2><blackface><shadow>
<spacer type=block width=1 height=4>
<br>
Download HackTV Updates
</shadow>
</blackface>
</font>
<tr>
<td width=104 height=20>
<td width=20>
<td width=416>
<td width=20>
<tr>
<td colspan=2>
<td>
<font size=+1>
Your HackTV Unit is downloading updates.
<p>This may take a while.
</font>
<tr>
<td colspan=2>
<td>
<br><br>
<font color=white>
<progressindicator name="downloadprogress"
message="Preparing..."
height=40 width=250>
</font>
</table>
</body>
</html>

View File

@@ -0,0 +1,4 @@
200 OK
Content-Type: text/url
client:ShowAlert?message=HackTV%20Update%20was%20successful%21&buttonlabel2=Go%20to%20HackTV&action2=file%3A%2F%2FDisk%2FBrowser%2FGames%2FGames.html&buttonlabel1=Okay&buttonaction1=client:goback&image=file://disk/browser/Games/hacktv2.gif&noback=true

791
zefie_wtvp_minisrv/app.js Normal file
View File

@@ -0,0 +1,791 @@
'use strict';
const fs = require('fs');
const http = require('http');
const https = require('https');
const strftime = require('strftime');
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 zdebug = true;
var ports = [];
var service_vault_dir = __dirname + "/ServiceVault";
String.prototype.reverse = function () {
var splitString = this.split("");
var reverseArray = splitString.reverse();
var joinArray = reverseArray.join("");
return joinArray;
}
function getServiceString(service) {
if (service === "all") {
var out = "";
Object.keys(services_configured.services).forEach(function (k) {
out += services_configured.services[k].toString() + "\n";
});
return out;
} else {
if (!services_configured.services[service]) {
throw ("SERVICE ERROR: Attempted to provision unconfigured service: " + service)
} else {
return services_configured.services[service].toString();
}
}
}
var ssid_data = new Array();
var socket_buffer = new Array();
var socket_session_data = new Array();
var script_processing_timeout = 10; // seconds
function getSessionData(ssid, key = null) {
if (typeof (ssid_data[ssid]) === 'undefined') return null;
if (key == null) return ssid_data[ssid];
else if (ssid_data[ssid][key]) return ssid_data[ssid][key];
else return null;
}
function setSessionData(ssid, key, value) {
if (typeof (ssid_data[ssid]) === 'undefined') ssid_data[ssid] = new Array();
ssid_data[ssid][key] = value;
}
function getFile(path, deps = false) {
var dir = null;
if (deps) dir = __dirname + "/ServiceDeps/";
else dir = __dirname + "/ServiceVault/";
if (fs.lstatSync(dir + path).isFile()) {
return fs.readFileSync(dir + path, {
encoding: null,
flags: 'r'
});
}
return null;
}
function getFileExt(path) {
return path.reverse().split(".")[0].reverse();
}
function 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;
}
return new Array(headers, data);
}
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";
}
return mime.lookup(path);
}
async function processPath(socket, path, request_headers = new Array(), query = new Array(), service_name) {
var headers, data = null;
var request_is_direct_file = false;
var request_is_async_js = false;
path = path.replace(/\\/g, "/");
try {
try {
// try to see if the exact request exists
if (fs.lstatSync(path).isFile()) {
request_is_direct_file = true;
}
} catch (e) {
// do nothing its fine
}
if (request_is_direct_file) {
// file exists, read it and return it
console.log(" * Found " + path + " to handle request (Direct File Mode) [Socket " + socket.id +"]");
var contype = getConType(path);
request_is_async_js = true;
headers = "200 OK\n"
headers += "Content-Type: " + contype;
fs.readFile(path, null, function (err, data) {
sendToClient(socket, headers, data);
});
} else if (fs.existsSync(path + ".txt")) {
// raw text format, entire payload expected (headers and content)
console.log(" * Found " + path + ".txt to handle request (Raw TXT Mode) [Socket " + socket.id +"]");
var file_raw = fs.readFileSync(path + ".txt").toString();
if (file_raw.indexOf("\n\n") > 0) {
var file_raw_split = file_raw.split("\n\n");
headers = file_raw_split[0];
file_raw_split.shift();
data = file_raw_split.join("\n");
} else if (file_raw.indexOf("\r\n\r\n") > 0) {
var file_raw_split = file_raw.split("\r\n\r\n");
headers = file_raw_split[0].replace(/\r/g, "");
file_raw_split.shift();
data = file_raw_split.join("\r\n");
} else {
headers = fdat;
}
} else if (fs.existsSync(path + ".async.js")) {
// asynchronous js scripting, process with vars, must manually call sendToClient(socket, headers, data);
// (hint: socket is already defined)
// loaded script will have r/w access to any JavaScript vars this function does.
// any query args are in an array named 'query'
request_is_async_js = true;
console.log(" * Found " + path + ".async.js to handle request (Async 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;
socket_session_data[socket.id].starttime = Math.floor(new Date().getTime() / 1000);
fs.readFile(path + ".async.js", "utf-8", function (err, data) {
eval(data);
});
} else if (fs.existsSync(path + ".js")) {
// synchronous js scripting, process with vars, must set 'headers' and 'data' appropriately.
// loaded script will have r/w access to any JavaScript vars this function does.
// any query args are in an array named 'query'
console.log(" * Found " + 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;
socket_session_data[socket.id].starttime = Math.floor(new Date().getTime() / 1000);
var jscript_eval = fs.readFileSync(path + ".js").toString();
eval(jscript_eval);
}
else if (fs.existsSync(path + ".html")) {
// Standard HTML with no headers, WTV Style
console.log(" * Found " + path + ".html to handle request (HTML Mode) [Socket " + socket.id +"]");
data = fs.readFileSync(path + ".html").toString();
headers = "200 OK\n"
headers += "Content-Type: text/html"
} else {
var errpage = doErrorPage(404);
headers = errpage[0];
data = errpage[1];
}
// 'headers' and 'data' should both be set with content by this point!
if (headers == null && !request_is_async_js) {
var errpage = doErrorPage(400);
headers = errpage[0];
data = errpage[1];
console.log(" * Scripting or Data error: Headers were not defined. (headers,data) as follows:")
console.log(socket.id,headers,data)
}
if (data === null) {
data = '';
}
} catch (e) {
var errpage = doErrorPage(400);
headers = errpage[0];
data = errpage[1] + "<br><br>The interpreter said:<br><pre>" + e.toString() + "</pre>";
console.log(e);
}
if (!request_is_async_js) {
sendToClient(socket, headers, data);
}
}
async function processURL(socket, request_headers) {
if (request_headers === null) {
return;
}
var shortURL, headers, data = "";
var query = new Array();
if (request_headers['request_url']) {
if (request_headers['request_url'].indexOf('?') >= 0) {
shortURL = request_headers['request_url'].split('?')[0];
var qraw = request_headers['request_url'].split('?')[1];
if (qraw.length > 0) {
qraw = qraw.split("&");
for (let i = 0; i < qraw.length; i++) {
var k = qraw[i].split("=")[0];
if (k) {
query[k] = qraw[i].split("=")[1];
}
}
console.log(" * Request query:", query);
}
} else {
shortURL = request_headers['request_url'];
}
if (shortURL.indexOf(':/') >= 0 && shortURL.indexOf('://') < 0) {
var ssid = socket_session_data[socket.id].ssid;
if (ssid == null) {
ssid = request_headers['wtv-client-serial-number'];
}
var reqverb = "Request";
if (request_headers['encrypted'] || request_headers['secure']) {
reqverb = "Encrypted " + reqverb;
}
if (request_headers['psuedo-encryption']) {
reqverb = "Psuedo-encrypted " + reqverb;
}
if (ssid != null) {
console.log(" * " + reqverb + " for " + request_headers['request_url'] + " from WebTV SSID " + ssid, 'on', socket.id);
} else {
console.log(" * " + reqverb + " for " + request_headers['request_url'], 'on', socket.id);
}
// assume webtv since there is a :/ in the GET
var service_name = shortURL.split(':/')[0];
var urlToPath = service_vault_dir.replace(/\\/g, "/") + "/" + service_name + "/" + shortURL.split(':/')[1];
console.log(" * Incoming headers on socket ID", socket.id, request_headers);
processPath(socket, urlToPath, request_headers, query, service_name);
} else if (shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0) {
doHTTPProxy(socket, request_headers);
} else {
// error reading headers (no request_url provided)
var errpage = doErrorPage(400);
headers = errpage[0];
data = errpage[1]
socket_session_data[socket.id].close_me = true;
sendToClient(socket, headers, data);
}
}
}
async function doHTTPProxy(socket, request_headers) {
console.log(socket.id, request_headers);
var request_type = request_headers['request'].indexOf('https://') ? 'http' : 'https'
switch (request_type) {
case "https":
var proxy_agent = https;
break;
case "http":
var proxy_agent = http;
break;
}
var request_data = new Array();
request_data['method'] = request_headers['request'].split(' ')[0];
var request_url_split = request_headers['request'].split(' ')[1].split('/');
request_data['host'] = request_url_split[2];
if (request_data['host'].indexOf(':') > 0) {
request_data['port'] = request_data['host'].split(':')[1];
request_data['host'] = request_data['host'].split(':')[0];
} else {
if (request_type === 'https') request_data['port'] = 443;
else request_data['port'] = 80;
}
for (var i = 0; i < 3; i++) request_url_split.shift();
request_data['path'] = "/" + request_url_split.join('/');
if (request_data['method'] && request_data['host'] && request_data['path']) {
var options = {
host: request_data['host'],
port: request_data['port'],
path: request_data['path'],
method: request_data['method'],
headers: {
"User-Agent": request_headers['User-Agent'] || "WebTV"
}
}
if (request_headers['post_data']) {
if (request_headers['Content-type']) options.headers['Content-type'] = request_headers['Content-type'];
if (request_headers['Content-length']) options.headers['Content-length'] = request_headers['Content-length'];
}
if (services_configured.services[request_type].use_external_proxy && services_configured.services[request_type].external_proxy_port) {
options.host = services_configured.services[request_type].external_proxy_host;
options.port = services_configured.services[request_type].external_proxy_port;
options.path = request_headers['request'].split(' ')[1];
options.headers['Host'] = request_data['host'];
}
const req = proxy_agent.request(options, function (res) {
var data = [];
res.on('data', d => {
data.push(d);
})
res.on('error', function (err) {
console.log(" * Unhandled Proxy Request Error:", err);
});
res.on('end', function () {
var data_hex = Buffer.concat(data).toString('hex');
console.log(` * Proxy Request ${request_type.toUpperCase()} ${res.statusCode} for ${request_headers['request']}`)
var headers = new Array();
headers['http_response'] = res.statusCode + " " + res.statusMessage;
headers['wtv-connection-close'] = false;
if (res.headers['server']) headers['Server'] = res.headers['server'];
if (res.headers['connection']) headers['Connection'] = res.headers['connection'] == "close" ? "Keep-Alive" : "Close";
if (res.headers['date']) headers['Date'] = res.headers['date'];
if (res.headers['content-type']) headers['Content-type'] = res.headers['content-type'];
if (res.headers['cookie']) headers['Cookie'] = res.headers['cookie'];
// content-length is best auto-calculated
//if (res.headers['content-length']) headers['Content-Length'] = res.headers['content-length'];
if (res.headers['vary']) headers['Vary'] = res.headers['vary'];
if (res.headers['location']) headers['Location'] = res.headers['location'];
if (data_hex.substring(0, 8) == "0d0a0d0a") data_hex = data_hex.substring(8);
if (data_hex.substring(0, 4) == "0a0a") data_hex = data_hex.substring(4);
sendToClient(socket, headers, Buffer.from(data_hex,'hex'));
});
}).on('error', function (err) {
var errpage, headers, data = null;
if (err.code == "ENOTFOUND") {
errpage = doErrorPage(400,`The publisher ${err.hostname} is unknown.`);
} else {
console.log(" * Unhandled Proxy Request Error:", err);
errpage = doErrorPage(400);
}
headers = errpage[0];
data = errpage[1];
sendToClient(socket, headers, data);
});;
if (request_headers['post_data']) {
req.write(Buffer.from(request_headers['post_data'].toString(CryptoJS.enc.Hex), 'hex'), function () {
req.end();
});
} else {
req.end();
}
}
}
async function headerStringToObj(headers, response = false) {
var inc_headers = 0;
var headers_obj = new Array();
var headers_obj_pre = headers.split("\n");
headers_obj_pre.forEach(function (d) {
if (/^SECURE ON/.test(d) && !response) {
headers_obj['secure'] = true;
//socket_session_data[socket.id].secure_headers = true;
} else if (/^([0-9]{3}) $/.test(d.substring(0, 4)) && response) {
headers_obj['http_response'] = d.replace("\r", "");
} else if (/^(GET |PUT |POST)$/.test(d.substring(0, 4)) && !response) {
headers_obj['request'] = d.replace("\r", "");
headers_obj['request_url'] = decodeURI(d.split(' ')[1]).replace("\r", "");
} else if (d.indexOf(":") > 0) {
var d_split = d.split(':');
var header_name = d_split[0];
if (headers_obj[header_name] != null) {
header_name = header_name + "_" + inc_headers;
inc_headers++;
}
d_split.shift();
d = d_split.join(':');
headers_obj[header_name] = (d).replace("\r", "");
if (headers_obj[header_name].substring(0, 1) == " ") {
headers_obj[header_name] = headers_obj[header_name].substring(1);
}
}
});
return headers_obj;
}
async function sendToClient(socket, headers_obj, data) {
var headers = "";
if (typeof (headers_obj) === 'string') {
// string to header object
headers_obj = await headerStringToObj(headers_obj, true);
}
// add Connection header if missing, default to Keep-Alive
if (!headers_obj['Connection']) {
headers_obj['Connection'] = "Keep-Alive";
headers_obj = moveObjectElement('Connection', 'http_response', headers_obj);
}
// encrypt if needed
if (socket_session_data[socket.id].secure == true) {
var clen = null;
if (typeof data.length !== 'undefined') {
clen = data.length;
} else if (typeof data.byteLength !== 'undefined') {
clen = data.byteLength;
}
headers_obj['wtv-encrypted'] = 'true';
headers_obj = moveObjectElement('wtv-encrypted', 'Connection', headers_obj);
if (clen > 0) {
console.log(" * Encrypting response to client ...")
var enc_data = socket_session_data[socket.id].wtvsec.Encrypt(1, data);
data = enc_data;
}
}
// set content-length after encryption
if (!headers_obj["Content-length"] && !headers_obj["Content-Length"]) {
if (typeof data.length !== 'undefined') {
headers_obj['Content-Length'] = data.length;
} else if (typeof data.byteLength !== 'undefined') {
headers_obj['Content-Length'] = data.byteLength;
}
}
// header object to string
console.log(" * Outgoing headers on socket ID", socket.id, headers_obj);
Object.keys(headers_obj).forEach(function (k) {
if (k == "http_response") {
headers += headers_obj[k] + "\r\n";
} else {
if (k.indexOf('_') >= 0) {
var j = k.split('_')[0];
headers += j + ": " + headers_obj[k] + "\n";
} else {
headers += k + ": " + headers_obj[k] + "\n";
}
}
});
// send to client
var toClient = null;
if (typeof data == 'string') {
toClient = headers + "\n" + data;
socket.write(toClient);
} else if (typeof data == 'object') {
if (socket_session_data[socket.id].secure_headers == true) {
// encrypt headers
var enc_headers = socket_session_data[socket.id].wtvsec.Encrypt(1, headers + "\n");
socket.write(new Uint8Array(concatArrayBuffer(enc_headers, data)));
} else {
socket.write(new Uint8Array(concatArrayBuffer(Buffer.from(headers + "\n"), data)));
}
}
socket_session_data[socket.id].buffer = null;
if (socket_session_data[socket.id].close_me) socket.end();
if (headers_obj['Connection']) {
if (headers_obj['Connection'].toLowerCase() == "close" && !headers['wtv-connection-close'] == "false") {
socket.destroy();
}
}
}
function concatArrayBuffer(buffer1, buffer2) {
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
}
function moveObjectElement(currentKey, afterKey, obj) {
var result = {};
var val = obj[currentKey];
delete obj[currentKey];
var next = -1;
var i = 0;
if (typeof afterKey == 'undefined' || afterKey == null) afterKey = '';
Object.keys(obj).forEach(function (k) {
var v = obj[k];
if ((afterKey == '' && i == 0) || next == 1) {
result[currentKey] = val;
next = 0;
}
if (k == afterKey) { next = 1; }
result[k] = v;
++i;
});
if (next == 1) {
result[currentKey] = val;
}
if (next !== -1) return result; else return obj;
}
function headersAreStandard(string, verbose) {
// the test will see the binary compressed/enrypted data as ASCII, so a generic "isAscii"
// is not suffuicent. This checks for characters expected in unecrypted headers, and returns
// true only if every character in the string matches the regex. Once we know the string is binary
// we can better process it with the raw base64 data in processRequest() below.
var test = /^([A-Za-z0-9\+\/\=\-\.\,\ \"\;\:\?\&\r\n\(\)\%\<\>\_]{8,})$/.test(string);
if (verbose) {
if (zdebug) console.log(" # Request is ascii: " + test);
if (zdebug) console.log(" # Request is SECURE ON: " + /^SECURE ON/.test(string));
}
return test;
}
async function processRequest(socket, data_hex, returnHeadersBeforeSecure = false, encryptedRequest = false) {
var url = "";
var data = CryptoJS.enc.Latin1.stringify(CryptoJS.enc.Hex.parse(data_hex));
var headers = new Array();
if (typeof data === "string") {
if (data.length > 1) {
if (data.indexOf("\r\n\r\n") != -1) {
data = data.split("\r\n\r\n")[0];
} else {
data = data.split("\n\n")[0];
}
if (headersAreStandard(data)) {
headers = await headerStringToObj(data);
} else if (!returnHeadersBeforeSecure) {
// if its a POST request, assume its a binary blob and not encrypted (dangerous)
if (!encryptedRequest) {
// its not a POST and it 1failed the headersAreStandard test, so we think this is an encrypted blob
if (socket_session_data[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);
socket_session_data[socket.id].wtvsec = new WTVSec();
socket_session_data[socket.id].wtvsec.IssueChallenge();
socket_session_data[socket.id].wtvsec.SecureOn();
socket_session_data[socket.id].secure = true;
}
var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2));
if (enc_data.sigBytes > 0) {
var dec_data = CryptoJS.lib.WordArray.create(socket_session_data[socket.id].wtvsec.Decrypt(0, enc_data));
var secure_headers = await processRequest(socket, dec_data.toString(CryptoJS.enc.Hex), true, true);
headers['encrypted'] = true;
Object.keys(secure_headers).forEach(function (k, v) {
headers[k] = secure_headers[k];
});
}
}
}
if (headers['wtv-client-serial-number'] != null) {
socket_session_data[socket.id].ssid = headers['wtv-client-serial-number'];
}
if (headers['wtv-client-rom-type'] != null) {
if (socket_session_data[socket.id].ssid) {
setSessionData(socket_session_data[socket.id].ssid, 'wtv-client-rom-type', headers['wtv-client-rom-type']);
}
}
if (headers['wtv-incarnation'] != null) {
if (socket_session_data[socket.id].wtvsec) {
socket_session_data[socket.id].wtvsec.set_incarnation(headers['wtv-incarnation']);
} else {
setSessionData(socket_session_data[socket.id].ssid, 'incarnation', headers['wtv-incarnation'])
}
}
if (returnHeadersBeforeSecure) {
headers = await checkForPostData(socket, headers, data, data_hex);
return headers;
}
if (headers['secure'] === true) {
if (!socket_session_data[socket.id].wtvsec) {
console.log(" * Starting new WTVSec instance on socket", socket.id);
socket_session_data[socket.id].wtvsec = new WTVSec();
socket_session_data[socket.id].wtvsec.DecodeTicket(headers['wtv-ticket']);
socket_session_data[socket.id].wtvsec.ticket_b64 = headers['wtv-ticket'];
if (getSessionData(socket_session_data[socket.id].ssid, 'incarnation')) {
socket_session_data[socket.id].wtvsec.incarnation = getSessionData(socket_session_data[socket.id].ssid, 'incarnation');
}
socket_session_data[socket.id].wtvsec.SecureOn();
}
if (socket_session_data[socket.id].secure != true) {
// first time so reroll sessions
if (zdebug) console.log(" # [ SECURE ON BLOCK (" + socket.id + ")]");
socket_session_data[socket.id].secure = true;
}
if (!headers['request_url']) {
if (data_hex.indexOf("0d0a0d0a")) {
// \r\n\r\n
var header_length = data.length + 4;
} else if (data_hex.indexOf("0a0a")) {
// \n\n
var header_length = data.length + 2;
}
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))) {
// 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
headers['psuedo-encryption'] = true;
setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', true);
socket_session_data[socket.id].secure = false;
var secure_headers = await processRequest(socket, enc_data.toString(CryptoJS.enc.Hex), true);
} else {
// SECURE ON and detected encrypted data
setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', false);
var dec_data = CryptoJS.lib.WordArray.create(socket_session_data[socket.id].wtvsec.Decrypt(0, enc_data))
var secure_headers = await processRequest(socket, dec_data.toString(CryptoJS.enc.Hex), true);
if (zdebug) console.log(" # Encrypted Request (SECURE ON)", "on", socket.id);
}
// Merge new headers into existing headers object
Object.keys(secure_headers).forEach(function (k, v) {
headers[k] = secure_headers[k];
});
}
}
}
headers = await checkForPostData(socket, headers, data, data_hex);
if (!headers['request_url']) {
// still no url, likely lost encryption stream, tell client to relog
socket_session_data[socket.id].secure = false;
headers = `200 OK
Connection: Keep-Alive
Expires: Wed, 09 Oct 1991 22:00:00 GMT
wtv-expire-all: wtv-head-waiter:
wtv-expire-all: wtv-1800:
wtv-visit: client:relog
Content-type: text/html`;
data = '';
delete socket_session_data[socket.id].wtvsec;
sendToClient(socket, headers, data);
} else {
processURL(socket, headers);
}
} else {
// socket error, terminate it.
socket.destroy();
}
}
}
async function checkForPostData(socket, headers, data, data_hex) {
if (headers['request']) {
if (headers['request'].substring(0, 4) == "POST") {
if (data_hex.indexOf("0d0a0d0a") != -1) {
// \r\n\r\n
var header_length = data.length + 4;
} else if (data_hex.indexOf("0a0a") != -1) {
// \n\n
var header_length = data.length + 2;
}
if (socket_session_data[socket.id].secure == true) {
var enc_data = CryptoJS.enc.Hex.parse(socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex).substring(header_length * 2));
if (enc_data.sigBytes > 0) {
if (headersAreStandard(enc_data.toString(CryptoJS.enc.Latin1))) {
// some builds (like our targeted 3833), send SECURE ON but then unencrypted headers
if (zdebug) console.log(" # Psuedo-encrypted POST Content (SECURE ON)", "on", socket.id);
// don't actually encrypt output
headers['psuedo-encryption'] = true;
setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', true);
socket_session_data[socket.id].secure = false;
headers['post_data'] = await processRequest(socket, enc_data.toString(CryptoJS.enc.Hex), true);
} else {
// SECURE ON and detected encrypted data
setSessionData(socket_session_data[socket.id].ssid, 'box-does-psuedo-encryption', false);
headers['post_data'] = CryptoJS.lib.WordArray.create(socket_session_data[socket.id].wtvsec.Decrypt(0, enc_data))
if (zdebug) console.log(" # Encrypted POST Content (SECURE ON)", "on", socket.id);
}
}
} else {
if (zdebug) console.log(" # Unencrypted POST Content", "on", socket.id);
headers['post_data'] = CryptoJS.enc.Hex.parse(socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex).substring(header_length * 2));
}
}
}
return headers;
}
async function cleanupSocket(socket) {
try {
console.log(" * Destroying old WTVSec instance on disconnected socket", socket.id);
delete socket_session_data[socket.id].buffer;
delete socket_session_data[socket.id].wtvsec;
delete socket_session_data[socket.id];
socket.end();
} catch (e) {
console.log(" # Could not clean up socket data for socket ID", socket.id, e);
}
}
async function handleSocket(socket) {
// create unique socket id with client address and port
socket.id = parseInt(crc16('CCITT-FALSE', Buffer.from(String(socket.remoteAddress) + String(socket.remotePort), "utf8")).toString(16), 16);
socket_session_data[socket.id] = [];
socket.setEncoding('hex'); //set data encoding (either 'ascii', 'utf8', or 'base64')
socket.on('data', function (data_hex) {
socket.setTimeout(300);
if (socket_session_data[socket.id].buffer) {
socket_session_data[socket.id].buffer.concat(CryptoJS.enc.Hex.parse(data_hex));
} else {
socket_session_data[socket.id].buffer = CryptoJS.enc.Hex.parse(data_hex);
}
});
socket.on('timeout', async function () {
// start the async chain
if (socket_session_data[socket.id].buffer) {
processRequest(this, socket_session_data[socket.id].buffer.toString(CryptoJS.enc.Hex));
}
});
socket.on('error', (err) => {
socket.end();
});
socket.on('end', function () {
cleanupSocket(socket);
});
}
var z_title = "zefie's wtv minisrv v" + require('./package.json').version;
console.log("**** Welcome to " + z_title + " ****");
console.log(" *** Reading service configuration...");
try {
var services_configured = JSON.parse(fs.readFileSync(__dirname + "/services.json"));
} catch (e) {
throw("ERROR: Could not read services.json", e);
}
var service_ip = services_configured.config.service_ip;
Object.keys(services_configured.services).forEach(function (k) {
services_configured.services[k].name = k;
if (!services_configured.services[k].host) {
services_configured.services[k].host = service_ip;
}
if (services_configured.services[k].port && !services_configured.services[k].nobind) {
ports.push(services_configured.services[k].port);
}
services_configured.services[k].toString = function () {
var outstr = "wtv-service: name=" + this.name + " host=" + this.host + " port=" + this.port;
if (this.flags) outstr += " flags=" + this.flags;
if (this.connections) outstr += " flags=" + this.connections;
if (k == "wtv-star") {
outstr += "\nwtv-service: name=wtv-* host=" + this.host + " port=" + this.port;
if (this.flags) outstr += " flags=" + this.flags;
if (this.connections) outstr += " flags=" + this.connections;
}
return outstr;
}
console.log(" * Configured Service", k, "on Port", services_configured.services[k].port, "- Host", services_configured.services[k].host, "- Bind Port:", !services_configured.services[k].nobind);
})
var initstring = '';
ports.sort();
// de-duplicate ports in case user configured multiple services on same port
const bind_ports = [...new Set(ports)]
bind_ports.forEach(function (v) {
try {
var server = net.createServer(handleSocket);
server.listen(v, '0.0.0.0');
initstring += v + ", ";
} catch (e) {
throw ("Could not bind to port", v, e.toString());
}
});
initstring = initstring.substring(0, initstring.length - 2);
console.log(" * Started server on ports " + initstring + "... Service IP is " + service_ip);

41
zefie_wtvp_minisrv/package-lock.json generated Normal file
View File

@@ -0,0 +1,41 @@
{
"name": "zefie_wtvp_minisrv",
"version": "0.7.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"crypto-js": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
},
"easy-crc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/easy-crc/-/easy-crc-0.0.2.tgz",
"integrity": "sha512-h6eqIdhJRe0p271/xZJbM/0klCi13mW5IB7ZEzEGH11yV4QXWAkD4yPErz0fXdhFrd6sZ4Q1k5Iky2kMsMNePw=="
},
"endianness": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/endianness/-/endianness-8.0.2.tgz",
"integrity": "sha512-IU+77+jJ7lpw2qZ3NUuqBZFy3GuioNgXUdsL1L9tooDNTaw0TgOnwNuc+8Ns+haDaTifK97QLzmOANJtI/rGvw=="
},
"mime-db": {
"version": "1.48.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz",
"integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ=="
},
"mime-types": {
"version": "2.1.31",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz",
"integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==",
"requires": {
"mime-db": "1.48.0"
}
},
"strftime": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.0.tgz",
"integrity": "sha1-s/D6QZKVICpaKJ9ta+n0kJphcZM="
}
}
}

View File

@@ -0,0 +1,31 @@
{
"name": "zefie_wtvp_minisrv",
"version": "0.7.2",
"description": "WebTV Service (WTVP) Emulation Server",
"main": "app.js",
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv",
"license": "GPL3",
"author": {
"name": "zefie",
"email": "zefie@zefie.net",
"url": "https://zef.pw/"
},
"bugs": {
"url": "https://github.com/zefie/zefie_wtvp_minisrv/issues"
},
"funding": {
"type": "patreon",
"url": "https://www.patreon.com/zefie"
},
"repository": {
"type": "git",
"url": "https://github.com/zefie/zefie_wtvp_minisrv.git"
},
"dependencies": {
"crypto-js": "^4.0.0",
"easy-crc": "0.0.2",
"endianness": "^8.0.2",
"mime-types": "^2.1.31",
"strftime": "^0.10.0"
}
}

View File

@@ -0,0 +1,56 @@
{
"config": {
"service_ip": "192.168.11.8",
"service_name": "HackTV"
},
"services": {
"wtv-1800": {
"host": null,
"port": 1615,
"connections": 1
},
"wtv-star": {
"port": 1603,
"flags": "0x00000007"
},
"wtv-head-waiter": {
"port": 1601,
"flags": "0x00000001",
"connections": 1
},
"wtv-update": {
"port": 1619,
"flags": "0x04",
"connections": 3
},
"wtv-log": {
"port": 1609,
"connections": 1
},
"wtv-home": {
"port": 1612,
"flags": "0x00000010"
},
"wtv-tricks": {
"port": 1602,
"flags": "0x00000004"
},
"wtv-flashrom": {
"port": 1618,
"flags": "0x00000040"
},
"wtv-music": {
"port": 1656,
"flags": "0x04",
"connections": 3
},
"http": {
"port": 1650,
"connections": 3,
"use_external_proxy": false,
"external_proxy_host": null,
"external_proxy_port": 8080
}
}
}

View File

@@ -0,0 +1,310 @@
const CryptoJS = require('crypto-js');
const endianness = require('endianness');
var crypto = require('crypto');
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 = null;
current_shared_key = null;
challenge_key = null;
challenge_signed_key = null;
challenge_raw = null;
challenge_response = null;
ticket_b64 = null;
incarnation = 0;
session_key1 = null;
session_key2 = null;
hRC4_Key1 = null;
hRC4_Key2 = null;
RC4Session = new Array();
zdebug = false;
constructor(wtv_incarnation = 1) {
this.zdebug = true;
this.initial_shared_key = CryptoJS.enc.Base64.parse(this.initial_shared_key_b64);
if (this.initial_shared_key.sigBytes === 8) {
this.incarnation = wtv_incarnation;
this.current_shared_key = this.initial_shared_key;
} else {
throw ("Invalid initial key length");
}
}
set_incarnation(wtv_incarnation) {
if (this.incarnation != wtv_incarnation) {
this.incarnation = wtv_incarnation;
this.SecureOn();
}
}
increment_incarnation() {
this.set_incarnation(parseInt(this.incarnation) + 1);
}
DuplicateWordArray(wa) {
return CryptoJS.lib.WordArray.create(this.wordArrayToUint8Array(wa).buffer);
}
PrepareTicket() {
// store last challenge response in ticket
var ticket_data = this.challenge_raw;
try {
var ticket_data_enc = CryptoJS.DES.encrypt(ticket_data, this.current_shared_key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.NoPadding
});
// create a copy of WordArray since concat modifies the original
var current_shared_key = this.DuplicateWordArray(this.current_shared_key);
var challenge_signed_key = this.DuplicateWordArray(this.challenge_signed_key);
this.ticket_b64 = current_shared_key.concat(challenge_signed_key.concat(ticket_data_enc.ciphertext)).toString(CryptoJS.enc.Base64);
} catch (e) {
console.log("Error encrypting ticket: " + e.toString());
return null;
}
return this.ticket_b64;
}
DecodeTicket(ticket_b64) {
var ticket_hex = CryptoJS.enc.Base64.parse(ticket_b64).toString(CryptoJS.enc.Hex);
var ticket_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(0,16));
var challenge_key = CryptoJS.enc.Hex.parse(ticket_hex.substring(16, 32));
var challenge_enc = CryptoJS.enc.Hex.parse(ticket_hex.substring(32));
var ticket_dec = CryptoJS.DES.decrypt(
{
ciphertext: challenge_enc
},
ticket_key,
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.NoPadding
}
);
this.ProcessChallenge(ticket_dec.toString(CryptoJS.enc.Base64), challenge_key);
console.log(" * Decoded session from wtv-ticket");
}
ProcessChallenge(wtv_challenge, key = this.current_shared_key) {
var challenge_raw = CryptoJS.enc.Base64.parse(wtv_challenge);
if (challenge_raw.sigBytes > 8) {
var challenge_raw_hex = challenge_raw.toString(CryptoJS.enc.Hex);
var challenge_id_hex = challenge_raw_hex.substring(0, (8 * 2));
var challenge_enc_hex = challenge_raw_hex.substring((8*2));
var challenge_enc = CryptoJS.enc.Hex.parse(challenge_enc_hex);
var challenge_decrypted = CryptoJS.DES.decrypt(
{
ciphertext: challenge_enc
},
key,
{
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.NoPadding
}
);
var challenge_dec_hex = challenge_decrypted.toString(CryptoJS.enc.Hex);
var challenge_md5_challenge = CryptoJS.MD5(CryptoJS.enc.Hex.parse(challenge_dec_hex.substring(0, (80 * 2))));
var test = challenge_dec_hex.substring((80 * 2), (96 * 2));
var test2 = challenge_md5_challenge.toString(CryptoJS.enc.Hex);
if (test == test2) {
this.current_shared_key = CryptoJS.enc.Hex.parse(challenge_dec_hex.substring((72*2), (80*2)));
var challenge_echo = CryptoJS.enc.Hex.parse(challenge_dec_hex.substr(0, (40*2)));
// RC4 encryption keys.Stored in the wtv-ticket on the server side.
this.session_key1 = CryptoJS.enc.Hex.parse(challenge_dec_hex.substring((40*2), (56*2)));
this.session_key2 = CryptoJS.enc.Hex.parse(challenge_dec_hex.substring((56*2), (72*2)));
var echo_encrypted = CryptoJS.DES.encrypt(CryptoJS.MD5(challenge_echo).concat(challenge_echo).concat(CryptoJS.enc.Utf8.parse("\x08".repeat(8))), this.current_shared_key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.NoPadding
});
// Last bytes is just extra padding
this.challenge_raw = challenge_raw;
this.challenge_key = this.current_shared_key;
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 {
throw ("Invalid challenge length");
}
}
IssueChallenge() {
/*
* bytes 0-8: Random id? Just echoed in the response
* bytes 8 - XX: DES encrypted block.Encrypted with the initial key or subsequent keys from the challenge.
* bytes 8 - 48: hidden random data we echo back in the response
* bytes 48 - 64: session key 1 used in RC4 encryption triggered by SECURE ON
* bytes 64 - 80: session key 2 used in RC4 encryption triggered by SECURE ON
* bytes 80 - 88: new key for future challenges
* bytes 88 - 104: MD5 of 8 - 88
* bytes 104 - 112: padding.not important
*/
var challenge_id = CryptoJS.lib.WordArray.random(8);
var echo_me = CryptoJS.lib.WordArray.random(40);
this.session_key1 = CryptoJS.lib.WordArray.random(16);
this.session_key2 = CryptoJS.lib.WordArray.random(16);
var new_shared_key = CryptoJS.lib.WordArray.random(8);
var session_key1 = this.DuplicateWordArray(this.session_key1);
var session_key2 = this.DuplicateWordArray(this.session_key2);
var challenge_puzzle = echo_me.concat(session_key1.concat(session_key2.concat(new_shared_key)));
var challenge_secret = challenge_puzzle.concat(CryptoJS.MD5(challenge_puzzle).concat(CryptoJS.enc.Hex.parse("\x08".repeat(8))));
// Shhhh!!
var challenge_secreted = CryptoJS.DES.encrypt(challenge_secret, this.current_shared_key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.NoPadding
});
var challenge = challenge_id.concat(challenge_secreted.ciphertext);
var challenge_b64 = challenge.toString(CryptoJS.enc.Base64);
// get the expected response for when client sends it
this.challenge_signed_key = this.current_shared_key;
this.challenge_response = this.ProcessChallenge(challenge_b64);
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;
}
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));
}
SecureOn(rc4session = null) {
if (this.zdebug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation);
var buf = new Uint8Array([0xff & this.incarnation, 0xff & (this.incarnation >> 8), 0xff & (this.incarnation >> 16), 0xff & (this.incarnation >> 24)]);
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))));
switch (rc4session) {
case 0:
this.RC4Session[0] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)),'');
break;
case 1:
this.RC4Session[1] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key1)),'');
break;
case 2:
this.RC4Session[2] = crypto.createCipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_Key2)),'');
break;
case 3:
this.RC4Session[3] = crypto.createDecipheriv('rc4', Buffer.from(this.wordArrayToUint8Array(this.hRC4_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)), '');
break;
}
}
NewRC4Session(num) {
this.SecureOn(num);
}
Encrypt(keynum, data) {
var session_id;
switch (keynum) {
case 0:
session_id = 0;
break;
case 1:
session_id = 2
break;
default:
throw ("Invalid key option (0 or 1 only)");
break;
}
if (!this.RC4Session[session_id]) {
this.NewRC4Session(session_id);
}
if (data.words) {
data = new Buffer.from(this.wordArrayToUint8Array(data));
} else if (data.constructor === ArrayBuffer) {
data = new Buffer.from(data);
}
return this.RC4Session[session_id].update(data);
}
Decrypt(keynum, data) {
var session_id;
switch (keynum) {
case 0:
session_id = 1;
break;
case 1:
session_id = 3;
break;
default:
throw ("Invalid key option (0 or 1 only)");
break;
}
if (!this.RC4Session[session_id]) {
this.NewRC4Session(session_id);
}
if (data.words) {
data = new Buffer.from(this.wordArrayToUint8Array(data));
} else if (data.constructor === ArrayBuffer) {
data = new Buffer.from(data);
}
return this.RC4Session[session_id].update(data);
}
Test() {
console.log("TEST RUN");
console.log("Test python challenge");
this.current_shared_key = CryptoJS.enc.Base64.parse("CC5rWmRUE0o=");
var current_challenge = "0kjyqIYAu0ziFBbSERN6DGaZ6S0fT+DBUCtpHCJ4lpuM7CbXdAm+x83BIDoJYztd1Z+5KFZ7ghmb3LJCT/6mhWUYkqqKOyfPRW8ZIdbICK/CV+Kxm8EUjRXZSk/97tsmFpH3hcCJ7C2TBw+TX38uQQ==";
var expected_result = "0kjyqIYAu0zI5QrLhSuEUFgKkoVSxI3zBlUMfhnIYoMy0ExfIX4s/mHvILseDFx+17trk7YO+xG9D2qSY6v9XVUS1OP1m8ee";
console.log("Expected: " + expected_result);
console.log("Got: " + this.ProcessChallenge(current_challenge));
}
}
module.exports = WTVSec;

View File

@@ -0,0 +1,97 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<Name>zefie_wtvp_minisrv</Name>
<RootNamespace>hacktv_updsrv</RootNamespace>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>31b77d0c-f03d-43a7-bf7f-fad7174728bf</ProjectGuid>
<ProjectHome>
</ProjectHome>
<StartupFile>app.js</StartupFile>
<StartWebBrowser>False</StartWebBrowser>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
<StartWebBrowser>false</StartWebBrowser>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Content Include=".gitignore" />
<Content Include="app.js" />
<Content Include="services.json">
<SubType>Code</SubType>
</Content>
<Content Include="ServiceVault\wtv-flashrom\get-lc2-page.js" />
<Content Include="ServiceVault\wtv-flashrom\lc2-download-complete.js" />
<Content Include="ServiceVault\wtv-flashrom\lc2-download-failed.html" />
<Content Include="ServiceVault\wtv-flashrom\ROMCache\MSNLogo.gif" />
<Content Include="ServiceVault\wtv-flashrom\ROMCache\S40H1.gif" />
<Content Include="ServiceVault\wtv-flashrom\ROMCache\Shadow.gif" />
<Content Include="ServiceVault\wtv-flashrom\ROMCache\Spacer.gif" />
<Content Include="ServiceVault\wtv-flashrom\ROMCache\up-arrows.swf" />
<Content Include="ServiceVault\wtv-flashrom\ROMCache\WebTVLogoJewel.gif" />
<Content Include="ServiceVault\wtv-flashrom\willie.async.js" />
<Content Include="ServiceVault\wtv-head-waiter\ssl-cert.der" />
<Content Include="ServiceVault\wtv-home\zefie.html" />
<Content Include="ServiceVault\wtv-star\images\HackTVLogo.gif" />
<Content Include="ServiceVault\wtv-star\images\HackTVLogoJewel.gif" />
<Content Include="ServiceVault\wtv-star\images\WebTVLogoJewel.gif" />
<Content Include="ServiceVault\wtv-tricks\access.js" />
<Content Include="ServiceVault\wtv-tricks\blastcache.js" />
<Content Include="ServiceVault\wtv-tricks\go-offline.js" />
<Content Include="ServiceVault\wtv-update\content\diskmaps\htvupdate.txt" />
<Content Include="ServiceVault\wtv-update\content\htvupdate\Games\cSetup.html" />
<Content Include="ServiceVault\wtv-update\content\htvupdate\Games\Games.html" />
<Content Include="ServiceVault\wtv-update\content\htvupdate\MattMan\Tricks\tricks.html" />
<Content Include="ServiceVault\wtv-update\sync.js">
<SubType>Code</SubType>
</Content>
<Content Include="ServiceVault\wtv-update\update.html" />
<Content Include="ServiceVault\wtv-home\home.js" />
<Content Include="ServiceVault\wtv-update\updatesuccess.txt" />
<Content Include="ServiceVault\wtv-1800\finish-prereg.js" />
<Content Include="ServiceVault\wtv-1800\preregister.js" />
<Content Include="ServiceVault\wtv-head-waiter\finalize-security.js" />
<Content Include="ServiceVault\wtv-head-waiter\login-stage-two.js" />
<Content Include="ServiceVault\wtv-head-waiter\login.js" />
<Content Include="ServiceVault\wtv-home\splash.txt" />
<Content Include="ServiceVault\wtv-log\log.js" />
<Content Include="wtvsec.js">
<SubType>Code</SubType>
</Content>
<Content Include="package.json" />
</ItemGroup>
<ItemGroup>
<Folder Include="ServiceVault\" />
<Folder Include="ServiceVault\wtv-flashrom\" />
<Folder Include="ServiceVault\wtv-flashrom\ROMCache\" />
<Folder Include="ServiceVault\wtv-star\" />
<Folder Include="ServiceVault\wtv-star\images\" />
<Folder Include="ServiceVault\wtv-tricks\" />
<Folder Include="ServiceVault\wtv-update\" />
<Folder Include="ServiceVault\wtv-1800\" />
<Folder Include="ServiceVault\wtv-head-waiter\" />
<Folder Include="ServiceVault\wtv-home\" />
<Folder Include="ServiceVault\wtv-log\" />
<Folder Include="ServiceVault\wtv-update\content\" />
<Folder Include="ServiceVault\wtv-update\content\diskmaps\" />
<Folder Include="ServiceVault\wtv-update\content\htvupdate\" />
<Folder Include="ServiceVault\wtv-update\content\htvupdate\Games\" />
<Folder Include="ServiceVault\wtv-update\content\htvupdate\MattMan\" />
<Folder Include="ServiceVault\wtv-update\content\htvupdate\MattMan\Tricks\" />
</ItemGroup>
<Import Project="$(VSToolsPath)\Node.js Tools\Microsoft.NodejsToolsV2.targets" />
</Project>