- numerous bug fixes
 - wtv-news goodies, ready for local testing
   - custom patched nntp-server node module with support for POSTing
   - should be able to post locally
   - 4 groups are made by default
   - can override in user_config.json (look at the config.json changes but dont do them there)
   - can sync down from an upstream server with sync_nntp.js
   - sync does not push new posts to upstream yet
This commit is contained in:
zefie
2022-10-11 21:44:18 -04:00
parent 1165b245ce
commit b89e0e932c
181 changed files with 4333 additions and 688 deletions

View File

@@ -8,8 +8,8 @@ const fs = require('fs');
const tls = require('tls');
const path = require('path');
const zlib = require('zlib');
const http = require('http');
const https = require('https');
const http = require('follow-redirects').http
const https = require('follow-redirects').https
const net = require('net');
const crypto = require('crypto')
const CryptoJS = require('crypto-js');
@@ -23,6 +23,7 @@ const WTVMime = require(classPath + "/WTVMime.js");
const WTVFlashrom = require(classPath + "/WTVFlashrom.js");
const vm = require('vm');
const express = require('express');
var wtvnewsserver = null;
process
.on('SIGTERM', shutdown('SIGTERM'))
@@ -38,6 +39,44 @@ function shutdown(signal = 'SIGTERM') {
};
}
function findServiceByPort(port) {
var service_name = null;
Object.keys(minisrv_config.services).forEach(function(k) {
if (service_name) return;
if (minisrv_config.services[k].port) {
if (port == parseInt(minisrv_config.services[k].port))
service_name = k;
}
})
return service_name;
}
function getPortByService(service) {
if (minisrv_config.services[service]) return minisrv_config.services[service].port;
else return null;
}
function getSocketDestinationPort(socket) {
return parseInt(socket._server._connectionKey.split(':')[2]);
}
function verifyServicePort(service_name, socket) {
if (!minisrv_config.config.enable_port_isolation) return service_name;
if (socket._server._connectionKey) {
var socketPort = getSocketDestinationPort(socket);
if (minisrv_config.services[service_name]) {
if (minisrv_config.services[service_name].port == socketPort) {
if (minisrv_config.services[service_name].servicevault_dir)
return minisrv_config.services[service_name].servicevault_dir;
else
return service_name;
}
}
}
return false;
}
// Where we store our session information
var ssid_sessions = new Array();
var socket_sessions = new Array();
@@ -45,6 +84,7 @@ var socket_sessions = new Array();
var ports = [];
var pc_ports = [];
// add .reverse() feature to all JavaScript Strings in this application
// works for service vault scripts too.
if (!String.prototype.reverse) {
@@ -164,16 +204,16 @@ var runScriptInVM = function (script_data, user_contextObj = {}, privileged = fa
contextObj[vm_modules[k]] = require(module_file);
modules_loaded.push(module_file)
} catch (e) {
console.error(" *!* Could not load module", module_file, "requested by service", contextObj.service_name)
console.error(" *!* Could not load module", module_file, "requested by service", contextObj.service_name, e)
}
if (vm_modules[k] === "WTVNews") contextObj['wtvnewsserver'] = wtvnewsserver;
})
}
}
switch (contextObj.service_name) {
case "wtv-guide":
// wtv-guide is a special case due to needing this function
modules_to_load.push({ "name": "WTVGuide", "file": classPath + "/WTVGuide.js" });
contextObj.wtvguide = new tmpmod(minisrv_config, ssid_sessions[contextObj.socket.ssid], contextObj.socket, runScriptInVM);
contextObj.wtvguide = new contextObj["WTVGuide"](minisrv_config, ssid_sessions[contextObj.socket.ssid], contextObj.socket, runScriptInVM);
break;
case "wtv-1800":
@@ -254,9 +294,16 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
]
if (!pc_services) {
updateFromVM.push([`ssid_sessions[${socket.ssid}]`, "session_data"]); // user-specific session data from unprivileged scripts
}
var privileged = false;
if (minisrv_config.services[service_name]) privileged = (minisrv_config.services[service_name].privileged) ? true : false;
else if (pc_services) privileged = (minisrv_config.services['pc_services'].privileged) ? true : false;
if (privileged) {
updateFromVM.push(["ssid_sessions", "ssid_sessions"]); // global ssid_sessions object for privileged service scripts, such as wtv-setup, wtv-head-waiter, etc
updateFromVM.push(["socket_sessions", "socket_sessions"]); // global socket_sessions object for privileged service scripts, such as wtv-1800, etc
updateFromVM.push([`ssid_sessions[${socket.ssid}]`, "session_data"]); // user-specific session data from unprivileged scripts
}
try {
@@ -417,11 +464,8 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
// expose var service_dir for script path to the root of the wtv-service
socket_sessions[socket.id].starttime = Math.floor(new Date().getTime() / 1000);
var script_data = fs.readFileSync(service_vault_file_path + ".js").toString();
var priv = false;
if (minisrv_config.services[service_name]) priv = (minisrv_config.services[service_name].privileged) ? true : false;
else if (pc_services) priv = (minisrv_config.services['pc_services'].privileged) ? true : false;
var vmResults = runScriptInVM(script_data, contextObj, priv, service_vault_file_path + ".js");
var vmResults = runScriptInVM(script_data, contextObj, privileged, service_vault_file_path + ".js");
// Here we read back certain data from the ServiceVault Script Context Object
updateFromVM.forEach((item) => {
try {
@@ -461,11 +505,8 @@ async function processPath(socket, service_vault_file_path, request_headers = ne
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Found catchall at " + catchall_file + " to handle request (JS Interpreter Mode) [Socket " + socket.id + "]");
request_headers.service_file_path = catchall_file;
var script_data = fs.readFileSync(catchall_file).toString();
var priv = false;
if (minisrv_config.services[service_name]) priv = (minisrv_config.services[service_name].privileged) ? true : false;
else if (pc_services) priv = (minisrv_config.services['pc_services'].privileged) ? true : false;
runScriptInVM(script_data, contextObj, priv, catchall_file);
runScriptInVM(script_data, contextObj, privileged, catchall_file);
// Here we read back certain data from the ServiceVault Script Context Object
try {
@@ -771,6 +812,7 @@ async function doHTTPProxy(socket, request_headers) {
port: request_data.port,
path: request_data.path,
method: request_data.method,
followAllRedirects: true,
headers: {
"User-Agent": request_headers["User-Agent"] || "WebTV"
}
@@ -968,7 +1010,7 @@ function headerStringToObj(headers, response = false) {
async function sendToClient(socket, headers_obj, data) {
var headers = "";
var content_length = 0;
if (typeof (data) === 'undefined') data = '';
if (typeof (data) === 'undefined' || data === null) data = '';
if (typeof (headers_obj) === 'string') {
// string to header object
headers_obj = headerStringToObj(headers_obj, true);
@@ -984,7 +1026,11 @@ async function sendToClient(socket, headers_obj, data) {
if (!headers_obj['minisrv-no-mail-count']) {
if (ssid_sessions[socket.ssid]) {
if (ssid_sessions[socket.ssid].isRegistered()) {
if (ssid_sessions[socket.ssid].mailstore) {
if (!ssid_sessions[socket.ssid].isUserLoggedIn()) {
// not logged in probe all users
headers_obj['wtv-mail-count'] = ssid_sessions[socket.ssid].getAccountTotalUnreadMessages();
} else if (ssid_sessions[socket.ssid].mailstore) {
// logged in
headers_obj['wtv-mail-count'] = ssid_sessions[socket.ssid].mailstore.countUnreadMessages(0);
}
}
@@ -998,7 +1044,7 @@ async function sendToClient(socket, headers_obj, data) {
// add Connection header if missing, default to Keep-Alive
if (!headers_obj.Connection) {
headers_obj.Connection = "Keep-Alive";
headers_obj = moveObjectElement('Connection', 'Response', headers_obj);
headers_obj = wtvshared.moveObjectElement('Connection', 'Response', headers_obj);
}
var content_length = 0;
@@ -1090,7 +1136,7 @@ async function sendToClient(socket, headers_obj, data) {
// encrypt if needed
if (socket_sessions[socket.id].secure == true && !socket_sessions[socket.id].do_not_encrypt) {
headers_obj["wtv-encrypted"] = 'true';
headers_obj = moveObjectElement('wtv-encrypted', 'Connection', headers_obj);
headers_obj = wtvshared.moveObjectElement('wtv-encrypted', 'Connection', headers_obj);
if (content_length > 0 && socket_sessions[socket.id].wtvsec) {
if (!minisrv_config.config.debug_flags.quiet) console.log(" * Encrypting response to client ...")
var enc_data = socket_sessions[socket.id].wtvsec.Encrypt(1, data);
@@ -1127,7 +1173,7 @@ async function sendToClient(socket, headers_obj, data) {
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64) {
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.update_ticket) {
headers_obj["wtv-ticket"] = ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64;
headers_obj = moveObjectElement("wtv-ticket", "Connection", headers_obj);
headers_obj = wtvshared.moveObjectElement("wtv-ticket", "Connection", headers_obj);
ssid_sessions[socket.ssid].data_store.wtvsec_login.update_ticket = false;
}
}
@@ -1241,30 +1287,6 @@ function concatArrayBuffer(buffer1, buffer2) {
return tmp.buffer;
}
function moveObjectElement(currentKey, afterKey, obj) {
var result = {};
var val = obj[currentKey];
delete obj[currentKey];
var next = -1;
var i = 0;
if (typeof afterKey == 'undefined' || afterKey == null) afterKey = '';
Object.keys(obj).forEach(function (k) {
var v = obj[k];
if ((afterKey == '' && i == 0) || next == 1) {
result[currentKey] = val;
next = 0;
}
if (k == afterKey) { next = 1; }
result[k] = v;
++i;
});
if (next == 1) {
result[currentKey] = val;
}
if (next !== -1) return result; else return obj;
}
function isUnencryptedString(string) {
// 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
@@ -1392,8 +1414,10 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
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);
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id != null) {
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id >= 0)
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id >= 0) {
ssid_sessions[socket.ssid].switchUserID(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id, true, false);
ssid_sessions[socket.ssid].setUserLoggedIn(true);
}
}
} else {
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64 != headers["wtv-ticket"]) {
@@ -1403,8 +1427,10 @@ async function processRequest(socket, data_hex, skipSecure = false, encryptedReq
ssid_sessions[socket.ssid].data_store.wtvsec_login.DecodeTicket(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_b64);
if (headers["wtv-incarnation"]) ssid_sessions[socket.ssid].data_store.wtvsec_login.set_incarnation(headers["wtv-incarnation"]);
if (ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id >= 0) {
if (ssid_sessions[socket.ssid].user_id != ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id)
if (ssid_sessions[socket.ssid].user_id != ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id) {
ssid_sessions[socket.ssid].switchUserID(ssid_sessions[socket.ssid].data_store.wtvsec_login.ticket_store.user_id, true, false);
ssid_sessions[socket.ssid].setUserLoggedIn(true);
}
}
}
}
@@ -1836,6 +1862,7 @@ Object.keys(minisrv_config.services).forEach(function (k) {
if (minisrv_config.services[k].pc_services) pc_ports.push(minisrv_config.services[k].port);
else ports.push(minisrv_config.services[k].port);
}
// minisrv_config service toString
minisrv_config.services[k].toString = function (overrides) {
var self = Object.assign({}, this);
@@ -1858,7 +1885,37 @@ Object.keys(minisrv_config.services).forEach(function (k) {
}
return outstr;
}
console.log(" * Configured Service:", k, "on Port", minisrv_config.services[k].port, "- Service Host:", minisrv_config.services[k].host, "- Bind Port:", !minisrv_config.services[k].nobind, "- PC Services Mode:", (minisrv_config.services[k].pc_services) ? true : false);
if (minisrv_config.services[k].local_nntp_port) {
if (!wtvnewsserver) {
const WTVNewsServer = require(classPath + "/WTVNewsServer.js");
var local_nntp_using_auth = false;
if (minisrv_config.services[k].local_nntp_requires_auth) {
local_nntp_using_auth = true;
if (minisrv_config.services[k].local_auth) {
// auth required, and info defined in config
wtvnewsserver = new WTVNewsServer(minisrv_config, minisrv_config.services[k].local_nntp_port, true, minisrv_config.services[k].local_auth.username, minisrv_config.services[k].local_auth.password);
console.log(" * Configured Service: Local NNTP", "on 127.0.0.1:" + minisrv_config.services[k].local_nntp_port, "(TLS) - Auth required:", local_nntp_using_auth, "- Auth: As Configured");
} else {
// auth required, but randomly generated
wtvnewsserver = new WTVNewsServer(minisrv_config, minisrv_config.services[k].local_nntp_port, true);
console.log(" * Configured Service: Local NNTP", "on 127.0.0.1:" + minisrv_config.services[k].local_nntp_port, "(TLS) - Auth required:", local_nntp_using_auth, "- Auth (randgen): User:", wtvnewsserver.username, "Pass:", wtvnewsserver.password);
}
} else {
// no auth required on local server
wtvnewsserver = new WTVNewsServer(minisrv_config, minisrv_config.services[k].local_nntp_port);
console.log(" * Configured Service: Local NNTP", "on 127.0.0.1:" + minisrv_config.services[k].local_nntp_port, "(TLS) - Auth required:", local_nntp_using_auth, "- Auth: None");
}
if (minisrv_config.services[k].featuredGroups) {
Object.keys(minisrv_config.services[k].featuredGroups).forEach((j) => {
wtvnewsserver.createGroup(minisrv_config.services[k].featuredGroups[j].group, minisrv_config.services[k].featuredGroups[j].description || null);
})
}
}
}
})
if (minisrv_config.config.hide_ssid_in_logs) console.log(" * Masking SSIDs in console logs for security");
else console.log(" * Full SSIDs will be shown in console logs");
@@ -1894,44 +1951,6 @@ process.on('uncaughtException', function (err) {
console.error((err && err.stack) ? err.stack : err);
});
function findServiceByPort(port) {
var service_name = null;
Object.keys(minisrv_config.services).forEach(function (k) {
if (service_name) return;
if (minisrv_config.services[k].port) {
if (port == parseInt(minisrv_config.services[k].port))
service_name = k;
}
})
return service_name;
}
function getPortByService(service) {
if (minisrv_config.services[service]) return minisrv_config.services[service].port;
else return null;
}
function getSocketDestinationPort(socket) {
return parseInt(socket._server._connectionKey.split(':')[2]);
}
function verifyServicePort(service_name, socket) {
if (socket._server._connectionKey) {
var socketPort = getSocketDestinationPort(socket);
if (minisrv_config.services[service_name]) {
if (minisrv_config.services[service_name].port == socketPort) {
if (minisrv_config.services[service_name].servicevault_dir)
return minisrv_config.services[service_name].servicevault_dir;
else
return service_name;
}
}
}
return false;
}
var initstring = '';
var initstring_pc = '';
ports.sort();