Compare commits
13 Commits
eba447cd06
...
feature/Fi
| Author | SHA1 | Date | |
|---|---|---|---|
| 17e0e6e526 | |||
|
|
02a3eef5e7 | ||
|
|
11d2ab8c86 | ||
|
|
778c0a2827 | ||
|
|
00e385cdbe | ||
|
|
cf9cc22a1c | ||
|
|
0c5dc17ae6 | ||
|
|
4347543ef7 | ||
|
|
9d51abd9ab | ||
|
|
118443305b | ||
|
|
ab4453487e | ||
|
|
e003d9795b | ||
|
|
e88dbd98cc |
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## user_config.json
|
## user_config.json
|
||||||
|
|
||||||
`user_config.json` (in the project root) is where you put your local configuration overrides. It merges on top of `includes/config.json` — **do not edit `includes/config.json` directly**.
|
`user_config.json` (in the same folder as `app.js`) is where you put your local configuration overrides. It merges on top of `includes/config.json` — **do not edit `includes/config.json` directly**.
|
||||||
|
|
||||||
You only need to include keys you want to override. Copy `user_config.example.json` as a starting point, or start with a minimal file:
|
You only need to include keys you want to override. Copy `user_config.example.json` as a starting point, or start with a minimal file:
|
||||||
|
|
||||||
@@ -35,17 +35,19 @@ node tools/configurator.js <dot.path.key> --delete [--overwrite]
|
|||||||
|
|
||||||
## Setting service_ip
|
## Setting service_ip
|
||||||
|
|
||||||
`service_ip` tells the box where to connect, this CANNOT be `0.0.0.0`, and must be an address reachable by your box when it connects via your setup. Can be `127.0.0.1` if you are running MAME/Viewer on the same machine as minisrv.
|
`service_ip` tells the box where to connect, this CANNOT be `0.0.0.0`, and must be an address reachable by your box when it connects via your setup. Can be `127.0.0.1` if you are running TouchPPP or WebTV Viewer on the same machine as minisrv.
|
||||||
|
|
||||||
```
|
```
|
||||||
node tools/configurator.js config.bind_ip 192.168.1.x --overwrite
|
node tools/configurator.js config.service_ip 192.168.1.x --overwrite
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Setting user_data_key
|
## Setting user_data_key
|
||||||
|
|
||||||
`user_data_key` is used to encrypt user data. It should be a random secret string and **must be set before registering any users**. Changing it after users have registered will break existing accounts.
|
`user_data_key` is used to encrypt user data. It should be a random secret string and **must be set before registering any users**.
|
||||||
|
Changing it after users have registered will require updating the userdata with `tools/update_user_data_key.js`. Making a backup
|
||||||
|
of `SessionStore/accounts` is recommended before running `tools/update_user_data_key.js`, it is pretty resilent against corruption, but just in case.
|
||||||
|
|
||||||
```
|
```
|
||||||
node tools/configurator.js config.keys.user_data_key YOUR_RANDOM_SECRET --overwrite
|
node tools/configurator.js config.keys.user_data_key YOUR_RANDOM_SECRET --overwrite
|
||||||
@@ -55,3 +57,31 @@ To generate a random key:
|
|||||||
```
|
```
|
||||||
openssl rand -base64 32
|
openssl rand -base64 32
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Disabling a standard service
|
||||||
|
|
||||||
|
You can disable a configured service by setting the `disabled: true` flag for that service. For example, to disable `wtv-admin`:
|
||||||
|
|
||||||
|
```
|
||||||
|
node tools/configurator.js services.wtv-admin.disabled true
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Enabling a disabled service
|
||||||
|
|
||||||
|
You can disable a configured service by setting the `disabled: false` flag for that service. For example, to enable `pc_services`:
|
||||||
|
|
||||||
|
```
|
||||||
|
node tools/configurator.js services.pc_services.disabled false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom service pages
|
||||||
|
|
||||||
|
You can place your custom pages in `UserServiceVault/servicename/page.js`. For example, to override `wtv-home:/home`, you would create
|
||||||
|
`UserServiceVault/wtv-home/home.js`, and the server will automatically prioritize your page. You can mix and match service vaults, accessing
|
||||||
|
resources in the standard service vault within your custom pages.
|
||||||
|
|
||||||
|
## Updating minisrv
|
||||||
|
|
||||||
|
You can `git pull`, or extract a new archive over the existing folder. If you followed the directions and kept your changes in `user_config.json` and `UserServiceVault`,
|
||||||
|
then you can update minisrv without worrying about breakage or losing data. Do pay attention to the console, and if any deprecreations appear, fix them before updating to the version listed in the notice.
|
||||||
@@ -228,24 +228,43 @@ function getServiceString(service_name, overrides = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const DEPRECIATED_CONFIG_PATH = path.join(__dirname, 'includes', 'depreciated.json');
|
||||||
|
|
||||||
|
function loadDepreciatedPatterns() {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(DEPRECIATED_CONFIG_PATH)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = fs.readFileSync(DEPRECIATED_CONFIG_PATH, 'utf8');
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
|
||||||
|
if (!Array.isArray(parsed) || parsed.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapped = parsed
|
||||||
|
.filter((entry) => entry && typeof entry.pattern === 'string')
|
||||||
|
.map((entry) => ({
|
||||||
|
id: entry.id || entry.pattern,
|
||||||
|
pattern: new RegExp(entry.pattern, entry.flags || 'g'),
|
||||||
|
message: entry.message || 'Deprecated API usage found',
|
||||||
|
removeVersion: entry.removeVersion || 'unknown',
|
||||||
|
replacement: entry.replacement || null
|
||||||
|
}));
|
||||||
|
|
||||||
|
return mapped.length > 0 ? mapped : {};
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Failed to load includes/depreciated.json, using fallback deprecation patterns:', error.message);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Deprecation warnings configuration
|
// Deprecation warnings configuration
|
||||||
const deprecationWarnings = {
|
const deprecationWarnings = {
|
||||||
// Array of deprecated patterns with their details
|
// Array of deprecated patterns with their details
|
||||||
patterns: [
|
patterns: loadDepreciatedPatterns(),
|
||||||
{
|
|
||||||
// Example deprecations - you can modify these as needed
|
|
||||||
pattern: /session\_data\.hasCap\s*\(/g,
|
|
||||||
message: "session_data.hasCap() is deprecated and will be removed",
|
|
||||||
removeVersion: "0.9.80",
|
|
||||||
replacement: "Use session_data.capabilities.get() instead"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
pattern: /(?<!wtvshared\.)getServiceString\s*\(/g,
|
|
||||||
message: "getServiceString() is deprecated and will be removed",
|
|
||||||
removeVersion: "0.9.80",
|
|
||||||
replacement: "Use wtvshared.getServiceString() instead"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
// Enable/disable deprecation warnings globally
|
// Enable/disable deprecation warnings globally
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -363,7 +382,7 @@ const runScriptInVM = function (script_data, user_contextObj = {}, privileged =
|
|||||||
"service_vaults": service_vaults,
|
"service_vaults": service_vaults,
|
||||||
"service_deps": service_deps,
|
"service_deps": service_deps,
|
||||||
"ssid_sessions": ssid_sessions,
|
"ssid_sessions": ssid_sessions,
|
||||||
"moveArrayKey": wtvshared.moveArrayKey,
|
"moveArrayKey": wtvshared.moveArrayKey, // deprecated - use wtvshared.moveArrayKey() instead
|
||||||
"cwd": (filename) ? path.dirname(filename) : __dirname, // current working directory
|
"cwd": (filename) ? path.dirname(filename) : __dirname, // current working directory
|
||||||
|
|
||||||
// Our prototype overrides
|
// Our prototype overrides
|
||||||
@@ -2685,9 +2704,11 @@ if (protocolHandledPorts.size > 0) console.log(` * Started ${protocolHandledPort
|
|||||||
const listening_ip_string = (minisrv_config.config.bind_ip !== "0.0.0.0") ? "IP: " + minisrv_config.config.bind_ip : "all interfaces";
|
const listening_ip_string = (minisrv_config.config.bind_ip !== "0.0.0.0") ? "IP: " + minisrv_config.config.bind_ip : "all interfaces";
|
||||||
console.log(" * Listening on", listening_ip_string, "~", "Service IP:", service_ip);
|
console.log(" * Listening on", listening_ip_string, "~", "Service IP:", service_ip);
|
||||||
|
|
||||||
|
// Security warning for default user data encryption key
|
||||||
if (minisrv_config.config.keys.user_data_key === "PNa$WN7gz}!T=t6X7^=|Ii##CEB~p\\EP") {
|
if (minisrv_config.config.keys.user_data_key === "PNa$WN7gz}!T=t6X7^=|Ii##CEB~p\\EP") {
|
||||||
console.log(" * WARNING: You are using the default user data encryption key. This is not secure, and you should change it in the configuration file before registering any users.");
|
console.log(" * WARNING: You are using the default user data encryption key. This is not secure, and you should change it in the configuration file before registering any users.");
|
||||||
console.log(" * To generate a random key in bash or PowerShell, you can run: node ./tools/configurator.js config.keys.user_data_key $(openssl rand -base64 32)");
|
console.log(" * To generate a random key in bash or PowerShell, you can run: node ./tools/configurator.js config.keys.user_data_key $(openssl rand -base64 32)");
|
||||||
console.log(" * After changing the key in the user_config, you can run tools/update_user_data_key.js to update existing accounts with the new key.");
|
console.log(" * After changing the key in the user_config, please restart the server.");
|
||||||
|
console.log(" * If you had existing users prior to changing the key, you can run tools/update_user_data_key.js to update existing accounts with the new key.");
|
||||||
console.log(" * Making a backup of your user accounts before doing this is highly recommended, in case something goes wrong during the update process.");
|
console.log(" * Making a backup of your user accounts before doing this is highly recommended, in case something goes wrong during the update process.");
|
||||||
}
|
}
|
||||||
27
zefie_wtvp_minisrv/includes/ServiceDeps/msntv2/minisrv.crt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEqzCCA5OgAwIBAgIQ3Zq6hcFrpKh4v3/G9sTw3DANBgkqhkiG9w0BAQUFADBw
|
||||||
|
MQswCQYDVQQGEwJVUzELMAkGA1UECAwCT0gxEzARBgNVBAcMCkJ1dHQgQ3JhY2sx
|
||||||
|
IDAeBgNVBAoMF1VuZGVyd2VhciBJbnNwZWN0b3IgIzEyMR0wGwYDVQQLDBRUaGlu
|
||||||
|
ZyBMb29rZXIgRXhwZXJ0czAgFw0wMDAxMDExMjAwMDBaGA8yMDk5MTIzMTIzNTk1
|
||||||
|
OVowUTEpMCcGA1UEAxMgaGVhZHdhaXRlci50cnVzdGVkLm1zbnR2Lm1zbi5jb20x
|
||||||
|
FzAVBgNVBAoTDlplZmllIE5ldHdvcmtzMQswCQYDVQQGEwJVUzCCASIwDQYJKoZI
|
||||||
|
hvcNAQEBBQADggEPADCCAQoCggEBAMtl9J068AFSHykffAlcpspq3D7mE7fFRoyf
|
||||||
|
+tQCM3Wy7PqJvDAegoI4Zf/QdToTJMS6dkcsEx+dgD01VKJ1B0RdHbg6rFQfymc4
|
||||||
|
GKyNk6tuqp7YQqElCUc91oFz4pJaJaOYaNBqkAG3MfTg+tSoBXl2YyjPrT0TPhXX
|
||||||
|
1Cm7BuFZORqNhvTdf33QXzgCQVso9U5X9YBgDaiTcu55etjFKUBEYhSYwTHmennA
|
||||||
|
FWOjY7ux6HFXBfKAz1QeCE6+corl5+6srCfh7Uz3ZFV9vntEYnyzbJuo6gR5P7GI
|
||||||
|
IYsygkADQAETHivl6GxeB7SEUfYLnfrZFwZc235tUz7USBdg3gcCAwEAAaOCAVww
|
||||||
|
ggFYMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMB
|
||||||
|
MIIBJwYDVR0RBIIBHjCCARqCIGhlYWR3YWl0ZXIudHJ1c3RlZC5tc250di5tc24u
|
||||||
|
Y29tghcqLnRydXN0ZWQubXNudHYubXNuLmNvbYINbXNudHYubXNuLmNvbYIWbWFp
|
||||||
|
bC5zZXJ2aWNlcy5saXZlLmNvbYIObG9naW4ubGl2ZS5jb22CEXBvcHRpbWl6ZS5t
|
||||||
|
c24uY29tghFmYXZvcml0ZXMubXNuLmNvbYIRbWVzc2VuZ2VyLm1zbi5jb22CEWxp
|
||||||
|
dmVmaWxlc3RvcmUuY29tghZ1c2Vycy5zdG9yYWdlLmxpdmUuY29tgglnLm1zbi5j
|
||||||
|
b22CF21zbmlhbG9naW4ucGFzc3BvcnQuY29tgg1taW5pc3J2LmxvY2Fsgg8qLm1p
|
||||||
|
bmlzcnYubG9jYWwwDQYJKoZIhvcNAQEFBQADggEBAAGEINTBTrkbpO0CJPv9w4Nj
|
||||||
|
IMuOSZETA7uXWyPwoLBIa57yTjNEVvWmjAc2nnrI3D6ijLMiF1eDIEsP4DI/qfMs
|
||||||
|
J82cS/IOIxXCmReU132NaZ6mSNEZx9QDkz/R8rFq5jKPRYSeguZSsWDxYlaQsbRr
|
||||||
|
qxQsKkRIOpm5pIOA/UT2gwV0L84a/NHXHNfc+CnPvvy7R9kmUC0XynsqU3lkj4Ah
|
||||||
|
SNZOgYyWkGWW7AytQWnMxyWm+xQjG4Fwl1Os9en4qwCK0ADyMCQyG3O68Gffu2go
|
||||||
|
YciXaJquI52fEKDQV4mDxy4B1V8BQ3ywm1iGebLzLgrKK7xPucUU5fqz7v2IUIs=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
27
zefie_wtvp_minisrv/includes/ServiceDeps/msntv2/minisrv.key
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAy2X0nTrwAVIfKR98CVymymrcPuYTt8VGjJ/61AIzdbLs+om8
|
||||||
|
MB6Cgjhl/9B1OhMkxLp2RywTH52APTVUonUHRF0duDqsVB/KZzgYrI2Tq26qnthC
|
||||||
|
oSUJRz3WgXPiklolo5ho0GqQAbcx9OD61KgFeXZjKM+tPRM+FdfUKbsG4Vk5Go2G
|
||||||
|
9N1/fdBfOAJBWyj1Tlf1gGANqJNy7nl62MUpQERiFJjBMeZ6ecAVY6Nju7HocVcF
|
||||||
|
8oDPVB4ITr5yiuXn7qysJ+HtTPdkVX2+e0RifLNsm6jqBHk/sYghizKCQANAARMe
|
||||||
|
K+XobF4HtIRR9gud+tkXBlzbfm1TPtRIF2DeBwIDAQABAoIBAADoPGfDhQfq4IyA
|
||||||
|
gVjRvhHmDLbTgOACp1aZbUPeNKUmpKVWniCOA+GZZ2V1ZEIOwGaH0sVtF8xXmZeF
|
||||||
|
5UUKOS7GLL7b6CJB6z2vNt3CJ35av03d3UI2iSzCEYxadd0jcMqJGfwsyLKkdgd6
|
||||||
|
rDO5m7ikdtT0qpGjEQi46AKkCZseSdIZqtgm1t0NupbXuBKbR13N6iNOr6eVnEpZ
|
||||||
|
tTrwIvw/OZ3Wf9tVW0DzjB1ejUYXCYAd6mHskMRrNfu/11x2pyXMHOSl24sVZxHe
|
||||||
|
Dxe+cYjw61/5pMMBq6ZQ61SPvAWZbIOA/PINHIUjisIGxpeqluz0zb+SLLrZnBe5
|
||||||
|
i9WEHsUCgYEA5lKEc5w02/n9+6wZCK/sPWhNJF3cPw2E0O+kqHPWLx2qPNhKf2Zh
|
||||||
|
8RycdVCnCcEXVQedYnNpBeAktMNGxAR9IhiVhfwiG0dFYXNUHBIHgVBZqtOeW8sS
|
||||||
|
uy4/fOw/qCCLWDfnDaozj8RnKsvu6zLm4c73Q4FEr4R6rk4EjfsGA90CgYEA4hMD
|
||||||
|
9PBdIWAYapP2FRangOmP50YNQVznrmDsfFlm2vh3aFrx83f0IFjYxeUI7OdTjkk3
|
||||||
|
7m5pEeV/liQwB/D7uXF+gNoUQab61QRhuKd58TbQELJkeBwevUb3rWVZ3LiFNNVB
|
||||||
|
vcunKTsL4qEADw56qMaczuZ27xXwatgUVtOdbTMCgYAmOA0ojgQreIlPyNgCnAas
|
||||||
|
jfE3Fqgkgl7GuO1u0oH5IYgNPqrmBxw7gU7pHLALK1Ju1qukGZiU1APjRwAoKMKG
|
||||||
|
9ONi71rNgf8eU5/iZI+AQtAOS71caA88pkj8tss9X+Efi2840kRqF+IytNJ5juHH
|
||||||
|
GKvpNLssEOS2pdWVpdDytQKBgF47WNne2qLPwD2kYN1XbQhh0lavL1VAWV2pDsmi
|
||||||
|
Jio9iOAZkGJQbJSTFAAgwICmx4A2arbalLcd9vlpKhAVVYdtlDI3NFxNMp5ZzjW+
|
||||||
|
sShnFYDwKsqZxgJVM2W2KifDTdrAzT9ERO/9qa2UOEcOXPg+mRvwmkB735NZb9jl
|
||||||
|
KehbAoGBANXDltYtJh28hMopP2sZUEx/4A2fnGjVMxBmpSsrk6irz5u/wdhimlDP
|
||||||
|
+JHZZXrb/OTU2BjCPjCCOj953XUqsCpBvWgGdX8uab0O4Gg8x8ucu/K6/kp0X0jN
|
||||||
|
EWQYaMAXJlLQM+k7bXrABNIGu8BF3JzC9oYjlKiFpLqNOQpfHdbK
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIFCzCCA/OgAwIBAgIQdCYWD0et5QHOJ3OYuF0r2TANBgkqhkiG9w0BAQUFADBw
|
|
||||||
MQswCQYDVQQGEwJVUzELMAkGA1UECAwCT0gxEzARBgNVBAcMCkJ1dHQgQ3JhY2sx
|
|
||||||
IDAeBgNVBAoMF1VuZGVyd2VhciBJbnNwZWN0b3IgIzEyMR0wGwYDVQQLDBRUaGlu
|
|
||||||
ZyBMb29rZXIgRXhwZXJ0czAeFw0yNjA1MDEwMjMzMDhaFw00MTA0MjgwMjMzMDha
|
|
||||||
MFExKTAnBgNVBAMTIGhlYWR3YWl0ZXIudHJ1c3RlZC5tc250di5tc24uY29tMRcw
|
|
||||||
FQYDVQQKEw5aZWZpZSBOZXR3b3JrczELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3
|
|
||||||
DQEBAQUAA4IBDwAwggEKAoIBAQCpSe+Vpv9qwb5aDgMNfWRTeXAGvTzhP+olLX+J
|
|
||||||
2WczAXr4FUSLE4LPyD43se26u4GBvGmKD9512/GZKCtMbKPmfBdIqeq/CF2gx8xh
|
|
||||||
e55qF8OuOdxMukOLXsTmvf4slwp3/N6gyze/PMmX+ku/gbotwPL0sv/9Vf1+PVTY
|
|
||||||
6Fje2EU0ra6xJADeL9gazdl6QBxiJ+py+49SiZMS8N4MICOfklykENmjDoM211W6
|
|
||||||
mIRgRZebxijNiZNFeWeXzjxzAAWi701TDs8ksNHSRBG2pajDZ+XgB8D1T+yXWbPz
|
|
||||||
zylePg6HlG8n+asd43wakF8aER26eCT5hyCb4+SkkRClRXLHAgMBAAGjggG+MIIB
|
|
||||||
ujAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATCC
|
|
||||||
AYkGA1UdEQSCAYAwggF8giBoZWFkd2FpdGVyLnRydXN0ZWQubXNudHYubXNuLmNv
|
|
||||||
bYIZc2cxLnRydXN0ZWQubXNudHYubXNuLmNvbYIZc2cyLnRydXN0ZWQubXNudHYu
|
|
||||||
bXNuLmNvbYIZc2czLnRydXN0ZWQubXNudHYubXNuLmNvbYIZc2c0LnRydXN0ZWQu
|
|
||||||
bXNudHYubXNuLmNvbYINbXNudHYubXNuLmNvbYIWbWFpbC5zZXJ2aWNlcy5saXZl
|
|
||||||
LmNvbYIec3luYy1zZzEudHJ1c3RlZC5tc250di5tc24uY29tgg5sb2dpbi5saXZl
|
|
||||||
LmNvbYIRcG9wdGltaXplLm1zbi5jb22CEWZhdm9yaXRlcy5tc24uY29tghFtZXNz
|
|
||||||
ZW5nZXIubXNuLmNvbYIRbGl2ZWZpbGVzdG9yZS5jb22CFnVzZXJzLnN0b3JhZ2Uu
|
|
||||||
bGl2ZS5jb22CCWcubXNuLmNvbYIXbXNuaWFsb2dpbi5wYXNzcG9ydC5jb22CDW1p
|
|
||||||
bmlzcnYubG9jYWwwDQYJKoZIhvcNAQEFBQADggEBAD9O6j8bWtsX9OGf0kT3u1dy
|
|
||||||
n6F+MQWX+vI4C9131Nso7cf7/+FyPcg17ewKw1MJ33ZpzCqhupAnN1lZPikGnl+t
|
|
||||||
VacegsqI2mX1ycD11s1EleobHLc28uEQHDd79Dwn6fA2/EOijyqsILJHB6kzLjH6
|
|
||||||
DV/sapv4JtNMlKDjfHDhtiI2jtpYTfkoZqjs7WsNmaJBcJ/NgTtl3hFSMiN/MLQ0
|
|
||||||
O9wyrvNheINIJ01trpcgLDpmwCG0lYoa8AOdRZccl0KR7IsdVBcV1ANFguepQXI5
|
|
||||||
dc/VJcFWsYs0puGdhPPZHgiZV4pzmfU+rCM/AoNxDdRBrVSal6Um5YyhlmFtobA=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEowIBAAKCAQEAqUnvlab/asG+Wg4DDX1kU3lwBr084T/qJS1/idlnMwF6+BVE
|
|
||||||
ixOCz8g+N7HturuBgbxpig/eddvxmSgrTGyj5nwXSKnqvwhdoMfMYXueahfDrjnc
|
|
||||||
TLpDi17E5r3+LJcKd/zeoMs3vzzJl/pLv4G6LcDy9LL//VX9fj1U2OhY3thFNK2u
|
|
||||||
sSQA3i/YGs3ZekAcYifqcvuPUomTEvDeDCAjn5JcpBDZow6DNtdVupiEYEWXm8Yo
|
|
||||||
zYmTRXlnl848cwAFou9NUw7PJLDR0kQRtqWow2fl4AfA9U/sl1mz888pXj4Oh5Rv
|
|
||||||
J/mrHeN8GpBfGhEdungk+Ycgm+PkpJEQpUVyxwIDAQABAoIBAAeA8Rr4Irs/wtqU
|
|
||||||
17BXv20LyGXG+74z3oKu/iTFKfyYnAUfDaouqB+z0z4tG+yo/B3XIK0BC8sY3NpD
|
|
||||||
EKPkkfi19B0qD0xnUON5LA//j0l4lI3dtrHpaSJ9nRCvatuANa2sOPzqIiZDVYLB
|
|
||||||
Y8qCkP+yLqn71HSY81dUl4Zgsznr7w0VGXLzJd3nUKYL/FRVpsBPy7uHsUemB8YO
|
|
||||||
mgGIVg7l7SpVX/tzZFnoQyPeiABv3hHTWoquxQfT9fqGQEm66cmzQP8yefaVvw1l
|
|
||||||
Eiikx/QWX2SsXfqXdMmiQUI/y+1WV2XL955e9BFzlH6HrePaj9CIrP3+HLtw1fMZ
|
|
||||||
It20WqECgYEA6FWcM2auqqVWrbee8oaoUT/XlQH8TZndlbWGSsFcQZptVTyB+ZMf
|
|
||||||
DyDBAAh18ix7XVEtm6+2pioBZtou5Vp/RjWM1OI3f0mvVgVi2zFIIy52WrWxTHiv
|
|
||||||
OSZpAv3NZEoiq/K3InrF3RolVklfQFv9TJBLmfNfTVtuEd47eBiLZckCgYEAuohW
|
|
||||||
n0pPQhnBb1OiDv82GfyUVORVX4zcjvFGnTbqHiyH1l3+KNBtWQ2Wy5TsBifhXk5h
|
|
||||||
TbGydO4RePeCaUBf4QFtABFsGH+et7Ci5ALvZBTLbCouvZRcDINnKsF0Wd1ZP9TW
|
|
||||||
F7ToN6q3vkhbHIpY59Il96Ije7F6CwV7SQR0nA8CgYBcgzpffVOvv4Z0RdmU2OnM
|
|
||||||
8I73VoMQo2QIaO/AdJ43wTYn6qAWsO59J52yVawhcnTtA5YVmDIymCdWvSpPSWnE
|
|
||||||
my4o1qsilEStDBgBD+6Zk7atCAxBVwzuxMyr1EQk2yBTN6KUqC6BjBex9CVpizeh
|
|
||||||
dROlibM5Kl753nPvrlZTgQKBgQC0fi6LbgUpafChv6RlrI/2L1B8oID3tz7IVjFE
|
|
||||||
+RkrX12FkWfYqG3WqO6MSark/fv2HBPNcS/EM4TWr3ESVUcxWwbU9QbK4dp71kCY
|
|
||||||
LzrjdbetD1gw+3jiZtgSKCVku2mb+V+8isHU861eQ3deM4R5tQAmEU8SZpY4SfKU
|
|
||||||
oeoQAwKBgDhaHQinuYCa1w9Mzy6/AagSmS9yhXX1zR4OSgvF2lU0hT18CAR6LgOR
|
|
||||||
gHOc6OscxDxheS38dHSIyRqD7F5hu1mO0KsGSCdmWIEKMkge8/yjrg4E4CPB5ICZ
|
|
||||||
RcKZRL/rSQZIsdeVJM9i5FiDUaJUX+cvLJQAC1XLfRrBxCHsY0E1
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
headwaiter.trusted.msntv.msn.com
|
|
||||||
sg1.trusted.msntv.msn.com
|
|
||||||
sg2.trusted.msntv.msn.com
|
|
||||||
sg3.trusted.msntv.msn.com
|
|
||||||
sg4.trusted.msntv.msn.com
|
|
||||||
msntv.msn.com
|
|
||||||
mail.services.live.com
|
|
||||||
sync-sg1.trusted.msntv.msn.com
|
|
||||||
login.live.com
|
|
||||||
poptimize.msn.com
|
|
||||||
favorites.msn.com
|
|
||||||
messenger.msn.com
|
|
||||||
livefilestore.com
|
|
||||||
users.storage.live.com
|
|
||||||
g.msn.com
|
|
||||||
msnialogin.passport.com
|
|
||||||
minisrv.local
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
-----BEGIN DH PARAMETERS-----
|
|
||||||
MIGHAoGBAOjeZEDvMxiY+T4AMUIJ6jPFhflzUwO6EPBc0+Fn3C13WGQgsx9N3Rjg
|
|
||||||
bZsF4Sbqs62+KFTYb5/1PVPSOxyif0CJLRC8VhvCl5CZ2DsS6nJ3sstPxtfhQdn+
|
|
||||||
X1kbvqAbHlvNtE6w5ketHv3gK6y4d9qdVnwicZW3uV1sJ2dg4RfDAgEC
|
|
||||||
-----END DH PARAMETERS-----
|
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
const minisrv_service_file = true;
|
const minisrv_service_file = true;
|
||||||
|
|
||||||
|
const title = minisrv_config.config.hide_minisrv_version ? "zefie's minisrv PC Services" : `zefie minisrv v${minisrv_config.version} PC Services`;
|
||||||
|
|
||||||
headers = `200 OK
|
headers = `200 OK
|
||||||
Content-Type: text/html`
|
Content-Type: text/html`
|
||||||
|
|
||||||
data = `<html>
|
data = `<html>
|
||||||
<head>
|
<head>
|
||||||
<title>zefie minisrv v${minisrv_config.version}</title>
|
<title>${title}</title>
|
||||||
</head>
|
</head>
|
||||||
<body bgcolor="#000000" text="#449944">
|
<body bgcolor="#000000" text="#449944">
|
||||||
<p>
|
<p>
|
||||||
Welcome to the zefie minisrv v${minisrv_config.version} PC Services
|
Welcome to ${title}
|
||||||
</p>
|
</p>
|
||||||
<hr>
|
<hr>
|
||||||
<a href="/viewergen/">WebTV Viewer Generator</a><br>
|
<a href="/viewergen/">WebTV Viewer Generator</a><br>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ data = `<HTML>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function GotoBoxCheck() {
|
function GotoBoxCheck() {
|
||||||
var url = 'https://headwaiter.trusted.msntv.msn.com/connection/boxcheck.html';
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=BoxCheck&purpose=Authorize';
|
||||||
var parms='';
|
var parms='';
|
||||||
parms += 'BoxId=' + tvShell.SystemInfo.BoxIDService + '&';
|
parms += 'BoxId=' + tvShell.SystemInfo.BoxIDService + '&';
|
||||||
parms += 'WANProvider=' + tvShell.ConnectionManager.WANProvider + '&';
|
parms += 'WANProvider=' + tvShell.ConnectionManager.WANProvider + '&';
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
const minisrv_service_file = true;
|
|
||||||
|
|
||||||
// Todo: auth if not guest
|
|
||||||
|
|
||||||
headers = `Content-type: text/html`;
|
|
||||||
|
|
||||||
data = `<html>
|
|
||||||
<head>
|
|
||||||
<title id="title"></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<iframe id="checkmail" style="display:none"></iframe>
|
|
||||||
<script language="javascript">
|
|
||||||
var tvShell = new ActiveXObject("MSNTV.TVShell");
|
|
||||||
var UserManager = tvShell.UserManager;
|
|
||||||
|
|
||||||
var home = "https://sg1.trusted.msntv.msn.com/Home/Home.aspx";
|
|
||||||
tvShell.ConnectionManager.ServiceState = 'Authorized';
|
|
||||||
UserManager.SetCurrentUserIsAuthorized(true);
|
|
||||||
var currentUser = UserManager.CurrentUser;
|
|
||||||
if (currentUser != null) {
|
|
||||||
currentUser.IsAuthorized = true;
|
|
||||||
}
|
|
||||||
var myPanel = tvShell.PanelManager.Item('main');
|
|
||||||
if (myPanel) {
|
|
||||||
myPanel.ClearTravelLog();
|
|
||||||
myPanel.NoBackToMe = true;
|
|
||||||
myPanel.GotoURL(home);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
@@ -0,0 +1,444 @@
|
|||||||
|
const minisrv_service_file = true;
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
// Sorry Zef :kek
|
||||||
|
// https://git.computernewb.com/yellows111/msnp-wiki/src/branch/master/docs/services/rst.md
|
||||||
|
// the RST_ cookie stuff was code that was temp until we had proper token authentication
|
||||||
|
const NS = {
|
||||||
|
SOAP: "http://schemas.xmlsoap.org/soap/envelope/",
|
||||||
|
WSSE: "http://schemas.xmlsoap.org/ws/2003/06/secext",
|
||||||
|
WSP: "http://schemas.xmlsoap.org/ws/2002/12/policy",
|
||||||
|
WSU: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
|
||||||
|
WSA: "http://schemas.xmlsoap.org/ws/2004/03/addressing",
|
||||||
|
WST: "http://schemas.xmlsoap.org/ws/2004/04/trust",
|
||||||
|
PSF: "http://schemas.microsoft.com/Passport/SoapServices/SOAPFault",
|
||||||
|
ENC: "http://www.w3.org/2001/04/xmlenc#",
|
||||||
|
DS: "http://www.w3.org/2000/09/xmldsig#"
|
||||||
|
};
|
||||||
|
|
||||||
|
function getCookie(cookieString, name) {
|
||||||
|
if (!cookieString) return null;
|
||||||
|
const match = cookieString.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
|
||||||
|
return match ? decodeURIComponent(match[1]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCookie(name, value, options = {}) {
|
||||||
|
const cookie = `${name}=${encodeURIComponent(value)}`;
|
||||||
|
const path = options.path || '/';
|
||||||
|
const expires = options.expires || '';
|
||||||
|
return `${cookie}; path=${path}${expires ? `; expires=${expires}` : ''}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTime(dt) {
|
||||||
|
return dt.toISOString().replace(/\.\d{3}Z$/, 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClientIP() {
|
||||||
|
const forwarded = request_headers['x-forwarded-for'];
|
||||||
|
if (forwarded) {
|
||||||
|
const ips = forwarded.split(',');
|
||||||
|
return ips[0].trim();
|
||||||
|
}
|
||||||
|
return request_headers['x-real-ip'] || '127.0.0.1';
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateRandomToken(userId, appliesTo, isLegacy = false) {
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const randomPart = crypto.randomBytes(32).toString('hex');
|
||||||
|
|
||||||
|
if (isLegacy) {
|
||||||
|
const tokenData = `${userId}|${appliesTo}|${timestamp}|${randomPart}`;
|
||||||
|
return crypto.createHash('sha256').update(tokenData).digest('hex');
|
||||||
|
} else {
|
||||||
|
const tokenData = {
|
||||||
|
uid: userId,
|
||||||
|
app: appliesTo,
|
||||||
|
ts: timestamp,
|
||||||
|
rand: randomPart,
|
||||||
|
ver: '1.0'
|
||||||
|
};
|
||||||
|
return Buffer.from(JSON.stringify(tokenData)).toString('base64');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractXmlValue(xml, elementName) {
|
||||||
|
if (!xml) return null;
|
||||||
|
|
||||||
|
const patterns = [
|
||||||
|
new RegExp(`<${elementName}>([\\s\\S]*?)</${elementName}>`, 'i'),
|
||||||
|
new RegExp(`<wsse:${elementName}>([\\s\\S]*?)</wsse:${elementName}>`, 'i'),
|
||||||
|
new RegExp(`<wst:${elementName}>([\\s\\S]*?)</wst:${elementName}>`, 'i'),
|
||||||
|
new RegExp(`<ps:${elementName}>([\\s\\S]*?)</ps:${elementName}>`, 'i')
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const regex of patterns) {
|
||||||
|
const match = xml.match(regex);
|
||||||
|
if (match && match[1]) {
|
||||||
|
let value = match[1].trim();
|
||||||
|
value = value.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractTokenFromCipherValue(xml) {
|
||||||
|
if (!xml) return null;
|
||||||
|
|
||||||
|
const cipherRegex = /<CipherValue>([\s\S]*?)<\/CipherValue>/gi;
|
||||||
|
let match;
|
||||||
|
let token = null;
|
||||||
|
|
||||||
|
while ((match = cipherRegex.exec(xml)) !== null) {
|
||||||
|
let cipherValue = match[1].trim();
|
||||||
|
if (cipherValue && cipherValue.length > 0) {
|
||||||
|
token = cipherValue;
|
||||||
|
console.log("Found CipherValue token:", token.substring(0, 50) + "...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateTokenAndGetUser(token) {
|
||||||
|
try {
|
||||||
|
let userId = null;
|
||||||
|
let email = null;
|
||||||
|
|
||||||
|
if (request_headers.cookie) {
|
||||||
|
userId = getCookie(request_headers.cookie, 'RST_Auth');
|
||||||
|
email = getCookie(request_headers.cookie, 'RST_Email');
|
||||||
|
if (!email) email = getCookie(request_headers.cookie, 'rst_email');
|
||||||
|
if (!email) email = getCookie(request_headers.cookie, 'rst_username');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
userId = crypto.createHash('md5').update(token).digest('hex');
|
||||||
|
email = `user_${userId.substring(0, 8)}@example.com`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Token validated - UserId: ${userId}, Email: ${email}`);
|
||||||
|
return { success: true, userId, email };
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Token validation error:", error);
|
||||||
|
return { success: false, userId: null, email: null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateErrorResponse(errorCode, errorText) {
|
||||||
|
const now = formatDateTime(new Date());
|
||||||
|
headers = `Status: 200 OK
|
||||||
|
Content-type: text/xml; charset=utf-8`;
|
||||||
|
|
||||||
|
return `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<S:Envelope xmlns:S="${NS.SOAP}" xmlns:psf="${NS.PSF}">
|
||||||
|
<S:Header>
|
||||||
|
<psf:pp>
|
||||||
|
<psf:serverVersion>1</psf:serverVersion>
|
||||||
|
<psf:authstate>0x80048800</psf:authstate>
|
||||||
|
<psf:reqstatus>${errorCode}</psf:reqstatus>
|
||||||
|
<psf:serverInfo Path="Live1" RollingUpgradeState="ExclusiveNew" LocVersion="0" ServerTime="${now}">
|
||||||
|
NOBELLIUM 16.0.30846.6
|
||||||
|
</psf:serverInfo>
|
||||||
|
<psf:cookies></psf:cookies>
|
||||||
|
<psf:response></psf:response>
|
||||||
|
</psf:pp>
|
||||||
|
</S:Header>
|
||||||
|
<S:Body>
|
||||||
|
<S:Fault>
|
||||||
|
<S:Code>
|
||||||
|
<S:Value>S:Sender</S:Value>
|
||||||
|
<S:Subcode>
|
||||||
|
<S:Value>wst:FailedAuthentication</S:Value>
|
||||||
|
</S:Subcode>
|
||||||
|
</S:Code>
|
||||||
|
<S:Reason>
|
||||||
|
<S:Text xml:lang="en-US">Authentication Failure</S:Text>
|
||||||
|
</S:Reason>
|
||||||
|
<S:Detail>
|
||||||
|
<psf:error>
|
||||||
|
<psf:value>${errorCode}</psf:value>
|
||||||
|
<psf:internalerror>
|
||||||
|
<psf:code>0x80041012</psf:code>
|
||||||
|
<psf:text>${errorText}</psf:text>
|
||||||
|
</psf:internalerror>
|
||||||
|
</psf:error>
|
||||||
|
</S:Detail>
|
||||||
|
</S:Fault>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateSuccessResponse(requestBody, userId, email, firstName, lastName) {
|
||||||
|
const now = new Date();
|
||||||
|
const tomorrow = new Date(now.getTime() + 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
const createdTime = formatDateTime(now);
|
||||||
|
const expiresTime = formatDateTime(tomorrow);
|
||||||
|
|
||||||
|
const puid = crypto.randomBytes(16).toString('hex').toUpperCase();
|
||||||
|
const cid = crypto.randomBytes(8).toString('hex').toUpperCase();
|
||||||
|
|
||||||
|
const safeFirstName = firstName || email.split('@')[0] || "User";
|
||||||
|
const safeLastName = lastName || "User";
|
||||||
|
const clientIp = getClientIP();
|
||||||
|
|
||||||
|
const rstRegex = /<wst:RequestSecurityToken[\s\S]*?<\/wst:RequestSecurityToken>/gi;
|
||||||
|
const responses = [];
|
||||||
|
let match;
|
||||||
|
let foundRst = false;
|
||||||
|
let rstIndex = 0;
|
||||||
|
|
||||||
|
while ((match = rstRegex.exec(requestBody)) !== null) {
|
||||||
|
foundRst = true;
|
||||||
|
const rstBlock = match[0];
|
||||||
|
|
||||||
|
const addressMatch = rstBlock.match(/<wsa:Address>(.*?)<\/wsa:Address>/i);
|
||||||
|
let appliesTo = addressMatch ? addressMatch[1] : "urn:passport:compact";
|
||||||
|
|
||||||
|
const policyMatch = rstBlock.match(/<wsse:PolicyReference\s+URI="([^"]+)"/i);
|
||||||
|
const policy = policyMatch ? policyMatch[1] : null;
|
||||||
|
|
||||||
|
const isLegacy = appliesTo.includes("Passport.NET");
|
||||||
|
const tokenType = isLegacy ? "urn:passport:legacy" : "urn:passport:compact";
|
||||||
|
const needsProofToken = policy === "MBI_KEY_OLD";
|
||||||
|
|
||||||
|
const token = generateRandomToken(userId, appliesTo, isLegacy);
|
||||||
|
const tokenId = isLegacy ? `BinaryDAToken${rstIndex}` : `Compact${rstIndex}`;
|
||||||
|
const binarySecret = crypto.randomBytes(32).toString('base64');
|
||||||
|
|
||||||
|
let requestedSecurityToken;
|
||||||
|
if (isLegacy) {
|
||||||
|
requestedSecurityToken = `
|
||||||
|
<wst:RequestedSecurityToken>
|
||||||
|
<EncryptedData xmlns="${NS.ENC}" Id="${tokenId}" Type="http://www.w3.org/2001/04/xmlenc#Element">
|
||||||
|
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
|
||||||
|
<ds:KeyInfo xmlns:ds="${NS.DS}">
|
||||||
|
<ds:KeyName>http://Passport.NET/STS</ds:KeyName>
|
||||||
|
</ds:KeyInfo>
|
||||||
|
<CipherData>
|
||||||
|
<CipherValue>${token}</CipherValue>
|
||||||
|
</CipherData>
|
||||||
|
</EncryptedData>
|
||||||
|
</wst:RequestedSecurityToken>`;
|
||||||
|
} else {
|
||||||
|
let tokenValue = `t=${token}`;
|
||||||
|
if (needsProofToken) {
|
||||||
|
tokenValue += `&p=profile`;
|
||||||
|
}
|
||||||
|
requestedSecurityToken = `
|
||||||
|
<wst:RequestedSecurityToken>
|
||||||
|
<wsse:BinarySecurityToken Id="${tokenId}">${tokenValue}</wsse:BinarySecurityToken>
|
||||||
|
</wst:RequestedSecurityToken>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let responseXml = `
|
||||||
|
<wst:RequestSecurityTokenResponse>
|
||||||
|
<wst:TokenType>${tokenType}</wst:TokenType>
|
||||||
|
<wsp:AppliesTo xmlns:wsa="${NS.WSA}">
|
||||||
|
<wsa:EndpointReference>
|
||||||
|
<wsa:Address>${appliesTo}</wsa:Address>
|
||||||
|
</wsa:EndpointReference>
|
||||||
|
</wsp:AppliesTo>
|
||||||
|
<wst:LifeTime>
|
||||||
|
<wsu:Created>${createdTime}</wsu:Created>
|
||||||
|
<wsu:Expires>${expiresTime}</wsu:Expires>
|
||||||
|
</wst:LifeTime>
|
||||||
|
${requestedSecurityToken}
|
||||||
|
<wst:RequestedTokenReference>
|
||||||
|
<wsse:KeyIdentifier ValueType="${tokenType}"/>
|
||||||
|
<wsse:Reference URI="#${tokenId}"/>
|
||||||
|
</wst:RequestedTokenReference>`;
|
||||||
|
|
||||||
|
if (needsProofToken || isLegacy) {
|
||||||
|
responseXml += `
|
||||||
|
<wst:RequestedProofToken>
|
||||||
|
<wst:BinarySecret>${binarySecret}</wst:BinarySecret>
|
||||||
|
</wst:RequestedProofToken>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
responseXml += `
|
||||||
|
</wst:RequestSecurityTokenResponse>`;
|
||||||
|
|
||||||
|
responses.push(responseXml);
|
||||||
|
rstIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundRst) {
|
||||||
|
const defaultToken = generateRandomToken(userId, "urn:passport:compact", false);
|
||||||
|
responses.push(`
|
||||||
|
<wst:RequestSecurityTokenResponse>
|
||||||
|
<wst:TokenType>urn:passport:compact</wst:TokenType>
|
||||||
|
<wst:RequestedSecurityToken>
|
||||||
|
<wsse:BinarySecurityToken Id="Compact0">t=${defaultToken}</wsse:BinarySecurityToken>
|
||||||
|
</wst:RequestedSecurityToken>
|
||||||
|
<wst:LifeTime>
|
||||||
|
<wsu:Created>${createdTime}</wsu:Created>
|
||||||
|
<wsu:Expires>${expiresTime}</wsu:Expires>
|
||||||
|
</wst:LifeTime>
|
||||||
|
</wst:RequestSecurityTokenResponse>`);
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = `Status: 200 OK
|
||||||
|
Content-type: text/xml; charset=utf-8
|
||||||
|
Set-Cookie: RST_Auth=${userId}; path=/; HttpOnly
|
||||||
|
Set-Cookie: RST_Email=${email}; path=/`;
|
||||||
|
|
||||||
|
return `<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<S:Envelope xmlns:S="${NS.SOAP}">
|
||||||
|
<S:Header>
|
||||||
|
<psf:pp xmlns:psf="${NS.PSF}">
|
||||||
|
<psf:serverVersion>1</psf:serverVersion>
|
||||||
|
<psf:PUID>${puid}</psf:PUID>
|
||||||
|
<psf:configVersion>16.000.26889.00</psf:configVersion>
|
||||||
|
<psf:uiVersion>3.100.2179.0</psf:uiVersion>
|
||||||
|
<psf:mobileConfigVersion>16.000.26208.0</psf:mobileConfigVersion>
|
||||||
|
<psf:authstate>0x48803</psf:authstate>
|
||||||
|
<psf:reqstatus>0x0</psf:reqstatus>
|
||||||
|
<psf:serverInfo Path="Live1" RollingUpgradeState="ExclusiveNew" LocVersion="0" ServerTime="${now.toISOString()}">
|
||||||
|
NOBELLIUM 16.0.30846.6
|
||||||
|
</psf:serverInfo>
|
||||||
|
<psf:cookies></psf:cookies>
|
||||||
|
<psf:browserCookies>
|
||||||
|
<psf:browserCookie Name="MH" URL="http://www.msn.com">MSFT; path=/; domain=.msn.com; expires=Wed, 30-Dec-2037 16:00:00 GMT</psf:browserCookie>
|
||||||
|
<psf:browserCookie Name="MH" URL="http://www.live.com">MSFT; path=/; domain=.live.com; expires=Wed, 30-Dec-2037 16:00:00 GMT</psf:browserCookie>
|
||||||
|
</psf:browserCookies>
|
||||||
|
<psf:credProperties>
|
||||||
|
<psf:credProperty Name="MainBrandID">MSFT</psf:credProperty>
|
||||||
|
<psf:credProperty Name="IsWinLiveUser">true</psf:credProperty>
|
||||||
|
<psf:credProperty Name="CID">${cid}</psf:credProperty>
|
||||||
|
<psf:credProperty Name="AuthMembername">${email}</psf:credProperty>
|
||||||
|
<psf:credProperty Name="Country">US</psf:credProperty>
|
||||||
|
<psf:credProperty Name="Language">1033</psf:credProperty>
|
||||||
|
<psf:credProperty Name="FirstName">${safeFirstName}</psf:credProperty>
|
||||||
|
<psf:credProperty Name="LastName">${safeLastName}</psf:credProperty>
|
||||||
|
<psf:credProperty Name="Flags">40100643</psf:credProperty>
|
||||||
|
<psf:credProperty Name="IP">${clientIp}</psf:credProperty>
|
||||||
|
</psf:credProperties>
|
||||||
|
<psf:extProperties>
|
||||||
|
<psf:extProperty Name="CID">${cid}</psf:extProperty>
|
||||||
|
</psf:extProperties>
|
||||||
|
<psf:response></psf:response>
|
||||||
|
</psf:pp>
|
||||||
|
</S:Header>
|
||||||
|
<S:Body>
|
||||||
|
<wst:RequestSecurityTokenResponseCollection
|
||||||
|
xmlns:wst="${NS.WST}"
|
||||||
|
xmlns:wsse="${NS.WSSE}"
|
||||||
|
xmlns:wsu="${NS.WSU}"
|
||||||
|
xmlns:wsp="${NS.WSP}"
|
||||||
|
xmlns:psf="${NS.PSF}">
|
||||||
|
${responses.join('\n ')}
|
||||||
|
</wst:RequestSecurityTokenResponseCollection>
|
||||||
|
</S:Body>
|
||||||
|
</S:Envelope>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function rstHandler() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Get POST data
|
||||||
|
let requestBody = '';
|
||||||
|
if (request_headers.post_data) {
|
||||||
|
if (Buffer.isBuffer(request_headers.post_data)) {
|
||||||
|
requestBody = request_headers.post_data.toString('utf8');
|
||||||
|
} else if (typeof request_headers.post_data === 'string') {
|
||||||
|
requestBody = request_headers.post_data;
|
||||||
|
} else if (typeof request_headers.post_data === 'object') {
|
||||||
|
requestBody = JSON.stringify(request_headers.post_data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("No post_data found. Available keys:", Object.keys(request_headers));
|
||||||
|
return generateErrorResponse("0x80048820", "No POST data received");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!requestBody || requestBody.trim() === '') {
|
||||||
|
console.log("Empty request body");
|
||||||
|
return generateErrorResponse("0x80048820", "Empty request body");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authentication
|
||||||
|
let email = extractXmlValue(requestBody, 'Username');
|
||||||
|
let password = extractXmlValue(requestBody, 'Password');
|
||||||
|
|
||||||
|
let userId = null;
|
||||||
|
let userEmail = null;
|
||||||
|
let firstName = "User";
|
||||||
|
let lastName = "User";
|
||||||
|
|
||||||
|
if ((!email || !password) && requestBody.includes('CipherValue')) {
|
||||||
|
console.log("No username/password found, trying token authentication...");
|
||||||
|
const token = extractTokenFromCipherValue(requestBody);
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
const tokenValidation = validateTokenAndGetUser(token);
|
||||||
|
if (tokenValidation.success) {
|
||||||
|
userId = tokenValidation.userId;
|
||||||
|
userEmail = tokenValidation.email;
|
||||||
|
console.log(`Token authentication successful for: ${userEmail} (${userId})`);
|
||||||
|
|
||||||
|
if (request_headers.cookie) {
|
||||||
|
const cookieEmail = getCookie(request_headers.cookie, 'RST_Email');
|
||||||
|
const cookieUsername = getCookie(request_headers.cookie, 'rst_username');
|
||||||
|
if (cookieEmail) userEmail = cookieEmail;
|
||||||
|
if (cookieUsername) firstName = cookieUsername;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("Token validation failed");
|
||||||
|
return generateErrorResponse("0x80048821", "Invalid token");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("No token found in CipherValue");
|
||||||
|
return generateErrorResponse("0x80048820", "Missing credentials/token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (email && password) {
|
||||||
|
console.log(`Extracted - Email: ${email}, Password: ${password ? '***' : 'empty'}`);
|
||||||
|
|
||||||
|
if (email && email.indexOf('@') < 0) {
|
||||||
|
const domain = (minisrv_config && minisrv_config.config && minisrv_config.config.domain_name) || 'wtv.zefie.com';
|
||||||
|
email = `${email}@${domain}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
userEmail = email;
|
||||||
|
firstName = email.split('@')[0];
|
||||||
|
userId = crypto.createHash('md5').update(email).digest('hex');
|
||||||
|
console.log(`Authentication successful for: ${userEmail} (${userId})`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Missing both credentials and token");
|
||||||
|
return generateErrorResponse("0x80048820", "Missing credentials/token");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userId || !userEmail) {
|
||||||
|
console.log("Failed to get user identity");
|
||||||
|
return generateErrorResponse("0x80048821", "User identity not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const cookieHeaders = [
|
||||||
|
setCookie('rst_email', userEmail, { path: '/' }),
|
||||||
|
setCookie('rst_username', firstName, { path: '/' }),
|
||||||
|
setCookie('rst_authenticated', 'true', { path: '/', expires: 'Wed, 30-Dec-2037 16:00:00 GMT' })
|
||||||
|
];
|
||||||
|
|
||||||
|
const response = generateSuccessResponse(requestBody, userId, userEmail, firstName, lastName);
|
||||||
|
|
||||||
|
for (const cookie of cookieHeaders) {
|
||||||
|
headers += `\nSet-Cookie: ${cookie}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("RST Handler Error:", error);
|
||||||
|
console.error("Error stack:", error.stack);
|
||||||
|
return generateErrorResponse("0x80048820", `Internal error: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = rstHandler();
|
||||||
|
if (result) {
|
||||||
|
data = result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const minisrv_service_file = true;
|
||||||
|
|
||||||
|
// Wrong email return: <LoginResponse Success="false"><Error Code="e5b"/></LoginResponse>
|
||||||
|
// Wrong Password return: <LoginResponse Success="false"><Error Code="e5a"/></LoginResponse>
|
||||||
|
|
||||||
|
// Example Client request: <LoginRequest><ClientInfo name="MSNTV" version="1.35"/><User><SignInName>example@example.com</SignInName><Password>example</Password><SavePassword>false</SavePassword></User><DAOption>1</DAOption><TargetOption>1</TargetOption></LoginRequest>
|
||||||
|
|
||||||
|
data = `<LoginResponse Success="true"><TnP>t=Disabled&p=Disabled</TnP></LoginResponse>`; // T and P cant be nulled they have to have some content in it
|
||||||
|
|
||||||
|
headers = `200 OK
|
||||||
|
Content-Type: text/xml`;
|
||||||
|
|
||||||
|
console.log(request_headers.query);
|
||||||
@@ -4,15 +4,15 @@ const minisrv_service_file = true;
|
|||||||
const WeatherCity = 'Your City';
|
const WeatherCity = 'Your City';
|
||||||
const WeatherTemp = '72';
|
const WeatherTemp = '72';
|
||||||
const WeatherDescription = 'Sunny';
|
const WeatherDescription = 'Sunny';
|
||||||
const WeatherIcon = '/Pages/Home/Weather/26.gif';
|
const WeatherIcon = '/Home/Weather/26.gif';
|
||||||
|
|
||||||
// News headlines
|
// News headlines
|
||||||
const NewsLink1 = 'http://sg1.trusted.msntv.msn.com/Pages/Tricks/he.mp3';
|
const NewsLink1 = '';
|
||||||
const NewsLink2 = 'http://sg1.trusted.msntv.msn.com/Pages/Tricks/pokemon-black-2.mp3';
|
const NewsLink2 = '';
|
||||||
const NewsLink3 = 'http://sg1.trusted.msntv.msn.com/Pages/Tricks/he.mp3';
|
const NewsLink3 = '';
|
||||||
const NewsTitle1 = 'Ryder Smells';
|
const NewsTitle1 = '...';
|
||||||
const NewsTitle2 = 'Ryder Smells';
|
const NewsTitle2 = '...';
|
||||||
const NewsTitle3 = 'Ryder Smells';
|
const NewsTitle3 = '...';
|
||||||
|
|
||||||
headers = `200 OK
|
headers = `200 OK
|
||||||
Content-type: text/html`;
|
Content-type: text/html`;
|
||||||
@@ -22,7 +22,7 @@ data = `<html xmlns:msntv>
|
|||||||
|
|
||||||
<head><title>Home</title>
|
<head><title>Home</title>
|
||||||
<?import namespace="msntv" implementation="HTC/Shared/CustomButton.htc"?>
|
<?import namespace="msntv" implementation="HTC/Shared/CustomButton.htc"?>
|
||||||
<?import namespace="msntv" implementation="/Pages/Home/Shared/BaseClient/HTCTransforms/en-us/LoopingDIV.htc"?>
|
<?import namespace="msntv" implementation="/Home/Shared/BaseClient/HTCTransforms/en-us/LoopingDIV.htc"?>
|
||||||
|
|
||||||
<script src="/Include/2.0.261.900/localhost-1700/Shared/BaseClient/JsTransforms/en-us/PaneHelp.js" language="javascript" defer="true"></script>
|
<script src="/Include/2.0.261.900/localhost-1700/Shared/BaseClient/JsTransforms/en-us/PaneHelp.js" language="javascript" defer="true"></script>
|
||||||
<link href="/Include/2.0.261.778/localhost-1700/Home/Anduril/CssTransforms/en-us/Home.css" type="text/css" rel="StyleSheet">
|
<link href="/Include/2.0.261.778/localhost-1700/Home/Anduril/CssTransforms/en-us/Home.css" type="text/css" rel="StyleSheet">
|
||||||
@@ -187,7 +187,7 @@ data = `<html xmlns:msntv>
|
|||||||
|
|
||||||
<div class="promoImgDiv">
|
<div class="promoImgDiv">
|
||||||
<div style="position:absolute; left:0px; top:0px">
|
<div style="position:absolute; left:0px; top:0px">
|
||||||
<img id="PromoImageID" width="178" height="135" border="0" hspace="0" alt="Promotional Image" src="/Pages/Home/ads/webtv3.gif">
|
<img id="PromoImageID" width="178" height="135" border="0" hspace="0" alt="Promotional Image" src="/Home/ads/webtv3.gif">
|
||||||
</div>
|
</div>
|
||||||
<div style="position:absolute; left:5px; top:0px"><a id="PromoImageLinkID" href="">
|
<div style="position:absolute; left:5px; top:0px"><a id="PromoImageLinkID" href="">
|
||||||
<table width="173" height="135"><tr><td></td></tr></table></a>
|
<table width="173" height="135"><tr><td></td></tr></table></a>
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 771 B |
|
After Width: | Height: | Size: 685 B |
|
After Width: | Height: | Size: 806 B |
|
After Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 383 B |
|
After Width: | Height: | Size: 403 B |
@@ -75,29 +75,42 @@ data = `<html xmlns:msntv>
|
|||||||
</STYLE>
|
</STYLE>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var tvShell = new ActiveXObject("MSNTV.TVShell");
|
var TVShell = new ActiveXObject("MSNTV.TVShell");
|
||||||
tvShell.UserManager.SetCurrentUserIsAuthorized(false);
|
TVShell.UserManager.SetCurrentUserIsAuthorized(false);
|
||||||
|
|
||||||
function AddUser() {
|
function AddUser() {
|
||||||
var user = tvShell.UserManager.AddNew("${username}");
|
var user = TVShell.UserManager.AddNew("${username}");
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
user.IsPersistent = true;
|
user.IsPersistent = true;
|
||||||
|
user.setAttribute("GuestUser", false);
|
||||||
|
var dt = new Date();
|
||||||
|
|
||||||
|
TVShell.UserManager.LastLoginTime = dt.getTime() / 1000 + dt.getTimezoneOffset() * 60;
|
||||||
|
|
||||||
|
TVShell.UserManager.OfflineAppMaxAccessDays = 20;
|
||||||
|
TVShell.UserManager.OfflineAppMaxAccessTimes = 20;
|
||||||
|
TVShell.UserManager.CurrentUser = user;
|
||||||
|
user.LargeIcon = "msntv:/SignInPics/big/${picture}.png";
|
||||||
|
user.SmallIcon = "msntv:/SignInPics/small/${picture}.gif";
|
||||||
|
TVShell.UserManager.Save();
|
||||||
} else {
|
} else {
|
||||||
user = tvShell.UserManager.Item("${username}");
|
user = TVShell.UserManager.Item("${username}");
|
||||||
if (user && !user.IsPersistent) {
|
if (user && !user.IsPersistent) {
|
||||||
user.IsPersistent = true;
|
user.IsPersistent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user) {
|
|
||||||
user.LargeIcon = "msntv:/tvshell/images/${picture}.png";
|
|
||||||
user.SmallIcon = "msntv:/tvshell/images/${picture}.gif";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GobacktoSignon() {
|
||||||
|
entry = TVShell.ServiceList.Add('connection::login');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Authorize';
|
||||||
|
entry.Description = '${minisrv_config.config.service_name}/sg1 [${minisrv_config.config.hide_minisrv_version ? "beta" : minisrv_version_string.replace("zefie's wtv minisrv ","")}]';
|
||||||
|
TVShell.ServiceList.Save();
|
||||||
|
TVShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||||
}
|
}
|
||||||
|
|
||||||
AddUser();
|
AddUser();
|
||||||
tvShell.UserManager.Save();
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -116,7 +129,7 @@ data = `<html xmlns:msntv>
|
|||||||
<table class="ApolloIcons" tabindex="-1">
|
<table class="ApolloIcons" tabindex="-1">
|
||||||
<tr height="70">
|
<tr height="70">
|
||||||
<td tabindex="-1">
|
<td tabindex="-1">
|
||||||
<span style='display:inline-block; width:142px; height:158px; behavior:url(#default#alphaImageLoader); src:url(msntv:/SignInPics/Big/${picture}.png);'></span>
|
<span style='display:inline-block; width:142px; height:158px; behavior:url(#default#alphaImageLoader); src:url(msntv:/SignInPics/big/${picture}.png);'></span>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -139,7 +152,7 @@ data = `<html xmlns:msntv>
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<msntv:CustomButton id="continue" label="Continue" href="/Home/Home.aspx" />
|
<msntv:CustomButton id="continue" label="Continue" onclick="GobacktoSignon()" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ if (!password && request_headers.cookie) {
|
|||||||
if (pm) password = decodeURIComponent(pm[1]);
|
if (pm) password = decodeURIComponent(pm[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (email && email.indexOf('@') < 0) email += "@"+minisrv_config.config.service_name;
|
if (email && email.indexOf('@') < 0) email += "@"+minisrv_config.config.domain_name;
|
||||||
let userAvail = false;
|
let userAvail = false;
|
||||||
|
|
||||||
if (email) {
|
if (email) {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ data = `<HTML xmlns:msntv>
|
|||||||
<p>Type your minisrv username:</p>
|
<p>Type your minisrv username:</p>
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<td><input type="text" id="email" class="inputText" name="email" maxlength="32" size="25"> </td>
|
<td><input type="text" id="email" class="inputText" name="email" maxlength="32" size="25"> </td>
|
||||||
<p style="display: inline; bottom: 4px; position: relative;">@${minisrv_config.config.service_name}</p>
|
<p style="display: inline; bottom: 4px; position: relative;">@${minisrv_config.config.domain_name}</p>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
<p>Next, enter a password:</p>
|
<p>Next, enter a password:</p>
|
||||||
|
|||||||
@@ -0,0 +1,706 @@
|
|||||||
|
const minisrv_service_file = true;
|
||||||
|
|
||||||
|
// Get the phase parameter from the query string
|
||||||
|
let phase = request_headers.query.phase;
|
||||||
|
if (Array.isArray(phase)) phase = phase[0];
|
||||||
|
|
||||||
|
let BoxId = request_headers.query.BoxId;
|
||||||
|
if (Array.isArray(BoxId)) BoxId = BoxId[0];
|
||||||
|
let clientIp = socket.remoteAddress;
|
||||||
|
let banned = false;
|
||||||
|
let sessionId = null;
|
||||||
|
ServiceDomain = minisrv_config.config.domain_name;
|
||||||
|
|
||||||
|
// Use the shared MSNTV2 helper injected by WTV-MSNTV2 VM context.
|
||||||
|
if (BoxId) {
|
||||||
|
if (!BoxId || BoxId.length != 20 || !/^\d+$/.test(BoxId))
|
||||||
|
{
|
||||||
|
console.warn("Invalid BoxId format "+BoxId+" from "+clientIp);
|
||||||
|
banned = true;
|
||||||
|
} else {
|
||||||
|
sessionId = encodeSessionID(BoxId);
|
||||||
|
}
|
||||||
|
} else if (request_headers.cookie && request_headers.cookie.SessionID) {
|
||||||
|
BoxID = decodeSessionID(request_headers.cookie.SessionID);
|
||||||
|
sessionId = request_headers.cookie.SessionID;
|
||||||
|
} else {
|
||||||
|
console.warn("No BoxId provided by client "+clientIp);
|
||||||
|
banned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sessionId && !banned) {
|
||||||
|
banned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session_data && BoxId) {
|
||||||
|
console.log("Missing session_data for BoxId %s", BoxId);
|
||||||
|
}
|
||||||
|
|
||||||
|
let registered = false;
|
||||||
|
let username = '';
|
||||||
|
let Profile_Picture
|
||||||
|
if (session_data) {
|
||||||
|
registered = session_data.isRegistered();
|
||||||
|
if (registered) {
|
||||||
|
username = session_data.getSessionData("subscriber_username") || '';
|
||||||
|
Profile_Picture = session_data.getSessionData('ProfilePicture') || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current UTC time
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
const timeData = {
|
||||||
|
hh: now.getUTCHours(),
|
||||||
|
mm: now.getUTCMinutes(),
|
||||||
|
ss: now.getUTCSeconds(),
|
||||||
|
mo: now.getUTCMonth() + 1,
|
||||||
|
dd: now.getUTCDate(),
|
||||||
|
yyyy: now.getUTCFullYear()
|
||||||
|
};
|
||||||
|
|
||||||
|
const timezoneMap = {
|
||||||
|
"UTC": {
|
||||||
|
standardName: "UTC",
|
||||||
|
standardOffset: 0,
|
||||||
|
daylightName: "UTC",
|
||||||
|
daylightOffset: 0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
standardName,
|
||||||
|
standardOffset,
|
||||||
|
daylightName,
|
||||||
|
daylightOffset
|
||||||
|
} = timezoneMap["UTC"];
|
||||||
|
|
||||||
|
// Set session cookie on the client
|
||||||
|
if (sessionId) {
|
||||||
|
setCookie('SessionID', sessionId, { path: '/' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle different phases
|
||||||
|
switch (phase) {
|
||||||
|
case "Bootstrap":
|
||||||
|
headers = `200 OK
|
||||||
|
Content-type: text/html`;
|
||||||
|
|
||||||
|
data = `<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<title id="title"></title>
|
||||||
|
</HEAD>
|
||||||
|
<body>
|
||||||
|
<iframe id=checkmail style="display:none"></iframe>
|
||||||
|
<script language="javascript">
|
||||||
|
var TVShell = new ActiveXObject("MSNTV.TVShell");
|
||||||
|
function IsNightlyEnabled() {
|
||||||
|
var taskScheduler = TVShell.TaskScheduler;
|
||||||
|
var updateTask = null;
|
||||||
|
for (var i = 0; ((i < taskScheduler.Count) && (updateTask == null)); i++) {
|
||||||
|
if (taskScheduler.Item(i).Caller == 'NightlyUpdate') {
|
||||||
|
updateTask = taskScheduler.Item(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updateTask != null) {
|
||||||
|
return(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function GotoBoxCheck() {
|
||||||
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=BoxCheck&purpose=Authorize';
|
||||||
|
var parms='';
|
||||||
|
parms += 'BoxId=' + TVShell.SystemInfo.BoxIDService + '&';
|
||||||
|
parms += 'WANProvider=' + TVShell.ConnectionManager.WANProvider + '&';
|
||||||
|
parms += 'version=' + encodeURIComponent(TVShell.SystemInfo.LastVersion) + '&';
|
||||||
|
if ((TVShell.ConnectionManager.MSNIAManager != null) && (TVShell.ConnectionManager.MSNIAManager.CurrentConnector != null)) parms += 'ConnectorName=' + encodeURIComponent(TVShell.ConnectionManager.MSNIAManager.CurrentConnector.Name) + '&';
|
||||||
|
if (TVShell.UserManager.CurrentUser != null) parms += 'domain=' + encodeURIComponent(TVShell.UserManager.CurrentUser.Domain) + '&';
|
||||||
|
parms += 'NumRedirects=0&';
|
||||||
|
parms += 'NightlyEnabled=' + IsNightlyEnabled() + '&';
|
||||||
|
parms += 'x=y';
|
||||||
|
var myPanel = TVShell.PanelManager.Item('service');
|
||||||
|
if (myPanel) myPanel.PostToURL(url, parms);
|
||||||
|
}
|
||||||
|
var progressPanel = TVShell.PanelManager.Item('progress');
|
||||||
|
function SetProgress(text, percent) {
|
||||||
|
if (progressPanel) {
|
||||||
|
progressPanel.Document.SetProgressText(text);
|
||||||
|
progressPanel.Document.SetProgressPercent(percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function IsServicePanel() {
|
||||||
|
if ((window.name == null) || ((window.name != null) && (window.name.toLowerCase() != 'service'))) {
|
||||||
|
return(false);
|
||||||
|
}
|
||||||
|
return(true);
|
||||||
|
}
|
||||||
|
function DontContinue() {
|
||||||
|
var currentUser = TVShell.UserManager.CurrentUser;
|
||||||
|
if (currentUser != null && currentUser.IsAuthorized) {
|
||||||
|
window.location.replace(TVShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TVShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!IsServicePanel()) {
|
||||||
|
DontContinue();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
TVShell.MeteringManager.Stop();
|
||||||
|
SetProgress('Please wait while we sign you into MSN TV.', 10);
|
||||||
|
GotoBoxCheck();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</HTML>`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "BoxCheck":
|
||||||
|
|
||||||
|
headers = `200 OK
|
||||||
|
Content-type: text/html`;
|
||||||
|
|
||||||
|
data = `<html>
|
||||||
|
<head>
|
||||||
|
<title id="title"></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe id="checkmail" style="display:none"></iframe>
|
||||||
|
<script src="msntv:/Javascript/TVShell.js" language="javascript"></script>
|
||||||
|
<script src="msntv:/Javascript/ServiceList.js" language="javascript"></script>
|
||||||
|
<script src="msntv:/Javascript/GuestUser.js" language="javascript"></script>
|
||||||
|
<script language="javascript">
|
||||||
|
try {
|
||||||
|
var TVShell = new ActiveXObject("MSNTV.TVShell");
|
||||||
|
var Sink = new ActiveXObject("MSNTV.MultipleEventSink");
|
||||||
|
var email = TVShell.UserManager.EMail;
|
||||||
|
var wanProvider = TVShell.ConnectionManager.WANProvider;
|
||||||
|
|
||||||
|
var banned = ${banned};
|
||||||
|
var registered = ${registered};
|
||||||
|
var username = "${username}";
|
||||||
|
var picture = "${Profile_Picture}";
|
||||||
|
var ServiceDomain = "${ServiceDomain}";
|
||||||
|
var MSNTVToken = "";
|
||||||
|
var serviceArgs = new Array();
|
||||||
|
var ProductionArgs = new Array("msntv.msn.com", "MBI", 0, 0,
|
||||||
|
"mail.services.live.com", "MBI", 0, 0,
|
||||||
|
"livefilestore.com", "MBI", 0, 0,
|
||||||
|
"messenger.msn.com", "?id=507", 0, 0,
|
||||||
|
"spaces.live.com", "MBI", 0, 0
|
||||||
|
);
|
||||||
|
serviceArgs[0] = ProductionArgs;
|
||||||
|
|
||||||
|
if (!banned) {
|
||||||
|
// DEBUG ONLY! USE WITH CAUTION!
|
||||||
|
TVShell.AddSecretCode(10000); // Power-on for nightly update
|
||||||
|
TVShell.AddSecretCode(10001); // Power-on for nightly email check at anchor time
|
||||||
|
TVShell.AddSecretCode(10002); // Power-on for nightly email check at non-anchor time
|
||||||
|
TVShell.AddSecretCode(77437); // spooky dialing options
|
||||||
|
TVShell.AddSecretCode(93288); // Service Selection Page
|
||||||
|
TVShell.AddSecretCode(6145539); // crash the system
|
||||||
|
TVShell.AddSecretCode(3932397); // update loop test
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIDCRLErrorCode( theCode )
|
||||||
|
{
|
||||||
|
// when high bit is set, it is an error
|
||||||
|
if ( theCode & 0x80000000 )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIDCRLCode( theCode )
|
||||||
|
{
|
||||||
|
return (theCode & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
function CheckForUser(usernameToCheck) {
|
||||||
|
var UserManager = TVShell.UserManager;
|
||||||
|
for (var i=0; i<UserManager.Count; i++) {
|
||||||
|
var user = UserManager.Item(i);
|
||||||
|
if (user == usernameToCheck) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
function DoLogin() {
|
||||||
|
var currentUser = TVShell.UserManager.CurrentUser;
|
||||||
|
if (currentUser == null) {
|
||||||
|
if (banned === true) {
|
||||||
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/banned.html';
|
||||||
|
var myPanel = TVShell.PanelManager.Item('main');
|
||||||
|
if (myPanel) myPanel.GotoURL(url);
|
||||||
|
} else {
|
||||||
|
if (registered === true) {
|
||||||
|
var user = TVShell.UserManager.AddNew(username + '@' + ServiceDomain);
|
||||||
|
if (user) {
|
||||||
|
var useroptions = UserManager.Item(username + '@' + ServiceDomain);
|
||||||
|
useroptions.IsPersistent = true;
|
||||||
|
//user.setAttribute("GuestUser", false);
|
||||||
|
useroptions.LargeIcon = "msntv:/SignInPics/big/"+ picture + ".png";
|
||||||
|
useroptions.SmallIcon = "msntv:/SignInPics/small/"+ picture + ".gif";
|
||||||
|
TVShell.UserManager.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var myPanel = TVShell.PanelManager.Item('main')
|
||||||
|
if (registered === true) {
|
||||||
|
entry = TVShell.ServiceList.Add('connection::login');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Authorize';
|
||||||
|
entry.Description = '${minisrv_config.config.service_name}/sg1 [${minisrv_config.config.hide_minisrv_version ? "beta" : minisrv_version_string.replace("zefie's wtv minisrv ","")}]';
|
||||||
|
TVShell.ServiceList.Save();
|
||||||
|
var signon = TVShell.BuiltinServiceList.Item("SignOn");
|
||||||
|
var panel = TVShell.PanelManager.FocusedPanel;
|
||||||
|
var atLogin = false;
|
||||||
|
TVShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||||
|
if ( signon && panel && panel.Name == "main" )
|
||||||
|
{
|
||||||
|
if ( IsMainPanelOnPage( signon.URL ) ) atLogin = true;
|
||||||
|
}
|
||||||
|
if (!atLogin) {
|
||||||
|
myPanel.ClearTravelLog();
|
||||||
|
myPanel.NoBackToMe = true;
|
||||||
|
GotoSignOn();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (myPanel) myPanel.GotoURL('https://sg1.trusted.msntv.msn.com/Register/Establish-your-MSN-TV-Account.html');
|
||||||
|
}
|
||||||
|
if (myPanel) {
|
||||||
|
myPanel.ClearTravelLog();
|
||||||
|
myPanel.NoBackToMe = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (banned === true) {
|
||||||
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/banned.html';
|
||||||
|
var myPanel = TVShell.PanelManager.Item('service');
|
||||||
|
if (myPanel) myPanel.GotoURL(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentUser != null) {
|
||||||
|
// Check if We can do IDCRL if not fall back to Legacy XMLlogin
|
||||||
|
if (TVShell.LoginManager.IDCRLInitialize) {
|
||||||
|
DoIDCRLLogin();
|
||||||
|
} else {
|
||||||
|
// Non IDCRL Auth Code (Pre 5.x)
|
||||||
|
Sink.AttachEvent(TVShell.LoginManager, 'OnLoginResult', OnLoginResult);
|
||||||
|
TVShell.LoginManager.PassportSiteIDs = '507';
|
||||||
|
TVShell.LoginManager.LoginURL = "https://login.live.com/ppsecure/clientpost.srf";
|
||||||
|
TVShell.LoginManager.LogoutURL = "https://login.live.com/ppsecure/logoutxml.srf";
|
||||||
|
TVShell.LoginManager.ResetPasswordURL = "https://login.live.com/ppsecure/MSRV_ResetPW_ClientPost.srf";
|
||||||
|
TVShell.LoginManager.ChangePasswordURL = "https://login.live.com/ppsecure/MSRV_ChangePW_ClientPost.srf";
|
||||||
|
TVShell.LoginManager.RequestProfileURL = "https://login.live.com/ppsecure/ClientProfileRequest.srf";
|
||||||
|
TVShell.LoginManager.UpdateProfileURL = "https://login.live.com/ClientEditProf.srf";
|
||||||
|
TVShell.LoginManager.Authenticate(email, "", "https://login.live.com/ppsecure/clientpost.srf");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function OnLoginResult(hr,t,p)
|
||||||
|
{
|
||||||
|
MSNTVToken = t;
|
||||||
|
GoToUserCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
function DoIDCRLLogin()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
TVShell.LoginManager.IDCRLInitialize(0);
|
||||||
|
Sink.AttachEvent(TVShell.LoginManager, "IDCRLOnAuthStateChanged",IDCRLOnAuthStateChanged);
|
||||||
|
TVShell.LoginManager.IDCRLLogonAndAuthToServices(serviceArgs[0]);
|
||||||
|
} catch (e) {
|
||||||
|
TVShell.EventLog.Important("IDCRL error: " + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function IDCRLOnAuthStateChanged(result, authState, requestStatus, user, serviceTarget, servicePolicy, token, webFlowUrl)
|
||||||
|
{
|
||||||
|
// Find the matching policy in ProductionArgs for this serviceTarget
|
||||||
|
var expectedPolicy = "";
|
||||||
|
for(var i = 0; i < ProductionArgs.length; i++) {
|
||||||
|
if(ProductionArgs[i] == serviceTarget && i+1 < ProductionArgs.length) {
|
||||||
|
expectedPolicy = ProductionArgs[i+1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check with the correctly matched policy
|
||||||
|
if(TVShell.UserManager.CurrentUser.EMail != user ||
|
||||||
|
ProductionArgs[0] != serviceTarget ||
|
||||||
|
expectedPolicy != servicePolicy ||
|
||||||
|
(isIDCRLErrorCode(authState) || getIDCRLCode(authState) != 0x03) ||
|
||||||
|
(isIDCRLErrorCode(requestStatus) || getIDCRLCode(requestStatus) != 0x00)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token != ""){
|
||||||
|
var tIndex = token.indexOf("t=");
|
||||||
|
var pIndex = token.indexOf("&p=");
|
||||||
|
// make sure there is only the "t" token exists, this is for RPS compact ticket
|
||||||
|
// it is possible that compact ticket contains empty p parameter.
|
||||||
|
MSNTVToken = token;
|
||||||
|
GoToUserCheck();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
TVShell.EventLog.Important("No token");
|
||||||
|
}
|
||||||
|
|
||||||
|
function DoPoptimization() {
|
||||||
|
if (wanProvider === "MSNIANB") {
|
||||||
|
var connector = GetConnectorByName("LocalPOP");
|
||||||
|
if (connector == null) {
|
||||||
|
connector = TVShell.ConnectionManager.MSNIAManager.Connectors.Add("modem");
|
||||||
|
connector.AreaCode = "";
|
||||||
|
connector.Exchange = "";
|
||||||
|
connector.DialingFlags = 0x00001000;
|
||||||
|
connector.Name = "LocalPOP";
|
||||||
|
connector.LocationName = "LocalPOP";
|
||||||
|
TVShell.ConnectionManager.Save();
|
||||||
|
connector.Poptimize("0", connector.AreaCode, connector.Exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connector.Phonebook == null || connector.Phonebook.length === 0) {
|
||||||
|
if (connector.AreaCode && connector.Exchange) {
|
||||||
|
connector.Poptimize(connector.AreaCode, connector.Exchange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetConnectorByName(name) {
|
||||||
|
var connectors = TVShell.ConnectionManager.MSNIAManager.Connectors;
|
||||||
|
for (var i = 0; i < connectors.length; i++) {
|
||||||
|
if (connectors[i].Name === name) {
|
||||||
|
return connectors[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GoToUserCheck() {
|
||||||
|
if (banned === true) {
|
||||||
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/banned.html';
|
||||||
|
var myPanel = TVShell.PanelManager.Item('service');
|
||||||
|
if (myPanel) myPanel.GotoURL(url);
|
||||||
|
} else if (registered) {
|
||||||
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=UserCheck&purpose=Authorize&t=' + MSNTVToken ;
|
||||||
|
var myPanel = TVShell.PanelManager.Item('service');
|
||||||
|
if (myPanel) myPanel.GotoURL(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SetProgress(text, percent) {
|
||||||
|
if (progressPanel) {
|
||||||
|
progressPanel.Document.SetProgressText(text);
|
||||||
|
progressPanel.Document.SetProgressPercent(percent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var progressPanel = TVShell.PanelManager.Item('progress');
|
||||||
|
|
||||||
|
function IsServicePanel() {
|
||||||
|
if ((window.name == null) || ((window.name != null) && (window.name.toLowerCase() != 'service'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DontContinue() {
|
||||||
|
var currentUser = TVShell.UserManager.CurrentUser;
|
||||||
|
if (currentUser != null && currentUser.IsAuthorized) {
|
||||||
|
window.location.replace(TVShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||||
|
} else {
|
||||||
|
TVShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsServicePanel()) {
|
||||||
|
DontContinue();
|
||||||
|
} else {
|
||||||
|
DoPoptimization();
|
||||||
|
DoLogin();
|
||||||
|
|
||||||
|
try {
|
||||||
|
TVShell.DeviceControl.SetTimeZone(${standardOffset}, "${standardName}", 0, "");
|
||||||
|
} catch (e) {
|
||||||
|
TVShell.EventLog.Important("SetTimeZone error: " + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
TVShell.DeviceControl.SetClock(${timeData.hh}, ${timeData.mm}, ${timeData.ss}, ${timeData.mo}, ${timeData.dd}, ${timeData.yyyy});
|
||||||
|
} catch (e) {
|
||||||
|
TVShell.EventLog.Important("SetClock error: " + e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
TVShell.EventLog.Important("Error in boxcheck: " + e.message);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "UserCheck":
|
||||||
|
headers = `Content-type: text/html`;
|
||||||
|
|
||||||
|
// Check if the msntv.msn.com token is correct TODO
|
||||||
|
|
||||||
|
data = `<html>
|
||||||
|
<head>
|
||||||
|
<title id="title"></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe id="checkmail" style="display:none"></iframe>
|
||||||
|
<script language="javascript">
|
||||||
|
var TVShell = new ActiveXObject("MSNTV.TVShell");
|
||||||
|
var wanProvider = TVShell.ConnectionManager.WANProvider;
|
||||||
|
function SetServiceList() {
|
||||||
|
var entry;
|
||||||
|
|
||||||
|
// BuiltinServiceList - for main MSN TV services
|
||||||
|
|
||||||
|
TVShell.UserManager.CurrentUser.ServiceList.Clear(); //Always clear the list first to avoid dupes.
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('help::help');
|
||||||
|
entry.URL = 'http://sg1.msntv.msn.com/health/Help.aspx';
|
||||||
|
entry.KeyCode = 0xAC; // VK_BROWSER_HOME
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('home::home');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Home/Home.aspx?WANProvider=' + wanProvider;
|
||||||
|
entry.KeyCode = 0xAC; // VK_BROWSER_HOME
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('home::bgmusic');
|
||||||
|
entry.URL = '';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('home::backendproxy');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/BackendProxy';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('home::radioplus');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/Stations.xml';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('music::radiohome');
|
||||||
|
entry.URL = 'http://msntv.msn.com/pages/radio/home.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Livefilestore::AuthServer');
|
||||||
|
entry.URL = 'livefilestore.com';
|
||||||
|
entry.Description = 'MBI'
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Skydrive::AuthServer');
|
||||||
|
entry.URL = 'favorites.live.com';
|
||||||
|
entry.Description = 'MBI'
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Skydrive::Browse');
|
||||||
|
// entry.URL = 'users.storage.live.com';
|
||||||
|
entry.URL = 'favorites.msn.com';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Skydrive::AppId');
|
||||||
|
entry.Description = '1'
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Skydrive::ApiServer');
|
||||||
|
entry.URL = 'api.live.net';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('onlinestorage::root');
|
||||||
|
entry.URL = 'https://livefilestore/onlinestorage/';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Favorites::RoamingServer');
|
||||||
|
entry.URL = 'https://livefilestore.com/onlinestorage/';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Favorites::Migration');
|
||||||
|
entry.URL = 'https://livefilestore.com/onlinestorage/';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Favorites::SyncServer');
|
||||||
|
entry.URL = 'https://livefilestore.com/onlinestorage/';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('onlinestorage::authServer');
|
||||||
|
entry.URL = 'http://77.68.90.130/';
|
||||||
|
entry.Description = 'MBI'
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('mail::listmail');
|
||||||
|
entry.URL = 'http://mail-sgN.msntv.msn.com/apps/mail/listmail.aspx';
|
||||||
|
entry.KeyCode = 0xB4; // VK_LAUNCH_MAIL
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('mail::writemail');
|
||||||
|
entry.URL = 'http://mail-sg1.trusted.msntv.msn.com/apps/mail/writemail.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('chat::home');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/Pages/Chat/Chat.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('chat::ServiceTarget');
|
||||||
|
entry.URL = 'chat.msn.com';
|
||||||
|
entry.Description = '?id=2260'
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('messenger::root');
|
||||||
|
entry.URL = 'http://ms.msgrsvcs.ctsrv.gay:1863';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('messenger::passport');
|
||||||
|
entry.URL = 'https://login.live.com/messenger';
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('messenger::ServiceTarget');
|
||||||
|
entry.URL = 'messenger.msn.com';
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('search::search');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/Pages/Search/search.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('search::main');
|
||||||
|
entry.URL = 'https://sg1.msntv.msn.com/search/Search.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('discuss::home');
|
||||||
|
entry.URL = 'http://sg1.msntv.msn.com/apps/discuss/DiscussLobby.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('maps::main');
|
||||||
|
entry.URL = 'https://sg1.msntv.msn.com/apps/maps/GetMap.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Settings::HomeNetwork');
|
||||||
|
entry.URL = 'msntv:/Settings/Network/HomeNetworking.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('settings::mainindex');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/apps/settings/MainIndex.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('UAM::UAMbase');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/apps/uam/pages/settings.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Photo::Home');
|
||||||
|
entry.URL = 'msntv:/Photo/PhotoHome.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = TVShell.UserManager.CurrentUser.ServiceList.Add('Photos');
|
||||||
|
entry.URL = 'msntv:/Photo/PhotoHome.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
// Add services to ServiceList
|
||||||
|
TVShell.ServiceList.Clear();
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('home::cinemanow');
|
||||||
|
entry.URL = 'http://g.msn.com/5TVANDURIL/4000';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('msn::radioplus');
|
||||||
|
entry.URL = 'http://radio.msn.com/asx/generate';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('msn::musicnews');
|
||||||
|
entry.URL = 'http://www.msnbc.msn.com/id/3032433/';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('connection::popupcontrol');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/PopupControlWhiteList.ashx';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('connection::reconnect');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=ReAuthorize';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('connection::nightly_login');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Nightly';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('mail::check');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/apps/connection/CheckMail.aspx?phase=CheckMail&purpose=CheckMail';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
if (wanProvider === "BYOA") {
|
||||||
|
entry = TVShell.ServiceList.Add('home::videoplus');
|
||||||
|
entry.URL = 'http://msntv.msn.com/pages/msnvideo/main.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wanProvider === "BYOA") {
|
||||||
|
entry = TVShell.ServiceList.Add('home::musicvideo');
|
||||||
|
entry.URL = 'http://msntv.msn.com/pages/msnvideo/main.aspx?p=music';
|
||||||
|
entry.Safe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('connection::resetpassword');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=ResetPassword';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('connection::pagepatch');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/PagePatch.ashx';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('connection::login');
|
||||||
|
entry.URL = 'https://sg1.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Authorize';
|
||||||
|
entry.Description = '${minisrv_config.config.service_name}/sg1 [${minisrv_config.config.hide_minisrv_version ? "beta" : minisrv_version_string.replace("zefie's wtv minisrv ","")}]';
|
||||||
|
|
||||||
|
entry = TVShell.ServiceList.Add('ctags::main');
|
||||||
|
entry.URL = 'http://c.msn.com/c.gif?di=1455&pi=68206&tp=http%3a%2f%2fmsntv.msn.com%2fclient%2f';
|
||||||
|
TVShell.ServiceList.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsServicePanel() {
|
||||||
|
if ((window.name == null) || ((window.name != null) && (window.name.toLowerCase() != 'service'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DontContinue() {
|
||||||
|
var currentUser = TVShell.UserManager.CurrentUser;
|
||||||
|
if (currentUser != null && currentUser.IsAuthorized) {
|
||||||
|
TVShell.PanelManager.Item('main').GotoURL(TVShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||||
|
TVShell.PanelManager.Item('main').ClearTravelLog();
|
||||||
|
TVShell.PanelManager.Item('main').NoBackToMe = true;
|
||||||
|
} else {
|
||||||
|
TVShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsServicePanel()) {
|
||||||
|
DontContinue();
|
||||||
|
} else {
|
||||||
|
SetServiceList();
|
||||||
|
|
||||||
|
TVShell.UserManager.SetCurrentUserIsAuthorized(true);
|
||||||
|
TVShell.ConnectionManager.ServiceState = 'Authorized';
|
||||||
|
var dt = new Date();
|
||||||
|
TVShell.UserManager.LastLoginTime = dt.getTime() / 1000 + dt.getTimezoneOffset() * 60;
|
||||||
|
TVShell.UserManager.OfflineAppMaxAccessDays = 20;
|
||||||
|
TVShell.UserManager.OfflineAppMaxAccessTimes = 20;
|
||||||
|
TVShell.UserManager.Save();
|
||||||
|
TVShell.PanelManager.Item('main').GotoURL(TVShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||||
|
TVShell.PanelManager.Item('main').ClearTravelLog();
|
||||||
|
TVShell.PanelManager.Item('main').NoBackToMe = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
headers = `200 OK
|
||||||
|
Content-type: text/html`;
|
||||||
|
|
||||||
|
data = `<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<title id="title"></title>
|
||||||
|
</HEAD>
|
||||||
|
</HTML>`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
@@ -37,6 +37,8 @@ if (session_data) {
|
|||||||
registered = session_data.isRegistered();
|
registered = session_data.isRegistered();
|
||||||
if (registered) {
|
if (registered) {
|
||||||
username = session_data.getSessionData("subscriber_username") || '';
|
username = session_data.getSessionData("subscriber_username") || '';
|
||||||
|
Profile_Picture = session_data.getSessionData('ProfilePicture') || '';
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,23 +105,23 @@ data = `<html>
|
|||||||
var email = TVShell.UserManager.EMail;
|
var email = TVShell.UserManager.EMail;
|
||||||
var wanProvider = TVShell.ConnectionManager.WANProvider;
|
var wanProvider = TVShell.ConnectionManager.WANProvider;
|
||||||
|
|
||||||
var banned = ${banned}; // JavaScript boolean value
|
var banned = ${banned};
|
||||||
var registered = ${registered}; // JavaScript boolean value
|
var registered = ${registered};
|
||||||
var username = "${username}"; // JavaScript string value
|
var username = "${username}";
|
||||||
|
var picture = "${Profile_Picture}";
|
||||||
|
|
||||||
InitializeGuestMode();
|
InitializeGuestMode();
|
||||||
RemoveGuestUsers();
|
RemoveGuestUsers();
|
||||||
|
|
||||||
if (!banned) {
|
if (!banned) {
|
||||||
TVShell.AddSecretCode(10000); // sync shit
|
// DEBUG ONLY! USE WITH CAUTION!
|
||||||
TVShell.AddSecretCode(10001); // sync shit
|
TVShell.AddSecretCode(10000); // Power-on for nightly update
|
||||||
TVShell.AddSecretCode(10002); // sync shit
|
TVShell.AddSecretCode(10001); // Power-on for nightly email check at anchor time
|
||||||
TVShell.AddSecretCode(93288); // Service Select
|
TVShell.AddSecretCode(10002); // Power-on for nightly email check at non-anchor time
|
||||||
TVShell.AddSecretCode(77437); // Spooky Options
|
TVShell.AddSecretCode(77437); // spooky dialing options
|
||||||
TVShell.AddSecretCode(6145539); // Force Crash
|
TVShell.AddSecretCode(93288); // Service Selection Page
|
||||||
var entry = TVShell.ServiceList.Add("connection::login");
|
TVShell.AddSecretCode(6145539); // crash the system
|
||||||
entry.URL = "https://headwaiter.trusted.msntv.msn.com/connection/login.aspx?BoxId=${BoxId}";
|
TVShell.AddSecretCode(3932397); // update loop test
|
||||||
TVShell.ServiceList.Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function CheckForUser(usernameToCheck) {
|
function CheckForUser(usernameToCheck) {
|
||||||
@@ -146,15 +148,30 @@ data = `<html>
|
|||||||
var user = TVShell.UserManager.AddNew(username);
|
var user = TVShell.UserManager.AddNew(username);
|
||||||
if (user) {
|
if (user) {
|
||||||
user.IsPersistent = true;
|
user.IsPersistent = true;
|
||||||
|
user.setAttribute("GuestUser", false);
|
||||||
|
var dt = new Date();
|
||||||
|
|
||||||
|
TVShell.UserManager.LastLoginTime = dt.getTime() / 1000 + dt.getTimezoneOffset() * 60;
|
||||||
|
|
||||||
|
TVShell.UserManager.OfflineAppMaxAccessDays = 20;
|
||||||
|
TVShell.UserManager.OfflineAppMaxAccessTimes = 20;
|
||||||
|
TVShell.UserManager.CurrentUser = user;
|
||||||
|
user.LargeIcon = "msntv:/SignInPics/big/"+ picture + ".png";
|
||||||
|
user.SmallIcon = "msntv:/SignInPics/small/"+ picture + ".gif";
|
||||||
|
TVShell.UserManager.Save();
|
||||||
|
entry = TVShell.ServiceList.Add('connection::login');
|
||||||
|
entry.URL = 'https://headwaiter.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Authorize';
|
||||||
|
entry.Description = '${minisrv_config.config.service_name}/sg1 [${minisrv_config.config.hide_minisrv_version ? "beta" : minisrv_version_string.replace("zefie's wtv minisrv ","")}]';
|
||||||
|
TVShell.ServiceList.Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetProgress('Welcome, New User!', 100);
|
|
||||||
var myPanel = TVShell.PanelManager.Item('main')
|
var myPanel = TVShell.PanelManager.Item('main')
|
||||||
if (registered === true) {
|
if (registered === true) {
|
||||||
var signon = TVShell.BuiltinServiceList.Item("SignOn");
|
var signon = TVShell.BuiltinServiceList.Item("SignOn");
|
||||||
var panel = TVShell.PanelManager.FocusedPanel;
|
var panel = TVShell.PanelManager.FocusedPanel;
|
||||||
var atLogin = false;
|
var atLogin = false;
|
||||||
|
TVShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||||
if ( signon && panel && panel.Name == "main" )
|
if ( signon && panel && panel.Name == "main" )
|
||||||
{
|
{
|
||||||
if ( IsMainPanelOnPage( signon.URL ) ) atLogin = true;
|
if ( IsMainPanelOnPage( signon.URL ) ) atLogin = true;
|
||||||
@@ -165,7 +182,7 @@ data = `<html>
|
|||||||
GotoSignOn();
|
GotoSignOn();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (myPanel) myPanel.GotoURL('https://sg1.trusted.msntv.msn.com/register/Establish-your-MSN-TV-Account.html');
|
if (myPanel) myPanel.GotoURL('https://sg1.trusted.msntv.msn.com/Register/Establish-your-MSN-TV-Account.html');
|
||||||
}
|
}
|
||||||
if (myPanel) {
|
if (myPanel) {
|
||||||
myPanel.ClearTravelLog();
|
myPanel.ClearTravelLog();
|
||||||
@@ -188,11 +205,7 @@ data = `<html>
|
|||||||
"messenger.msn.com", "?id=507", 0, 0,
|
"messenger.msn.com", "?id=507", 0, 0,
|
||||||
"spaces.live.com", "MBI", 0, 0
|
"spaces.live.com", "MBI", 0, 0
|
||||||
);
|
);
|
||||||
var PPEArgs = new Array();
|
|
||||||
var INTArgs = new Array();
|
|
||||||
serviceArgs[0] = ProductionArgs;
|
serviceArgs[0] = ProductionArgs;
|
||||||
serviceArgs[1] = PPEArgs;
|
|
||||||
serviceArgs[2] = INTArgs;
|
|
||||||
try {
|
try {
|
||||||
TVShell.LoginManager.IDCRLInitialize(0);
|
TVShell.LoginManager.IDCRLInitialize(0);
|
||||||
TVShell.LoginManager.IDCRLLogonAndAuthToServices(serviceArgs[0]);
|
TVShell.LoginManager.IDCRLLogonAndAuthToServices(serviceArgs[0]);
|
||||||
@@ -236,17 +249,15 @@ data = `<html>
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function CheckBoxID() {
|
|
||||||
SetProgress("${minisrv_config.config.service_name} [${minisrv_config.config.hide_minisrv_version ? "beta" : minisrv_version_string.replace("zefie's wtv minisrv ","")}] Welcome, ${username != '' ? username : 'Guest'}!", 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
function GoToUserCheck() {
|
function GoToUserCheck() {
|
||||||
if (banned === true) {
|
if (banned === true) {
|
||||||
var url = 'https://headwaiter.trusted.msntv.msn.com/connection/banned.html';
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/banned.html';
|
||||||
var myPanel = TVShell.PanelManager.Item('service');
|
var myPanel = TVShell.PanelManager.Item('service');
|
||||||
if (myPanel) myPanel.GotoURL(url);
|
if (myPanel) myPanel.GotoURL(url);
|
||||||
} else if (registered) {
|
} else if (registered) {
|
||||||
GotoSignOn();
|
var url = 'https://sg1.trusted.msntv.msn.com/connection/usercheck.html';
|
||||||
|
var myPanel = tvShell.PanelManager.Item('service');
|
||||||
|
if (myPanel) myPanel.GotoURL(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +289,6 @@ data = `<html>
|
|||||||
if (!IsServicePanel()) {
|
if (!IsServicePanel()) {
|
||||||
DontContinue();
|
DontContinue();
|
||||||
} else {
|
} else {
|
||||||
CheckBoxID();
|
|
||||||
DoPoptimization();
|
DoPoptimization();
|
||||||
DoLogin();
|
DoLogin();
|
||||||
|
|
||||||
@@ -300,7 +310,7 @@ data = `<html>
|
|||||||
if (window.console) console.log("Error in boxcheck: " + e.message);
|
if (window.console) console.log("Error in boxcheck: " + e.message);
|
||||||
|
|
||||||
var myPanel = TVShell ? TVShell.PanelManager.Item('main') : null;
|
var myPanel = TVShell ? TVShell.PanelManager.Item('main') : null;
|
||||||
if (myPanel) myPanel.GotoURL('https://headwaiter.trusted.msntv.msn.com/connection/error.html');
|
if (myPanel) myPanel.GotoURL('https://sg1.trusted.msntv.msn.com/connection/error.html');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
const minisrv_service_file = true;
|
||||||
|
|
||||||
|
headers = `Content-type: text/html`;
|
||||||
|
|
||||||
|
data = `<html>
|
||||||
|
<head>
|
||||||
|
<title id="title"></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe id="checkmail" style="display:none"></iframe>
|
||||||
|
<script language="javascript">
|
||||||
|
var tvShell = new ActiveXObject("MSNTV.TVShell");
|
||||||
|
var wanProvider = tvShell.ConnectionManager.WANProvider;
|
||||||
|
function SetServiceList() {
|
||||||
|
var entry;
|
||||||
|
|
||||||
|
// BuiltinServiceList - for main MSN TV services
|
||||||
|
|
||||||
|
tvShell.UserManager.CurrentUser.ServiceList.Clear(); //Always clear the list first to avoid dupes.
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('home::home');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Pages/Home/Home.aspx';
|
||||||
|
entry.KeyCode = 0xAC; // VK_BROWSER_HOME
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('home::bgmusic');
|
||||||
|
entry.URL = '}';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('home::backendproxy');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/BackendProxy';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('home::radioplus');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Stations.xml';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('mail::listmail');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Pages/Mail/listmail.aspx';
|
||||||
|
entry.KeyCode = 0xB4; // VK_LAUNCH_MAIL
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('mail::writemail');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Pages/Mail/writemail.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('chat::home');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Pages/Chat/Chat.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('messenger::root');
|
||||||
|
entry.URL = 'http://login.live.com:1863';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('messenger::passport');
|
||||||
|
entry.URL = 'https://login.live.com';
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('search::search');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Pages/Search/search.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('search::main');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/Pages/Search/categories.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('discuss::home');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('maps::main');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('Settings::HomeNetwork');
|
||||||
|
entry.URL = 'msntv:/Settings/Network/HomeNetworking.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('settings::mainindex');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/settings/main.aspx';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('UAM::UAMbase');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('Photo::Home');
|
||||||
|
entry.URL = 'msntv:/Photo/PhotoHome.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
entry = tvShell.UserManager.CurrentUser.ServiceList.Add('Photos');
|
||||||
|
entry.URL = 'msntv:/Photo/PhotoHome.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
// Add services to ServiceList
|
||||||
|
tvShell.ServiceList.Clear();
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('home::cinemanow');
|
||||||
|
entry.URL = 'http://g.msn.com/5TVANDURIL/4000';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('msn::radioplus');
|
||||||
|
entry.URL = 'http://radio.msn.com/asx/generate';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('music::radiohome');
|
||||||
|
entry.URL = 'http://radio.msn.com/asx/generate';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('msn::musicnews');
|
||||||
|
entry.URL = 'http://www.msnbc.msn.com/id/3032433/';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('connection::popupcontrol');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/connection/PopupControlWhiteList.ashx';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('connection::reconnect');
|
||||||
|
entry.URL = 'http://headwaiter.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Authorize';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('connection::nightly_login');
|
||||||
|
entry.URL = 'http://headwaiter.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Nightly';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('mail::check');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/apps/connection/CheckMail.aspx?phase=CheckMail&purpose=CheckMail';
|
||||||
|
entry.Safe = true;
|
||||||
|
|
||||||
|
if (wanProvider === "MSNIANB") {
|
||||||
|
entry = tvShell.ServiceList.Add('home::videoplus');
|
||||||
|
entry.URL = 'msntv:/Video/VideoHome.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
} else {
|
||||||
|
entry = tvShell.ServiceList.Add('home::videoplus');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/pages/msnvideo/main';
|
||||||
|
entry.Safe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wanProvider === "MSNIANB") {
|
||||||
|
entry = tvShell.ServiceList.Add('home::musicvideo');
|
||||||
|
entry.URL = 'msntv:/Music/MusicHome.html';
|
||||||
|
entry.Safe = true;
|
||||||
|
} else {
|
||||||
|
entry = tvShell.ServiceList.Add('home::musicvideo');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/pages/msnvideo/main?p=music';
|
||||||
|
entry.Safe = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('connection::resetpassword');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/connection/GatePage?phase=Bootstrap&purpose=ResetPassword';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('connection::pagepatch');
|
||||||
|
entry.URL = 'http://sg1.trusted.msntv.msn.com/connection/PagePatch.ashx';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('connection::login');
|
||||||
|
entry.URL = 'http://headwaiter.trusted.msntv.msn.com/connection/GatePage.aspx?phase=Bootstrap&purpose=Authorize';
|
||||||
|
entry.Description = '${minisrv_config.config.service_name}/sg1 [${minisrv_config.config.hide_minisrv_version ? "beta" : minisrv_version_string.replace("zefie's wtv minisrv ","")}]';
|
||||||
|
|
||||||
|
entry = tvShell.ServiceList.Add('ctags::main');
|
||||||
|
entry.URL = 'http://c.msn.com/c.gif?di=1455&pi=68206&tp=http%3a%2f%2fmsntv.msn.com%2fclient%2f';
|
||||||
|
tvShell.ServiceList.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsServicePanel() {
|
||||||
|
if ((window.name == null) || ((window.name != null) && (window.name.toLowerCase() != 'service'))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function DontContinue() {
|
||||||
|
var currentUser = tvShell.UserManager.CurrentUser;
|
||||||
|
if (currentUser != null && currentUser.IsAuthorized) {
|
||||||
|
tvShell.PanelManager.Item('main').GotoURL(tvShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||||
|
tvShell.PanelManager.Item('main').ClearTravelLog();
|
||||||
|
tvShell.PanelManager.Item('main').NoBackToMe = true;
|
||||||
|
} else {
|
||||||
|
tvShell.ConnectionManager.ServiceState = 'ReSignIn';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsServicePanel()) {
|
||||||
|
DontContinue();
|
||||||
|
} else {
|
||||||
|
SetServiceList();
|
||||||
|
|
||||||
|
tvShell.UserManager.SetCurrentUserIsAuthorized(true);
|
||||||
|
tvShell.ConnectionManager.ServiceState = 'Authorized';
|
||||||
|
var dt = new Date();
|
||||||
|
tvShell.UserManager.LastLoginTime = dt.getTime() / 1000 + dt.getTimezoneOffset() * 60;
|
||||||
|
tvShell.UserManager.OfflineAppMaxAccessDays = 20;
|
||||||
|
tvShell.UserManager.OfflineAppMaxAccessTimes = 20;
|
||||||
|
tvShell.UserManager.Save();
|
||||||
|
tvShell.PanelManager.Item('main').GotoURL(tvShell.UserManager.CurrentUser.ServiceList.Item('home::home').URL);
|
||||||
|
tvShell.PanelManager.Item('main').ClearTravelLog();
|
||||||
|
tvShell.PanelManager.Item('main').NoBackToMe = true;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
@@ -197,10 +197,12 @@ if (minisrv_config.services["wtv-author"].max_pages) {
|
|||||||
</table>
|
</table>
|
||||||
<p>A maximum of <b>${minisrv_config.services["wtv-author"].max_pages}</b> pages can be created, regardless of publish status.
|
<p>A maximum of <b>${minisrv_config.services["wtv-author"].max_pages}</b> pages can be created, regardless of publish status.
|
||||||
<br><br>
|
<br><br>
|
||||||
Your published pages are available at<br>
|
`
|
||||||
|
if (numofpages > 0) {
|
||||||
|
`Your published pages are available at<br>
|
||||||
<a href="http://${site}/${session_data.getSessionData("subscriber_username")}/">http://${site}/${session_data.getSessionData("subscriber_username")}/</a>
|
<a href="http://${site}/${session_data.getSessionData("subscriber_username")}/">http://${site}/${session_data.getSessionData("subscriber_username")}/</a>
|
||||||
</table>`
|
</table>`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data += `
|
data += `
|
||||||
<SCRIPT language=JavaScript>
|
<SCRIPT language=JavaScript>
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ async function processLC2DownloadPage(flashrom_info, headers, numparts = null) {
|
|||||||
if (!flashrom_info.part_count) flashrom_info.part_count = parseInt(flashrom_info.message.slice(flashrom_info.message.length - 4).replace(/\D/g, ''));
|
if (!flashrom_info.part_count) flashrom_info.part_count = parseInt(flashrom_info.message.slice(flashrom_info.message.length - 4).replace(/\D/g, ''));
|
||||||
if (parseInt(flashrom_info.part_number) >= 0 && flashrom_info.rompath && flashrom_info.next_rompath) {
|
if (parseInt(flashrom_info.part_number) >= 0 && flashrom_info.rompath && flashrom_info.next_rompath) {
|
||||||
if (!flashrom_info.message && flashrom_info.is_bootrom) {
|
if (!flashrom_info.message && flashrom_info.is_bootrom) {
|
||||||
|
flashrom_info.part_count = 16;
|
||||||
flashrom_info.message = "BootRom Part " + (flashrom_info.part_number + 1) + " of " + flashrom_info.part_count;
|
flashrom_info.message = "BootRom Part " + (flashrom_info.part_number + 1) + " of " + flashrom_info.part_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +74,8 @@ async function processLC2DownloadPage(flashrom_info, headers, numparts = null) {
|
|||||||
}
|
}
|
||||||
session.lastDownloadTime = now;
|
session.lastDownloadTime = now;
|
||||||
|
|
||||||
|
if (isNaN(downloadTime) || downloadTime < 1) downloadTime = 1;
|
||||||
|
|
||||||
|
|
||||||
headers = `200 OK
|
headers = `200 OK
|
||||||
Content-type: text/html
|
Content-type: text/html
|
||||||
@@ -127,7 +130,7 @@ Updating now
|
|||||||
<font size=+1>
|
<font size=+1>
|
||||||
Your ${session_data.getBoxName()} is being<br>updated automatically.
|
Your ${session_data.getBoxName()} is being<br>updated automatically.
|
||||||
<p> <font size=+1>
|
<p> <font size=+1>
|
||||||
This will take about ${downloadTime} minutes and<br>then you can use your ${session_data.getBoxName()} again.
|
This will take about ${downloadTime} minute${downloadTime !== 1 ? "s" : ""} and<br>then you can use your ${session_data.getBoxName()} again.
|
||||||
`;
|
`;
|
||||||
if (flashrom_info.is_bootrom && flashrom_info.part_number === (flashrom_info.part_count - 1)) {
|
if (flashrom_info.is_bootrom && flashrom_info.part_number === (flashrom_info.part_count - 1)) {
|
||||||
data += `<p>
|
data += `<p>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ if (minisrv_config.config.hide_incomplete_features) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We need to fix most webtv viewers for this, since they spoof a build that doesn't support messenger?
|
/* We need to fix most webtv viewers for this, since they spoof a build that doesn't support messenger?
|
||||||
if (!session_data.hasCap("client-can-use-messenger")) {
|
if (!session_data.capabilities.get("client-can-use-messenger")) {
|
||||||
removeSettingByUrl("wtv-setup:/messenger");
|
removeSettingByUrl("wtv-setup:/messenger");
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1163,23 +1163,11 @@ class WTVMSNTV2 {
|
|||||||
|
|
||||||
loadTlsContext() {
|
loadTlsContext() {
|
||||||
try {
|
try {
|
||||||
const certCandidates = [
|
const candidateCert = (this.service_config.ssl) ? this.wtvshared.parseConfigVars(this.service_config.ssl.cert) : null;
|
||||||
['msntv2/msn_domains.crt', 'msntv2/msn_domains.key']
|
const candidateKey = (this.service_config.ssl) ? this.wtvshared.parseConfigVars(this.service_config.ssl.key) : null;
|
||||||
];
|
if (!candidateCert || !candidateKey) return null;
|
||||||
let certFile = null;
|
const certPem = fs.readFileSync(candidateCert);
|
||||||
let keyFile = null;
|
const keyPem = fs.readFileSync(candidateKey);
|
||||||
for (const [certPath, keyPath] of certCandidates) {
|
|
||||||
const candidateCert = this.wtvshared.getServiceDep(certPath, true);
|
|
||||||
const candidateKey = this.wtvshared.getServiceDep(keyPath, true);
|
|
||||||
if (candidateCert && candidateKey) {
|
|
||||||
certFile = candidateCert;
|
|
||||||
keyFile = candidateKey;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!certFile || !keyFile) return null;
|
|
||||||
const certPem = fs.readFileSync(certFile);
|
|
||||||
const keyPem = fs.readFileSync(keyFile);
|
|
||||||
return tls.createSecureContext({
|
return tls.createSecureContext({
|
||||||
cert: certPem,
|
cert: certPem,
|
||||||
key: keyPem,
|
key: keyPem,
|
||||||
@@ -1195,23 +1183,12 @@ class WTVMSNTV2 {
|
|||||||
|
|
||||||
loadForgeTlsCredentials() {
|
loadForgeTlsCredentials() {
|
||||||
try {
|
try {
|
||||||
const certCandidates = [
|
const candidateCert = (this.service_config.ssl) ? this.wtvshared.parseConfigVars(this.service_config.ssl.cert) : null;
|
||||||
['msntv2/msn_domains.crt', 'msntv2/msn_domains.key']
|
const candidateKey = (this.service_config.ssl) ? this.wtvshared.parseConfigVars(this.service_config.ssl.key) : null;
|
||||||
];
|
|
||||||
let certFile = null;
|
if (!candidateCert || !candidateKey) return null;
|
||||||
let keyFile = null;
|
const certPem = fs.readFileSync(candidateCert, 'utf8');
|
||||||
for (const [certPath, keyPath] of certCandidates) {
|
const keyPem = fs.readFileSync(candidateKey, 'utf8');
|
||||||
const candidateCert = this.wtvshared.getServiceDep(certPath, true);
|
|
||||||
const candidateKey = this.wtvshared.getServiceDep(keyPath, true);
|
|
||||||
if (candidateCert && candidateKey) {
|
|
||||||
certFile = candidateCert;
|
|
||||||
keyFile = candidateKey;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!certFile || !keyFile) return null;
|
|
||||||
const certPem = fs.readFileSync(certFile, 'utf8');
|
|
||||||
const keyPem = fs.readFileSync(keyFile, 'utf8');
|
|
||||||
return {
|
return {
|
||||||
certPem,
|
certPem,
|
||||||
keyPem,
|
keyPem,
|
||||||
|
|||||||
@@ -56,18 +56,32 @@ class WTVClientSessionData {
|
|||||||
this.loginWhitelist.push("wtv-head-waiter:/confirm-transfer");
|
this.loginWhitelist.push("wtv-head-waiter:/confirm-transfer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns a new WTVMail instance to the session's mailstore property, using the current minisrv_config and session data.
|
||||||
|
*/
|
||||||
assignMailStore() {
|
assignMailStore() {
|
||||||
this.mailstore = new WTVMail(this.minisrv_config, this)
|
this.mailstore = new WTVMail(this.minisrv_config, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns a new WTVFavorites instance to the session's favstore property, using the current minisrv_config and session data.
|
||||||
|
*/
|
||||||
assignFavoriteStore() {
|
assignFavoriteStore() {
|
||||||
this.mailstore = this.favstore = new WTVFavorites(this.minisrv_config, this)
|
this.favstore = new WTVFavorites(this.minisrv_config, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new WTVSec session. Used for RC4 SECURE ON requests.
|
||||||
|
* @returns {WTVSec} A new WTVSec session instance
|
||||||
|
*/
|
||||||
createWTVSecSession() {
|
createWTVSecSession() {
|
||||||
return new WTVSec(this.minisrv_config)
|
return new WTVSec(this.minisrv_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the total number of unread messages for the primary account.
|
||||||
|
* @returns {number} Number of unread messages
|
||||||
|
*/
|
||||||
getAccountTotalUnreadMessages() {
|
getAccountTotalUnreadMessages() {
|
||||||
if (!this.isRegistered()) return false; // unregistered
|
if (!this.isRegistered()) return false; // unregistered
|
||||||
if (this.user_id > 0) return false; // not primary user or pre-login
|
if (this.user_id > 0) return false; // not primary user or pre-login
|
||||||
@@ -88,6 +102,9 @@ class WTVClientSessionData {
|
|||||||
return total_unread_messages;
|
return total_unread_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all user session data from memory, including session store and data store, and resets mail and favorite stores.
|
||||||
|
*/
|
||||||
clearUserSessionMemory() {
|
clearUserSessionMemory() {
|
||||||
this.setUserLoggedIn(false);
|
this.setUserLoggedIn(false);
|
||||||
this.data_store = [];
|
this.data_store = [];
|
||||||
@@ -96,6 +113,13 @@ class WTVClientSessionData {
|
|||||||
this.assignMailStore()
|
this.assignMailStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches the current user ID and optionally updates related data stores.
|
||||||
|
* @param {number} user_id The user ID to switch to
|
||||||
|
* @param {boolean} update_mail Whether to update the mail store
|
||||||
|
* @param {boolean} update_ticket Whether to update the ticket data
|
||||||
|
* @param {boolean} update_favorite Whether to update the favorite store
|
||||||
|
*/
|
||||||
switchUserID(user_id, update_mail = true, update_ticket = true, update_favorite = true) {
|
switchUserID(user_id, update_mail = true, update_ticket = true, update_favorite = true) {
|
||||||
this.user_id = parseInt(user_id);
|
this.user_id = parseInt(user_id);
|
||||||
if (user_id !== null) {
|
if (user_id !== null) {
|
||||||
@@ -143,6 +167,11 @@ class WTVClientSessionData {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a given mail address is in the address book.
|
||||||
|
* @param {string} addr The mail address to check against the address book
|
||||||
|
* @returns {boolean} True if the address is in the address book, false otherwise
|
||||||
|
*/
|
||||||
isAddressInAddressBook(addr) {
|
isAddressInAddressBook(addr) {
|
||||||
const addresses = this.getSessionData("address_book");
|
const addresses = this.getSessionData("address_book");
|
||||||
if (addresses) {
|
if (addresses) {
|
||||||
@@ -156,6 +185,11 @@ class WTVClientSessionData {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the first available user slot for a new user.
|
||||||
|
* Can only be used by the primary account (user_id 0).
|
||||||
|
* @returns {number|boolean} The first available user slot index, or false if no slots are available
|
||||||
|
*/
|
||||||
findFreeUserSlot() {
|
findFreeUserSlot() {
|
||||||
if (this.user_id !== 0) return false; // subscriber only command
|
if (this.user_id !== 0) return false; // subscriber only command
|
||||||
const master_directory = this.getUserStoreDirectory(true);
|
const master_directory = this.getUserStoreDirectory(true);
|
||||||
@@ -170,16 +204,30 @@ class WTVClientSessionData {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the display name of the current user.
|
||||||
|
* @returns {string} The subscriber's display name if user_id is 0, otherwise the current user's display name.
|
||||||
|
*/
|
||||||
getDisplayName() {
|
getDisplayName() {
|
||||||
return (this.user_id === 0) ? this.getSessionData("subscriber_name") : this.getSessionData("display_name");
|
return (this.user_id === 0) ? this.getSessionData("subscriber_name") : this.getSessionData("display_name");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of users for this SSID.
|
||||||
|
* Can only be used by the primary account (user_id 0).
|
||||||
|
* @returns {number} The number of users this SSID has
|
||||||
|
*/
|
||||||
getNumberOfUserAccounts() {
|
getNumberOfUserAccounts() {
|
||||||
if (!this.isRegistered()) return false;
|
if (!this.isRegistered()) return false;
|
||||||
if (this.user_id !== 0) return false; // subscriber only command
|
if (this.user_id !== 0) return false; // subscriber only command
|
||||||
return Object.keys(this.listPrimaryAccountUsers()).length;
|
return Object.keys(this.listPrimaryAccountUsers()).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists all primary account users for this SSID.
|
||||||
|
*
|
||||||
|
* @returns {Array} An array containing the account data of all users for this SSID
|
||||||
|
*/
|
||||||
listPrimaryAccountUsers() {
|
listPrimaryAccountUsers() {
|
||||||
if (this.user_id !== 0) return false; // subscriber only command
|
if (this.user_id !== 0) return false; // subscriber only command
|
||||||
|
|
||||||
@@ -206,6 +254,10 @@ class WTVClientSessionData {
|
|||||||
return account_data;
|
return account_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively creates directories for the given path.
|
||||||
|
* @param {string} thedir The directory path to create
|
||||||
|
*/
|
||||||
mkdirRecursive(thedir) {
|
mkdirRecursive(thedir) {
|
||||||
thedir.split(this.path.sep).reduce(
|
thedir.split(this.path.sep).reduce(
|
||||||
(directories, directory) => {
|
(directories, directory) => {
|
||||||
@@ -307,6 +359,13 @@ class WTVClientSessionData {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A part of the account transfer process, creates a pending transfer file in both the source and target account
|
||||||
|
* store directories with the relevant SSID and transfer type (source or target) for each account.
|
||||||
|
* This allows the transfer process to be completed or cancelled later, and ensures that only accounts
|
||||||
|
* with a pending transfer can complete the transfer process.
|
||||||
|
* @param {string} ssid
|
||||||
|
*/
|
||||||
setPendingTransfer(ssid) {
|
setPendingTransfer(ssid) {
|
||||||
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||||
let ssidobj = { "ssid": ssid, "type": "source" };
|
let ssidobj = { "ssid": ssid, "type": "source" };
|
||||||
@@ -319,6 +378,10 @@ class WTVClientSessionData {
|
|||||||
this.fs.writeFileSync(dest_pending_file, JSON.stringify(ssidobj));
|
this.fs.writeFileSync(dest_pending_file, JSON.stringify(ssidobj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels a pending account transfer, if it exists.
|
||||||
|
* @returns {string|null} The SSID of the cancelled transfer if a pending transfer was found and cancelled, or null if no pending transfer was found
|
||||||
|
*/
|
||||||
cancelPendingTransfer() {
|
cancelPendingTransfer() {
|
||||||
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||||
if (this.fs.existsSync(pending_file)) {
|
if (this.fs.existsSync(pending_file)) {
|
||||||
@@ -334,6 +397,10 @@ class WTVClientSessionData {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize the transfer, completely moving all user data from the source account to the target account, and removing the pending transfer files.
|
||||||
|
* @returns {boolean} Success of the transfer
|
||||||
|
*/
|
||||||
finalizePendingTransfer() {
|
finalizePendingTransfer() {
|
||||||
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||||
const file = this.fs.readFileSync(pending_file)
|
const file = this.fs.readFileSync(pending_file)
|
||||||
@@ -353,13 +420,18 @@ class WTVClientSessionData {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is a pending transfer for this account, and optionally check if the pending transfer type matches the specified dtype.
|
||||||
|
* @param {string} dtype {source|target} If specified, only returns the SSID if the pending transfer type matches the specified dtype. If null, returns the pending transfer object with ssid and type.
|
||||||
|
* @returns {string|object|boolean} The SSID of the pending transfer if dtype matches, the pending transfer object if dtype is null, or false if no pending transfer is found
|
||||||
|
*/
|
||||||
hasPendingTransfer(dtype = null) {
|
hasPendingTransfer(dtype = null) {
|
||||||
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
const pending_file = this.getUserStoreDirectory(true) + this.path.sep + "pending_transfer.json";
|
||||||
if (this.fs.existsSync(pending_file)) {
|
if (this.fs.existsSync(pending_file)) {
|
||||||
const ssidobj = JSON.parse(this.fs.readFileSync(pending_file));
|
const ssidobj = JSON.parse(this.fs.readFileSync(pending_file));
|
||||||
console.log(ssidobj)
|
console.log(ssidobj)
|
||||||
if (dtype) {
|
if (dtype) {
|
||||||
(ssidobj.type === dtype) ? ssidobj.ssid : false;
|
return (ssidobj.type === dtype) ? ssidobj.ssid : false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return ssidobj;
|
return ssidobj;
|
||||||
@@ -402,6 +474,10 @@ class WTVClientSessionData {
|
|||||||
return result !== false;
|
return result !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the user has a scrapbook directory.
|
||||||
|
* @returns {boolean} True if the scrapbook directory exists, false otherwise.
|
||||||
|
*/
|
||||||
scrapbookExists() {
|
scrapbookExists() {
|
||||||
if (this.scrapbook_dir === null) {
|
if (this.scrapbook_dir === null) {
|
||||||
const userstore_dir = this.getUserStoreDirectory();
|
const userstore_dir = this.getUserStoreDirectory();
|
||||||
@@ -411,6 +487,10 @@ class WTVClientSessionData {
|
|||||||
return this.fs.existsSync(this.scrapbook_dir);
|
return this.fs.existsSync(this.scrapbook_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a scrapbook directory for the user if it does not already exist.
|
||||||
|
* @returns {boolean} Success of the creation
|
||||||
|
*/
|
||||||
createScrapbook() {
|
createScrapbook() {
|
||||||
if (!this.scrapbookExists()) {
|
if (!this.scrapbookExists()) {
|
||||||
try {
|
try {
|
||||||
@@ -421,6 +501,10 @@ class WTVClientSessionData {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper that returns the scrapbook directory, and creates it if it doesn't exist.
|
||||||
|
* @returns {string} The path to the scrapbook directory
|
||||||
|
*/
|
||||||
scrapbookDir() {
|
scrapbookDir() {
|
||||||
if (!this.scrapbookExists()) {
|
if (!this.scrapbookExists()) {
|
||||||
this.createScrapbook();
|
this.createScrapbook();
|
||||||
@@ -428,6 +512,10 @@ class WTVClientSessionData {
|
|||||||
return this.scrapbook_dir;
|
return this.scrapbook_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List the files in the user's scrapbook directory, sorted in ascending order, and excluding any .meta files.
|
||||||
|
* @returns {Array} A filelist of the user's scrapbook files
|
||||||
|
*/
|
||||||
listScrapbook() {
|
listScrapbook() {
|
||||||
if (!this.scrapbookExists()) {
|
if (!this.scrapbookExists()) {
|
||||||
this.createScrapbook();
|
this.createScrapbook();
|
||||||
@@ -442,6 +530,10 @@ class WTVClientSessionData {
|
|||||||
return filteredFiles;
|
return filteredFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the next available ID slot for a new scrapbook entry.
|
||||||
|
* @returns {number} An available ID slot
|
||||||
|
*/
|
||||||
getFreeScrapbookID() {
|
getFreeScrapbookID() {
|
||||||
if (!this.scrapbookExists()) {
|
if (!this.scrapbookExists()) {
|
||||||
this.createScrapbook();
|
this.createScrapbook();
|
||||||
@@ -458,6 +550,10 @@ class WTVClientSessionData {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the total size of the user's scrapbook directory.
|
||||||
|
* @returns {number} The total size in bytes
|
||||||
|
*/
|
||||||
getScrapbookUsage() {
|
getScrapbookUsage() {
|
||||||
if (!this.scrapbookExists()) {
|
if (!this.scrapbookExists()) {
|
||||||
this.createScrapbook();
|
this.createScrapbook();
|
||||||
@@ -475,6 +571,10 @@ class WTVClientSessionData {
|
|||||||
return total_size;
|
return total_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the percentage of the scrapbook storage space that is in use.
|
||||||
|
* @returns {number} Percentage of the scrapbook storage space that is in use, out of the total allotted.
|
||||||
|
*/
|
||||||
getScrapbookUsagePercent() {
|
getScrapbookUsagePercent() {
|
||||||
if (!this.scrapbookExists()) {
|
if (!this.scrapbookExists()) {
|
||||||
this.createScrapbook();
|
this.createScrapbook();
|
||||||
@@ -486,6 +586,11 @@ class WTVClientSessionData {
|
|||||||
return Math.round(usage_percent, 2);
|
return Math.round(usage_percent, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a scrapbook image by its ID.
|
||||||
|
* @param {number} id
|
||||||
|
* @returns {Buffer|null} The image data as a Buffer, or null if the image does not exist
|
||||||
|
*/
|
||||||
getScrapbookImage(id) {
|
getScrapbookImage(id) {
|
||||||
if (!this.scrapbookExists()) {
|
if (!this.scrapbookExists()) {
|
||||||
this.createScrapbook();
|
this.createScrapbook();
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class WTVFTP {
|
|||||||
stream.on('data', (chunk) => {
|
stream.on('data', (chunk) => {
|
||||||
chunks.push(chunk);
|
chunks.push(chunk);
|
||||||
totalsize += chunk.length;
|
totalsize += chunk.length;
|
||||||
if (totalsize > 1024 * 1024 * 4) {
|
if (totalsize > 1024 * 1024 * this.minisrv_config.services[this.service_name].max_response_size) {
|
||||||
this.sendToClient(socket, { 'Status': '413 The item chosen contains too much information to be used.', 'Content-Type': 'text/plain' }, 'Item too large');
|
this.sendToClient(socket, { 'Status': '413 The item chosen contains too much information to be used.', 'Content-Type': 'text/plain' }, 'Item too large');
|
||||||
ftpClient.end();
|
ftpClient.end();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1130,6 +1130,7 @@ class WTVShared {
|
|||||||
// DON'T USE THIS
|
// DON'T USE THIS
|
||||||
// Saved for reference until I come up with a better way
|
// Saved for reference until I come up with a better way
|
||||||
// If used, this will exceed the stack limit over time
|
// If used, this will exceed the stack limit over time
|
||||||
|
/*
|
||||||
unloadModule(moduleName) {
|
unloadModule(moduleName) {
|
||||||
// Prevent usage
|
// Prevent usage
|
||||||
return;
|
return;
|
||||||
@@ -1141,6 +1142,7 @@ class WTVShared {
|
|||||||
delete require.cache[resolvedPath];
|
delete require.cache[resolvedPath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an absolute path without an trailing path seperator
|
* Returns an absolute path without an trailing path seperator
|
||||||
|
|||||||
@@ -351,6 +351,7 @@
|
|||||||
"ftp": {
|
"ftp": {
|
||||||
"port": 1650,
|
"port": 1650,
|
||||||
"connections": 3,
|
"connections": 3,
|
||||||
|
"max_response_size": 8, // Megabytes
|
||||||
"handler_module": "WTVFTP",
|
"handler_module": "WTVFTP",
|
||||||
"handler_extra_vars": ["wtvmime"]
|
"handler_extra_vars": ["wtvmime"]
|
||||||
},
|
},
|
||||||
@@ -521,6 +522,10 @@
|
|||||||
],
|
],
|
||||||
"handler_extra_vars": ["runScriptInVM", "handlePHP", "handleCGI", "ssid_sessions", "WTVClientSessionData", "socket_sessions"],
|
"handler_extra_vars": ["runScriptInVM", "handlePHP", "handleCGI", "ssid_sessions", "WTVClientSessionData", "socket_sessions"],
|
||||||
"show_verbose_errors": false,
|
"show_verbose_errors": false,
|
||||||
|
"ssl": {
|
||||||
|
"cert": "%ServiceDeps%/msntv2/minisrv.crt",
|
||||||
|
"key": "%ServiceDeps%/msntv2/minisrv.key"
|
||||||
|
},
|
||||||
"modules": [
|
"modules": [
|
||||||
"WTVRegister"
|
"WTVRegister"
|
||||||
]
|
]
|
||||||
|
|||||||
26
zefie_wtvp_minisrv/includes/depreciated.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "session_data.hasCap",
|
||||||
|
"pattern": "session\\_data\\.hasCap\\s*\\(",
|
||||||
|
"flags": "g",
|
||||||
|
"message": "session_data.hasCap() is deprecated and will be removed",
|
||||||
|
"removeVersion": "0.9.80",
|
||||||
|
"replacement": "Use session_data.capabilities.get() instead"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "getServiceString",
|
||||||
|
"pattern": "(?<!wtvshared\\.)getServiceString\\s*\\(",
|
||||||
|
"flags": "g",
|
||||||
|
"message": "getServiceString() is deprecated and will be removed",
|
||||||
|
"removeVersion": "0.9.80",
|
||||||
|
"replacement": "Use wtvshared.getServiceString() instead"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "moveArrayKey",
|
||||||
|
"pattern": "(?<!wtvshared\\.)moveArrayKey\\s*\\(",
|
||||||
|
"flags": "g",
|
||||||
|
"message": "moveArrayKey() is deprecated and will be removed",
|
||||||
|
"removeVersion": "0.9.80",
|
||||||
|
"replacement": "Use wtvshared.moveArrayKey() instead"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
"start": "node app.js",
|
"start": "node app.js",
|
||||||
"test": "node test.js",
|
"test": "node test.js",
|
||||||
"debug": "cross-env DEBUG=* node app.js",
|
"debug": "cross-env DEBUG=* node app.js",
|
||||||
"modem-proxy": "node modem_proxy.js"
|
"modem-proxy": "node modem_proxy.js",
|
||||||
|
"scan-service-deprecations": "node tools/scan_service_vault_deprecations.js"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "zefie",
|
"name": "zefie",
|
||||||
|
|||||||
@@ -225,45 +225,33 @@ function buildWebTVPS(videoES, audioES, outputPath, audioIntervalOverride, baHea
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match known-working WebTV cadence (attract.mpg is ~1 audio per 7 video packs)
|
// Use natural A/V ratio — matches actual bitrate split in the encoded file.
|
||||||
const inferredInterval = Math.max(1, Math.round(vChunks.length / aChunks.length));
|
// attract.mpg uses 7 because its video bitrate is ~7x its audio bitrate.
|
||||||
|
// Our encoded video is lower bitrate so the natural ratio is ~3.
|
||||||
|
const naturalInterval = Math.max(1, Math.round(vChunks.length / aChunks.length));
|
||||||
const audioInterval = Number.isFinite(audioIntervalOverride) && audioIntervalOverride > 0
|
const audioInterval = Number.isFinite(audioIntervalOverride) && audioIntervalOverride > 0
|
||||||
? Math.floor(audioIntervalOverride)
|
? Math.floor(audioIntervalOverride)
|
||||||
: Math.max(1, Math.round((inferredInterval + 7) / 2));
|
: naturalInterval;
|
||||||
console.log(`[*] ${vChunks.length} video chunks, ${aChunks.length} audio chunks, ` +
|
console.log(`[*] ${vChunks.length} video chunks, ${aChunks.length} audio chunks, ` +
|
||||||
`1 audio per ~${audioInterval} video`);
|
`1 audio per ~${audioInterval} video`);
|
||||||
|
|
||||||
const packs = [];
|
const packs = [];
|
||||||
let aIdx = 0;
|
let aIdx = 0;
|
||||||
|
|
||||||
// Pre-fill: 3 audio packs to prime the WebTV audio buffer
|
// Pre-fill: 3 audio packs to prime the WebTV audio buffer (matches attract.mpg)
|
||||||
const preFill = Math.min(3, aChunks.length);
|
const preFill = Math.min(3, aChunks.length);
|
||||||
for (let k = 0; k < preFill; k++, aIdx++) {
|
for (let k = 0; k < preFill; k++, aIdx++) {
|
||||||
packs.push(makePack(0xC0, aChunks[aIdx]));
|
packs.push(makePack(0xC0, aChunks[aIdx]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple fixed-interval interleave: emit audioInterval video packs, then 1 audio pack
|
||||||
let vIdx = 0;
|
let vIdx = 0;
|
||||||
|
|
||||||
// Spread audio over the full video timeline to avoid starving early playback
|
|
||||||
// and dumping remaining audio at EOF.
|
|
||||||
while (vIdx < vChunks.length || aIdx < aChunks.length) {
|
while (vIdx < vChunks.length || aIdx < aChunks.length) {
|
||||||
if (vIdx >= vChunks.length) {
|
for (let k = 0; k < audioInterval && vIdx < vChunks.length; k++) {
|
||||||
packs.push(makePack(0xC0, aChunks[aIdx++]));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (aIdx >= aChunks.length) {
|
|
||||||
packs.push(makePack(0xE0, vChunks[vIdx++]));
|
packs.push(makePack(0xE0, vChunks[vIdx++]));
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
if (aIdx < aChunks.length) {
|
||||||
const videoProgress = vIdx / vChunks.length;
|
|
||||||
const audioProgress = aIdx / aChunks.length;
|
|
||||||
|
|
||||||
// Prefer video until audio falls behind target cadence.
|
|
||||||
if (audioProgress + (1 / Math.max(1, audioInterval * aChunks.length)) < videoProgress) {
|
|
||||||
packs.push(makePack(0xC0, aChunks[aIdx++]));
|
packs.push(makePack(0xC0, aChunks[aIdx++]));
|
||||||
} else {
|
|
||||||
packs.push(makePack(0xE0, vChunks[vIdx++]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,28 @@ const forge = require('node-forge');
|
|||||||
const workspaceRoot = __dirname;
|
const workspaceRoot = __dirname;
|
||||||
const httpsDir = path.join(workspaceRoot, '..', 'includes', 'ServiceDeps', 'https');
|
const httpsDir = path.join(workspaceRoot, '..', 'includes', 'ServiceDeps', 'https');
|
||||||
const msnDir = path.join(workspaceRoot, '..', 'includes', 'ServiceDeps', 'msntv2');
|
const msnDir = path.join(workspaceRoot, '..', 'includes', 'ServiceDeps', 'msntv2');
|
||||||
const domainsFile = path.join(msnDir, 'msn_domains.txt');
|
|
||||||
|
|
||||||
const defaultCaCertPath = path.join(msnDir, 'msntv2.crt');
|
const domains = [
|
||||||
const defaultCaKeyPath = path.join(msnDir, 'msntv2.key');
|
"headwaiter.trusted.msntv.msn.com",
|
||||||
const defaultOutCertPath = path.join(msnDir, 'msn_domains.crt');
|
"*.trusted.msntv.msn.com",
|
||||||
const defaultOutKeyPath = path.join(msnDir, 'msn_domains.key');
|
"msntv.msn.com",
|
||||||
|
"mail.services.live.com",
|
||||||
|
"login.live.com",
|
||||||
|
"poptimize.msn.com",
|
||||||
|
"favorites.msn.com",
|
||||||
|
"messenger.msn.com",
|
||||||
|
"livefilestore.com",
|
||||||
|
"users.storage.live.com",
|
||||||
|
"g.msn.com",
|
||||||
|
"msnialogin.passport.com",
|
||||||
|
"minisrv.local",
|
||||||
|
"*.minisrv.local"
|
||||||
|
]
|
||||||
|
|
||||||
|
const defaultCaCertPath = path.join(msnDir, 'emac.crt');
|
||||||
|
const defaultCaKeyPath = path.join(msnDir, 'emac.key');
|
||||||
|
const defaultOutCertPath = path.join(msnDir, 'minisrv.crt');
|
||||||
|
const defaultOutKeyPath = path.join(msnDir, 'minisrv.key');
|
||||||
|
|
||||||
function parseArgs(argv) {
|
function parseArgs(argv) {
|
||||||
const out = {};
|
const out = {};
|
||||||
@@ -31,42 +47,6 @@ function parseArgs(argv) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractDomainsFromRedirectMap(text) {
|
|
||||||
const found = [];
|
|
||||||
const seen = new Set();
|
|
||||||
const re = /"([A-Za-z0-9.-]+\.)"\s*:\s*self\.redirect_ip/g;
|
|
||||||
let match;
|
|
||||||
while ((match = re.exec(text))) {
|
|
||||||
const clean = match[1].replace(/\.$/, '').toLowerCase();
|
|
||||||
if (!seen.has(clean)) {
|
|
||||||
seen.add(clean);
|
|
||||||
found.push(clean);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadDomains(args) {
|
|
||||||
if (args['from-map-file']) {
|
|
||||||
const mapText = fs.readFileSync(path.resolve(workspaceRoot, args['from-map-file']), 'utf8');
|
|
||||||
const domains = extractDomainsFromRedirectMap(mapText);
|
|
||||||
if (!domains.length) {
|
|
||||||
throw new Error('No domains were extracted from --from-map-file.');
|
|
||||||
}
|
|
||||||
return domains;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(domainsFile)) {
|
|
||||||
throw new Error('Domain file not found: ' + domainsFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
const domains = fs.readFileSync(domainsFile, 'utf8')
|
|
||||||
.split(/\r?\n/)
|
|
||||||
.map((s) => s.trim().toLowerCase())
|
|
||||||
.filter((s) => s && !s.startsWith('#'));
|
|
||||||
|
|
||||||
return Array.from(new Set(domains));
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadPemOrThrow(filePath, label) {
|
function loadPemOrThrow(filePath, label) {
|
||||||
if (!fs.existsSync(filePath)) {
|
if (!fs.existsSync(filePath)) {
|
||||||
@@ -99,9 +79,8 @@ function generateCert({ domains, caCertPem, caKeyPem, outCertPath, outKeyPath, y
|
|||||||
cert.publicKey = keys.publicKey;
|
cert.publicKey = keys.publicKey;
|
||||||
cert.serialNumber = forge.util.bytesToHex(forge.random.getBytesSync(16));
|
cert.serialNumber = forge.util.bytesToHex(forge.random.getBytesSync(16));
|
||||||
|
|
||||||
const now = new Date();
|
cert.validity.notBefore = new Date('2000-01-01T12:00:00Z');
|
||||||
cert.validity.notBefore = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
cert.validity.notAfter = new Date('2099-12-31T23:59:59Z');
|
||||||
cert.validity.notAfter = new Date(now.getTime() + years * 365 * 24 * 60 * 60 * 1000);
|
|
||||||
|
|
||||||
const cn = domains[0] || 'headwaiter.trusted.msntv.msn.com';
|
const cn = domains[0] || 'headwaiter.trusted.msntv.msn.com';
|
||||||
cert.setSubject([
|
cert.setSubject([
|
||||||
@@ -141,7 +120,6 @@ function main() {
|
|||||||
const years = Number(args.years || 15);
|
const years = Number(args.years || 15);
|
||||||
const sig = String(args.sig || 'sha1');
|
const sig = String(args.sig || 'sha1');
|
||||||
|
|
||||||
const domains = loadDomains(args);
|
|
||||||
const caCertPem = loadPemOrThrow(caCertPath, 'CA cert');
|
const caCertPem = loadPemOrThrow(caCertPath, 'CA cert');
|
||||||
const caKeyPem = loadPemOrThrow(caKeyPath, 'CA key');
|
const caKeyPem = loadPemOrThrow(caKeyPath, 'CA key');
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIErzCCA5egAwIBAgIQ/K/Ib/7QA0rO4hjgM53sGDANBgkqhkiG9w0BAQUFADB0
|
||||||
|
MRkwFwYDVQQDDBBtaW5pc3J2IHNlcnZpY2VzMREwDwYDVQQIDAhOZXcgWW9yazEL
|
||||||
|
MAkGA1UEBhMCVVMxHjAcBgkqhkiG9w0BCQEWD3plZmllQHplZmllLm5ldDEXMBUG
|
||||||
|
A1UECgwOWmVmaWUgTmV0d29ya3MwIBcNMDAwMTAxMTIwMDAwWhgPMjA5OTEyMzEy
|
||||||
|
MzU5NTlaMFExKTAnBgNVBAMTIGhlYWR3YWl0ZXIudHJ1c3RlZC5tc250di5tc24u
|
||||||
|
Y29tMRcwFQYDVQQKEw5aZWZpZSBOZXR3b3JrczELMAkGA1UEBhMCVVMwggEiMA0G
|
||||||
|
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhh5bdTvB2q03vo/hwikBsfRt3p5Sg
|
||||||
|
Y7N6+e+UfUK8LxZRLwLwk3TIS7LcU+RE6d8UHO8A68joX61kYggtMAGEvYC2JWGC
|
||||||
|
XiUb37CZBJrIMTqU+tFn2zTyCvpNBU7Pv6t/PAKPc0XztrQEZ7RmqywDMbWu2B1M
|
||||||
|
86eil0HN4n4fGMYkVmVjkHJJYNChck+edkL4rDkKnbg4Ar37lYYHofrQDWTAW9eD
|
||||||
|
QSxXBPQbbqDIDHMdXwHnfCbJZIkgQ7ClK78PN6s9DRgagvnXI8vclyW38YQ/cqlk
|
||||||
|
DNkGDGcfAOg9BGtwTAUd7Bu/pAjrAR/WRY5jdv3++4/taibz5hKildA7AgMBAAGj
|
||||||
|
ggFcMIIBWDAJBgNVHRMEAjAAMAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEF
|
||||||
|
BQcDATCCAScGA1UdEQSCAR4wggEagiBoZWFkd2FpdGVyLnRydXN0ZWQubXNudHYu
|
||||||
|
bXNuLmNvbYIXKi50cnVzdGVkLm1zbnR2Lm1zbi5jb22CDW1zbnR2Lm1zbi5jb22C
|
||||||
|
Fm1haWwuc2VydmljZXMubGl2ZS5jb22CDmxvZ2luLmxpdmUuY29tghFwb3B0aW1p
|
||||||
|
emUubXNuLmNvbYIRZmF2b3JpdGVzLm1zbi5jb22CEW1lc3Nlbmdlci5tc24uY29t
|
||||||
|
ghFsaXZlZmlsZXN0b3JlLmNvbYIWdXNlcnMuc3RvcmFnZS5saXZlLmNvbYIJZy5t
|
||||||
|
c24uY29tghdtc25pYWxvZ2luLnBhc3Nwb3J0LmNvbYINbWluaXNydi5sb2NhbIIP
|
||||||
|
Ki5taW5pc3J2LmxvY2FsMA0GCSqGSIb3DQEBBQUAA4IBAQBRY2KlKxhVUCv0h86q
|
||||||
|
J66TAJocqyPEwnnvuEAxM209DhO84GR4+D9r+/U3aV18MN0tUEFOy/qx918zpwgC
|
||||||
|
kNghNmtydvW9phMFB//tX56c8UUT0rYESylKCdYDraCh9G3avI8A5hgJQCgfeUGA
|
||||||
|
l0XJvc/yA3piNAPohLB1zyOBlIONLWJxI4kyKjhOM2mkIkJWmLKXOHGPnnqCUAx+
|
||||||
|
+NzEZiJst22sngmHikN53zKyUfp2DO9CUY7hbjctAKo0GUC/Q0yQmL95idqnw56j
|
||||||
|
Gv1deZcTth/1qUqcZRbQDT+546d87rDJLcQDXs/Q34IcmZa0v7jsIL5hJP+/PtOs
|
||||||
|
fpyG
|
||||||
|
-----END CERTIFICATE-----
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA4YeW3U7wdqtN76P4cIpAbH0bd6eUoGOzevnvlH1CvC8WUS8C
|
||||||
|
8JN0yEuy3FPkROnfFBzvAOvI6F+tZGIILTABhL2AtiVhgl4lG9+wmQSayDE6lPrR
|
||||||
|
Z9s08gr6TQVOz7+rfzwCj3NF87a0BGe0ZqssAzG1rtgdTPOnopdBzeJ+HxjGJFZl
|
||||||
|
Y5BySWDQoXJPnnZC+Kw5Cp24OAK9+5WGB6H60A1kwFvXg0EsVwT0G26gyAxzHV8B
|
||||||
|
53wmyWSJIEOwpSu/DzerPQ0YGoL51yPL3Jclt/GEP3KpZAzZBgxnHwDoPQRrcEwF
|
||||||
|
Hewbv6QI6wEf1kWOY3b9/vuP7Wom8+YSopXQOwIDAQABAoIBAGQK4Q3f2ARUHhjZ
|
||||||
|
HDpT4ZsebiJIaMIzJ+k72J5+aC2RF63AlGXCi3HUPo3E+wPk2KuT4COA9Fi8JG7c
|
||||||
|
m7Lr0iifZWqnL1eEyqJQOobOR5jZWZq/nkebMiPSYdDrs1ettvYUWTBoGpYvLDCu
|
||||||
|
DhTjBn2OPgFG5cx+YxTZUvvw5jCFV7u7CDzC3dD/KvPuGdQPOjBnFM9p9NL7AfRw
|
||||||
|
qOF4lw5M8ZT9caDuM/J77MuPXLVh5p6LlV9auVeo3s03E7BOw6Iutje0ZcBqKkjr
|
||||||
|
lDV1QPkXCbbywW1YQDVv1kv9KgAEG1ftbGzq1REEX8SxNWDSzHl1Q04erqsJAI2y
|
||||||
|
eO4RRVUCgYEA/8vgxZAF38605YPcddSzYzQLLABbQJP+1LFXM8fjpI7I5kb34QNN
|
||||||
|
YY6tjhuha9fyOdFTZb3Qj5bA661Hh26BCwA9mc/X/49SJqzriQiZt9ZXVJ3ri8Xv
|
||||||
|
N6N87ELr0uneVeP2zzjJS3E9G2fGqb2ZnRV3sdKpW2m+BNgOEmSdFrUCgYEA4bWL
|
||||||
|
RhrnN9ZNHWRQTLZSX1ixb9HtFV9AbiPrVPbmzLiqmoV/kppIinr94T3ymlygBTBS
|
||||||
|
mowlQViQbEfivmG12QNIs3W3nxBc2jw7vz3XYA/TANdQeNCW5mmgdKGN8IukVLHe
|
||||||
|
tO4Vu5L7lOck+W+LYVgOTgp/c5tFSMPKB4COMS8CgYBbtfRDwQxqHsl61JkRYg5j
|
||||||
|
DgDHaOVOtQETrvWN6ifzEwJylZVABpgS1z3gioWIjecZ1bQp8TE6mhlXJkxUAUmg
|
||||||
|
8Rgk8oEF7pPMrAjSm9PJNr8e5nPSLEhFUYdzidbVSuZdMxuFVl3Nf68iCUYQC5ts
|
||||||
|
14qPpfD0hmgLgo8hBxPPVQKBgEfy0gmv68K92mkjIAHEIK/qGu21MmkcqvIxGwRD
|
||||||
|
jED4INIO+iKmcbdLC4DweVRBcHUW+U3wnLOe4rLRm3LqvMgHpvYl8TmQQrkCeF02
|
||||||
|
/l/Ny4o6GJnajC+4vgBPu2pRaTniVUbBmkXnzbCimgO1Te3i3vR7njMg7M4MM2t+
|
||||||
|
zFTbAoGBAKh9KJs6t6K6bK4I7L7zmRME4L7TCvzXfnqTJHYjXUIuAPDqTaNZAgCd
|
||||||
|
pV45DfUWIIAis/RswLuR3yT6aH3Wpxx7fTW/DTInvBKfU7Kw2Oyko1jWboS/2E3D
|
||||||
|
0HWVZzdbU4Hj48XGeldjgPV0D1/vr1JRTYJGXtjcNDkRMk3U3xrT
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
354
zefie_wtvp_minisrv/tools/scan_service_vault_deprecations.js
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const ROOT_DIR = path.resolve(__dirname, '..');
|
||||||
|
const DEFAULT_USER_CONFIG_PATH = path.join(ROOT_DIR, 'user_config.json');
|
||||||
|
const DEFAULT_BASE_CONFIG_PATH = path.join(ROOT_DIR, 'includes', 'config.json');
|
||||||
|
const DEPRECIATED_CONFIG_PATH = path.join(ROOT_DIR, 'includes', 'depreciated.json');
|
||||||
|
|
||||||
|
|
||||||
|
function printUsage() {
|
||||||
|
console.log('Usage: node tools/scan_service_vault_deprecations.js [options]');
|
||||||
|
console.log('');
|
||||||
|
console.log('Options:');
|
||||||
|
console.log(' --vault <path> Add a vault root to scan (can be repeated).');
|
||||||
|
console.log(' --config <path> Config file to read ServiceVaults from (default: user_config.json).');
|
||||||
|
console.log(' --base-config <path> Base config fallback (default: includes/config.json).');
|
||||||
|
console.log(' --ext <csv> File extensions to scan (default: .js). Example: --ext .js,.txt');
|
||||||
|
console.log(' --json Emit machine-readable JSON output.');
|
||||||
|
console.log(' --fail-on-found Exit with code 2 if any deprecations are found.');
|
||||||
|
console.log(' -h, --help Show this help text.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJsonWithComments(json) {
|
||||||
|
if (typeof json !== 'string') json = json ? json.toString() : '';
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
let i = 0;
|
||||||
|
let isString = false;
|
||||||
|
let isEscape = false;
|
||||||
|
let isBlockComment = false;
|
||||||
|
let isLineComment = false;
|
||||||
|
|
||||||
|
while (i < json.length) {
|
||||||
|
const ch = json[i];
|
||||||
|
const next = json[i + 1];
|
||||||
|
|
||||||
|
if (!isString && !isEscape && ch === '/' && next === '*') {
|
||||||
|
isBlockComment = true;
|
||||||
|
i += 1;
|
||||||
|
} else if (isBlockComment && ch === '*' && next === '/') {
|
||||||
|
isBlockComment = false;
|
||||||
|
i += 1;
|
||||||
|
} else if (!isString && !isEscape && ch === '/' && next === '/') {
|
||||||
|
isLineComment = true;
|
||||||
|
i += 1;
|
||||||
|
} else if (isLineComment && (ch === '\n' || ch === '\r')) {
|
||||||
|
isLineComment = false;
|
||||||
|
} else if (!isBlockComment && !isLineComment) {
|
||||||
|
if (ch === '"' && !isEscape) {
|
||||||
|
isString = !isString;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEscape = ch === '\\' && !isEscape;
|
||||||
|
result += ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function readConfigIfExists(filePath) {
|
||||||
|
if (!filePath || !fs.existsSync(filePath)) return null;
|
||||||
|
const raw = fs.readFileSync(filePath, 'utf8');
|
||||||
|
if (!raw.trim()) return {};
|
||||||
|
return parseJsonWithComments(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDepreciatedPatterns() {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(DEPRECIATED_CONFIG_PATH)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const raw = fs.readFileSync(DEPRECIATED_CONFIG_PATH, 'utf8');
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
|
||||||
|
if (!Array.isArray(parsed) || parsed.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapped = parsed
|
||||||
|
.filter((entry) => entry && typeof entry.pattern === 'string')
|
||||||
|
.map((entry) => ({
|
||||||
|
id: entry.id || entry.pattern,
|
||||||
|
pattern: new RegExp(entry.pattern, entry.flags || 'g'),
|
||||||
|
message: entry.message || 'Deprecated API usage found',
|
||||||
|
removeVersion: entry.removeVersion || null,
|
||||||
|
replacement: entry.replacement || null
|
||||||
|
}));
|
||||||
|
|
||||||
|
return mapped.length > 0 ? mapped : {};
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Warning: failed to load ${DEPRECIATED_CONFIG_PATH}: ${error.message}`);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(argv) {
|
||||||
|
const args = argv.slice(2);
|
||||||
|
const options = {
|
||||||
|
vaults: [],
|
||||||
|
configPath: DEFAULT_USER_CONFIG_PATH,
|
||||||
|
baseConfigPath: DEFAULT_BASE_CONFIG_PATH,
|
||||||
|
extensions: new Set(['.js']),
|
||||||
|
json: false,
|
||||||
|
failOnFound: false
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i += 1) {
|
||||||
|
const arg = args[i];
|
||||||
|
|
||||||
|
if (arg === '-h' || arg === '--help') {
|
||||||
|
printUsage();
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--vault') {
|
||||||
|
i += 1;
|
||||||
|
const value = args[i];
|
||||||
|
if (!value) throw new Error('Missing value for --vault');
|
||||||
|
options.vaults.push(value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--config') {
|
||||||
|
i += 1;
|
||||||
|
const value = args[i];
|
||||||
|
if (!value) throw new Error('Missing value for --config');
|
||||||
|
options.configPath = path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--base-config') {
|
||||||
|
i += 1;
|
||||||
|
const value = args[i];
|
||||||
|
if (!value) throw new Error('Missing value for --base-config');
|
||||||
|
options.baseConfigPath = path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--ext') {
|
||||||
|
i += 1;
|
||||||
|
const value = args[i];
|
||||||
|
if (!value) throw new Error('Missing value for --ext');
|
||||||
|
|
||||||
|
options.extensions = new Set(
|
||||||
|
value
|
||||||
|
.split(',')
|
||||||
|
.map((ext) => ext.trim().toLowerCase())
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((ext) => (ext.startsWith('.') ? ext : `.${ext}`))
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--json') {
|
||||||
|
options.json = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg === '--fail-on-found') {
|
||||||
|
options.failOnFound = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown argument: ${arg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConfiguredVaults(configPath, baseConfigPath) {
|
||||||
|
const userConfig = readConfigIfExists(configPath) || {};
|
||||||
|
const baseConfig = readConfigIfExists(baseConfigPath) || {};
|
||||||
|
|
||||||
|
const userVaults = userConfig.config && Array.isArray(userConfig.config.ServiceVaults)
|
||||||
|
? userConfig.config.ServiceVaults
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const baseVaults = baseConfig.config && Array.isArray(baseConfig.config.ServiceVaults)
|
||||||
|
? baseConfig.config.ServiceVaults
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const selected = userVaults && userVaults.length > 0 ? userVaults : (baseVaults || []);
|
||||||
|
return selected.map((vault) => path.resolve(ROOT_DIR, String(vault)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function walkFiles(rootDir, extensions, fileList = []) {
|
||||||
|
if (!fs.existsSync(rootDir)) return fileList;
|
||||||
|
|
||||||
|
const entries = fs.readdirSync(rootDir, { withFileTypes: true });
|
||||||
|
for (const entry of entries) {
|
||||||
|
const fullPath = path.join(rootDir, entry.name);
|
||||||
|
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
walkFiles(fullPath, extensions, fileList);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entry.isFile()) continue;
|
||||||
|
const ext = path.extname(entry.name).toLowerCase();
|
||||||
|
if (extensions.has(ext)) {
|
||||||
|
fileList.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toLineColumn(text, index) {
|
||||||
|
let line = 1;
|
||||||
|
let column = 1;
|
||||||
|
for (let i = 0; i < index; i += 1) {
|
||||||
|
if (text[i] === '\n') {
|
||||||
|
line += 1;
|
||||||
|
column = 1;
|
||||||
|
} else {
|
||||||
|
column += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { line, column };
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanFile(filePath, patterns) {
|
||||||
|
const content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const findings = [];
|
||||||
|
|
||||||
|
for (const rule of patterns) {
|
||||||
|
rule.pattern.lastIndex = 0;
|
||||||
|
let match;
|
||||||
|
while ((match = rule.pattern.exec(content)) !== null) {
|
||||||
|
const loc = toLineColumn(content, match.index);
|
||||||
|
findings.push({
|
||||||
|
ruleId: rule.id,
|
||||||
|
match: match[0],
|
||||||
|
message: rule.message,
|
||||||
|
removeVersion: rule.removeVersion,
|
||||||
|
replacement: rule.replacement,
|
||||||
|
line: loc.line,
|
||||||
|
column: loc.column
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return findings;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatRelative(targetPath) {
|
||||||
|
return path.relative(ROOT_DIR, targetPath) || targetPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const options = parseArgs(process.argv);
|
||||||
|
const deprecationPatterns = loadDepreciatedPatterns();
|
||||||
|
if (deprecationPatterns.length === 0) {
|
||||||
|
console.warn('No deprecation patterns found. Exiting without scanning.');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const configuredVaults = getConfiguredVaults(options.configPath, options.baseConfigPath);
|
||||||
|
const explicitVaults = options.vaults.map((vault) => (
|
||||||
|
path.isAbsolute(vault) ? path.resolve(vault) : path.resolve(process.cwd(), vault)
|
||||||
|
));
|
||||||
|
|
||||||
|
const vaultsToScan = [...new Set([...configuredVaults, ...explicitVaults])];
|
||||||
|
if (vaultsToScan.length === 0) {
|
||||||
|
throw new Error('No ServiceVault paths found. Define config.ServiceVaults or pass --vault.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingVaults = [];
|
||||||
|
const filesToScan = [];
|
||||||
|
|
||||||
|
for (const vaultPath of vaultsToScan) {
|
||||||
|
if (!fs.existsSync(vaultPath)) {
|
||||||
|
missingVaults.push(vaultPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
walkFiles(vaultPath, options.extensions, filesToScan);
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
let totalFindings = 0;
|
||||||
|
|
||||||
|
for (const filePath of filesToScan) {
|
||||||
|
const findings = scanFile(filePath, deprecationPatterns);
|
||||||
|
if (findings.length > 0) {
|
||||||
|
totalFindings += findings.length;
|
||||||
|
results.push({ file: filePath, findings });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.json) {
|
||||||
|
const payload = {
|
||||||
|
rootDir: ROOT_DIR,
|
||||||
|
scannedVaults: vaultsToScan,
|
||||||
|
missingVaults,
|
||||||
|
scannedFiles: filesToScan.length,
|
||||||
|
matchedFiles: results.length,
|
||||||
|
totalFindings,
|
||||||
|
results
|
||||||
|
};
|
||||||
|
console.log(JSON.stringify(payload, null, 2));
|
||||||
|
} else {
|
||||||
|
console.log('ServiceVault deprecation scan');
|
||||||
|
console.log('- Deprecation count: ' + deprecationPatterns.length);
|
||||||
|
console.log(`- Vault roots: ${vaultsToScan.length}`);
|
||||||
|
console.log(`- Missing vault roots: ${missingVaults.length}`);
|
||||||
|
console.log(`- Files scanned: ${filesToScan.length}`);
|
||||||
|
console.log(`- Files with deprecations: ${results.length}`);
|
||||||
|
console.log(`- Total deprecations: ${totalFindings}`);
|
||||||
|
|
||||||
|
if (missingVaults.length > 0) {
|
||||||
|
console.log('');
|
||||||
|
console.log('Missing vault roots:');
|
||||||
|
for (const missing of missingVaults) {
|
||||||
|
console.log(` - ${formatRelative(missing)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.length > 0) {
|
||||||
|
console.log('');
|
||||||
|
for (const result of results) {
|
||||||
|
console.log(formatRelative(result.file));
|
||||||
|
for (const finding of result.findings) {
|
||||||
|
console.log(` ${finding.line}:${finding.column} ${finding.ruleId}`);
|
||||||
|
console.log(` ${finding.message}`);
|
||||||
|
if (finding.removeVersion) {
|
||||||
|
console.log(` Remove version: ${finding.removeVersion}`);
|
||||||
|
}
|
||||||
|
console.log(` Match: ${finding.match}`);
|
||||||
|
if (finding.replacement) {
|
||||||
|
console.log(` Fix: ${finding.replacement}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalFindings > 0 && options.failOnFound) {
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
main();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error: ${error.message || error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||