- numerous bug fixes
 - too much to remember
   - rewrote sync system yet again
   - more classes
      - WTVShared class for shared functions
      - clientShowAlert class for easy client:showalert urls
    - User File Store
      - Can upload with PUT commands in wtv-disk
      - Programmically access files with new functions in WTVClientSessionData
      - TODO: file browser
    - other stuff I can't remember
  - work on post data bug
  - proper gzip download for disk system (aka WNI reinventing the Content-Encoding: gzip wheel)
  - send Last-Modified for static files
  - send wtv-checksum for all disk system downloads
  - update to v90 modem firmware
    - offer kflex with `Old` diskmap
This commit is contained in:
zefie
2021-08-10 01:17:39 -04:00
parent 6f2fa1d510
commit b96718555b
35 changed files with 1399 additions and 706 deletions

1
.gitignore vendored
View File

@@ -367,3 +367,4 @@ FodyWeavers.xsd
/zefie_wtvp_minisrv/UserServiceVault/*-*/
/zefie_wtvp_minisrv/ServiceLogPost/*.log
/zefie_wtvp_minisrv/SessionStore/*.json
/zefie_wtvp_minisrv/SessionStore/*/

View File

@@ -1,5 +1,5 @@
if (socket.ssid != null && !ssid_sessions[socket.ssid].get("wtvsec_login")) {
var wtvsec_login = new WTVSec();
var wtvsec_login = new WTVSec(minisrv_config);
wtvsec_login.IssueChallenge();
wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
ssid_sessions[socket.ssid].set("wtvsec_login", wtvsec_login);

View File

@@ -2,7 +2,7 @@
if (socket.ssid) {
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);
} else {
ssid_sessions[socket.ssid].session_data = {};
@@ -20,15 +20,15 @@
}
}
});
if (i > 0 && zdebug) console.log(" # Closed", i, "previous sockets for", filterSSID(socket.ssid));
if (i > 0 && minisrv_config.config.debug_flags.debug) console.log(" # Closed", i, "previous sockets for", wtvshared.filterSSID(socket.ssid));
}
}
if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
if (zdebug) console.log(" # Recreating primary WTVSec login instance for", filterSSID(socket.ssid));
if (minisrv_config.config.debug_flags.debug) console.log(" # Recreating primary WTVSec login instance for", wtvshared.filterSSID(socket.ssid));
delete ssid_sessions[socket.ssid].data_store.wtvsec_login;
}
ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec();
ssid_sessions[socket.ssid].data_store.wtvsec_login = new WTVSec(minisrv_config);
ssid_sessions[socket.ssid].data_store.wtvsec_login.IssueChallenge();
ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(request_headers["wtv-incarnation"] || 1);
} else {
@@ -115,24 +115,23 @@ if (ssid_sessions[socket.ssid].data_store.wtvsec_login) {
gourl = "wtv-head-waiter:/login-stage-two?relogin=true";
}
if (request_headers.query.reconnect) {
gourl = null;
}
if (!file_path != null && !zquiet) console.log(" * Sending TellyScript", file_path, "on socket", socket.id);
if (request_headers.query.reconnect) gourl = null;
if (request_headers.query.guest_login) {
send_tellyscript = false;
gourl += "&guest_login=true"
if (gourl != null) gourl += "&guest_login=true"
if (request_headers.query.skip_splash) gourl += "&skip_splash=true";
}
if (!file_path != null && send_tellyscript && !minisrv_config.config.debug_flags.quiet) console.log(" * Sending TellyScript", file_path, "on socket", socket.id);
headers = "200 OK\n"
if (bf0app_update) headers += "minisrv-use-carriage-return: false\n";
headers += "Connection: Keep-Alive\n";
headers += "wtv-initial-key: " + ssid_sessions[socket.ssid].data_store.wtvsec_login.challenge_key.toString(CryptoJS.enc.Base64) + "\n";
headers += "Content-Type: " + prereg_contype + "\n";
headers += "wtv-service: reset\n";
if (!request_headers.query.reconnect) headers += "wtv-service: reset\n";
if (!bf0app_update) headers += getServiceString('wtv-1800') + "\n";
if (bf0app_update) headers += getServiceString('wtv-head-waiter', { "flags": "0x00000001" }) + "\n";

View File

@@ -0,0 +1,18 @@
{
"ModemFirmware": {
"base": "file://Disk/Browser/Modem_Firmware/",
"location": "content/Modem_Firmware/",
"execute": "client:ModemReload",
"execute_when": "atEnd",
"service_owned": true,
"files": [
{
"file": "file://Disk/Browser/Modem_Firmware/Locale/en-US/modem_firmware.dat.gz",
"location": "content/Modem_Firmware/Locale/en-US/v90/modem_firmware.dat.gz"
},
{
"file": "file://Disk/Browser/Modem_Firmware/Locale/ja-JP/modem_firmware.dat.gz"
}
]
}
}

View File

@@ -0,0 +1,18 @@
{
"ModemFirmware": {
"base": "file://Disk/Browser/Modem_Firmware/",
"location": "content/Modem_Firmware/",
"execute": "client:ModemReload",
"execute_when": "atEnd",
"service_owned": true,
"files": [
{
"file": "file://Disk/Browser/Modem_Firmware/Locale/en-US/modem_firmware.dat.gz",
"location": "content/Modem_Firmware/Locale/en-US/kflex/modem_firmware.dat.gz"
},
{
"file": "file://Disk/Browser/Modem_Firmware/Locale/ja-JP/modem_firmware.dat.gz"
}
]
}
}

View File

@@ -1,88 +1,104 @@
// 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;
console.log(force_update);
if (request_headers['wtv-request-type'] == 'download') {
var path = require("path");
var content_dir = "content/"
var diskmap_dir = content_dir + "diskmaps/";
function generateDownloadList(diskmap_group_data, update_list, diskmap_data) {
// create WebTV Download List
var newest_file_epoch = 0;
var download_list = '';
if (diskmap_data.execute && diskmap_data.execute_when == "atStart") {
download_list += "EXECUTE " + diskmap_data.execute + "\n\n";
}
if (diskmap_data.partition_size) {
download_list += "CREATE " + diskmap_data.base + "\n";
download_list += "partition-size: " + diskmap_data.partition_size + "\n\n";
}
download_list += "CREATE-GROUP " + diskmap_group_data + "-UPDATE\n";
download_list += "state: invalid\n";
download_list += "base: " + diskmap_data.base + ".GROUP-UPDATE/\n\n";
download_list += "CREATE-GROUP " + diskmap_group_data + "\n";
download_list += "state: invalid\n";
download_list += "service-owned: " + (diskmap_data.service_owned || false) + "\n";
download_list += "base: " + diskmap_data.base + "\n\n";
Object.keys(update_list).forEach(function (k) {
if (!update_list[k].invalid) return;
download_list += "DELETE " + update_list[k].file.replace(diskmap_data.base, "") + "\n";
download_list += "group: " + diskmap_group_data + "\n\n";
});
wtvdl.reset();
var files_to_send = 0;
Object.keys(update_list).forEach(function (k) {
if (update_list[k].checksum_match && !force_update) return;
if (!update_list[k].invalid && !force_update) return;
download_list += "DISPLAY " + update_list[k].display + "\n\n";
download_list += "GET " + update_list[k].file.replace(diskmap_data.base, "") + "\n";
download_list += "group: " + diskmap_group_data + "-UPDATE\n";
download_list += "location: " + service_name + ":/" + update_list[k].location + "\n";
download_list += "file-permission: r\n"
download_list += "wtv-checksum: " + update_list[k].checksum + "\n";
download_list += "service-source-location: /webtv/content/" + service_name.replace("wtv-", "") + "d/" + update_list[k].location + "\n";
download_list += "client-dest-location: " + update_list[k].file + "\n\n";
files_to_send++;
});
download_list += "CREATE-GROUP " + diskmap_group_data + "\n";
download_list += "state: invalid\n";
download_list += "service-owned: " + (diskmap_data.service_owned || false) + "\n";
download_list += "base: " + diskmap_data.base + "\n\n";
Object.keys(update_list).forEach(function (k) {
if (!update_list[k].invalid) return;
download_list += "RENAME " + update_list[k].file.replace(diskmap_data.base, "") + "\n";
download_list += "group: " + diskmap_group_data + "-UPDATE\n";
download_list += "destination-group: " + diskmap_group_data + "\n";
download_list += "location: " + update_list[k].file.replace(diskmap_data.base, "") + "\n\n";
});
download_list += "SET-GROUP " + diskmap_group_data + "\n";
download_list += "state: ok\n";
download_list += "version: " + diskmap_data.version + "\n";
download_list += "last-checkup-time: " + new Date().toUTCString().replace("GMT", "+0000") + "\n\n";
if (diskmap_data.execute && diskmap_data.execute_when == "atEnd") {
download_list += "EXECUTE " + diskmap_data.execute + "\n\n";
// create WebTV Download List
if (diskmap_data.execute && diskmap_data.execute_when) {
if (diskmap_data.execute_when.toLowerCase().match(/start/)) {
wtvdl.execute(diskmap_data.execute);
}
}
download_list += "DELETE-GROUP " + diskmap_group_data + "-UPDATE\n\n";
download_list += "DELETE " + diskmap_data.base + ".GROUP-UPDATE/\n\n";
console.log(download_list);
if (diskmap_group_data.display) wtvdl.display(diskmap_group_data.display);
if (files_to_send > 0) {
if (diskmap_data.partition_size) {
wtvdl.createPartition(diskmap_data.base, diskmap_data.partition_size);
}
if (!diskmap_data.nogroup) {
// only send group commands if group mode is enable
// useful to disable for PUT
wtvdl.createUpdateGroup(diskmap_group_data, diskmap_data.base, "invalid", (diskmap_data.service_owned || false));
}
Object.keys(update_list).forEach(function (k) {
// file { "action": "delete" }
// Useful to purge files we no longer want on the client
if (update_list[k].action != "DELETE") {
// skip deleting valid files if we aren't specifically requesting their deletion
if (update_list[k].checksum_match && !force_update) return;
if (!update_list[k].invalid && !force_update) return;
}
wtvdl.delete(update_list[k].file.replace(diskmap_data.base, ""), diskmap_group_data);
});
Object.keys(update_list).forEach(function (k) {
if (update_list[k].checksum_match && !force_update) return;
if (!update_list[k].invalid && !force_update) return;
if (update_list[k].display) wtvdl.display(update_list[k].display);
switch (update_list[k].action) {
case "PUT":
wtvdl.put(update_list[k].file.replace(diskmap_data.base, ""), service_name + ":/" + update_list[k].location, update_list[k].display);
break;
case "GET":
wtvdl.get(update_list[k].file.replace(diskmap_data.base, ""), update_list[k].file, service_name + ":/" + update_list[k].location, diskmap_group_data, update_list[k].checksum, update_list[k].uncompressed_size || null, update_list[k].original_filename)
break;
}
});
if (!diskmap_data.nogroup) {
wtvdl.createGroup(diskmap_group_data, diskmap_data.base, "invalid", (diskmap_data.service_owned || false));
// this rename loop is a part of the group system
Object.keys(update_list).forEach(function (k) {
if (update_list[k].checksum_match && !force_update) return;
if (!update_list[k].invalid && !force_update) return;
wtvdl.rename(update_list[k].file.replace(diskmap_data.base, ""), update_list[k].file.replace(diskmap_data.base, ""), diskmap_group_data, diskmap_group_data);
});
wtvdl.setGroup(diskmap_group_data, 'ok', diskmap_data.version);
}
}
if (diskmap_data.execute && diskmap_data.execute_when) {
if (diskmap_data.execute_when.toLowerCase().match(/end/)) {
wtvdl.execute(diskmap_data.execute);
}
}
if (files_to_send > 0) {
if (!diskmap_data.nogroup) {
wtvdl.deleteGroupUpdate(diskmap_group_data, diskmap_data.base);
}
}
var download_list = wtvdl.getDownloadList();
if (minisrv_config.config.show_diskmap) console.log(download_list);
return download_list;
}
function processGroup(diskmap_primary_group, diskmap_group_data, diskmap_subgroup = null) {
// parse webtv post
var output_data = '';
var post_data = request_headers.post_data.toString(CryptoJS.enc.Latin1).split("\n");
var post_data = new Array();
if (request_headers.post_data) post_data = request_headers.post_data.toString(CryptoJS.enc.Latin1).split("\n");
var post_data_current_directory = '';
var post_data_current_file = false;
var post_data_current_group = '';
@@ -172,6 +188,8 @@ if (request_headers['wtv-request-type'] == 'download') {
if (!fs.existsSync(post_match_file)) post_match_file = null;
});
var post_match_file_lstat = fs.lstatSync(post_match_file);
var post_match_file_data = new Buffer.from(fs.readFileSync(post_match_file, {
encoding: null,
@@ -180,10 +198,23 @@ if (request_headers['wtv-request-type'] == 'download') {
diskmap_group_data.files[k].base = diskmap_group_data.base;
diskmap_group_data.files[k].last_modified = (new Date(new Date(post_match_file_lstat.mtime).toUTCString()) / 1000);
diskmap_group_data.files[k].content_length = post_match_file_lstat.size;
diskmap_group_data.files[k].checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(post_match_file_data)).toString(CryptoJS.enc.Hex).toLowerCase();
diskmap_group_data.files[k].action = (diskmap_group_data.files[k].action) ? diskmap_group_data.files[k].action.toUpperCase() : "GET";
if (wtvshared.getFileExt(post_match_file).toLowerCase() == "gz") {
// we need the checksum of the uncompressed data
var gunzipped = zlib.gunzipSync(post_match_file_data);
diskmap_group_data.files[k].checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(gunzipped)).toString(CryptoJS.enc.Hex).toLowerCase();
var gzip_fn_end = post_match_file_data.indexOf("\0", 10);
if (!diskmap_group_data.files[k].dont_extract_filename) {
diskmap_group_data.files[k].original_filename = post_match_file_data.toString('utf8', 10, gzip_fn_end);
}
//diskmap_group_data.files[k].uncompressed_size = gunzipped.byteLength;
gunzipped = null;
} else {
diskmap_group_data.files[k].checksum = CryptoJS.MD5(CryptoJS.lib.WordArray.create(post_match_file_data)).toString(CryptoJS.enc.Hex).toLowerCase();
}
if (parseInt(diskmap_group_data.files[k].last_modified) > newest_file_epoch) newest_file_epoch = parseInt(diskmap_group_data.files[k].last_modified);
if (!diskmap_group_data.files[k].display) diskmap_group_data.files[k].display = diskmap_group_data.display;
diskmap_group_data.files[k].invalid = true;
wtv_download_list.push(diskmap_group_data.files[k]);
@@ -205,7 +236,7 @@ if (request_headers['wtv-request-type'] == 'download') {
return output_data;
}
if (request_headers.query.diskmap && request_headers.query.group && request_headers.post_data) {
if (request_headers.query.diskmap && request_headers.query.group) {
var diskmap_json_file = null;
Object.keys(service_vaults).forEach(function (g) {
if (diskmap_json_file != null) return;
@@ -243,69 +274,17 @@ if (request_headers['wtv-request-type'] == 'download') {
var errpage = doErrorPage(404, "The requested DiskMap does not exist.");
headers = errpage[0];
data = errpage[1];
if (zdebug) console.error(" # " + service_name +":/sync error", "could not find diskmap");
if (minisrv_config.config.debug_flags.debug) console.error(" # " + service_name +":/sync error", "could not find diskmap");
}
} else {
var errpage = doErrorPage(400);
headers = errpage[0];
data = errpage[1];
if (zdebug) console.error(" # " + service_name + ":/sync error", "missing query arguments");
if (minisrv_config.config.debug_flags.debug) console.error(" # " + service_name + ":/sync error", "missing query arguments");
}
} else if (request_headers.query.group && request_headers.query.diskmap) {
var message = request_headers.query.message || "Retrieving files...";
var main_message = request_headers.query.main_message || "Your receiver is downloading files.";
headers = `200 OK
Content-Type: text/html`;
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>
`;
headers = "200 OK\nwtv-connection-close: close\nConnection: close\nContent-Type: text/html";
data = wtvdl.getSyncPage(message, request_headers.query.group, request_headers.query.diskmap, main_message, message, force_update)
}

View File

@@ -0,0 +1,21 @@
if (request_headers.post_data) {
if (request_headers.query.partialPath || request_headers.query.path) {
if (socket.ssid) {
if (ssid_sessions[socket.ssid]) {
if (ssid_sessions[socket.ssid].isRegistered()) {
var result = ssid_sessions[socket.ssid].storeUserStoreFile(request_headers.query.path || 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];
}

View File

@@ -1,6 +1,5 @@
const WTVFlashrom = require("./WTVFlashrom.js");
request_is_async = true;
console.log(request_headers);
var bf0app_update = false;
var request_path = request_headers.request_url.replace(service_name + ":/", "");
@@ -15,7 +14,7 @@ if ((romtype == "bf0app" || !romtype) && (bootver == "105" || !bootver)) {
}
if (!ssid_sessions[socket.ssid].data_store.WTVFlashrom) {
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug);
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(minisrv_config, service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update);
}
ssid_sessions[socket.ssid].data_store.WTVFlashrom.getFlashRom(request_path, function (data, headers) {

View File

@@ -22,7 +22,7 @@ if (ssid_sessions[socket.ssid].get("wtv-client-rom-type") == "bf0app" && ssid_se
}
if (!ssid_sessions[socket.ssid].data_store.WTVFlashrom) {
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug);
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(minisrv_config, service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update);
}
ssid_sessions[socket.ssid].data_store.WTVFlashrom.getFlashRom(request_path, function (data, headers) {

View File

@@ -15,7 +15,7 @@ if ((romtype == "bf0app" || !romtype) && (bootver == "105" || !bootver)) {
if (request_headers.query.raw || bf0app_update) {
if (!ssid_sessions[socket.ssid].data_store.WTVFlashrom) {
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug);
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(minisrv_config, service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, bf0app_update);
}
ssid_sessions[socket.ssid].data_store.WTVFlashrom.getFlashRom(request_path, function (data, headers) {

View File

@@ -8,7 +8,7 @@ if (!request_headers.query.path) {
headers = errpage[0];
data = errpage[1];
} else {
var wtvflashrom = new WTVFlashrom(service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, minisrv_config.services[service_name].debug);
var wtvflashrom = new WTVFlashrom(minisrv_config, service_vaults, service_name, minisrv_config.services[service_name].use_zefie_server, false, true);
var request_path = request_headers.query.path;
// read flashrom header info into array using WTVFlashrom class

View File

@@ -5,8 +5,9 @@ var romtype = ssid_sessions[socket.ssid].get("wtv-client-rom-type");
url = "client:updateflash?ipaddr=" + minisrv_config.services[service_name].host + "&port=" + minisrv_config.services[service_name].port + "&path=" + escape(service_name + ":/" +request_headers.query.path);
if (request_headers.query.numparts) url += escape("&numparts=" + request_headers.query.numparts);
}
headers = "200 OK\n";
headers = "300 OK\n";
headers += "wtv-visit: " + url + "\n";
headers += "Location: " + url + "\n";
headers += "Content-type: text/html";
data = '';
} else {

View File

@@ -22,7 +22,7 @@ if (ssid_sessions[socket.ssid].get("wtv-client-rom-type") == "bf0app" && ssid_se
}
if (!ssid_sessions[socket.ssid].data_store.WTVFlashrom) {
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(service_vaults, service_name, 0, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug);
ssid_sessions[socket.ssid].data_store.WTVFlashrom = new WTVFlashrom(minisrv_config, service_vaults, service_name, 0, minisrv_config.services[service_name].use_zefie_server, bf0app_update, minisrv_config.services[service_name].debug);
}
ssid_sessions[socket.ssid].data_store.WTVFlashrom.getFlashRom(request_path, function (data, headers) {

View File

@@ -32,7 +32,7 @@ const req = https.request(options, function (res) {
});
res.on('error', function (e) {
if (!zquiet) console.log(" * Upstream Ultra Willies HTTP Error:", e);
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Upstream Ultra Willies HTTP Error:", e);
var errpage = doErrorPage(400)
headers = errpage[0];
data = errpage[1];
@@ -40,7 +40,7 @@ const req = https.request(options, function (res) {
});
res.on('end', function () {
if (!zquiet) console.log(" * Upstream Ultra Willies HTTP Response:", res.statusCode, res.statusMessage);
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Upstream Ultra Willies HTTP Response:", res.statusCode, res.statusMessage);
if (request_headers.query.clear_cache) {
headers += "\nwtv-expire-all: "+service_name;
}

View File

@@ -2,7 +2,7 @@ var challenge_response, challenge_header = '';
var gourl;
if (socket.ssid != null && !ssid_sessions[socket.ssid].get("wtvsec_login")) {
var wtvsec_login = new WTVSec(1,zdebug);
var wtvsec_login = new WTVSec(minisrv_config);
wtvsec_login.IssueChallenge();
wtvsec_login.set_incarnation(request_headers["wtv-incarnation"]);
ssid_sessions[socket.ssid].set("wtvsec_login", wtvsec_login);
@@ -16,13 +16,13 @@ if (socket.ssid !== null) {
var client_challenge_response = request_headers["wtv-challenge-response"] || null;
if (challenge_response && client_challenge_response) {
if (challenge_response.toString(CryptoJS.enc.Base64) == client_challenge_response) {
console.log(" * wtv-challenge-response success for " + filterSSID(socket.ssid));
console.log(" * wtv-challenge-response success for " + wtvshared.filterSSID(socket.ssid));
wtvsec_login.PrepareTicket();
} else {
console.log(" * wtv-challenge-response FAILED for " + filterSSID(socket.ssid));
if (zdebug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64));
if (zdebug) console.log("Response Received:", client_challenge_response)
console.log(" * wtv-challenge-response FAILED for " + wtvshared.filterSSID(socket.ssid));
if (minisrv_config.config.debug_flags.debug) console.log("Response Expected:", challenge_response.toString(CryptoJS.enc.Base64));
if (minisrv_config.config.debug_flags.debug) console.log("Response Received:", client_challenge_response)
gourl = "wtv-head-waiter:/login?reissue_challenge=true";
}
} else {
@@ -147,6 +147,7 @@ wtv-open-isp-disabled: false
wtv-offline-mail-enable: false
wtv-demo-mode: 0
wtv-wink-deferrer-retries: 3
wtv-name-server: 8.8.8.8
wtv-visit: ${home_url}
Content-Type: text/html`;
}

View File

@@ -54,7 +54,7 @@ wtv-log-url: wtv-log:/log`;
if (challenge_header != "") headers += "\n" + challenge_header;
headers += `
wtv-relogin-url: wtv-head-waiter:/relogin?relogin=true
wtv-reconnect-url: wwtv-head-waiter:/relogin?reconnect=true
wtv-reconnect-url: wtv-head-waiter:/relogin?reconnect=true
wtv-visit: ${gourl}
Content-type: text/html`;
data = '';

View File

@@ -5,14 +5,9 @@ wtv-expire-all: wtv-flashrom:
Content-type: text/html`
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 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 comp_type = wtvmime.shouldWeCompress(ssid_sessions[socket.ssid],'text/html');
var compstatus = "uncompressed";
switch (comp_type) {
case 1:
@@ -58,7 +53,7 @@ if (ssid_sessions[socket.ssid].hasCap("client-has-disk")) {
data += "<li><a href=\"client:diskhax\">DiskHax</a> ~ <a href=\"client:vfathax\">VFatHax</a></li>\n";
if (ssid_sessions[socket.ssid].hasCap("client-can-do-macromedia-flash2")) {
// only show demo if client can do flash2
data += "<li>Old MSNTV DealerDemo: <a href=\"wtv-disk:/sync?group=DealerDemo&diskmap=DealerDemo\">Download</a> ~ <a href=\"file://Disk/Demo/index.html\"> Access (after Download)</a></li>\n";
data += "<li>Old DealerDemo: <a href=\"wtv-disk:/sync?group=DealerDemo&diskmap=DealerDemo\">Download</a> ~ <a href=\"file://Disk/Demo/index.html\">Access</a></li>\n";
}
}

View File

@@ -22,7 +22,7 @@ Content-length: 0`;
logdata_outstring_hex += request_headers.post_data.toString(CryptoJS.enc.Hex);
if (minisrv_config.services[service_name].write_logs_to_disk) {
fs.writeFile(fullpath, logdata_outstring_hex, "Hex", function () {
if (!zquiet) console.log(" * Wrote POST log data from", filterSSID(socket.ssid), "for", socket.id);
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Wrote POST log data from", wtvshared.filterSSID(socket.ssid), "for", socket.id);
sendToClient(socket, headers, data);
});
} else {
@@ -41,7 +41,7 @@ Content-length: 0`;
var logdata_outstring_hex = Buffer.from(logdata_outstring, 'utf8').toString('hex');
if (minisrv_config.services[service_name].write_logs_to_disk) {
fs.writeFile(fullpath, logdata_outstring_hex, "Hex", function () {
if (!zquiet) console.log(" * Wrote GET log data from", filterSSID(socket.ssid), "for", socket.id);
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Wrote GET log data from", wtvshared.filterSSID(socket.ssid), "for", socket.id);
sendToClient(socket, headers, data);
});
} else {

View File

@@ -17,7 +17,7 @@ if (!request_headers.query.registering ||
ssid_sessions[socket.ssid].setSessionData("subscriber_contact_method", request_headers.query.subscriber_contact_method);
ssid_sessions[socket.ssid].setSessionData("subscriber_userid", '1' + Math.floor(Math.random() * 1000000000000000000));
ssid_sessions[socket.ssid].setSessionData("registered", true);
if (!ssid_sessions[socket.ssid].storeSessionData()) {
if (!ssid_sessions[socket.ssid].storeSessionData(true)) {
var errpage = doErrorPage(400);
headers = errpage[0];
data = errpage[1];

View File

@@ -18,7 +18,7 @@ Content-Type: text/html`
var wtv_system_version = ssid_sessions[socket.ssid].get("wtv-system-version");
var wtv_client_bootrom_version = ssid_sessions[socket.ssid].get("wtv-client-bootrom-version");
var wtv_client_serial_number = filterSSID(ssid_sessions[socket.ssid].get("wtv-client-serial-number"));
var wtv_client_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_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);

View File

@@ -2,7 +2,9 @@ headers = `200 OK
Content-Type: text/html`;
if (!ssid_sessions[socket.ssid].getSessionData("registered")) {
var redirect = [10, "client:goback?"];
headers += "\nwtv-noback-all: wtv-";
headers += "\nwtv-expire-all: wtv-";
var redirect = [5, "client:relogin?"];
var message = "Error: Your box is not registered. You are accessing " + minisrv_config.config.service_name + " in Guest Mode. There is nothing to delete!";
} else if (request_headers.query.confirm_unregister) {
if (ssid_sessions[socket.ssid].unregisterBox()) {
@@ -14,7 +16,7 @@ if (!ssid_sessions[socket.ssid].getSessionData("registered")) {
} else {
var redirect = [10, "client:goback?"];
var message = "There was an error deleting your account data. Please try again later. If the problem persists, please contact " + minisrv_config.config.service_owner + " to request manual deletion.";
message += "SSID verifcation may be required to perform a manual deletion.< br > <br>Returning from whence you came...<br><br>";
message += "SSID verifcation may be required to perform a manual deletion.<br><br>Returning from whence you came...<br><br>";
message += `<a href="${redirect[1]}">Click here if you are not automatically redirected.</a>`;
}
} else {

View File

@@ -4,55 +4,109 @@ class WTVClientSessionData {
fs = require('fs');
path = require('path');
ssid = null;
data_store = null;
session_store = null;
login_security = null;
capabilities = null;
session_storage = "";
hide_ssid_in_logs = true;
minisrv_config = [];
wtvshared = null;
wtvmime = null;
filterSSID(obj) {
if (this.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;
}
}
constructor(minisrv_config, ssid) {
if (!minisrv_config) throw ("minisrv_config required");
var WTVShared = require('./WTVShared.js')['WTVShared'];
var WTVMime = require('./WTVMime.js');
this.minisrv_config = minisrv_config;
this.wtvshared = new WTVShared(minisrv_config);
this.wtvmime = new WTVMime(minisrv_config);
constructor(ssid, hide_ssid_in_logs, session_storage_directory) {
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.session_store = {};
}
getUTCTime(offset = 0) {
return new Date((new Date).getTime() + offset).toUTCString();
/**
* 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 the number of user cookies
* @returns {number} Number of cookies
*/
countCookies() {
return Object.keys(this.session_store.cookies).length || 0;
}
@@ -60,7 +114,7 @@ class WTVClientSessionData {
resetCookies() {
this.session_store.cookies = {};
// webtv likes to have at least one cookie in the list, set a dummy cookie for zefie's site expiring in 1 year.
this.addCookie("wtv.zefie.com", "/", this.getUTCTime(365 * 86400000), "cookie_type=chocolatechip");
this.addCookie("wtv.zefie.com", "/", this.wtvshared.getUTCTime(365 * 86400000), "cookie_type=chocolatechip");
}
addCookie(domain, path = null, expires = null, data = null) {
@@ -174,8 +228,8 @@ class WTVClientSessionData {
loadSessionData(raw_data = false) {
try {
if (this.fs.lstatSync(this.session_storage + this.path.sep + this.ssid + ".json")) {
var json_data = this.fs.readFileSync(this.session_storage + this.path.sep + this.ssid + ".json", 'Utf8')
if (this.fs.lstatSync(this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + ".json")) {
var json_data = this.fs.readFileSync(this.minisrv_config.config.SessionStore + this.path.sep + this.ssid + ".json", 'Utf8')
if (raw_data) return json_data;
var session_data = JSON.parse(json_data);
@@ -184,12 +238,12 @@ class WTVClientSessionData {
}
} catch (e) {
// Don't log error 'file not found', it just means the client isn't registered yet
if (e.code != "ENOENT") console.error(" # Error 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;
}
}
saveSessionData() {
saveSessionData(force_write = false) {
if (this.isRegistered()) {
// load data from disk and merge new data
var temp_store = this.session_store;
@@ -198,17 +252,18 @@ class WTVClientSessionData {
temp_store = null;
} else {
// do not write file if user is not registered, return true because this is not an error
return true;
// force write needed to set the initial reg
if (!force_write) return true;
}
try {
// only save if file has changed
var json_save_data = JSON.stringify(this.session_store);
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;
} 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;
}
}
@@ -218,9 +273,9 @@ class WTVClientSessionData {
return this.loadSessionData();
}
storeSessionData() {
storeSessionData(force_write = false) {
// alias
return this.saveSessionData();
return this.saveSessionData(force_write);
}
SaveIfRegistered() {
@@ -231,7 +286,7 @@ class WTVClientSessionData {
isRegistered() {
var self = this;
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 (ssid_match) return;
if (file.split('.')[0] == self.ssid) ssid_match = true;
@@ -240,15 +295,19 @@ class WTVClientSessionData {
}
unregisterBox() {
var user_store_base = this.wtvshared.makeSafePath(this.wtvshared.getAbsolutePath(this.minisrv_config.config.SessionStore), this.path.sep + this.ssid);
try {
if (this.fs.lstatSync(this.session_storage + this.path.sep + this.ssid + ".json")) {
this.fs.unlinkSync(this.session_storage + this.path.sep + this.ssid + ".json");
if (this.fs.existsSync(user_store_base + ".json")) {
this.fs.unlinkSync(user_store_base + ".json");
this.session_store = {};
return true;
}
if (this.fs.existsSync(user_store_base)) {
this.fs.rmdirSync(user_store_base, { recursive: true });
}
return true;
} catch (e) {
// 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;
}
}

View File

@@ -0,0 +1,310 @@
/**
* wtv/download-list creation helper class
* By: zefie
*/
class WTVDownloadList {
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();
}
/**
* Clears the download list
*/
clear() {
this.download_list = "";
}
/**
* Alias to clear() (clears the download list)
*/
reset() {
this.clear();
}
/**
* Returns the download list.
* @returns {string} Download list for client;
*/
getDownloadList() {
return this.download_list;
}
/**
* Adds a DISPLAY command to the download list
* @param {string} message Message to display to the client
*/
display(message) {
this.download_list += "DISPLAY " + message + "\n\n";
}
/**
* Adds an EXECUTE command to the download list
* @param {string} command client command to execute
*/
execute(command) {
this.download_list += "EXECUTE " + command + "\n\n";
}
/**
* Adds a CREATE partition command to the download list
* @param {string} path file://Disk/ path to desired partition
* @param {string} size Size of the desired partition
*/
createPartition(path, size) {
this.download_list += "CREATE " + path + "\n";
this.download_list += "partition-size: " + size + "\n\n";
}
/**
* Adds a CREATE-GROUP command to the download list
* @param {string} name Group name
* @param {string} path file://Disk/ path of desired group
* @param {string} state Group state
* @param {boolean|null} service_owned Sets service owned flag. (null = don't set)
*/
createGroup(name, path, state = 'invalid', service_owned = null) {
this.download_list += "CREATE-GROUP " + name + "\n";
this.download_list += "state: " + state + "\n";
if (service_owned !== null) this.download_list += "service-owned: " + service_owned + "\n";
this.download_list += "base: " + path + "\n\n";
}
/**
* An alias for createGroup() that handles creating the '-UPDATE' group for you
* @param {string} name Group name
* @param {string} path file://Disk/ path of desired group
* @param {string} state Group state
* @param {boolean} service_owned Sets service owned flag.
*/
createUpdateGroup(name, path, state = 'invalid', service_owned = false) {
this.createGroup(name + "-UPDATE", path, state);
this.createGroup(name, path, state, service_owned);
}
/**
* Adds a DELETE command to the download list
* @param {string} path Non-absolute path of client destination file (relative to group base) if group defined, otherwise absolute file://Disk/ path to delete
* @param {string} group Group to which it belongs
*/
delete(path, group = null) {
path = this.wtvshared.stripGzipFromPath(path);
this.download_list += "DELETE " + path + "\n";
if (group !== null) this.download_list += "group: " + group + "\n\n";
}
/**
* 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} destination Destination address (wtv url on service) in which to POST upload the file to
*/
put(path, destination) {
this.download_list += "PUT " + path + "\n";
this.download_list += "location: " + destination + "\n\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.put(path, this.service_name + ":/userstore?partialPath=" + escape(destination));
}
/**
* Alias to putUserStoreDest() that generates the destination
* @param {any} path
*/
putUserStore(path) {
var destination = path.replace("file://", "");
this.putUserStoreDest(path, destination);
}
/**
* Adds a GET command to the download list
* @param {string} file Non-absolute path of client destination file (relative to group base)
* @param {string} path Absolute file://Disk/ path of destination
* @param {string} source wtv-url to fetch file from
* @param {string} group Group this file belongs to
* @param {string} display Message to display while working on this file
* @param {string} checksum md5sum of the file
* @param {string} file_permission File permissions
*/
get(file, path, source, group, checksum = null, uncompressed_size = null, original_filename = null, file_permission = 'r') {
if (original_filename) {
file = file.split('/');
var file_name = file[file.length - 1];
path = path.replace(file_name, original_filename);
file.pop();
if (file.length > 0) file = file.join('/') + '/' + original_filename;
else file = original_filename;
}
this.download_list += "GET " + file + "\n";
this.download_list += "group: " + group + "-UPDATE\n";
this.download_list += "location: " + source + "\n";
this.download_list += "file-permission: " + file_permission + "\n";
if (checksum != null) this.download_list += "wtv-checksum: " + checksum + "\n";
if (uncompressed_size != null) this.download_list += "wtv-uncompressed-filesize: " + uncompressed_size + "\n";
this.download_list += "service-source-location: /webtv/content/" + source.substr(source.indexOf('-') + 1, source.indexOf(':/') - source.indexOf('-') - 1) + "d/" + source.substr(source.indexOf(':/') + 2) + "\n";
this.download_list += "client-dest-location: " + path + "\n\n";
}
/**
* Adds a RENAME command to the download list
* @param {string} srcfile Non-absolute path of client source file (relative to source group base)
* @param {string} destfile Non-absolute path of client destination file (relative to destination group base)
* @param {string} srcgroup Source Group
* @param {string} destgroup Destination Group
*/
rename(srcfile, destfile, srcgroup, destgroup) {
srcfile = this.wtvshared.stripGzipFromPath(srcfile);
destfile = this.wtvshared.stripGzipFromPath(destfile);
this.download_list += "RENAME " + srcfile + "\n";
this.download_list += "group: " + srcgroup + "-UPDATE\n";
this.download_list += "destination-group: " + destgroup + "\n";
this.download_list += "location: " + destfile + "\n\n";
}
/**
* Adds a SET-GROUP command to the download list
* @param {string} group Group to set state of
* @param {string} state State to set group to
* @param {string} version Version to set group to
*/
setGroup(group, state, version) {
this.download_list += "SET-GROUP " + group + "\n";
this.download_list += "state: " + state + "\n";
this.download_list += "version: " + version + "\n";
this.download_list += "last-checkup-time: " + new Date().toUTCString().replace("GMT", "+0000") + "\n\n";
}
/**
* Adds a DELETE-GROUP command to the download list
* @param {string} group Group to delete
*/
deleteGroup(group) {
this.download_list += "DELETE-GROUP " + group + "\n\n";
}
/**
* An alias for deleteGroup() that handles deleting the '-UPDATE' group files for you
* @param {string} group Group to delete
* @param {string} path Group base path
*/
deleteGroupUpdate(group, path) {
this.deleteGroup(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': "Fuck!",
'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;

View File

@@ -5,16 +5,18 @@ class WTVFlashrom {
use_zefie_server = true;
bf0app_update = false;
service_vaults = new Array();
no_debug = false;
service_name = "";
zdebug = false;
minisrv_config = [];
constructor(service_vaults, service_name, use_zefie_server = true, bf0app_update = false, debug = false) {
constructor(minisrv_config, service_vaults, service_name, use_zefie_server = true, bf0app_update = false, no_debug = false) {
this.service_vaults = service_vaults;
this.service_name = service_name;
this.use_zefie_server = use_zefie_server;
this.bf0app_update = bf0app_update;
this.zdebug = true;
this.no_debug = no_debug;
this.minisrv_config = minisrv_config;
}
@@ -95,31 +97,37 @@ class WTVFlashrom {
if (flashrom_info.magic == flashrom_magic) flashrom_info.valid_flashrom = true;
if (!flashrom_info.valid_flashrom) console.error(" * Warning! FlashROM File Magic (" + flashrom_info.magic + ") did not match expected magic (" + flashrom_magic + ")...");
if (this.zdebug) console.log(" # FlashROM File Magic (" + flashrom_info.magic + "), expected magic (" + flashrom_magic + "), OK = " + flashrom_info.valid_flashrom + "...");
//if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # FlashROM File Magic (" + flashrom_info.magic + "), expected magic (" + flashrom_magic + "), OK = " + flashrom_info.valid_flashrom + "...");
flashrom_info.byte_progress = data.readUInt32BE(68);
if (this.zdebug) console.log(" # Flashrom Part Bytes Sent:", flashrom_info.byte_progress);
flashrom_info.compression_type = parseInt(part_header[16], 16);
if (this.zdebug) console.log(" # Flashrom Part Compression Type:", flashrom_info.compression_type);
//if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Part Compression Type:", flashrom_info.compression_type);
flashrom_info.part_data_size = data.readUInt32BE(4);
if (this.zdebug) console.log(" # Flashrom Part Data Size:", flashrom_info.part_data_size);
//if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Part Data Size:", flashrom_info.part_data_size);
flashrom_info.part_total_size = flashrom_info.part_data_size + flashrom_info.header_length;
if (this.zdebug) console.log(" # Flashrom Part Total Size:", flashrom_info.part_total_size);
flashrom_info.total_parts_size = data.readUInt32BE(32);
if (this.zdebug) console.log(" # Flashrom All Parts Total Size:", flashrom_info.total_parts_size);
flashrom_info.percent_complete = ((((flashrom_info.byte_progress + flashrom_info.part_total_size) / flashrom_info.total_parts_size)) * 100).toFixed(1);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Part Size :", flashrom_info.part_total_size);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Bytes Sent :", flashrom_info.byte_progress);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Bytes Sent+:", flashrom_info.byte_progress + flashrom_info.part_total_size, "(" + flashrom_info.percent_complete + "% complete)");
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Total Size :", flashrom_info.total_parts_size);
// read current part number bit from part header
flashrom_info.part_number = data.readUInt16BE(28);
if (this.zdebug) console.log(" # Flashrom Current Part Number:", flashrom_info.part_number);
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Curr Part Number :", flashrom_info.part_number);
flashrom_info.is_last_part = ((flashrom_info.byte_progress + flashrom_info.part_total_size) == flashrom_info.total_parts_size) ? true : false;
if (flashrom_info.is_last_part) {
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Curr Part is Last:", flashrom_info.is_last_part);
} else {
flashrom_info.next_part_number = flashrom_info.part_number + 1;
if (this.minisrv_config.config.debug_flags.debug && !this.no_debug) console.log(" # Flashrom Next Part Number :", flashrom_info.next_part_number);
}
// read current part display message from part header
flashrom_info.message = new Buffer.from(part_header.toString('hex').substring(36 * 2, 68 * 2), 'hex').toString('ascii').replace(/[^0-9a-z\ \.\-]/gi, "");
flashrom_info.is_last_part = ((flashrom_info.byte_progress + flashrom_info.part_total_size) == flashrom_info.total_parts_size) ? true : false;
flashrom_info.rompath = `wtv-flashrom:/${path}`;
if (this.zdebug) console.log(" # Flashrom Part Bytes Sent (after this part):", flashrom_info.byte_progress + flashrom_info.part_total_size);
if (this.zdebug) console.log(" # Flashrom Part is Last Part", flashrom_info.is_last_part);
if (flashrom_info.is_last_part && this.bf0app_update) {
flashrom_info.next_rompath = null;
@@ -138,7 +146,7 @@ class WTVFlashrom {
var flashrom_info = this.getFlashromInfo(data, request_path)
if (flashrom_info.is_bootrom) headers += "Content-Type: binary/x-wtv-bootrom"; // maybe?
else headers += "Content-Type: binary/x-wtv-flashblock";
if (flashrom_info.next_rompath != null) headers += "\nwtv-visit: " + flashrom_info.next_rompath;
if (flashrom_info.next_rompath != null && this.bf0app_update) headers += "\nwtv-visit: " + flashrom_info.next_rompath;
callback(data, headers);
}
@@ -179,7 +187,7 @@ class WTVFlashrom {
})
res.on('end', function () {
console.log(` * Zefie's FlashROM Server HTTP Status: ${res.statusCode} ${res.statusMessage}`)
if (self.minisrv_config.config.debug_flags.debug) console.log(` * Zefie's FlashROM Server HTTP Status: ${res.statusCode} ${res.statusMessage}`)
if (res.statusCode == 200) {
var data = Buffer.from(data_hex, 'hex');
} else if (res.statusCode == 206) {

View File

@@ -400,8 +400,6 @@ class WTVLzpf {
this.EncodeLiteral(code_length, code);
}
}
return Buffer.from(this.encoded_data);
}
/**
@@ -442,32 +440,26 @@ class WTVLzpf {
// End
this.AddByte((this.current_literal >>> 0x18) & 0xFF);
this.AddByte(0x20);
return Buffer.from(this.encoded_data);
}
/**
* Converts the data to a Javascript Buffer object
*
* @param data {String|Buffer|CryptoJS.lib.WordArray} Data to convert
* @param data {String|Buffer} Data to convert
*
* @returns {Buffer} Javascript Buffer object
*/
ConvertToBuffer(data) {
if (data.words) {
var WTVSec = require("./WTVSec.js");
wtvsec = new WTVSec(1);
data = wtvsec.wordArrayToBuffer(data);
WTVSec, wtvsec = null;
} else if (!data.byteLength) {
// otherwise if its not already a Buffer, convert it to one
data = new Buffer.from(data);
}
data = new Buffer.from(data.toString('binary'));
return data;
}
/**
* Compress data using WebTV's Lzpf compression algorithm and adds the footer to the end.
*
* @param uncompressed_data {String|Buffer|CryptoJS.lib.WordArray} data to compress
* @param uncompressed_data {String|Buffer} data to compress
*
* @returns {Buffer} Lzpf compression data
*/
@@ -475,9 +467,7 @@ class WTVLzpf {
uncompressed_data = this.ConvertToBuffer(uncompressed_data);
this.Begin();
this.EncodeBlock(uncompressed_data, true);
this.Finish();
return Buffer.from(this.encoded_data);
return this.Finish();
}
}

View 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;

View File

@@ -31,19 +31,18 @@ class WTVSec {
hRC4_Key1 = null;
hRC4_Key2 = null;
RC4Session = new Array();
zdebug = false;
minisrv_config = [];
/**
*
* Initialize the WTVSec class.
*
* @param {Number} wtv_incarnation Sets the wtv-incarnation for this instance
* @param {Boolean} zdebug Enable debugging
* @param {Boolean} minisrv_config.config.debug_flags.debug Enable debugging
*
*/
constructor(wtv_incarnation = 1, zdebug = false) {
this.zdebug = zdebug;
constructor(minisrv_config, wtv_incarnation = 1) {
this.minisrv_config = minisrv_config;
this.initial_shared_key = CryptoJS.enc.Base64.parse(this.initial_shared_key_b64);
if (this.initial_shared_key.sigBytes === 8) {
@@ -238,7 +237,8 @@ class WTVSec {
* #returns {Buffer} JS Buffer object
*/
wordArrayToBuffer(wordArray) {
return new Buffer.from(wordArray.toString(CryptoJS.enc.Hex), 'hex');
if (wordArray) return new Buffer.from(wordArray.toString(CryptoJS.enc.Hex), 'hex');
else return null;
}
/**
@@ -247,7 +247,7 @@ class WTVSec {
*
*/
SecureOn(rc4session = null) {
if (this.zdebug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation);
if (this.minisrv_config.config.debug_flags.debug) console.log(" # Generating RC4 sessions with wtv-incarnation: " + this.incarnation);
var buf = new Uint8Array([0xff & this.incarnation, 0xff & (this.incarnation >> 8), 0xff & (this.incarnation >> 16), 0xff & (this.incarnation >> 24)]);
endianness(buf, 4);

View File

@@ -0,0 +1,195 @@
/**
* Shared functions across all classes and apps
*/
class WTVShared {
path = require('path');
fs = require('fs');
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 the Last-Modified date in Unix Timestamp format
* @param {string} file Path to a file
*/
getFileLastModified(file) {
var stats = this.fs.lstatSync(file);
if (stats) return new Date(stats.mtimeMs);
return false;
}
/**
* Returns the Last-Modified date in a RFC7231 compliant UTC Date String
* @param {string} file Path to a file
*/
getFileLastModifiedUTCString(file) {
return this.getFileLastModified(file).toUTCString();
}
/**
* 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) {
return new Date((new Date).getTime() + offset).toUTCString();
}
/**
* 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;
}
/**
* Returns a percentage
* @param {number} partialValue
* @param {number} totalValue
* @returns {number} percentage
*/
getPercentage = function (partialValue, totalValue) {
return Math.floor((100 * partialValue) / totalValue);
}
/**
* If the file ends with .gz, remove it
* @param {string} path
* @return {string} path without gz, or unmodified path if it isnt a gz
*/
stripGzipFromPath(path) {
var path_split = path.split('.');
if (path_split[path_split.length - 1].toLowerCase() == "gz") {
path_split.pop();
path = path_split.join(".");
}
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;

File diff suppressed because it is too large Load Diff

View File

@@ -13,12 +13,15 @@
"hide_ssid_in_logs": true,
"post_percentages": [ 0, 25, 50, 100 ],
"verbosity": 2,
"socket_timeout": 86400,
"post_data_socket_timeout": 30,
"error_log_file": "errors.log",
"catchall_file_name": "catchall.js",
"enable_lzpf_compression": false,
"enable_gzip_compression": true,
"pc_server_hidden_service": "http_pc",
"pc_server_hidden_service_enabled": false,
"show_diskmap": false,
"allow_guests": true
},
"services": {

View File

@@ -1,6 +1,6 @@
{
"name": "zefie_wtvp_minisrv",
"version": "0.9.16",
"version": "0.9.18",
"description": "WebTV Service (WTVP) Emulation Server",
"main": "app.js",
"homepage": "https://github.com/zefie/zefie_wtvp_minisrv",

View File

@@ -52,6 +52,10 @@
<Content Include="ServiceVault\wtv-cookie\reset.js">
<SubType>Code</SubType>
</Content>
<Content Include="ServiceVault\wtv-disk\content\diskmaps\ModemFirmware.json" />
<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\current-noflash.js">
<SubType>Code</SubType>
@@ -275,12 +279,18 @@
<Content Include="WTVClientCapabilities.js">
<SubType>Code</SubType>
</Content>
<Content Include="WTVDownloadList.js">
<SubType>Code</SubType>
</Content>
<Content Include="WTVFlashrom.js">
<SubType>Code</SubType>
</Content>
<Content Include="WTVLzpf.js">
<SubType>Code</SubType>
</Content>
<Content Include="WTVMime.js">
<SubType>Code</SubType>
</Content>
<Content Include="WTVRegister.js">
<SubType>Code</SubType>
</Content>
@@ -288,6 +298,9 @@
<SubType>Code</SubType>
</Content>
<Content Include="package.json" />
<Content Include="WTVShared.js">
<SubType>Code</SubType>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="ServiceVault\" />