using System; using System.IO; namespace Deploymentcenter.Client; public static class LicenseConfig { private static string? _storageDirectoryOverride; private static string? _hardwareIdOverride; public static string? HardwareIdOverride { get { if (!string.IsNullOrWhiteSpace(_hardwareIdOverride)) return _hardwareIdOverride; var envHwid = Environment.GetEnvironmentVariable("DEPLOYMENTCENTER_HWID") ?? Environment.GetEnvironmentVariable("LICENSELABRADOR_HWID"); if (!string.IsNullOrWhiteSpace(envHwid)) return envHwid; return null; } set => _hardwareIdOverride = value; } public static string GetStorageDirectory(string productSlug) { if (string.IsNullOrWhiteSpace(productSlug)) productSlug = "default_app"; // 1. Explicitly set property // // Auch hier wird /license angehaengt - genau wie beim Weg ueber // die Umgebungsvariable. Zuvor wurde der gesetzte Pfad roh benutzt: // Zwei Produkte im selben Prozess mit gesetztem Override schrieben // damit in dieselbe state.dat und ueberschrieben sich gegenseitig. if (!string.IsNullOrWhiteSpace(_storageDirectoryOverride)) return ValidateNonEmpty(Path.Combine(_storageDirectoryOverride!, productSlug, "license")); // 2. Environment Variable var envDir = Environment.GetEnvironmentVariable("DEPLOYMENTCENTER_STORAGE_DIR") ?? Environment.GetEnvironmentVariable("LICENSELABRADOR_STORAGE_DIR"); if (!string.IsNullOrWhiteSpace(envDir)) return ValidateNonEmpty(Path.Combine(envDir!, productSlug, "license")); // 3. Platform specific resolution if (OperatingSystemHelpers.IsLinux() || OperatingSystemHelpers.IsMacOS()) { var xdg = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); if (!string.IsNullOrWhiteSpace(xdg)) return ValidateNonEmpty(Path.Combine(xdg!, productSlug, "license")); var home = Environment.GetEnvironmentVariable("HOME"); if (!string.IsNullOrWhiteSpace(home)) return ValidateNonEmpty(Path.Combine(home!, ".config", productSlug, "license")); // Dienst ohne Heimatverzeichnis. // // Eine systemd-Unit mit User= und ohne eigenes $HOME hat weder // XDG_CONFIG_HOME noch HOME. Der Rueckfall landete dann im // Installationsverzeichnis - unter /opt fuer den Dienstbenutzer // typischerweise nicht beschreibbar. Ergebnis: kein Offline-Cache, // ausgerechnet im kopflosen Betrieb, fuer den er gedacht ist. // // /var/lib ist der vorgesehene Ort fuer veraenderlichen Zustand // eines Dienstes. Genommen wird er nur, wenn er auch beschreibbar // ist - sonst waere es derselbe stille Fehlschlag an anderer Stelle. var varLib = Path.Combine("/var", "lib", productSlug, "license"); if (CanWriteTo(varLib)) return ValidateNonEmpty(varLib); } else if (OperatingSystemHelpers.IsWindows()) { var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); if (!string.IsNullOrWhiteSpace(appData)) return ValidateNonEmpty(Path.Combine(appData, productSlug, "license")); } // 4. Fallback to AppContext.BaseDirectory var baseDir = AppContext.BaseDirectory; if (!string.IsNullOrWhiteSpace(baseDir)) return ValidateNonEmpty(Path.Combine(baseDir, "license")); throw new InvalidOperationException("Could not resolve valid non-empty StorageDirectory for licensing."); } public static void SetStorageDirectory(string path) { _storageDirectoryOverride = path; } /// /// Laesst sich dort wirklich schreiben? Ein Pfad, den man nur zurueckgibt, /// weil er ueblich ist, hilft nicht weiter - der Fehlschlag faellt dann /// erst beim Speichern auf. /// private static bool CanWriteTo(string directory) { try { Directory.CreateDirectory(directory); string probe = Path.Combine(directory, ".dc-write-test"); File.WriteAllText(probe, string.Empty); File.Delete(probe); return true; } catch { return false; } } private static string ValidateNonEmpty(string path) { if (string.IsNullOrWhiteSpace(path)) throw new InvalidOperationException("StorageDirectory resolved to an empty or invalid path."); return path; } }