Skip to content

Commit

Permalink
Fix privileges evaluation (CnCNet#527)
Browse files Browse the repository at this point in the history
* Fix obtaining administration privileges in .NET 4.8

* Always show security software tips

* Add doc comments to display DisplayErrorAction

* Merge display error actions

* Use .log extension for the temporary log

* Call Logger.Log only if the logger is initialized

* Add a TODO comment for LoggerInitialized

* Apply suggestions from code review

Co-authored-by: Kerbiter <[email protected]>

* {0} -> {1}

---------

Co-authored-by: Kerbiter <[email protected]>
  • Loading branch information
SadPencil and Metadorius committed Oct 4, 2024
1 parent a2a6070 commit 5c46573
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 59 deletions.
30 changes: 0 additions & 30 deletions ClientCore/ProgramConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
using System.Linq;
using System.Text;
using System.Reflection;
#if WINFORMS
using System.Windows.Forms;
#endif
using Rampastring.Tools;
using ClientCore.Extensions;

Expand Down Expand Up @@ -118,33 +115,6 @@ public static string GetAILevelName(int aiLevel)

public static string LogFileName { get; set; }

private static Action<string, string, bool> displayErrorAction = null;
/// <summary>
/// Gets or sets the action to perform to notify the user of an error.
/// </summary>
public static Action<string, string, bool> DisplayErrorAction
{
get => displayErrorAction ??= DefaultDisplayErrorAction;
set => displayErrorAction = value;
}

public static Action<string, string, bool> DefaultDisplayErrorAction = (title, error, exit) =>
{
Logger.Log(FormattableString.Invariant($"{(title is null ? null : title + Environment.NewLine + Environment.NewLine)}{error}"));
#if WINFORMS
#if NETFRAMEWORK
MessageBox.Show(error, title, MessageBoxButtons.OK);
#else
TaskDialog.ShowDialog(new() { Caption = title, Heading = error });
#endif
#else
ProcessLauncher.StartShellProcess(LogFileName);
#endif

if (exit)
Environment.Exit(1);
};

/// <summary>
/// This method finds the "Resources" directory by traversing the directory tree upwards from the startup path.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion DXMainClient/DXGUI/GameClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ protected override void Initialize()

wm.ControlINIAttributeParsers.Add(new TranslationINIParser());

ProgramConstants.DisplayErrorAction = (title, error, exit) =>
MainClientConstants.DisplayErrorAction = (title, error, exit) =>
{
new XNAMessageBox(wm, title, error, XNAMessageBoxButtons.OK)
{
Expand Down
61 changes: 60 additions & 1 deletion DXMainClient/Domain/MainClientConstants.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
using ClientCore;
using System;
using System.IO;

#if WINFORMS
using System.Windows.Forms;
#endif
using ClientCore;

using Rampastring.Tools;

namespace DTAClient.Domain
{
Expand All @@ -20,6 +28,57 @@ public static class MainClientConstants

public static OSVersion OSId = OSVersion.UNKNOWN;

// TODO: remove this variable after `Logger.Initialized` property is implemented by upstream
public static bool LoggerInitialized { get; set; } = false;

private static Action<string, string, bool> displayErrorAction = null;
/// <summary>
/// Gets or sets the action to perform to notify the user of an error.
/// </summary>
public static Action<string, string, bool> DisplayErrorAction
{
get => displayErrorAction ??= DefaultDisplayErrorAction;
set => displayErrorAction = value;
}

/// <summary>
/// Show an error in console as well as a Win32 MessageBox. For non-Windows platforms, this launches a text file in a GUI editor.
/// This action handles errors when XNA windows are not initialized yet.
/// </summary>
/// <param name="title">The title.</param>
/// <param name="error">The error.</param>
/// <param name="exit">Whether the client exits.</param>
public static void DefaultDisplayErrorAction(string title, string error, bool exit)
{
Console.WriteLine(title);
Console.WriteLine();
Console.WriteLine(error);

if (LoggerInitialized)
Logger.Log(FormattableString.Invariant($"{(title is null ? null : title + Environment.NewLine + Environment.NewLine)}{error}"));

#if WINFORMS
MessageBox.Show(error, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
#else
if (LoggerInitialized)
ProcessLauncher.StartShellProcess(ProgramConstants.LogFileName);
else
{
string tempfile = SafePath.CombineFilePath(Path.GetTempPath(), "xna-cncnet-client-error.log");
using (StreamWriter writer = new StreamWriter(tempfile))
{
writer.WriteLine(title);
writer.WriteLine();
writer.WriteLine(error);
}
ProcessLauncher.StartShellProcess(tempfile);
}
#endif

if (exit)
Environment.Exit(1);
}

public static void Initialize()
{
var clientConfiguration = ClientConfiguration.Instance;
Expand Down
53 changes: 26 additions & 27 deletions DXMainClient/PreStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ public static void Initialize(StartupParams parameters)

Environment.CurrentDirectory = gameDirectory.FullName;

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
CheckPermissions();

DirectoryInfo clientUserFilesDirectory = SafePath.GetDirectory(ProgramConstants.ClientUserFilesPath);
FileInfo clientLogFile = SafePath.GetFile(clientUserFilesDirectory.FullName, "client.log");
ProgramConstants.LogFileName = clientLogFile.FullName;

if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
CheckPermissions();

if (clientLogFile.Exists)
{
// Copy client.log file as client_previous.log. Override client_previous.log if it exists.
Expand All @@ -82,6 +82,7 @@ public static void Initialize(StartupParams parameters)

Logger.Initialize(clientUserFilesDirectory.FullName, clientLogFile.Name);
Logger.WriteLogFile = true;
MainClientConstants.LoggerInitialized = true;

if (!clientUserFilesDirectory.Exists)
clientUserFilesDirectory.Create();
Expand Down Expand Up @@ -189,20 +190,9 @@ public static void Initialize(StartupParams parameters)
"applications that could be using the file, and then start the client again." + "\n\n" +
"Message:").L10N("Client:Main:DeleteWsock32Failed") + " " + ex.Message;

ProgramConstants.DisplayErrorAction(null, error, true);
MainClientConstants.DisplayErrorAction(null, error, true);
}

#if WINFORMS
#if NET6_0_OR_GREATER
// .NET 6.0 brings a source generator ApplicationConfiguration which is not available in previous .NET versions
// https://medium.com/c-sharp-progarmming/whats-new-in-windows-forms-in-net-6-0-840c71856751
ApplicationConfiguration.Initialize();
#else
global::System.Windows.Forms.Application.EnableVisualStyles();
global::System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
#endif
#endif

Startup startup = new();
#if DEBUG
startup.Execute();
Expand All @@ -213,9 +203,9 @@ public static void Initialize(StartupParams parameters)
}
catch (Exception ex)
{
// ProgramConstants.DisplayErrorAction might have been overriden by XNA messagebox, which might be unable to display an error message.
// MainClientConstants.DisplayErrorAction might have been overriden by XNA messagebox, which might be unable to display an error message.
// Fallback to MessageBox.
ProgramConstants.DisplayErrorAction = ProgramConstants.DefaultDisplayErrorAction;
MainClientConstants.DisplayErrorAction = MainClientConstants.DefaultDisplayErrorAction;
HandleException(startup, ex);
}
#endif
Expand Down Expand Up @@ -268,7 +258,7 @@ static void HandleException(object sender, Exception ex)
MainClientConstants.GAME_NAME_SHORT,
MainClientConstants.SUPPORT_URL_SHORT);

ProgramConstants.DisplayErrorAction("KABOOOOOOOM".L10N("Client:Main:FatalErrorTitle"), error, true);
MainClientConstants.DisplayErrorAction("KABOOOOOOOM".L10N("Client:Main:FatalErrorTitle"), error, true);
}

[SupportedOSPlatform("windows")]
Expand All @@ -279,18 +269,27 @@ private static void CheckPermissions()

string error = string.Format(("You seem to be running {0} from a write-protected directory.\n\n" +
"For {1} to function properly when run from a write-protected directory, it needs administrative priveleges.\n\n" +
"Would you like to restart the client with administrative rights?\n\n" +
"Please also make sure that your security software isn't blocking {1}.").L10N("Client:Main:AdminRequiredText"), MainClientConstants.GAME_NAME_LONG, MainClientConstants.GAME_NAME_SHORT);
"Please also make sure that your security software isn't blocking {1}.").L10N("Client:Main:AdminRequiredExplanation"),
MainClientConstants.GAME_NAME_LONG, MainClientConstants.GAME_NAME_SHORT);

