Merge branch 'dev'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -367,3 +367,4 @@ FodyWeavers.xsd
|
|||||||
/zefie_wtvp_minisrv/UserServiceVault/*-*/
|
/zefie_wtvp_minisrv/UserServiceVault/*-*/
|
||||||
/zefie_wtvp_minisrv/ServiceLogPost/*.log
|
/zefie_wtvp_minisrv/ServiceLogPost/*.log
|
||||||
/zefie_wtvp_minisrv/SessionStore/*.json
|
/zefie_wtvp_minisrv/SessionStore/*.json
|
||||||
|
/zefie_wtvp_minisrv/SessionStore/*/
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
if (socket.ssid) {
|
if (socket.ssid) {
|
||||||
if (ssid_sessions[socket.ssid].loadSessionData() == true) {
|
if (ssid_sessions[socket.ssid].loadSessionData() == true) {
|
||||||
console.log(" * Loaded session data from disk for", filterSSID(socket.ssid))
|
console.log(" * Loaded session data from disk for", wtvshared.filterSSID(socket.ssid))
|
||||||
ssid_sessions[socket.ssid].setSessionData("registered", (ssid_sessions[socket.ssid].getSessionData("registered") == true) ? true : false);
|
ssid_sessions[socket.ssid].setSessionData("registered", (ssid_sessions[socket.ssid].getSessionData("registered") == true) ? true : false);
|
||||||
} else {
|
} else {
|
||||||
ssid_sessions[socket.ssid].session_data = {};
|
ssid_sessions[socket.ssid].session_data = {};
|
||||||
@@ -20,11 +20,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (i > 0 && zdebug) console.log(" # Closed", i, "previous sockets for", filterSSID(socket.ssid));
|
if (i > 0 && zdebug) console.log(" # Closed", i, "previous sockets for", wtvshared.filterSSID(socket.ssid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
|
if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
|
||||||
if (zdebug) console.log(" # Recreating primary WTVSec login instance for", filterSSID(socket.ssid));
|
if (zdebug) console.log(" # Recreating primary WTVSec login instance for", wtvshared.filterSSID(socket.ssid));
|
||||||
delete ssid_sessions[socket.ssid].data_store.wtvsec_login;
|
delete ssid_sessions[socket.ssid].data_store.wtvsec_login;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
// todo: async
|
const WTVDownloadList = require("./WTVDownloadList.js");
|
||||||
|
var wtvdl = new WTVDownloadList(minisrv_config, service_name);
|
||||||
|
|
||||||
var force_update = (request_headers.query.force == "true") ? true : false;
|
var force_update = (request_headers.query.force == "true") ? true : false;
|
||||||
if (request_headers['wtv-request-type'] == 'download') {
|
if (request_headers['wtv-request-type'] == 'download') {
|
||||||
const WTVDownloadList = require("./WTVDownloadList.js");
|
|
||||||
var wtvdl = new WTVDownloadList();
|
|
||||||
|
|
||||||
var content_dir = "content/"
|
var content_dir = "content/"
|
||||||
var diskmap_dir = content_dir + "diskmaps/";
|
var diskmap_dir = content_dir + "diskmaps/";
|
||||||
|
|
||||||
@@ -272,58 +270,6 @@ if (request_headers['wtv-request-type'] == 'download') {
|
|||||||
} else if (request_headers.query.group && request_headers.query.diskmap) {
|
} else if (request_headers.query.group && request_headers.query.diskmap) {
|
||||||
var message = request_headers.query.message || "Retrieving files...";
|
var message = request_headers.query.message || "Retrieving files...";
|
||||||
var main_message = request_headers.query.main_message || "Your receiver is downloading files.";
|
var main_message = request_headers.query.main_message || "Your receiver is downloading files.";
|
||||||
headers = `200 OK
|
headers = "200 OK\nContent-Type: text/html";
|
||||||
Content-Type: text/html`;
|
data = wtvdl.getSyncPage(message, request_headers.query.group, request_headers.query.diskmap, main_message, message, force_update)
|
||||||
|
|
||||||
data = `
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta
|
|
||||||
http-equiv=refresh
|
|
||||||
content="0;url=client:Fetch?group=${escape(request_headers.query.group)}&source=${service_name}:/sync%3Fdiskmap%3D${escape(escape(request_headers.query.diskmap))}%26force%3D${force_update}&message=${escape(message)}"
|
|
||||||
>
|
|
||||||
<display downloadsuccess="client:ShowAlert?message=Download%20successful%21&buttonlabel1=Okay&buttonaction1=client:goback&image=${minisrv_config.config.service_logo}&noback=true" downloadfail="client:ShowAlert?message=Download%20failed...&buttonlabel1=Okay...&buttonaction1=client:goback&image=${minisrv_config.config.service_logo}&noback=true">
|
|
||||||
<title>Retrieving files...</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="${minisrv_config.config.service_logo}" 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>
|
|
||||||
${message}
|
|
||||||
</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>
|
|
||||||
${main_message}
|
|
||||||
<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>
|
|
||||||
`;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
57
zefie_wtvp_minisrv/ServiceVault/wtv-disk/uptest.js
Normal file
57
zefie_wtvp_minisrv/ServiceVault/wtv-disk/uptest.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
const WTVDownloadList = require("./WTVDownloadList.js");
|
||||||
|
var wtvdl = new WTVDownloadList(minisrv_config);
|
||||||
|
if (request_headers['wtv-request-type'] == 'download') {
|
||||||
|
console.log(request_headers.post_data.toString(CryptoJS.enc.Latin1));
|
||||||
|
wtvdl.reset();
|
||||||
|
var froot = "file://disk/Demo/";
|
||||||
|
var fget = [
|
||||||
|
"attract.mpg",
|
||||||
|
"can-you-hear.mpg",
|
||||||
|
"explore.mpg",
|
||||||
|
"hear.html",
|
||||||
|
"index.html",
|
||||||
|
"menu1.html",
|
||||||
|
"menu2.html",
|
||||||
|
"menu3.html",
|
||||||
|
"menu4.html",
|
||||||
|
"service.html",
|
||||||
|
"splash-mits.mpg",
|
||||||
|
"splash-phil.mpg",
|
||||||
|
"splash-sony.mpg",
|
||||||
|
"splash1.html",
|
||||||
|
"splash2.html",
|
||||||
|
"splash3.html",
|
||||||
|
"what-is-it.html",
|
||||||
|
"what-is-it.mpg",
|
||||||
|
"what-others.html",
|
||||||
|
"what-others.mpg",
|
||||||
|
"whats-in-it.html",
|
||||||
|
"whats-in-it.mpg",
|
||||||
|
"whats-in-it.mpg.old"
|
||||||
|
];
|
||||||
|
|
||||||
|
Object.keys(fget).forEach(function (k) {
|
||||||
|
wtvdl.putUserStore(froot + fget[k]);
|
||||||
|
});
|
||||||
|
headers = "200 OK\nContent-type: " + wtvdl.content_type;
|
||||||
|
data = wtvdl.getDownloadList();
|
||||||
|
console.log(data);
|
||||||
|
} else {
|
||||||
|
headers = "200 OK\nContent-type: text/html";
|
||||||
|
var success_url = new clientShowAlert({
|
||||||
|
'image': minisrv_config.config.service_logo,
|
||||||
|
'message': "Upload successful!",
|
||||||
|
'buttonlabel1': "Okay",
|
||||||
|
'buttonaction1': "client:goback",
|
||||||
|
'noback': true,
|
||||||
|
}).getURL();
|
||||||
|
|
||||||
|
var fail_url = new clientShowAlert({
|
||||||
|
'image': minisrv_config.config.service_logo,
|
||||||
|
'message': "Upload failed...",
|
||||||
|
'buttonlabel1': "Okay...",
|
||||||
|
'buttonaction1': "client:goback",
|
||||||
|
'noback': true,
|
||||||
|
}).getURL();
|
||||||
|
data = wtvdl.getSyncPage("Testing", "UploadTest", null, "Your receiver is uploading files.", "Sending files", null, success_url, fail_url, "wtv-disk:/uptest");
|
||||||
|
}
|
||||||
23
zefie_wtvp_minisrv/ServiceVault/wtv-disk/userbrowser.js
Normal file
23
zefie_wtvp_minisrv/ServiceVault/wtv-disk/userbrowser.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// todo some fancy ass file manager or something
|
||||||
|
|
||||||
|
|
||||||
|
if (socket.ssid) {
|
||||||
|
if (ssid_sessions[socket.ssid]) {
|
||||||
|
if (ssid_sessions[socket.ssid].isRegistered()) {
|
||||||
|
data = ssid_sessions[socket.ssid].getUserStoreFileByURL("file://Disk/Demo/allyouneed.html");
|
||||||
|
//data = ssid_sessions[socket.ssid].getUserStoreFile("Disk/Demo/allyouneed.html");
|
||||||
|
var contype = ssid_sessions[socket.ssid].getUserStoreContentType("file://Disk/Demo/allyouneed.html");
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
headers = "200 OK\n";
|
||||||
|
headers += "Content-Type: " + contype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!headers) {
|
||||||
|
var errpage = doErrorPage(400)
|
||||||
|
headers = errpage[0];
|
||||||
|
data = errpage[1];
|
||||||
|
}
|
||||||
21
zefie_wtvp_minisrv/ServiceVault/wtv-disk/userstore.js
Normal file
21
zefie_wtvp_minisrv/ServiceVault/wtv-disk/userstore.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
if (request_headers.post_data) {
|
||||||
|
if (request_headers.query.partialPath) {
|
||||||
|
if (socket.ssid) {
|
||||||
|
if (ssid_sessions[socket.ssid]) {
|
||||||
|
if (ssid_sessions[socket.ssid].isRegistered()) {
|
||||||
|
var result = ssid_sessions[socket.ssid].storeUserStoreFile(request_headers.query.partialPath, new Buffer.from(request_headers.post_data.toString(CryptoJS.enc.Hex), 'hex'), request_headers.query['last-modified-seconds'] || null, (request_headers.query.no_overwrite) ? false : true);
|
||||||
|
if (result) {
|
||||||
|
headers = "200 OK\n";
|
||||||
|
headers += "Content-Type: text/plain";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!headers) {
|
||||||
|
var errpage = doErrorPage(400)
|
||||||
|
headers = errpage[0];
|
||||||
|
data = errpage[1];
|
||||||
|
}
|
||||||
@@ -16,11 +16,11 @@ if (socket.ssid !== null) {
|
|||||||
var client_challenge_response = request_headers["wtv-challenge-response"] || null;
|
var client_challenge_response = request_headers["wtv-challenge-response"] || null;
|
||||||
if (challenge_response && client_challenge_response) {
|
if (challenge_response && client_challenge_response) {
|
||||||
if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) {
|
if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) {
|
||||||
console.log(" * wtv-challenge-response success for " + filterSSID(socket.ssid));
|
console.log(" * wtv-challenge-response success for " + wtvshared.filterSSID(socket.ssid));
|
||||||
wtvsec_login.PrepareTicket();
|
wtvsec_login.PrepareTicket();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
console.log(" * wtv-challenge-response FAILED for " + filterSSID(socket.ssid));
|
console.log(" * wtv-challenge-response FAILED for " + wtvshared.filterSSID(socket.ssid));
|
||||||
if (zdebug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64));
|
if (zdebug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64));
|
||||||
if (zdebug) console.log("Response Received:", client_challenge_response)
|
if (zdebug) console.log("Response Received:", client_challenge_response)
|
||||||
gourl = "wtv-head-waiter:/login?reissue_challenge=true";
|
gourl = "wtv-head-waiter:/login?reissue_challenge=true";
|
||||||
|
|||||||
@@ -5,14 +5,9 @@ wtv-expire-all: wtv-flashrom:
|
|||||||
Content-type: text/html`
|
Content-type: text/html`
|
||||||
|
|
||||||
if (request_headers.query.url) headers += "\nwtv-visit: " + request_headers.query.url;
|
if (request_headers.query.url) headers += "\nwtv-visit: " + request_headers.query.url;
|
||||||
|
var cryptstatus = ((socket_sessions[socket.id].secure === true) ? "Encrypted" : "Not Encrypted")
|
||||||
|
|
||||||
if (ssid_sessions[socket.ssid].get('box-does-psuedo-encryption')) {
|
var comp_type = wtvmime.shouldWeCompress(ssid_sessions[socket.ssid],'text/html');
|
||||||
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_sessions[socket.id].secure === true) ? "Encrypted" : "Not Encrypted")
|
|
||||||
}
|
|
||||||
|
|
||||||
var comp_type = shouldWeCompress(socket.ssid,'text/html');
|
|
||||||
var compstatus = "uncompressed";
|
var compstatus = "uncompressed";
|
||||||
switch (comp_type) {
|
switch (comp_type) {
|
||||||
case 1:
|
case 1:
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Content-length: 0`;
|
|||||||
logdata_outstring_hex += request_headers.post_data.toString(CryptoJS.enc.Hex);
|
logdata_outstring_hex += request_headers.post_data.toString(CryptoJS.enc.Hex);
|
||||||
if (minisrv_config.services[service_name].write_logs_to_disk) {
|
if (minisrv_config.services[service_name].write_logs_to_disk) {
|
||||||
fs.writeFile(fullpath, logdata_outstring_hex, "Hex", function () {
|
fs.writeFile(fullpath, logdata_outstring_hex, "Hex", function () {
|
||||||
if (!zquiet) console.log(" * Wrote POST log data from", filterSSID(socket.ssid), "for", socket.id);
|
if (!zquiet) console.log(" * Wrote POST log data from", wtvshared.filterSSID(socket.ssid), "for", socket.id);
|
||||||
sendToClient(socket, headers, data);
|
sendToClient(socket, headers, data);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -41,7 +41,7 @@ Content-length: 0`;
|
|||||||
var logdata_outstring_hex = Buffer.from(logdata_outstring, 'utf8').toString('hex');
|
var logdata_outstring_hex = Buffer.from(logdata_outstring, 'utf8').toString('hex');
|
||||||
if (minisrv_config.services[service_name].write_logs_to_disk) {
|
if (minisrv_config.services[service_name].write_logs_to_disk) {
|
||||||
fs.writeFile(fullpath, logdata_outstring_hex, "Hex", function () {
|
fs.writeFile(fullpath, logdata_outstring_hex, "Hex", function () {
|
||||||
if (!zquiet) console.log(" * Wrote GET log data from", filterSSID(socket.ssid), "for", socket.id);
|
if (!zquiet) console.log(" * Wrote GET log data from", wtvshared.filterSSID(socket.ssid), "for", socket.id);
|
||||||
sendToClient(socket, headers, data);
|
sendToClient(socket, headers, data);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Content-Type: text/html`
|
|||||||
|
|
||||||
var wtv_system_version = ssid_sessions[socket.ssid].get("wtv-system-version");
|
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_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_serial_number = wtvshared.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_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_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 wtv_system_sysconfig_hex = parseInt(ssid_sessions[socket.ssid].get("wtv-system-sysconfig")).toString(16);
|
||||||
|
|||||||
@@ -4,55 +4,118 @@ class WTVClientSessionData {
|
|||||||
|
|
||||||
fs = require('fs');
|
fs = require('fs');
|
||||||
path = require('path');
|
path = require('path');
|
||||||
|
|
||||||
ssid = null;
|
ssid = null;
|
||||||
data_store = null;
|
data_store = null;
|
||||||
session_store = null;
|
session_store = null;
|
||||||
login_security = null;
|
login_security = null;
|
||||||
capabilities = null;
|
capabilities = null;
|
||||||
session_storage = "";
|
session_storage = "";
|
||||||
hide_ssid_in_logs = true;
|
minisrv_config = [];
|
||||||
|
wtvshared = null;
|
||||||
|
wtvmime = null;
|
||||||
|
|
||||||
filterSSID(obj) {
|
constructor(minisrv_config, ssid) {
|
||||||
if (this.hide_ssid_in_logs === true) {
|
if (!minisrv_config) throw ("minisrv_config required");
|
||||||
if (typeof (obj) == "string") {
|
var WTVShared = require('./WTVShared.js')['WTVShared'];
|
||||||
if (obj.substr(0, 8) == "MSTVSIMU") {
|
var WTVMime = require('./WTVMime.js');
|
||||||
return obj.substr(0, 10) + ('*').repeat(10) + obj.substr(20);
|
this.minisrv_config = minisrv_config;
|
||||||
} else if (obj.substr(0, 5) == "1SEGA") {
|
this.wtvshared = new WTVShared(minisrv_config);
|
||||||
return obj.substr(0, 6) + ('*').repeat(6) + obj.substr(13);
|
this.wtvmime = new WTVMime(minisrv_config);
|
||||||
} else {
|
|
||||||
return obj.substr(0, 6) + ('*').repeat(9);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (obj["wtv-client-serial-number"]) {
|
|
||||||
var ssid = obj["wtv-client-serial-number"];
|
|
||||||
if (ssid.substr(0, 8) == "MSTVSIMU") {
|
|
||||||
obj["wtv-client-serial-number"] = ssid.substr(0, 10) + ('*').repeat(10) + ssid.substr(20);
|
|
||||||
} else if (ssid.substr(0, 5) == "1SEGA") {
|
|
||||||
obj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(6) + ssid.substr(13);
|
|
||||||
} else {
|
|
||||||
obj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(ssid, hide_ssid_in_logs, session_storage_directory) {
|
|
||||||
this.ssid = ssid;
|
this.ssid = ssid;
|
||||||
if (hide_ssid_in_logs) this.hide_ssid_in_logs = hide_ssid_in_logs;
|
|
||||||
if (!session_storage_directory) session_storage_directory = __dirname + "/SessionStore";
|
|
||||||
this.session_storage = session_storage_directory;
|
|
||||||
this.data_store = new Array();
|
this.data_store = new Array();
|
||||||
this.session_store = {};
|
this.session_store = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the absolute path to the user's file store, or false if unregistered
|
||||||
|
* @returns {string|boolean} Absolute path to the user's file store, or false if unregistered
|
||||||
|
*/
|
||||||
|
getUserStoreDirectory() {
|
||||||
|
if (!this.isRegistered()) return false;
|
||||||
|
return this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + this.path.sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a file in the user's file store
|
||||||
|
* @param {string} path Relative path to User's file store
|
||||||
|
* @param {Buffer} data File data
|
||||||
|
* @param {number|null} last_modified Unix timestamp to set last modified date to
|
||||||
|
* @param {boolean} overwrite Overwrite if file exists
|
||||||
|
* @returns {boolean} Whether or not the file was written
|
||||||
|
*/
|
||||||
|
storeUserStoreFile(path, data, last_modified = null, overwrite = true) {
|
||||||
|
var store_dir = this.getUserStoreDirectory();
|
||||||
|
if (!store_dir) return false; // unregistered
|
||||||
|
var result = false;
|
||||||
|
var path_split = path.split('/');
|
||||||
|
var file_name = path_split.pop();
|
||||||
|
var store_dir_path = this.wtvshared.makeSafePath(store_dir, path_split.join('/').replace('/', this.path.sep));
|
||||||
|
var store_full_path = this.wtvshared.makeSafePath(store_dir_path, file_name);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!this.fs.existsSync(store_dir_path)) this.fs.mkdirSync(store_dir_path, { recursive: true });
|
||||||
|
var file_exists = this.fs.existsSync(store_full_path);
|
||||||
|
if (!file_exists || (file_exists && overwrite)) result = this.fs.writeFileSync(store_full_path, data);
|
||||||
|
if (result !== false && last_modified) {
|
||||||
|
var file_timestamp = new Date(last_modified * 1000);
|
||||||
|
fs.utimesSync(store_full_path, Date.now(), file_timestamp)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(" # User File Store failed", e);
|
||||||
|
}
|
||||||
|
return (result === false) ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a file from the user store
|
||||||
|
* @param {string} path Path relative to the User File Store
|
||||||
|
* @returns {Buffer|false} Buffer data, or false if could not open file
|
||||||
|
*/
|
||||||
|
getUserStoreFile(path) {
|
||||||
|
var store_dir = this.getUserStoreDirectory();
|
||||||
|
if (!store_dir) return false; // unregistered
|
||||||
|
var store_dir_path = this.wtvshared.makeSafePath(store_dir, path.replace('/', this.path.sep));
|
||||||
|
if (this.fs.existsSync(store_dir_path)) return this.fs.readFileSync(store_dir_path);
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a file from the user store with a file://Disk/ url
|
||||||
|
* @param {string} url file://Disk/ base url
|
||||||
|
* @returns {Buffer|false} Buffer data, or false if could not open file
|
||||||
|
*/
|
||||||
|
getUserStoreFileByURL(url) {
|
||||||
|
var path_split = url.split('/');
|
||||||
|
path_split.shift();
|
||||||
|
path_split.shift();
|
||||||
|
var store_dir_path = path_split.join('/').replace('/', this.path.sep);
|
||||||
|
return this.getUserStoreFile(store_dir_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the Content-Type of a User Store File
|
||||||
|
* @param {string} path Path relative to the User File Store
|
||||||
|
* @returns {string|false} Content-Type, or false if could not open file
|
||||||
|
*/
|
||||||
|
getUserStoreContentType(path) {
|
||||||
|
return this.wtvmime.getSimpleContentType(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a RFC7231 compliant UTC Date String from the current time
|
||||||
|
* @param {Number} offset Offset from current time (+/-)
|
||||||
|
* @returns {string} A RFC7231 compliant UTC Date String from the current time
|
||||||
|
*/
|
||||||
getUTCTime(offset = 0) {
|
getUTCTime(offset = 0) {
|
||||||
return new Date((new Date).getTime() + offset).toUTCString();
|
return new Date((new Date).getTime() + offset).toUTCString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of user cookies
|
||||||
|
* @returns {number} Number of cookies
|
||||||
|
*/
|
||||||
countCookies() {
|
countCookies() {
|
||||||
return Object.keys(this.session_store.cookies).length || 0;
|
return Object.keys(this.session_store.cookies).length || 0;
|
||||||
}
|
}
|
||||||
@@ -174,8 +237,8 @@ class WTVClientSessionData {
|
|||||||
|
|
||||||
loadSessionData(raw_data = false) {
|
loadSessionData(raw_data = false) {
|
||||||
try {
|
try {
|
||||||
if (this.fs.lstatSync(this.session_storage + this.path.sep + this.ssid + ".json")) {
|
if (this.fs.lstatSync(this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + ".json")) {
|
||||||
var json_data = this.fs.readFileSync(this.session_storage + this.path.sep + this.ssid + ".json", 'Utf8')
|
var json_data = this.fs.readFileSync(this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + ".json", 'Utf8')
|
||||||
if (raw_data) return json_data;
|
if (raw_data) return json_data;
|
||||||
|
|
||||||
var session_data = JSON.parse(json_data);
|
var session_data = JSON.parse(json_data);
|
||||||
@@ -184,7 +247,7 @@ class WTVClientSessionData {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Don't log error 'file not found', it just means the client isn't registered yet
|
// Don't log error 'file not found', it just means the client isn't registered yet
|
||||||
if (e.code != "ENOENT") console.error(" # Error loading session data for", this.filterSSID(this.ssid), e);
|
if (e.code != "ENOENT") console.error(" # Error loading session data for", this.wtvshared.filterSSID(this.ssid), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,10 +269,10 @@ class WTVClientSessionData {
|
|||||||
// only save if file has changed
|
// only save if file has changed
|
||||||
var json_save_data = JSON.stringify(this.session_store);
|
var json_save_data = JSON.stringify(this.session_store);
|
||||||
var json_load_data = this.loadSessionData(true);
|
var json_load_data = this.loadSessionData(true);
|
||||||
if (json_save_data != json_load_data) this.fs.writeFileSync(this.session_storage + this.path.sep + this.ssid + ".json", JSON.stringify(this.session_store), "Utf8");
|
if (json_save_data != json_load_data) this.fs.writeFileSync(this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + ".json", JSON.stringify(this.session_store), "Utf8");
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(" # Error saving session data for", this.filterSSID(this.ssid), e);
|
console.error(" # Error saving session data for", this.wtvshared.filterSSID(this.ssid), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +295,7 @@ class WTVClientSessionData {
|
|||||||
isRegistered() {
|
isRegistered() {
|
||||||
var self = this;
|
var self = this;
|
||||||
var ssid_match = false;
|
var ssid_match = false;
|
||||||
this.fs.readdirSync(this.session_storage).forEach(file => {
|
this.fs.readdirSync(this.minisrv_config.config.SessionStore).forEach(file => {
|
||||||
if (!file.match(/.*\.json/ig)) return;
|
if (!file.match(/.*\.json/ig)) return;
|
||||||
if (ssid_match) return;
|
if (ssid_match) return;
|
||||||
if (file.split('.')[0] == self.ssid) ssid_match = true;
|
if (file.split('.')[0] == self.ssid) ssid_match = true;
|
||||||
@@ -242,14 +305,14 @@ class WTVClientSessionData {
|
|||||||
|
|
||||||
unregisterBox() {
|
unregisterBox() {
|
||||||
try {
|
try {
|
||||||
if (this.fs.lstatSync(this.session_storage + this.path.sep + this.ssid + ".json")) {
|
if (this.fs.lstatSync(this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + ".json")) {
|
||||||
this.fs.unlinkSync(this.session_storage + this.path.sep + this.ssid + ".json");
|
this.fs.unlinkSync(this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + ".json");
|
||||||
this.session_store = {};
|
this.session_store = {};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Don't log error 'file not found', it just means the client isn't registered yet
|
// Don't log error 'file not found', it just means the client isn't registered yet
|
||||||
console.error(" # Error deleting session data for", this.filterSSID(this.ssid), e);
|
console.error(" # Error deleting session data for", this.wtvshared.filterSSID(this.ssid), e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,24 @@
|
|||||||
* By: zefie
|
* By: zefie
|
||||||
*/
|
*/
|
||||||
class WTVDownloadList {
|
class WTVDownloadList {
|
||||||
download_list = "";
|
|
||||||
content_type = "wtv/download-list";
|
|
||||||
|
|
||||||
constructor() {
|
download_list = "";
|
||||||
|
service_name = "";
|
||||||
|
content_type = "wtv/download-list";
|
||||||
|
wtvshared = null;
|
||||||
|
clientShowAlert = null;
|
||||||
|
minisrv_config = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the WTVDownloadList Class
|
||||||
|
* @param {string} service_name Service name to use in wtv-urls
|
||||||
|
*/
|
||||||
|
constructor(minisrv_config, service_name = "wtv-disk") {
|
||||||
|
var { WTVShared, clientShowAlert } = require('./WTVShared.js');
|
||||||
|
this.minisrv_config = minisrv_config;
|
||||||
|
this.wtvshared = new WTVShared(minisrv_config);
|
||||||
|
this.clientShowAlert = clientShowAlert;
|
||||||
|
this.service_name = service_name
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,13 +112,31 @@ class WTVDownloadList {
|
|||||||
* Adds a PUT command to the download list
|
* Adds a PUT command to the download list
|
||||||
* @param {string} path Absolute file://Disk/ path of a file to upload to the service
|
* @param {string} path Absolute file://Disk/ path of a file to upload to the service
|
||||||
* @param {string} destination Destination address (wtv url on service) in which to POST upload the file to
|
* @param {string} destination Destination address (wtv url on service) in which to POST upload the file to
|
||||||
* @param {string} display Message to display while working on this file
|
|
||||||
*/
|
*/
|
||||||
put(path, destination) {
|
put(path, destination) {
|
||||||
this.download_list += "PUT " + path + "\n";
|
this.download_list += "PUT " + path + "\n";
|
||||||
this.download_list += "location: " + destination + "\n";
|
this.download_list += "location: " + destination + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias to put() for User Store
|
||||||
|
* @param {string} path Absolute file://Disk/ path of a file to upload to the service
|
||||||
|
* @param {string} destination Destination file path in the User Store
|
||||||
|
*/
|
||||||
|
putUserStoreDest(path, destination) {
|
||||||
|
this.download_list += "PUT " + path + "\n";
|
||||||
|
this.download_list += "location: " + this.service_name + ":/userstore?partialPath="+escape(destination) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias to putUserStoreDest() that generates the destination
|
||||||
|
* @param {any} path
|
||||||
|
*/
|
||||||
|
putUserStore(path) {
|
||||||
|
var destination = path.replace("file://", "");
|
||||||
|
this.download_list += "PUT " + path + "\n";
|
||||||
|
this.download_list += "location: " + this.service_name + ":/userstore?partialPath=" + escape(destination) + "\n\n";
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Adds a GET command to the download list
|
* Adds a GET command to the download list
|
||||||
* @param {string} file Non-absolute path of client destination file (relative to group base)
|
* @param {string} file Non-absolute path of client destination file (relative to group base)
|
||||||
@@ -169,6 +201,99 @@ class WTVDownloadList {
|
|||||||
this.deleteGroup(group + "-UPDATE");
|
this.deleteGroup(group + "-UPDATE");
|
||||||
this.delete(path + ".GROUP-UPDATE/");
|
this.delete(path + ".GROUP-UPDATE/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the Download page
|
||||||
|
* @param {object} minisrv_config minisrv config object
|
||||||
|
* @param {string} title Page title
|
||||||
|
* @param {string} group
|
||||||
|
* @param {string|null} diskmap
|
||||||
|
* @param {string|null} main_message Message displayed in the center of the page
|
||||||
|
* @param {string|null} message Initial progress bar message
|
||||||
|
* @param {boolean|null} force_update Force this update even if the client reports the files are synced
|
||||||
|
* @param {string|null} success_url Where the client goes when the process succeeds
|
||||||
|
* @param {string|null} fail_url Where the client goes when the process fails.
|
||||||
|
* @param {string|null} url Use your own URL for client:fetch?source= instead of our generated one
|
||||||
|
* @returns {string} HTML Download Page
|
||||||
|
*/
|
||||||
|
getSyncPage(title, group, diskmap = null, main_message = null, message = null, force_update = null, success_url = null, fail_url = null, url = null) {
|
||||||
|
// Begin Set defaults
|
||||||
|
if (main_message === null) main_message = "Your receiver is downloading files.";
|
||||||
|
|
||||||
|
if (message === null) message = "Retrieving files";
|
||||||
|
|
||||||
|
if (force_update === null) force_update = false;
|
||||||
|
|
||||||
|
if (url === null) url = this.service_name + ":/sync?diskmap=" + escape(diskmap) + "&force=" + force_update;
|
||||||
|
|
||||||
|
if (success_url === null) success_url = new this.clientShowAlert({
|
||||||
|
'image': this.minisrv_config.config.service_logo,
|
||||||
|
'message': "Download successful!",
|
||||||
|
'buttonlabel1': "Okay",
|
||||||
|
'buttonaction1': "client:goback",
|
||||||
|
'noback': true,
|
||||||
|
}).getURL();
|
||||||
|
|
||||||
|
if (fail_url === null) fail_url = new this.clientShowAlert({
|
||||||
|
'image': this.minisrv_config.config.service_logo,
|
||||||
|
'message': "Download failed...",
|
||||||
|
'buttonlabel1': "Okay...",
|
||||||
|
'buttonaction1': "client:goback",
|
||||||
|
'noback': true,
|
||||||
|
}).getURL();
|
||||||
|
// End set defaults
|
||||||
|
return `<html>
|
||||||
|
<head>
|
||||||
|
<meta
|
||||||
|
http-equiv=refresh
|
||||||
|
content="0;url=client:Fetch?group=${escape(group)}&source=${escape(url)}&message=${escape(message)}"
|
||||||
|
>
|
||||||
|
<display downloadsuccess="${success_url}" downloadfail="${fail_url}">
|
||||||
|
<title>${title}</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="${this.minisrv_config.config.service_logo}" 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>
|
||||||
|
${message}
|
||||||
|
</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>
|
||||||
|
${main_message}
|
||||||
|
<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>
|
||||||
|
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = WTVDownloadList;
|
module.exports = WTVDownloadList;
|
||||||
198
zefie_wtvp_minisrv/WTVMime.js
Normal file
198
zefie_wtvp_minisrv/WTVMime.js
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* Simple class for WebTV Mime Types and overrides
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class WTVMime {
|
||||||
|
|
||||||
|
mime = require('mime-types');
|
||||||
|
wtvshared = null;
|
||||||
|
minisrv_config = [];
|
||||||
|
|
||||||
|
|
||||||
|
constructor(minisrv_config) {
|
||||||
|
var WTVShared = require('./WTVShared.js')['WTVShared'];
|
||||||
|
this.minisrv_config = minisrv_config;
|
||||||
|
this.wtvshared = new WTVShared(minisrv_config);
|
||||||
|
if (!String.prototype.reverse) {
|
||||||
|
String.prototype.reverse = function () {
|
||||||
|
var splitString = this.split("");
|
||||||
|
var reverseArray = splitString.reverse();
|
||||||
|
var joinArray = reverseArray.join("");
|
||||||
|
return joinArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
shouldWeCompress(ssid_session, headers_obj) {
|
||||||
|
var compress_data = false;
|
||||||
|
var compression_type = 0; // no compression
|
||||||
|
if (ssid_session) {
|
||||||
|
if (ssid_session.capabilities) {
|
||||||
|
if (ssid_session.capabilities['client-can-receive-compressed-data']) {
|
||||||
|
|
||||||
|
if (this.minisrv_config.config.enable_lzpf_compression || this.minisrv_config.config.force_compression_type) {
|
||||||
|
compression_type = 1; // lzpf
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssid_session) {
|
||||||
|
// if gzip is enabled...
|
||||||
|
if (this.minisrv_config.config.enable_gzip_compression || this.minisrv_config.config.force_compression_type) {
|
||||||
|
var is_bf0app = ssid_session.get("wtv-client-rom-type") == "bf0app";
|
||||||
|
var is_minibrowser = (ssid_session.get("wtv-needs-upgrade") || ssid_session.get("wtv-used-8675309"));
|
||||||
|
var is_softmodem = ssid_session.get("wtv-client-rom-type").match(/softmodem/);
|
||||||
|
if (!is_bf0app && ((!is_softmodem && !is_minibrowser) || (is_softmodem && !is_minibrowser))) {
|
||||||
|
// softmodem boxes do not appear to support gzip in the minibrowser
|
||||||
|
// LC2 appears to support gzip even in the MiniBrowser
|
||||||
|
// LC2 and newer approms appear to support gzip
|
||||||
|
// bf0app does not appear to support gzip
|
||||||
|
compression_type = 2; // gzip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// mostly for debugging
|
||||||
|
if (this.minisrv_config.config.force_compression_type == "lzpf") compression_type = 1;
|
||||||
|
if (this.minisrv_config.config.force_compression_type == "gzip") compression_type = 2;
|
||||||
|
|
||||||
|
// do not compress if already encoded
|
||||||
|
if (headers_obj["Content-Encoding"]) return 0;
|
||||||
|
|
||||||
|
// should we bother to compress?
|
||||||
|
var content_type = "";
|
||||||
|
if (typeof (headers_obj) == 'string') content_type = headers_obj;
|
||||||
|
else content_type = (typeof (headers_obj["wtv-modern-content-type"]) != 'undefined') ? headers_obj["wtv-modern-content-type"] : headers_obj["Content-Type"];
|
||||||
|
|
||||||
|
if (content_type) {
|
||||||
|
// both lzpf and gzip
|
||||||
|
if (content_type.match(/^text\//) && content_type != "text/tellyscript") compress_data = true;
|
||||||
|
else if (content_type.match(/^application\/(x-?)javascript$/)) compress_data = true;
|
||||||
|
else if (content_type == "application/json") compress_data = true;
|
||||||
|
if (compression_type == 2) {
|
||||||
|
// gzip only
|
||||||
|
if (content_type.match(/^audio\/(x-)?[s3m|mod|xm]$/)) compress_data = true; // s3m, mod, xm
|
||||||
|
if (content_type.match(/^audio\/(x-)?[midi|wav|wave]$/)) compress_data = true; // midi & wav
|
||||||
|
if (content_type.match(/^binary\/x-wtv-approm$/)) compress_data = true; // approms
|
||||||
|
if (content_type.match(/^binary\/doom-data$/)) compress_data = true; // DOOM WADs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return compression_type if compress_data = true
|
||||||
|
return (compress_data) ? compression_type : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the WebTV Content-Type
|
||||||
|
* @param {string} path Path to a file
|
||||||
|
* @returns {string} Content-Type
|
||||||
|
*/
|
||||||
|
getSimpleContentType(path) {
|
||||||
|
return this.getContentType(path)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets both the WebTV Content-Type and the Modern Content-Type
|
||||||
|
* @param {string} path Path to a file
|
||||||
|
* @returns {Array} (WebTV Content-Type, Modern Content-Type)
|
||||||
|
*/
|
||||||
|
getContentType(path) {
|
||||||
|
var file_ext = this.wtvshared.getFileExt(path).toLowerCase();
|
||||||
|
var wtv_mime_type = "";
|
||||||
|
var modern_mime_type = "";
|
||||||
|
// process WebTV overrides, fall back to generic mime lookup
|
||||||
|
switch (file_ext) {
|
||||||
|
case "aif":
|
||||||
|
wtv_mime_type = "audio/x-aif";
|
||||||
|
break;
|
||||||
|
case "aifc":
|
||||||
|
wtv_mime_type = "audio/x-aifc";
|
||||||
|
break;
|
||||||
|
case "aiff":
|
||||||
|
wtv_mime_type = "audio/x-aiff";
|
||||||
|
break;
|
||||||
|
case "ani":
|
||||||
|
wtv_mime_type = "x-wtv-animation";
|
||||||
|
break;
|
||||||
|
case "brom":
|
||||||
|
wtv_mime_type = "binary/x-wtv-bootrom";
|
||||||
|
break;
|
||||||
|
case "cdf":
|
||||||
|
wtv_mime_type = "application/netcdf";
|
||||||
|
break;
|
||||||
|
case "dat":
|
||||||
|
wtv_mime_type = "binary/cache-data";
|
||||||
|
break;
|
||||||
|
case "dl":
|
||||||
|
wtv_mime_type = "wtv/download-list";
|
||||||
|
break;
|
||||||
|
case "gsm":
|
||||||
|
wtv_mime_type = "audio/x-gsm";
|
||||||
|
break;
|
||||||
|
case "gz":
|
||||||
|
wtv_mime_type = "application/gzip";
|
||||||
|
break;
|
||||||
|
case "ini":
|
||||||
|
wtv_mime_type = "wtv/jack-configuration";
|
||||||
|
break;
|
||||||
|
case "mips-code":
|
||||||
|
wtv_mime_type = "code/x-wtv-code-mips";
|
||||||
|
break;
|
||||||
|
case "o":
|
||||||
|
wtv_mime_type = "binary/x-wtv-approm";
|
||||||
|
break;
|
||||||
|
case "ram":
|
||||||
|
wtv_mime_type = "audio/x-pn-realaudio";
|
||||||
|
break;
|
||||||
|
case "rom":
|
||||||
|
wtv_mime_type = "binary/x-wtv-flashblock";
|
||||||
|
break;
|
||||||
|
case "rsp":
|
||||||
|
wtv_mime_type = "wtv/jack-response";
|
||||||
|
break;
|
||||||
|
case "swa":
|
||||||
|
case "swf":
|
||||||
|
wtv_mime_type = "application/x-shockwave-flash";
|
||||||
|
break;
|
||||||
|
case "srf":
|
||||||
|
case "spl":
|
||||||
|
wtv_mime_type = "wtv/jack-data";
|
||||||
|
break;
|
||||||
|
case "ttf":
|
||||||
|
wtv_mime_type = "wtv/jack-fonts";
|
||||||
|
break;
|
||||||
|
case "tvch":
|
||||||
|
wtv_mime_type = "wtv/tv-channels";
|
||||||
|
break;
|
||||||
|
case "tvl":
|
||||||
|
wtv_mime_type = "wtv/tv-listings";
|
||||||
|
break;
|
||||||
|
case "tvsl":
|
||||||
|
wtv_mime_type = "wtv/tv-smartlinks";
|
||||||
|
break;
|
||||||
|
case "wad":
|
||||||
|
wtv_mime_type = "binary/doom-data";
|
||||||
|
break;
|
||||||
|
case "mp2":
|
||||||
|
case "hsb":
|
||||||
|
case "rmf":
|
||||||
|
case "s3m":
|
||||||
|
case "mod":
|
||||||
|
case "xm":
|
||||||
|
wtv_mime_type = "application/Music";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
modern_mime_type = this.mime.lookup(path);
|
||||||
|
if (wtv_mime_type == "") wtv_mime_type = modern_mime_type;
|
||||||
|
return new Array(wtv_mime_type, modern_mime_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = WTVMime;
|
||||||
143
zefie_wtvp_minisrv/WTVShared.js
Normal file
143
zefie_wtvp_minisrv/WTVShared.js
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* Shared functions across all classes and apps
|
||||||
|
*/
|
||||||
|
|
||||||
|
class WTVShared {
|
||||||
|
|
||||||
|
path = require('path');
|
||||||
|
minisrv_config = [];
|
||||||
|
|
||||||
|
constructor(minisrv_config) {
|
||||||
|
this.minisrv_config = minisrv_config;
|
||||||
|
if (!String.prototype.reverse) {
|
||||||
|
String.prototype.reverse = function () {
|
||||||
|
var splitString = this.split("");
|
||||||
|
var reverseArray = splitString.reverse();
|
||||||
|
var joinArray = reverseArray.join("");
|
||||||
|
return joinArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a censored SSID
|
||||||
|
* @param {string|Array} obj SSID String or Headers Object
|
||||||
|
*/
|
||||||
|
filterSSID(obj) {
|
||||||
|
if (this.minisrv_config.config.hide_ssid_in_logs === true) {
|
||||||
|
if (typeof (obj) == "string") {
|
||||||
|
if (obj.substr(0, 8) == "MSTVSIMU") {
|
||||||
|
return obj.substr(0, 10) + ('*').repeat(10) + obj.substr(20);
|
||||||
|
} else if (obj.substr(0, 5) == "1SEGA") {
|
||||||
|
return obj.substr(0, 6) + ('*').repeat(6) + obj.substr(13);
|
||||||
|
} else {
|
||||||
|
return obj.substr(0, 6) + ('*').repeat(9);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (obj["wtv-client-serial-number"]) {
|
||||||
|
var ssid = obj["wtv-client-serial-number"];
|
||||||
|
if (ssid.substr(0, 8) == "MSTVSIMU") {
|
||||||
|
obj["wtv-client-serial-number"] = ssid.substr(0, 10) + ('*').repeat(10) + ssid.substr(20);
|
||||||
|
} else if (ssid.substr(0, 5) == "1SEGA") {
|
||||||
|
obj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(6) + ssid.substr(13);
|
||||||
|
} else {
|
||||||
|
obj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an absolute path
|
||||||
|
* @param {string} path
|
||||||
|
* @param {string} directory Root directory
|
||||||
|
*/
|
||||||
|
getAbsolutePath(path, directory = __dirname) {
|
||||||
|
if (path.substring(0, 1) != this.path.sep && path.substring(1, 1) != ":") {
|
||||||
|
// non-absolute path, so use current directory as base
|
||||||
|
path = (directory + this.path.sep + path);
|
||||||
|
} else {
|
||||||
|
// already absolute path
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the file extension from a path
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {String} File Extension (without dot)
|
||||||
|
*/
|
||||||
|
getFileExt(path) {
|
||||||
|
return path.reverse().split(".")[0].reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips bad things from paths
|
||||||
|
* @param {string} base Base path
|
||||||
|
* @param {string} target Sub path
|
||||||
|
*/
|
||||||
|
makeSafePath(base, target) {
|
||||||
|
target.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, "");
|
||||||
|
if (this.path.sep != "/") target = target.replace(/\//g, this.path.sep);
|
||||||
|
var targetPath = this.path.posix.normalize(target)
|
||||||
|
return base + this.path.sep + targetPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes sure an SSID is clean, and doesn't contain any exploitable characters
|
||||||
|
* @param {string} ssid
|
||||||
|
* @returns {string} Sanitized SSID
|
||||||
|
*/
|
||||||
|
makeSafeSSID(ssid = "") {
|
||||||
|
ssid = ssid.replace(/[^a-zA-Z0-9]/g, "");
|
||||||
|
if (ssid.length == 0) ssid = null;
|
||||||
|
return ssid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class clientShowAlert {
|
||||||
|
message = null;
|
||||||
|
buttonlabel1 = null;
|
||||||
|
buttonlabel2 = null;
|
||||||
|
buttonaction1 = null;
|
||||||
|
buttonaction2 = null;
|
||||||
|
noback = null;
|
||||||
|
image = null;
|
||||||
|
|
||||||
|
constructor(image = null, message = null, buttonlabel1 = null, buttonaction1 = null, buttonlabel2 = null, buttonaction2 = null, noback = null) {
|
||||||
|
this.message = message;
|
||||||
|
this.buttonlabel1 = buttonlabel1;
|
||||||
|
this.buttonlabel2 = buttonlabel2;
|
||||||
|
this.buttonaction1 = buttonaction1;
|
||||||
|
this.buttonaction2 = buttonaction2;
|
||||||
|
this.message = message;
|
||||||
|
this.noback = noback;
|
||||||
|
if (typeof image === 'object') {
|
||||||
|
this.image = null;
|
||||||
|
Object.keys(image).forEach(function (k) {
|
||||||
|
if (this[k] === null) this[k] = image[k];
|
||||||
|
}, this);
|
||||||
|
} else {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getURL() {
|
||||||
|
var url = "client:ShowAlert?";
|
||||||
|
if (this.message) url += "message=" + escape(this.message) + "&";
|
||||||
|
if (this.buttonlabel1) url += "buttonlabel1=" + escape(this.buttonlabel1) + "&";
|
||||||
|
if (this.buttonaction1) url += "buttonaction1=" + escape(this.buttonaction1) + "&";
|
||||||
|
if (this.buttonlabel2) url += "buttonlabel2=" + escape(this.buttonlabel2) + "&";
|
||||||
|
if (this.buttonaction2) url += "buttonaction2=" + escape(this.buttonaction2) + "&";
|
||||||
|
if (this.image) url += "image=" + escape(this.image) + "&";
|
||||||
|
if (this.noback) url += "noback=true&";
|
||||||
|
return url.substring(0, url.length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.WTVShared = WTVShared;
|
||||||
|
module.exports.clientShowAlert = clientShowAlert;
|
||||||
@@ -8,13 +8,14 @@ const https = require('https');
|
|||||||
const strftime = require('strftime'); // used externally by service scripts
|
const strftime = require('strftime'); // used externally by service scripts
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const CryptoJS = require('crypto-js');
|
const CryptoJS = require('crypto-js');
|
||||||
const mime = require('mime-types');
|
|
||||||
const { crc16 } = require('easy-crc');
|
const { crc16 } = require('easy-crc');
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
var WTVSec = require('./WTVSec.js');
|
var WTVSec = require('./WTVSec.js');
|
||||||
var WTVLzpf = require('./WTVLzpf.js');
|
var WTVLzpf = require('./WTVLzpf.js');
|
||||||
var WTVClientCapabilities = require('./WTVClientCapabilities.js');
|
var WTVClientCapabilities = require('./WTVClientCapabilities.js');
|
||||||
var WTVClientSessionData = require('./WTVClientSessionData.js');
|
var WTVClientSessionData = require('./WTVClientSessionData.js');
|
||||||
|
var WTVMime = require("./WTVMime.js");
|
||||||
|
var { WTVShared, clientShowAlert } = require("./WTVShared.js");
|
||||||
|
|
||||||
process
|
process
|
||||||
.on('SIGTERM', shutdown('SIGTERM'))
|
.on('SIGTERM', shutdown('SIGTERM'))
|
||||||
@@ -70,10 +71,6 @@ function getServiceString(service, overrides = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileExt(path) {
|
|
||||||
return path.reverse().split(".")[0].reverse();
|
|
||||||
}
|
|
||||||
|
|
||||||
function doErrorPage(code, data = null, pc_mode = false) {
|
function doErrorPage(code, data = null, pc_mode = false) {
|
||||||
var headers = null;
|
var headers = null;
|
||||||
switch (code) {
|
switch (code) {
|
||||||
@@ -105,99 +102,6 @@ function doErrorPage(code, data = null, pc_mode = false) {
|
|||||||
return new Array(headers, data);
|
return new Array(headers, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getConType(path) {
|
|
||||||
var file_ext = getFileExt(path).toLowerCase();
|
|
||||||
var wtv_mime_type = "";
|
|
||||||
var modern_mime_type = "";
|
|
||||||
// process WebTV overrides, fall back to generic mime lookup
|
|
||||||
switch (file_ext) {
|
|
||||||
case "aif":
|
|
||||||
wtv_mime_type = "audio/x-aif";
|
|
||||||
break;
|
|
||||||
case "aifc":
|
|
||||||
wtv_mime_type = "audio/x-aifc";
|
|
||||||
break;
|
|
||||||
case "aiff":
|
|
||||||
wtv_mime_type = "audio/x-aiff";
|
|
||||||
break;
|
|
||||||
case "ani":
|
|
||||||
wtv_mime_type = "x-wtv-animation";
|
|
||||||
break;
|
|
||||||
case "brom":
|
|
||||||
wtv_mime_type = "binary/x-wtv-bootrom";
|
|
||||||
break;
|
|
||||||
case "cdf":
|
|
||||||
wtv_mime_type = "application/netcdf";
|
|
||||||
break;
|
|
||||||
case "dat":
|
|
||||||
wtv_mime_type = "binary/cache-data";
|
|
||||||
break;
|
|
||||||
case "dl":
|
|
||||||
wtv_mime_type = "wtv/download-list";
|
|
||||||
break;
|
|
||||||
case "gsm":
|
|
||||||
wtv_mime_type = "audio/x-gsm";
|
|
||||||
break;
|
|
||||||
case "gz":
|
|
||||||
wtv_mime_type = "application/gzip";
|
|
||||||
break;
|
|
||||||
case "ini":
|
|
||||||
wtv_mime_type = "wtv/jack-configuration";
|
|
||||||
break;
|
|
||||||
case "mips-code":
|
|
||||||
wtv_mime_type = "code/x-wtv-code-mips";
|
|
||||||
break;
|
|
||||||
case "o":
|
|
||||||
wtv_mime_type = "binary/x-wtv-approm";
|
|
||||||
break;
|
|
||||||
case "ram":
|
|
||||||
wtv_mime_type = "audio/x-pn-realaudio";
|
|
||||||
break;
|
|
||||||
case "rom":
|
|
||||||
wtv_mime_type = "binary/x-wtv-flashblock";
|
|
||||||
break;
|
|
||||||
case "rsp":
|
|
||||||
wtv_mime_type = "wtv/jack-response";
|
|
||||||
break;
|
|
||||||
case "swa":
|
|
||||||
case "swf":
|
|
||||||
wtv_mime_type = "application/x-shockwave-flash";
|
|
||||||
break;
|
|
||||||
case "srf":
|
|
||||||
case "spl":
|
|
||||||
wtv_mime_type = "wtv/jack-data";
|
|
||||||
break;
|
|
||||||
case "ttf":
|
|
||||||
wtv_mime_type = "wtv/jack-fonts";
|
|
||||||
break;
|
|
||||||
case "tvch":
|
|
||||||
wtv_mime_type = "wtv/tv-channels";
|
|
||||||
break;
|
|
||||||
case "tvl":
|
|
||||||
wtv_mime_type = "wtv/tv-listings";
|
|
||||||
break;
|
|
||||||
case "tvsl":
|
|
||||||
wtv_mime_type = "wtv/tv-smartlinks";
|
|
||||||
break;
|
|
||||||
case "wad":
|
|
||||||
wtv_mime_type = "binary/doom-data";
|
|
||||||
break;
|
|
||||||
case "mp2":
|
|
||||||
case "hsb":
|
|
||||||
case "rmf":
|
|
||||||
case "s3m":
|
|
||||||
case "mod":
|
|
||||||
case "xm":
|
|
||||||
wtv_mime_type = "application/Music";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
modern_mime_type = mime.lookup(path);
|
|
||||||
if (wtv_mime_type == "") wtv_mime_type = modern_mime_type;
|
|
||||||
return new Array(wtv_mime_type, modern_mime_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processPath(socket, service_vault_file_path, request_headers = new Array(), service_name) {
|
async function processPath(socket, service_vault_file_path, request_headers = new Array(), service_name) {
|
||||||
var headers, data = null;
|
var headers, data = null;
|
||||||
var request_is_async = false;
|
var request_is_async = false;
|
||||||
@@ -206,7 +110,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
|
|||||||
try {
|
try {
|
||||||
service_vaults.forEach(function (service_vault_dir) {
|
service_vaults.forEach(function (service_vault_dir) {
|
||||||
if (service_vault_found) return;
|
if (service_vault_found) return;
|
||||||
service_vault_file_path = makeSafePath(service_vault_dir, service_path);
|
service_vault_file_path = wtvshared.makeSafePath(service_vault_dir, service_path);
|
||||||
|
|
||||||
// deny access to catchall file name directly
|
// deny access to catchall file name directly
|
||||||
var service_path_split = service_path.split("/");
|
var service_path_split = service_path.split("/");
|
||||||
@@ -231,7 +135,7 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
|
|||||||
service_vault_found = true;
|
service_vault_found = true;
|
||||||
request_is_async = true;
|
request_is_async = true;
|
||||||
if (!zquiet) console.log(" * Found " + service_vault_file_path + " to handle request (Direct File Mode) [Socket " + socket.id + "]");
|
if (!zquiet) console.log(" * Found " + service_vault_file_path + " to handle request (Direct File Mode) [Socket " + socket.id + "]");
|
||||||
var contypes = getConType(service_vault_file_path);
|
var contypes = wtvmime.getContentType(service_vault_file_path);
|
||||||
headers = "200 OK\n"
|
headers = "200 OK\n"
|
||||||
headers += "Content-Type: " + contypes[0] + "\n";
|
headers += "Content-Type: " + contypes[0] + "\n";
|
||||||
headers += "wtv-modern-content-type" + contypes[1];
|
headers += "wtv-modern-content-type" + contypes[1];
|
||||||
@@ -345,47 +249,6 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterSSID(obj) {
|
|
||||||
if (minisrv_config.config.hide_ssid_in_logs === true) {
|
|
||||||
if (typeof (obj) == "string") {
|
|
||||||
if (obj.substr(0, 8) == "MSTVSIMU") {
|
|
||||||
return obj.substr(0, 10) + ('*').repeat(10) + obj.substr(20);
|
|
||||||
} else if (obj.substr(0, 5) == "1SEGA") {
|
|
||||||
return obj.substr(0, 6) + ('*').repeat(6) + obj.substr(13);
|
|
||||||
} else {
|
|
||||||
return obj.substr(0, 6) + ('*').repeat(9);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (makeSafeSSID(obj["wtv-client-serial-number"])) {
|
|
||||||
var ssid = makeSafeSSID(obj["wtv-client-serial-number"]);
|
|
||||||
if (ssid.substr(0, 8) == "MSTVSIMU") {
|
|
||||||
obj["wtv-client-serial-number"] = ssid.substr(0, 10) + ('*').repeat(10) + ssid.substr(20);
|
|
||||||
} else if (ssid.substr(0, 5) == "1SEGA") {
|
|
||||||
obj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(6) + ssid.substr(13);
|
|
||||||
} else {
|
|
||||||
obj["wtv-client-serial-number"] = ssid.substr(0, 6) + ('*').repeat(9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeSafeSSID(ssid = "") {
|
|
||||||
ssid = ssid.replace(/[^a-zA-Z0-9]/g, "");
|
|
||||||
if (ssid.length == 0) ssid = null;
|
|
||||||
return ssid;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeSafePath(base, target) {
|
|
||||||
target.replace(/[\|\&\;\$\%\@\"\<\>\+\,\\]/g, "");
|
|
||||||
if (path.sep != "/") target = target.replace(/\//g, path.sep);
|
|
||||||
var targetPath = path.posix.normalize(target)
|
|
||||||
return base + path.sep + targetPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processURL(socket, request_headers) {
|
async function processURL(socket, request_headers) {
|
||||||
var shortURL, headers, data = "";
|
var shortURL, headers, data = "";
|
||||||
request_headers.query = new Array();
|
request_headers.query = new Array();
|
||||||
@@ -408,28 +271,33 @@ async function processURL(socket, request_headers) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request_headers.post_data) {
|
if (request_headers.post_data) {
|
||||||
var post_data_string = request_headers.post_data.toString(CryptoJS.enc.Utf8).replace("\0", "");
|
var post_data_string = '';
|
||||||
if (isUnencryptedString(post_data_string)) {
|
try {
|
||||||
if (post_data_string.indexOf('=')) {
|
post_data_string = request_headers.post_data.toString(CryptoJS.enc.Utf8).replace("\0", ""); // if not text this will probably throw an exception
|
||||||
if (post_data_string.indexOf('&')) {
|
if (isUnencryptedString(post_data_string)) {
|
||||||
var qraw = post_data_string.split('&');
|
if (post_data_string.indexOf('=')) {
|
||||||
if (qraw.length > 0) {
|
if (post_data_string.indexOf('&')) {
|
||||||
for (let i = 0; i < qraw.length; i++) {
|
var qraw = post_data_string.split('&');
|
||||||
var qraw_split = qraw[i].split("=");
|
if (qraw.length > 0) {
|
||||||
if (qraw_split.length == 2) {
|
for (let i = 0; i < qraw.length; i++) {
|
||||||
var k = qraw_split[0];
|
var qraw_split = qraw[i].split("=");
|
||||||
request_headers.query[k] = unescape(qraw[i].split("=")[1].replace(/\+/g, "%20"));
|
if (qraw_split.length == 2) {
|
||||||
|
var k = qraw_split[0];
|
||||||
|
request_headers.query[k] = unescape(qraw[i].split("=")[1].replace(/\+/g, "%20"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
var qraw_split = post_data_string.split("=");
|
||||||
var qraw_split = post_data_string.split("=");
|
if (qraw_split.length == 2) {
|
||||||
if (qraw_split.length == 2) {
|
var k = qraw_split[0];
|
||||||
var k = qraw_split[0];
|
request_headers.query[k] = unescape(qraw_split[1].replace(/\+/g, "%20"));
|
||||||
request_headers.query[k] = unescape(qraw_split[1].replace(/\+/g, "%20"));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// do nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,26 +337,21 @@ async function processURL(socket, request_headers) {
|
|||||||
var ssid = socket.ssid;
|
var ssid = socket.ssid;
|
||||||
if (ssid == null) {
|
if (ssid == null) {
|
||||||
// prevent possible injection attacks via SSID and filesystem SessionStore
|
// prevent possible injection attacks via SSID and filesystem SessionStore
|
||||||
ssid = makeSafeSSID(request_headers["wtv-client-serial-number"]);
|
ssid = wtvshared.makeSafeSSID(request_headers["wtv-client-serial-number"]);
|
||||||
if (ssid == "") ssid = null;
|
if (ssid == "") ssid = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var reqverb = "Request";
|
var reqverb = "Request";
|
||||||
if (request_headers.encrypted || request_headers.secure) {
|
if (request_headers.encrypted || request_headers.secure) reqverb = "Encrypted " + reqverb;
|
||||||
reqverb = "Encrypted " + reqverb;
|
|
||||||
}
|
|
||||||
if (request_headers.psuedo_encryption) {
|
|
||||||
reqverb = "Psuedo-encrypted " + reqverb;
|
|
||||||
}
|
|
||||||
if (ssid != null) {
|
if (ssid != null) {
|
||||||
console.log(" * " + reqverb + " for " + request_headers.request_url + " from WebTV SSID " + (await filterSSID(ssid)), 'on', socket.id);
|
console.log(" * " + reqverb + " for " + request_headers.request_url + " from WebTV SSID " + (await wtvshared.filterSSID(ssid)), 'on', socket.id);
|
||||||
} else {
|
} else {
|
||||||
console.log(" * " + reqverb + " for " + request_headers.request_url, 'on', socket.id);
|
console.log(" * " + reqverb + " for " + request_headers.request_url, 'on', socket.id);
|
||||||
}
|
}
|
||||||
// assume webtv since there is a :/ in the GET
|
// assume webtv since there is a :/ in the GET
|
||||||
var service_name = shortURL.split(':/')[0];
|
var service_name = shortURL.split(':/')[0];
|
||||||
var urlToPath = service_name + path.sep + 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)));
|
if (zshowheaders) console.log(" * Incoming headers on socket ID", socket.id, (await wtvshared.filterSSID(request_headers)));
|
||||||
processPath(socket, urlToPath, request_headers, service_name);
|
processPath(socket, urlToPath, request_headers, service_name);
|
||||||
} else if (shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0) {
|
} else if (shortURL.indexOf('http://') >= 0 || shortURL.indexOf('https://') >= 0) {
|
||||||
doHTTPProxy(socket, request_headers);
|
doHTTPProxy(socket, request_headers);
|
||||||
@@ -505,7 +368,7 @@ async function processURL(socket, request_headers) {
|
|||||||
|
|
||||||
async function doHTTPProxy(socket, request_headers) {
|
async function doHTTPProxy(socket, request_headers) {
|
||||||
var request_type = (request_headers.request_url.substring(0, 5) == "https") ? "https" : "http";
|
var request_type = (request_headers.request_url.substring(0, 5) == "https") ? "https" : "http";
|
||||||
if (zshowheaders) console.log(request_type.toUpperCase() +" Proxy: Client Request Headers on socket ID", socket.id, (await filterSSID(request_headers)));
|
if (zshowheaders) console.log(request_type.toUpperCase() +" Proxy: Client Request Headers on socket ID", socket.id, (await wtvshared.filterSSID(request_headers)));
|
||||||
switch (request_type) {
|
switch (request_type) {
|
||||||
case "https":
|
case "https":
|
||||||
var proxy_agent = https;
|
var proxy_agent = https;
|
||||||
@@ -670,67 +533,6 @@ function headerStringToObj(headers, response = false) {
|
|||||||
return headers_obj;
|
return headers_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldWeCompress(ssid, headers_obj) {
|
|
||||||
var compress_data = false;
|
|
||||||
var compression_type = 0; // no compression
|
|
||||||
if (ssid_sessions[ssid]) {
|
|
||||||
if (ssid_sessions[ssid].capabilities) {
|
|
||||||
if (ssid_sessions[ssid].capabilities['client-can-receive-compressed-data']) {
|
|
||||||
|
|
||||||
if (minisrv_config.config.enable_lzpf_compression || minisrv_config.config.force_compression_type) {
|
|
||||||
compression_type = 1; // lzpf
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ssid_sessions[ssid]) {
|
|
||||||
// if gzip is enabled...
|
|
||||||
if (minisrv_config.config.enable_gzip_compression || minisrv_config.config.force_compression_type) {
|
|
||||||
var is_bf0app = ssid_sessions[ssid].get("wtv-client-rom-type") == "bf0app";
|
|
||||||
var is_minibrowser = (ssid_sessions[ssid].get("wtv-needs-upgrade") || ssid_sessions[ssid].get("wtv-used-8675309"));
|
|
||||||
var is_softmodem = ssid_sessions[ssid].get("wtv-client-rom-type").match(/softmodem/);
|
|
||||||
if (!is_bf0app && ((!is_softmodem && !is_minibrowser) || (is_softmodem && !is_minibrowser))) {
|
|
||||||
// softmodem boxes do not appear to support gzip in the minibrowser
|
|
||||||
// LC2 appears to support gzip even in the MiniBrowser
|
|
||||||
// LC2 and newer approms appear to support gzip
|
|
||||||
// bf0app does not appear to support gzip
|
|
||||||
compression_type = 2; // gzip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// mostly for debugging
|
|
||||||
if (minisrv_config.config.force_compression_type == "lzpf") compression_type = 1;
|
|
||||||
if (minisrv_config.config.force_compression_type == "gzip") compression_type = 2;
|
|
||||||
|
|
||||||
// do not compress if already encoded
|
|
||||||
if (headers_obj["Content-Encoding"]) return 0;
|
|
||||||
|
|
||||||
// should we bother to compress?
|
|
||||||
var content_type = "";
|
|
||||||
if (typeof (headers_obj) == 'string') content_type = headers_obj;
|
|
||||||
else content_type = (typeof (headers_obj["wtv-modern-content-type"]) != 'undefined') ? headers_obj["wtv-modern-content-type"] : headers_obj["Content-Type"];
|
|
||||||
|
|
||||||
if (content_type) {
|
|
||||||
// both lzpf and gzip
|
|
||||||
if (content_type.match(/^text\//) && content_type != "text/tellyscript") compress_data = true;
|
|
||||||
else if (content_type.match(/^application\/(x-?)javascript$/)) compress_data = true;
|
|
||||||
else if (content_type == "application/json") compress_data = true;
|
|
||||||
if (compression_type == 2) {
|
|
||||||
// gzip only
|
|
||||||
if (content_type.match(/^audio\/(x-)?[s3m|mod|xm]$/)) compress_data = true; // s3m, mod, xm
|
|
||||||
if (content_type.match(/^audio\/(x-)?[midi|wav|wave]$/)) compress_data = true; // midi & wav
|
|
||||||
if (content_type.match(/^binary\/x-wtv-approm$/)) compress_data = true; // approms
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return compression_type if compress_data = true
|
|
||||||
return (compress_data) ? compression_type : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendToClient(socket, headers_obj, data) {
|
async function sendToClient(socket, headers_obj, data) {
|
||||||
var headers = "";
|
var headers = "";
|
||||||
var content_length = 0;
|
var content_length = 0;
|
||||||
@@ -768,7 +570,7 @@ async function sendToClient(socket, headers_obj, data) {
|
|||||||
// if box can do compression, see if its worth enabling
|
// if box can do compression, see if its worth enabling
|
||||||
// small files actually get larger, so don't compress them
|
// small files actually get larger, so don't compress them
|
||||||
var compression_type = 0;
|
var compression_type = 0;
|
||||||
if (content_length >= 256) compression_type = shouldWeCompress(socket.ssid, headers_obj);
|
if (content_length >= 256) compression_type = wtvmime.shouldWeCompress(ssid_sessions[socket.ssid], headers_obj);
|
||||||
|
|
||||||
// compress if needed
|
// compress if needed
|
||||||
if (compression_type > 0 && content_length > 0 && headers_obj['http_response'].substring(0,3) == "200") {
|
if (compression_type > 0 && content_length > 0 && headers_obj['http_response'].substring(0,3) == "200") {
|
||||||
@@ -841,7 +643,7 @@ async function sendToClient(socket, headers_obj, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// header object to string
|
// header object to string
|
||||||
if (zshowheaders) console.log(" * Outgoing headers on socket ID", socket.id, (await filterSSID(headers_obj)));
|
if (zshowheaders) console.log(" * Outgoing headers on socket ID", socket.id, (await wtvshared.filterSSID(headers_obj)));
|
||||||
Object.keys(headers_obj).forEach(function (k) {
|
Object.keys(headers_obj).forEach(function (k) {
|
||||||
if (k == "http_response") {
|
if (k == "http_response") {
|
||||||
headers += headers_obj[k] + end_of_line;
|
headers += headers_obj[k] + end_of_line;
|
||||||
@@ -882,7 +684,7 @@ async function sendToClient(socket, headers_obj, data) {
|
|||||||
if (socket_sessions[socket.id].post_data) delete socket_sessions[socket.id].post_data;
|
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_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].post_data_percents_shown) delete socket_sessions[socket.id].post_data_percents_shown;
|
||||||
|
socket.setTimeout(minisrv_config.config.socket_timeout * 1000);
|
||||||
if (socket_sessions[socket.id].close_me) socket.end();
|
if (socket_sessions[socket.id].close_me) socket.end();
|
||||||
if (headers_obj["Connection"]) {
|
if (headers_obj["Connection"]) {
|
||||||
if (headers_obj["Connection"].toLowerCase() == "close" && wtv_connection_close == "true") {
|
if (headers_obj["Connection"].toLowerCase() == "close" && wtv_connection_close == "true") {
|
||||||
@@ -921,6 +723,89 @@ function moveObjectElement(currentKey, afterKey, obj) {
|
|||||||
if (next !== -1) return result; else return obj;
|
if (next !== -1) return result; else return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkSecurity(socket) {
|
||||||
|
var out = null;
|
||||||
|
var ip2long = function (ip) {
|
||||||
|
var components;
|
||||||
|
|
||||||
|
if (components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)) {
|
||||||
|
var iplong = 0;
|
||||||
|
var power = 1;
|
||||||
|
for (var i = 4; i >= 1; i -= 1) {
|
||||||
|
iplong += power * parseInt(components[i]);
|
||||||
|
power *= 256;
|
||||||
|
}
|
||||||
|
return iplong;
|
||||||
|
}
|
||||||
|
else return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
var isInSubnet = function (ip, subnet) {
|
||||||
|
var mask, base_ip, long_ip = ip2long(ip);
|
||||||
|
if ((mask = subnet.match(/^(.*?)\/(\d{1,2})$/)) && ((base_ip = ip2long(mask[1])) >= 0)) {
|
||||||
|
var freedom = Math.pow(2, 32 - parseInt(mask[2]));
|
||||||
|
return (long_ip > base_ip) && (long_ip < base_ip + freedom - 1);
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var rejectSSIDConnection = function (ssid, blacklist) {
|
||||||
|
if (blacklist) console.log(" * Request from SSID", wtvshared.filterSSID(ssid), "(" + socket.remoteAddr + "), but that SSID is in the blacklist, rejecting.");
|
||||||
|
else console.log(" * Request from SSID", wtvshared.filterSSID(socket.ssid), "(" + socket.remoteAddress + "), but that SSID is not in the whitelist, rejecting.");
|
||||||
|
|
||||||
|
var errpage = doErrorPage(401, "Access to this service is denied.");
|
||||||
|
out = errpage;
|
||||||
|
}
|
||||||
|
|
||||||
|
var checkSSIDIPWhitelist = function (ssid, blacklist) {
|
||||||
|
var ssid_access_list_ip_override = false;
|
||||||
|
if (minisrv_config.config.ssid_ip_allow_list) {
|
||||||
|
if (minisrv_config.config.ssid_ip_allow_list[socket.ssid]) {
|
||||||
|
Object.keys(minisrv_config.config.ssid_ip_allow_list[socket.ssid]).forEach(function (k) {
|
||||||
|
if (minisrv_config.config.ssid_ip_allow_list[socket.ssid][k].indexOf('/') > 0) {
|
||||||
|
if (isInSubnet(socket.remoteAddress, minisrv_config.config.ssid_ip_allow_list[socket.ssid][k])) {
|
||||||
|
// remoteAddr is in allowed subnet
|
||||||
|
ssid_access_list_ip_override = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (socket.remoteAddress == minisrv_config.config.ssid_ip_allow_list[socket.ssid][k]) {
|
||||||
|
// remoteAddr directly matches IP
|
||||||
|
ssid_access_list_ip_override = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!ssid_access_list_ip_override) rejectSSIDConnection(socket.ssid, blacklist);
|
||||||
|
} else {
|
||||||
|
rejectSSIDConnection(socket.ssid, blacklist);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rejectSSIDConnection(socket.ssid, blacklist);
|
||||||
|
}
|
||||||
|
if (ssid_access_list_ip_override && zdebug) console.log(" * Request from disallowed SSID", wtvshared.filterSSID(ssid), "was allowed due to IP address whitelist");
|
||||||
|
}
|
||||||
|
|
||||||
|
// process whitelist first
|
||||||
|
if (socket.ssid && minisrv_config.config.ssid_allow_list) {
|
||||||
|
var ssid_is_in_whitelist = minisrv_config.config.ssid_allow_list.findIndex(element => element == socket.ssid);
|
||||||
|
if (ssid_is_in_whitelist == -1) {
|
||||||
|
// no whitelist match, but lets see if the remoteAddress is allowed
|
||||||
|
checkSSIDIPWhitelist(socket.ssid, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now check blacklist
|
||||||
|
if (socket.ssid && minisrv_config.config.ssid_block_list) {
|
||||||
|
var ssid_is_in_blacklist = minisrv_config.config.ssid_block_list.findIndex(element => element == socket.ssid);
|
||||||
|
if (ssid_is_in_blacklist != -1) {
|
||||||
|
// blacklist match, but lets see if the remoteAddress is allowed
|
||||||
|
checkSSIDIPWhitelist(socket.ssid, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passed Security
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
function isUnencryptedString(string, verbose = false) {
|
function isUnencryptedString(string, verbose = false) {
|
||||||
// a generic "isAscii" check is not sufficient, as the test will see the binary
|
// a generic "isAscii" check is not sufficient, as the test will see the binary
|
||||||
// compressed / encrypted data as ASCII. This function checks for characters expected
|
// compressed / encrypted data as ASCII. This function checks for characters expected
|
||||||
@@ -930,11 +815,6 @@ function isUnencryptedString(string, verbose = false) {
|
|||||||
return /^([A-Za-z0-9\+\/\=\-\.\,\ \"\;\:\?\&\r\n\(\)\%\<\>\_\~\*\@\#\\]{8,})$/.test(string);
|
return /^([A-Za-z0-9\+\/\=\-\.\,\ \"\;\:\?\&\r\n\(\)\%\<\>\_\~\*\@\#\\]{8,})$/.test(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterSSID(ssid) {
|
|
||||||
var WTVCSD = new WTVClientSessionData(null,minisrv_config.config.hide_ssid_in_logs);
|
|
||||||
return WTVCSD.filterSSID(ssid);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processRequest(socket, data_hex, skipSecure = false, encryptedRequest = false) {
|
async function processRequest(socket, data_hex, skipSecure = false, encryptedRequest = false) {
|
||||||
|
|
||||||
// This function sucks and needs to be rewritten
|
// This function sucks and needs to be rewritten
|
||||||
@@ -1002,10 +882,10 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
if (!headers) return;
|
if (!headers) return;
|
||||||
|
|
||||||
if (headers["wtv-client-serial-number"] != null && socket.ssid == null) {
|
if (headers["wtv-client-serial-number"] != null && socket.ssid == null) {
|
||||||
socket.ssid = makeSafeSSID(headers["wtv-client-serial-number"]);
|
socket.ssid = wtvshared.makeSafeSSID(headers["wtv-client-serial-number"]);
|
||||||
if (socket.ssid != null) {
|
if (socket.ssid != null) {
|
||||||
if (!ssid_sessions[socket.ssid]) {
|
if (!ssid_sessions[socket.ssid]) {
|
||||||
ssid_sessions[socket.ssid] = new WTVClientSessionData(socket.ssid,minisrv_config.config.hide_ssid_in_logs);
|
ssid_sessions[socket.ssid] = new WTVClientSessionData(minisrv_config, socket.ssid);
|
||||||
ssid_sessions[socket.ssid].SaveIfRegistered();
|
ssid_sessions[socket.ssid].SaveIfRegistered();
|
||||||
}
|
}
|
||||||
if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Set();
|
if (!ssid_sessions[socket.ssid].data_store.sockets) ssid_sessions[socket.ssid].data_store.sockets = new Set();
|
||||||
@@ -1014,96 +894,21 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip2long = function (ip) {
|
var failed_security = checkSecurity(socket);
|
||||||
var components;
|
if (failed_security) {
|
||||||
|
|
||||||
if (components = ip.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)) {
|
|
||||||
var iplong = 0;
|
|
||||||
var power = 1;
|
|
||||||
for (var i = 4; i >= 1; i -= 1) {
|
|
||||||
iplong += power * parseInt(components[i]);
|
|
||||||
power *= 256;
|
|
||||||
}
|
|
||||||
return iplong;
|
|
||||||
}
|
|
||||||
else return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
var isInSubnet = function (ip, subnet) {
|
|
||||||
var mask, base_ip, long_ip = ip2long(ip);
|
|
||||||
if ((mask = subnet.match(/^(.*?)\/(\d{1,2})$/)) && ((base_ip = ip2long(mask[1])) >= 0)) {
|
|
||||||
var freedom = Math.pow(2, 32 - parseInt(mask[2]));
|
|
||||||
return (long_ip > base_ip) && (long_ip < base_ip + freedom - 1);
|
|
||||||
}
|
|
||||||
else return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
var rejectSSIDConnection = function (ssid, blacklist) {
|
|
||||||
if (blacklist) console.log(" * Request from SSID", filterSSID(ssid), "(" + socket.remoteAddr + "), but that SSID is in the blacklist, rejecting.");
|
|
||||||
else console.log(" * Request from SSID", filterSSID(socket.ssid), "(" + socket.remoteAddress + "), but that SSID is not in the whitelist, rejecting.");
|
|
||||||
|
|
||||||
var errpage = doErrorPage(401, "Access to this service is denied.");
|
|
||||||
headers = errpage[0];
|
|
||||||
data = errpage[1];
|
|
||||||
socket_sessions[socket.id].close_me = true;
|
socket_sessions[socket.id].close_me = true;
|
||||||
|
headers = failed_security[0];
|
||||||
|
data = failed_security[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkSSIDIPWhitelist = function (ssid, blacklist) {
|
|
||||||
var ssid_access_list_ip_override = false;
|
|
||||||
if (minisrv_config.config.ssid_ip_allow_list) {
|
|
||||||
if (minisrv_config.config.ssid_ip_allow_list[socket.ssid]) {
|
|
||||||
Object.keys(minisrv_config.config.ssid_ip_allow_list[socket.ssid]).forEach(function (k) {
|
|
||||||
if (minisrv_config.config.ssid_ip_allow_list[socket.ssid][k].indexOf('/') > 0) {
|
|
||||||
if (isInSubnet(socket.remoteAddress, minisrv_config.config.ssid_ip_allow_list[socket.ssid][k])) {
|
|
||||||
// remoteAddr is in allowed subnet
|
|
||||||
ssid_access_list_ip_override = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (socket.remoteAddress == minisrv_config.config.ssid_ip_allow_list[socket.ssid][k]) {
|
|
||||||
// remoteAddr directly matches IP
|
|
||||||
ssid_access_list_ip_override = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!ssid_access_list_ip_override) rejectSSIDConnection(socket.ssid, blacklist);
|
|
||||||
} else {
|
|
||||||
rejectSSIDConnection(socket.ssid, blacklist);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rejectSSIDConnection(socket.ssid, blacklist);
|
|
||||||
}
|
|
||||||
if (ssid_access_list_ip_override && zdebug) console.log(" * Request from disallowed SSID", filterSSID(ssid), "was allowed due to IP address whitelist");
|
|
||||||
}
|
|
||||||
|
|
||||||
// process whitelist first
|
|
||||||
if (socket.ssid && minisrv_config.config.ssid_allow_list) {
|
|
||||||
var ssid_is_in_whitelist = minisrv_config.config.ssid_allow_list.findIndex(element => element == socket.ssid);
|
|
||||||
if (ssid_is_in_whitelist == -1) {
|
|
||||||
// no whitelist match, but lets see if the remoteAddress is allowed
|
|
||||||
checkSSIDIPWhitelist(socket.ssid, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now check blacklist
|
|
||||||
if (socket.ssid && minisrv_config.config.ssid_block_list) {
|
|
||||||
var ssid_is_in_blacklist = minisrv_config.config.ssid_block_list.findIndex(element => element == socket.ssid);
|
|
||||||
if (ssid_is_in_blacklist != -1) {
|
|
||||||
// blacklist match, but lets see if the remoteAddress is allowed
|
|
||||||
checkSSIDIPWhitelist(socket.ssid, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Passed Security
|
|
||||||
|
|
||||||
if (headers["wtv-capability-flags"] != null) {
|
if (headers["wtv-capability-flags"] != null) {
|
||||||
if (!ssid_sessions[socket.ssid]) {
|
if (!ssid_sessions[socket.ssid]) {
|
||||||
ssid_sessions[socket.ssid] = new WTVClientSessionData(socket.ssid,minisrv_config.config.hide_ssid_in_logs);
|
ssid_sessions[socket.ssid] = new WTVClientSessionData(minisrv_config, socket.ssid);
|
||||||
ssid_sessions[socket.ssid].SaveIfRegistered();
|
ssid_sessions[socket.ssid].SaveIfRegistered();
|
||||||
}
|
}
|
||||||
if (!ssid_sessions[socket.ssid].capabilities) ssid_sessions[socket.ssid].capabilities = new WTVClientCapabilities(headers["wtv-capability-flags"]);
|
if (!ssid_sessions[socket.ssid].capabilities) ssid_sessions[socket.ssid].capabilities = new WTVClientCapabilities(headers["wtv-capability-flags"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// log all client wtv- headers to the SessionData for that SSID
|
// 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
|
// this way we can pull up client info such as wtv-client-rom-type or wtv-system-sysconfig
|
||||||
if (socket.ssid) {
|
if (socket.ssid) {
|
||||||
@@ -1165,46 +970,37 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
}
|
}
|
||||||
var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2));
|
var enc_data = CryptoJS.enc.Hex.parse(data_hex.substring(header_length * 2));
|
||||||
if (enc_data.sigBytes > 0) {
|
if (enc_data.sigBytes > 0) {
|
||||||
if (isUnencryptedString(enc_data.toString(CryptoJS.enc.Latin1), (!skipSecure && !encryptedRequest))) {
|
|
||||||
// some builds (like our targeted 3833), send SECURE ON but then unencrypted headers
|
// SECURE ON and detected encrypted data
|
||||||
if (zdebug) console.log(" # Psuedo-encrypted Request (SECURE ON)", "on", socket.id);
|
var dec_data = CryptoJS.lib.WordArray.create(socket_sessions[socket.id].wtvsec.Decrypt(0, enc_data))
|
||||||
// don't actually encrypt output
|
if (!socket_sessions[socket.id].secure_buffer) socket_sessions[socket.id].secure_buffer = "";
|
||||||
headers.psuedo_encryption = true;
|
socket_sessions[socket.id].secure_buffer += dec_data.toString(CryptoJS.enc.Hex);
|
||||||
ssid_sessions[socket.ssid].set("box-does-psuedo-encryption", true);
|
var secure_headers = null;
|
||||||
socket_sessions[socket.id].secure = false;
|
if (headers['request']) {
|
||||||
var secure_headers = await processRequest(socket, enc_data.toString(CryptoJS.enc.Hex), true, true);
|
if (headers['request'] == "GET") {
|
||||||
} else {
|
if (socket_sessions[socket.id].secure_buffer.indexOf("0d0a0d0a") || socket_sessions[socket.id].secure_buffer.indexOf("0a0a")) {
|
||||||
// SECURE ON and detected encrypted data
|
secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
|
||||||
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))
|
|
||||||
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 {
|
} else {
|
||||||
secure_headers = await processRequest(socket, socket_sessions[socket.id].secure_buffer, true, true);
|
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) {
|
|
||||||
socket_sessions[socket.id].secure = false;
|
|
||||||
var errpage = doErrorPage(400);
|
|
||||||
headers = errpage[0];
|
|
||||||
data = errpage[1];
|
|
||||||
sendToClient(socket, headers, data);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} 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) {
|
||||||
|
socket_sessions[socket.id].secure = false;
|
||||||
|
var errpage = doErrorPage(400);
|
||||||
|
headers = errpage[0];
|
||||||
|
data = errpage[1];
|
||||||
|
sendToClient(socket, headers, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Merge new headers into existing headers object
|
// Merge new headers into existing headers object
|
||||||
Object.keys(secure_headers).forEach(function (k) {
|
Object.keys(secure_headers).forEach(function (k) {
|
||||||
headers[k] = secure_headers[k];
|
headers[k] = secure_headers[k];
|
||||||
@@ -1231,8 +1027,9 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle POST
|
// handle POST
|
||||||
if (headers['request']) {
|
if (headers['request'] && !socket_sessions[socket.id].expecting_post_data) {
|
||||||
if (headers['request'].substring(0, 4) == "POST") {
|
if (headers['request'].substring(0, 4) == "POST") {
|
||||||
|
socket.setTimeout(minisrv_config.config.post_data_socket_timeout * 1000);
|
||||||
if (typeof socket_sessions[socket.id].post_data == "undefined") {
|
if (typeof socket_sessions[socket.id].post_data == "undefined") {
|
||||||
if (socket_sessions[socket.id].post_data_percents_shown) delete socket_sessions[socket.id].post_data_percents_shown;
|
if (socket_sessions[socket.id].post_data_percents_shown) delete socket_sessions[socket.id].post_data_percents_shown;
|
||||||
socket_sessions[socket.id].post_data_length = headers['Content-length'] || headers['Content-Length'] || 0;
|
socket_sessions[socket.id].post_data_length = headers['Content-length'] || headers['Content-Length'] || 0;
|
||||||
@@ -1252,14 +1049,23 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) {
|
if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) {
|
||||||
// got all expected data
|
// got all expected data
|
||||||
if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_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)");
|
console.log(" * Incoming", post_string, "request on", socket.id, "from", wtvshared.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);
|
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;
|
if (socket_sessions[socket.id].headers) delete socket_sessions[socket.id].headers;
|
||||||
processURL(socket, headers);
|
processURL(socket, headers);
|
||||||
} else {
|
} else {
|
||||||
// expecting more data (see below)
|
// expecting more data (see below)
|
||||||
socket_sessions[socket.id].expecting_post_data = true;
|
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) socket_sessions[socket.id].post_data = '';
|
||||||
|
if (socket_sessions[socket.id].secure) {
|
||||||
|
// decrypt if encrypted
|
||||||
|
socket_sessions[socket.id].post_data = CryptoJS.lib.WordArray.create(socket_sessions[socket.id].wtvsec.Decrypt(0, CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data))).toString(CryptoJS.enc.Hex);
|
||||||
|
} else {
|
||||||
|
// just pass it over
|
||||||
|
socket_sessions[socket.id].post_data = socket_sessions[socket.id].post_data;
|
||||||
|
}
|
||||||
|
socket_sessions[socket.id].post_data += CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data);
|
||||||
|
console.log(" * Incoming", post_string, "request on", socket.id, "from", wtvshared.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)) {
|
if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) {
|
||||||
// got too much data ? ... should not ever reach this code
|
// got too much data ? ... should not ever reach this code
|
||||||
@@ -1313,7 +1119,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
if (minisrv_config.config.post_percentages.includes(postPercent)) {
|
if (minisrv_config.config.post_percentages.includes(postPercent)) {
|
||||||
if (!socket_sessions[socket.id].post_data_percents_shown) socket_sessions[socket.id].post_data_percents_shown = new Array();
|
if (!socket_sessions[socket.id].post_data_percents_shown) socket_sessions[socket.id].post_data_percents_shown = new Array();
|
||||||
if (!socket_sessions[socket.id].post_data_percents_shown[postPercent]) {
|
if (!socket_sessions[socket.id].post_data_percents_shown[postPercent]) {
|
||||||
console.log(" * Received", postPercent, "% of", socket_sessions[socket.id].post_data_length, "bytes on", socket.id, "from", filterSSID(socket.ssid));
|
console.log(" * Received", postPercent, "% of", socket_sessions[socket.id].post_data_length, "bytes on", socket.id, "from", wtvshared.filterSSID(socket.ssid));
|
||||||
socket_sessions[socket.id].post_data_percents_shown[postPercent] = true;
|
socket_sessions[socket.id].post_data_percents_shown[postPercent] = true;
|
||||||
}
|
}
|
||||||
if (postPercent == 100) delete socket_sessions[socket.id].post_data_percents_shown;
|
if (postPercent == 100) delete socket_sessions[socket.id].post_data_percents_shown;
|
||||||
@@ -1324,6 +1130,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) {
|
if (socket_sessions[socket.id].post_data.length == (socket_sessions[socket.id].post_data_length * 2)) {
|
||||||
// got all expected data
|
// got all expected data
|
||||||
if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data;
|
if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data;
|
||||||
|
socket.setTimeout(minisrv_config.config.socket_timeout * 1000);
|
||||||
headers.post_data = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data);
|
headers.post_data = CryptoJS.enc.Hex.parse(socket_sessions[socket.id].post_data);
|
||||||
if (socket_sessions[socket.id].secure == true) {
|
if (socket_sessions[socket.id].secure == true) {
|
||||||
if (zdebug) console.log(" # Encrypted POST Content (SECURE ON)", "on", socket.id, "[", headers.post_data.sigBytes, "bytes ]");
|
if (zdebug) console.log(" # Encrypted POST Content (SECURE ON)", "on", socket.id, "[", headers.post_data.sigBytes, "bytes ]");
|
||||||
@@ -1338,6 +1145,7 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
|
|||||||
}
|
}
|
||||||
if (socket_sessions[socket.id].post_data.length > (socket_sessions[socket.id].post_data_length * 2)) {
|
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;
|
if (socket_sessions[socket.id].expecting_post_data) delete socket_sessions[socket.id].expecting_post_data;
|
||||||
|
socket.setTimeout(minisrv_config.config.socket_timeout * 1000);
|
||||||
// got too much data ? ... should not ever reach this code
|
// 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);
|
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];
|
headers = errpage[0];
|
||||||
@@ -1433,7 +1241,7 @@ async function cleanupSocket(socket) {
|
|||||||
// set timeout to check
|
// set timeout to check
|
||||||
ssid_sessions[socket.ssid].data_store.socket_check = setTimeout(function (ssid) {
|
ssid_sessions[socket.ssid].data_store.socket_check = setTimeout(function (ssid) {
|
||||||
if (ssid_sessions[ssid].currentConnections() === 0) {
|
if (ssid_sessions[ssid].currentConnections() === 0) {
|
||||||
if (!zquiet) console.log(" * WebTV SSID", filterSSID(ssid), " has not been seen in", (timeout / 1000), "seconds, cleaning up session data for this SSID");
|
if (!zquiet) console.log(" * WebTV SSID", wtvshared.filterSSID(ssid), " has not been seen in", (timeout / 1000), "seconds, cleaning up session data for this SSID");
|
||||||
delete ssid_sessions[ssid];
|
delete ssid_sessions[ssid];
|
||||||
}
|
}
|
||||||
}, timeout, socket.ssid);
|
}, timeout, socket.ssid);
|
||||||
@@ -1452,21 +1260,25 @@ async function handleSocket(socket) {
|
|||||||
socket_sessions[socket.id] = [];
|
socket_sessions[socket.id] = [];
|
||||||
socket.minisrv_pc_mode = false;
|
socket.minisrv_pc_mode = false;
|
||||||
socket.setEncoding('hex'); //set data encoding (Text: 'ascii', 'utf8' ~ Binary: 'hex', 'base64' (do not trust 'binary' encoding))
|
socket.setEncoding('hex'); //set data encoding (Text: 'ascii', 'utf8' ~ Binary: 'hex', 'base64' (do not trust 'binary' encoding))
|
||||||
socket.setTimeout(10800000); // 3 hours
|
socket.setTimeout(minisrv_config.config.socket_timeout * 1000);
|
||||||
socket.on('data', function (data_hex) {
|
socket.on('data', function (data_hex) {
|
||||||
if (!socket_sessions[socket.id].secure && !socket_sessions[socket.id].expecting_post_data) {
|
if (socket_sessions[socket.id]) {
|
||||||
// buffer unencrypted data until we see the classic double-newline, or get blank
|
if (!socket_sessions[socket.id].secure && !socket_sessions[socket.id].expecting_post_data) {
|
||||||
if (!socket_sessions[socket.id].header_buffer) socket_sessions[socket.id].header_buffer = "";
|
// buffer unencrypted data until we see the classic double-newline, or get blank
|
||||||
socket_sessions[socket.id].header_buffer += data_hex;
|
if (!socket_sessions[socket.id].header_buffer) socket_sessions[socket.id].header_buffer = "";
|
||||||
if (socket_sessions[socket.id].header_buffer.indexOf("0d0a0d0a") != -1 || socket_sessions[socket.id].header_buffer.indexOf("0a0a") != -1) {
|
socket_sessions[socket.id].header_buffer += data_hex;
|
||||||
data_hex = socket_sessions[socket.id].header_buffer;
|
if (socket_sessions[socket.id].header_buffer.indexOf("0d0a0d0a") != -1 || socket_sessions[socket.id].header_buffer.indexOf("0a0a") != -1) {
|
||||||
delete socket_sessions[socket.id].header_buffer;
|
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);
|
processRequest(this, data_hex);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// stream encrypted requests through the processor
|
cleanupSocket(socket);
|
||||||
if (socket_sessions[socket.id].header_buffer) delete socket_sessions[socket.id].header_buffer;
|
|
||||||
processRequest(this, data_hex);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1701,6 +1513,8 @@ bind_ports.forEach(function (v) {
|
|||||||
initstring = initstring.substring(0, initstring.length - 2);
|
initstring = initstring.substring(0, initstring.length - 2);
|
||||||
|
|
||||||
|
|
||||||
|
const wtvshared = new WTVShared(minisrv_config);
|
||||||
|
const wtvmime = new WTVMime(minisrv_config);
|
||||||
|
|
||||||
console.log(" * Started server on ports " + initstring + "...")
|
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";
|
var listening_ip_string = (minisrv_config.config.bind_ip != "0.0.0.0") ? "IP: " + minisrv_config.config.bind_ip : "all interfaces";
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
"hide_ssid_in_logs": true,
|
"hide_ssid_in_logs": true,
|
||||||
"post_percentages": [ 0, 25, 50, 100 ],
|
"post_percentages": [ 0, 25, 50, 100 ],
|
||||||
"verbosity": 2,
|
"verbosity": 2,
|
||||||
|
"socket_timeout": 10800,
|
||||||
|
"post_data_socket_timeout": 30,
|
||||||
"error_log_file": "errors.log",
|
"error_log_file": "errors.log",
|
||||||
"catchall_file_name": "catchall.js",
|
"catchall_file_name": "catchall.js",
|
||||||
"enable_lzpf_compression": false,
|
"enable_lzpf_compression": false,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "zefie_wtvp_minisrv",
|
"name": "zefie_wtvp_minisrv",
|
||||||
"version": "0.9.16",
|
"version": "0.9.17",
|
||||||
"description": "WebTV Service (WTVP) Emulation Server",
|
"description": "WebTV Service (WTVP) Emulation Server",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv",
|
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv",
|
||||||
|
|||||||
@@ -52,6 +52,15 @@
|
|||||||
<Content Include="ServiceVault\wtv-cookie\reset.js">
|
<Content Include="ServiceVault\wtv-cookie\reset.js">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="ServiceVault\wtv-disk\uptest.js">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Content>
|
||||||
|
<Content Include="ServiceVault\wtv-disk\userbrowser.js">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Content>
|
||||||
|
<Content Include="ServiceVault\wtv-disk\userstore.js">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Content>
|
||||||
<Content Include="ServiceVault\wtv-flashrom\content\content-serve.js" />
|
<Content Include="ServiceVault\wtv-flashrom\content\content-serve.js" />
|
||||||
<Content Include="ServiceVault\wtv-flashrom\current-noflash.js">
|
<Content Include="ServiceVault\wtv-flashrom\current-noflash.js">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
@@ -283,6 +292,9 @@
|
|||||||
<Content Include="WTVLzpf.js">
|
<Content Include="WTVLzpf.js">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="WTVMime.js">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Content>
|
||||||
<Content Include="WTVRegister.js">
|
<Content Include="WTVRegister.js">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Content>
|
</Content>
|
||||||
@@ -290,6 +302,9 @@
|
|||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="package.json" />
|
<Content Include="package.json" />
|
||||||
|
<Content Include="WTVShared.js">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="ServiceVault\" />
|
<Folder Include="ServiceVault\" />
|
||||||
|
|||||||
Reference in New Issue
Block a user