using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace SQLInsight.Core.Servers;
/// Task Scheduler's result codes (LastTaskResult) in Danish, and which of them mean the task failed.
public static partial class TaskResults
{
public const uint Success = 0;
public const uint Ready = 0x41300;
public const uint Running = 0x41301;
public const uint Disabled = 0x41302;
public const uint HasNotRun = 0x41303;
public const uint NoMoreRuns = 0x41304;
public const uint TerminatedByUser = 0x41306;
public const uint Queued = 0x41325;
public const uint AlreadyRunning = 0x8004131F;
/// Whether the last run ended badly. Running, queued, never run and "already running" are not failures.
public static bool IsFailure(uint? result) => result is { } r && r is not (Success or Ready or Running or Disabled or HasNotRun or NoMoreRuns or Queued or AlreadyRunning);
public static string Hex(uint result) => "0x" + result.ToString("X", CultureInfo.InvariantCulture);
public static string Describe(uint result) => result switch
{
Success => "Fuldført (0x0)",
Ready => "Klar (0x41300)",
Running => "Kører nu (0x41301)",
Disabled => "Deaktiveret (0x41302)",
HasNotRun => "Har ikke kørt endnu (0x41303)",
NoMoreRuns => "Ingen flere planlagte kørsler (0x41304)",
TerminatedByUser => "Afbrudt af en bruger eller fordi den kørte for længe (0x41306)",
Queued => "I kø (0x41325)",
AlreadyRunning => "Kørte allerede, en ny kørsel blev ikke startet (0x8004131F)",
0x1 => "Programmet sluttede med fejlkode 1 (typisk en fejl i scriptet eller programmet) (0x1)",
0x2 => "Filen blev ikke fundet (0x2)",
0xC000013A => "Programmet blev afbrudt (Ctrl+C eller nedlukning) (0xC000013A)",
0x80070002 => "Filen blev ikke fundet (0x80070002)",
0x80070005 => "Adgang nægtet: kontoen, opgaven kører som, mangler rettigheder (0x80070005)",
0x8007052E => "Logon fejlede: forkert brugernavn eller adgangskode til opgavens konto (0x8007052E)",
0x800710E0 => "Opgaven blev afvist (fx fordi computeren kørte på batteri eller kontoen ikke var logget på) (0x800710E0)",
0x80041326 => "Opgaven er deaktiveret (0x80041326)",
_ => "Sluttede med fejlkode " + Hex(result),
};
/// Whether a task is Microsoft's own (\Microsoft\…), which is not watched unless asked for.
public static bool IsMicrosoftTask(string path)
{
ArgumentNullException.ThrowIfNull(path);
return path.StartsWith(@"\Microsoft\", StringComparison.OrdinalIgnoreCase);
}
///
/// Tasks that programs create per user or to update themselves, in the root folder rather than under \Microsoft\:
/// they are not the server's own work, and their failures are rarely anyone's business. A name, or a prefix ending in *.
///
public static readonly IReadOnlyList DefaultIgnoredNames =
[
"OneDrive Standalone Update Task*",
"OneDrive Per-Machine Standalone Update Task*",
"OneDrive Reporting Task*",
"OneDrive Startup Task*",
"MicrosoftEdgeUpdateTask*",
"GoogleUpdateTask*",
"GoogleUpdaterTask*",
"User_Feed_Synchronization-*",
];
///
/// Whether a task belongs to one user (its name carries the user's SID, S-1-5-21-…) or is a program's own updater
/// (). Such tasks are not watched unless asked for.
///
public static bool IsUserOrUpdaterTask(string path)
{
ArgumentNullException.ThrowIfNull(path);
if (path.Contains("S-1-5-21-", StringComparison.OrdinalIgnoreCase))
{
return true;
}
var name = path[(path.LastIndexOf('\\') + 1)..];
return DefaultIgnoredNames.Any(p => p.EndsWith('*')
? name.StartsWith(p[..^1], StringComparison.OrdinalIgnoreCase)
: name.Equals(p, StringComparison.OrdinalIgnoreCase));
}
/// Whether the task's path names a user by SID.
public static bool HasUserSid(string path)
{
ArgumentNullException.ThrowIfNull(path);
return Sid().IsMatch(path);
}
///
/// The task's path for titles and subjects: a user's SID is replaced by "bruger-" and a short hash of it, which tells
/// two users' tasks apart without naming the user.
///
public static string Display(string path)
{
ArgumentNullException.ThrowIfNull(path);
return Sid().Replace(path, m => "bruger-" + Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(m.Value.ToUpperInvariant())))[..8].ToLowerInvariant());
}
[GeneratedRegex(@"S-1-5-21(-[0-9]+)+", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
private static partial Regex Sid();
}