#!/usr/bin/env python3
#
# A fake RD Web Access server for the tests: the feed behind the NTLM login,
# the forms ticket it hands out, .rdp files and icons. It checks the NTLMv2
# answer for real, against one user and password.
#
# python3 tests/fake_rdweb.py [port] serve until Ctrl+C
#
# Copyright (C) 2026 Felitendo
# SPDX-License-Identifier: GPL-3.0-or-later
import base64
import hmac
import http.server
import os
import socketserver
import struct
import sys
import threading
import urllib.parse
import importlib.machinery # noqa: E402
import importlib.util # noqa: E402
# rdfeed-fetch has no .py ending, so it is loaded by path.
_loader = importlib.machinery.SourceFileLoader(
"rdfeed_fetch", os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "src", "fetch", "rdfeed-fetch"))
_spec = importlib.util.spec_from_loader("rdfeed_fetch", _loader)
fetch = importlib.util.module_from_spec(_spec)
_loader.exec_module(fetch)
DOMAIN = "CONTOSO"
USER = "jdoe"
PASSWORD = "Correct horse 1"
TOKEN = "5EC2E7F00DBA11ADE0F7EED5ACC0FFEE"
APPS = [
("winword-RemoteApps-CmsRdsh", "Word", "RemoteApp"),
("excel-RemoteApps-CmsRdsh", "Excel", "RemoteApp"),
("powerpnt-RemoteApps-CmsRdsh", "PowerPoint", "RemoteApp"),
("desktop-RemoteApps-CmsRdsh", "Contoso Desktop", "Desktop"),
]
def extensions(alias):
"""Excel comes with its file types, as an administrator would enter them."""
if not alias.startswith("excel"):
return ""
return (''
'')
def feed_xml():
resources = ""
for alias, title, kind in APPS:
resources += f"""
{extensions(alias)}
"""
return f"""
{resources}
""".encode()
def rdp_file(alias, title, kind):
lines = [
"full address:s:RDBROKER.contoso.test",
"gatewayhostname:s:gateway.contoso.test",
"gatewayusagemethod:i:1",
"promptcredentialonce:i:0",
"prompt for credentials on client:i:1",
"drivestoredirect:s:*",
"devicestoredirect:s:*",
"camerastoredirect:s:*",
"loadbalanceinfo:s:tsv://MS Terminal Services Plugin.1.RemoteApps",
]
if kind == "RemoteApp":
lines += ["remoteapplicationmode:i:1", f"remoteapplicationprogram:s:||{alias.split('-')[0]}",
f"remoteapplicationname:s:{title}"]
return ("\r\n".join(lines) + "\r\n").encode("utf-16")
def ico_32bpp(size=48, rgba=(30, 110, 220, 255)):
"""An .ico with one 32-bit bitmap frame, the way Windows writes them."""
r, g, b, a = rgba
header = struct.pack("not here