string question = "Would you like to restart the client with administrative rights?".L10N("Client:Main:AdminRequiredRestartPrompt");

ProgramConstants.DisplayErrorAction("Administrative privileges required".L10N("Client:Main:AdminRequiredTitle"), error, false);
string title = "Administrative privileges required".L10N("Client:Main:AdminRequiredTitle");

using var _ = Process.Start(new ProcessStartInfo
#if WINFORMS && NETFRAMEWORK
DialogResult result = MessageBox.Show(error + "\n\n" + question, title, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
if (result == DialogResult.Yes)
{
FileName = "dotnet",
Arguments = SafePath.CombineFilePath(ProgramConstants.StartupExecutable),
Verb = "runas",
CreateNoWindow = true
});
using var _ = Process.Start(new ProcessStartInfo
{
FileName = SafePath.CombineFilePath(ProgramConstants.StartupExecutable),
Verb = "runas",
UseShellExecute = true,
});
}
#else
MainClientConstants.DisplayErrorAction(title, error, true);
#endif
Environment.Exit(1);
}

Expand Down
15 changes: 15 additions & 0 deletions DXMainClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ Yuri has won
private static string COMMON_LIBRARY_PATH;
private static string SPECIFIC_LIBRARY_PATH;

static void InitializeApplicationConfiguration() {
#if WINFORMS
#if NET6_0_OR_GREATER
// .NET 6.0 brings a source generator ApplicationConfiguration which is not available in previous .NET versions
// https://medium.com/c-sharp-progarmming/whats-new-in-windows-forms-in-net-6-0-840c71856751
ApplicationConfiguration.Initialize();
#else
System.Windows.Forms.Application.EnableVisualStyles();
System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
#endif
#endif
}

/// <summary>
/// The main entry point for the application.
/// </summary>
Expand All @@ -65,6 +78,8 @@ Yuri has won
#endif
static void Main(string[] args)
{
InitializeApplicationConfiguration();

bool noAudio = false;
bool multipleInstanceMode = false;
List<string> unknownStartupParams = new List<string>();
Expand Down

0 comments on commit 5c46573

Please sign in to comment.