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