mirror of
https://github.com/tribufu/ServerManagers
synced 2026-05-06 15:17:34 +00:00
source code checkin
This commit is contained in:
parent
5f8fb2c825
commit
7e57b72e35
675 changed files with 168433 additions and 0 deletions
48
src/ServerManager.Common/Utils/AppUtils.cs
Normal file
48
src/ServerManager.Common/Utils/AppUtils.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Xml;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class AppUtils
|
||||
{
|
||||
public static string GetDeployedVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
return GetDeployedVersion(assembly);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDeployedVersion(Assembly assembly)
|
||||
{
|
||||
if (assembly == null)
|
||||
return "Unknown";
|
||||
|
||||
try
|
||||
{
|
||||
string executePath = new Uri(assembly.GetName().CodeBase).LocalPath;
|
||||
|
||||
var xmlDoc = new XmlDocument();
|
||||
xmlDoc.Load(executePath + ".manifest");
|
||||
|
||||
var ns = new XmlNamespaceManager(xmlDoc.NameTable);
|
||||
ns.AddNamespace("asmv1", "urn:schemas-microsoft-com:asm.v1");
|
||||
|
||||
var xPath = "/asmv1:assembly/asmv1:assemblyIdentity/@version";
|
||||
var node = xmlDoc.SelectSingleNode(xPath, ns);
|
||||
|
||||
return node.Value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
173
src/ServerManager.Common/Utils/AssemblyUtils.cs
Normal file
173
src/ServerManager.Common/Utils/AssemblyUtils.cs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class AssemblyUtils
|
||||
{
|
||||
public static string GetAttributePropertyValue(Assembly assembly, Type attributeType, string propertyName)
|
||||
{
|
||||
var attributes = (Attribute[])assembly.GetCustomAttributes(attributeType, true);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
PropertyInfo property = attributeType.GetProperty(propertyName, typeof(string));
|
||||
if (property != null)
|
||||
{
|
||||
//Get value on first attribute.
|
||||
return (string)property.GetValue(attributes[0], null);
|
||||
}
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
public static string GetBuildDate()
|
||||
{
|
||||
return GetBuildDate(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetBuildDate(Assembly assembly)
|
||||
{
|
||||
if (assembly != null && assembly.Location != null)
|
||||
{
|
||||
return File.GetLastWriteTime(assembly.Location).ToString();
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetCompanyName()
|
||||
{
|
||||
return GetCompanyName(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetCompanyName(Assembly assembly)
|
||||
{
|
||||
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attribute = (AssemblyCompanyAttribute)attributes[0];
|
||||
if (!string.IsNullOrWhiteSpace(attribute.Company))
|
||||
{
|
||||
return attribute.Company;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetCopyrightText()
|
||||
{
|
||||
return GetCopyrightText(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetCopyrightText(Assembly assembly)
|
||||
{
|
||||
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attribute = (AssemblyCopyrightAttribute)attributes[0];
|
||||
if (!string.IsNullOrWhiteSpace(attribute.Copyright))
|
||||
{
|
||||
return attribute.Copyright;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetDescription()
|
||||
{
|
||||
return GetDescription(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetDescription(Assembly assembly)
|
||||
{
|
||||
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attribute = (AssemblyDescriptionAttribute)attributes[0];
|
||||
if (!string.IsNullOrWhiteSpace(attribute.Description))
|
||||
{
|
||||
return attribute.Description;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetFileVersion()
|
||||
{
|
||||
return GetFileVersion(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetFileVersion(Assembly assembly)
|
||||
{
|
||||
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attribute = (AssemblyFileVersionAttribute)attributes[0];
|
||||
if (!string.IsNullOrWhiteSpace(attribute.Version))
|
||||
{
|
||||
return attribute.Version;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetProductName()
|
||||
{
|
||||
return GetProductName(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetProductName(Assembly assembly)
|
||||
{
|
||||
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyProductAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attribute = (AssemblyProductAttribute)attributes[0];
|
||||
if (!string.IsNullOrWhiteSpace(attribute.Product))
|
||||
{
|
||||
return attribute.Product;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetTitle()
|
||||
{
|
||||
return GetTitle(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetTitle(Assembly assembly)
|
||||
{
|
||||
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attribute = (AssemblyTitleAttribute)attributes[0];
|
||||
if (!string.IsNullOrWhiteSpace(attribute.Title))
|
||||
{
|
||||
return attribute.Title;
|
||||
}
|
||||
}
|
||||
// if there is no title attribute or if the title attribute was an empty string, then return the .exe name
|
||||
return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
|
||||
}
|
||||
|
||||
public static string GetTrademark()
|
||||
{
|
||||
return GetTrademark(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetTrademark(Assembly assembly)
|
||||
{
|
||||
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyTrademarkAttribute), false);
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
var attribute = (AssemblyTrademarkAttribute)attributes[0];
|
||||
if (!string.IsNullOrWhiteSpace(attribute.Trademark))
|
||||
{
|
||||
return attribute.Trademark;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static string GetVersion()
|
||||
{
|
||||
return GetVersion(Assembly.GetEntryAssembly());
|
||||
}
|
||||
public static string GetVersion(Assembly assembly)
|
||||
{
|
||||
return assembly.GetName().Version.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/ServerManager.Common/Utils/DateTimeUtils.cs
Normal file
20
src/ServerManager.Common/Utils/DateTimeUtils.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class DateTimeUtils
|
||||
{
|
||||
public static double DateTimeToUnixTimestamp(DateTime dateTime)
|
||||
{
|
||||
TimeSpan timespan = (dateTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc));
|
||||
return timespan.TotalSeconds;
|
||||
}
|
||||
|
||||
public static DateTime UnixTimeStampToDateTime(double unixTimeStamp)
|
||||
{
|
||||
// Unix timestamp is seconds past epoch
|
||||
DateTime datetime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
|
||||
return datetime.AddSeconds(unixTimeStamp).ToLocalTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/ServerManager.Common/Utils/DebugUtils.cs
Normal file
13
src/ServerManager.Common/Utils/DebugUtils.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class DebugUtils
|
||||
{
|
||||
public static async Task WriteFormatThreadSafeAsync(string format, params object[] args)
|
||||
{
|
||||
await TaskUtils.RunOnUIThreadAsync(() => Debug.WriteLine(format, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
137
src/ServerManager.Common/Utils/EmailUtil.cs
Normal file
137
src/ServerManager.Common/Utils/EmailUtil.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public class EmailUtil
|
||||
{
|
||||
public EmailUtil()
|
||||
{
|
||||
Credentials = null;
|
||||
DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;
|
||||
EnableSsl = false;
|
||||
MailServer = string.Empty;
|
||||
Port = 25;
|
||||
Timeout = 100000;
|
||||
UseDefaultCredentials = false;
|
||||
}
|
||||
|
||||
|
||||
public ICredentialsByHost Credentials
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DeliveryNotificationOptions DeliveryNotificationOptions
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool EnableSsl
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string MailServer
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int Port
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public int Timeout
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool UseDefaultCredentials
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
public void SendEmail(string fromAddress, string toAddress, string subject, string body, bool isBodyHtml)
|
||||
{
|
||||
SendEmailImplementation(fromAddress, new[] { toAddress }, subject, body, isBodyHtml, null);
|
||||
}
|
||||
|
||||
public void SendEmail(string fromAddress, string toAddress, string subject, string body, bool isBodyHtml, Attachment[] mailAttachments)
|
||||
{
|
||||
SendEmailImplementation(fromAddress, new[] { toAddress }, subject, body, isBodyHtml, mailAttachments);
|
||||
}
|
||||
|
||||
public void SendEmail(string fromAddress, string[] toAddresses, string subject, string body, bool isBodyHtml)
|
||||
{
|
||||
SendEmailImplementation(fromAddress, toAddresses, subject, body, isBodyHtml, null);
|
||||
}
|
||||
|
||||
public void SendEmail(string fromAddress, string[] toAddresses, string subject, string body, bool isBodyHtml, Attachment[] mailAttachments)
|
||||
{
|
||||
SendEmailImplementation(fromAddress, toAddresses, subject, body, isBodyHtml, mailAttachments);
|
||||
}
|
||||
|
||||
private void SendEmailImplementation(string fromAddress, IEnumerable<string> toAddresses, string subject, string body, bool isBodyHtml, IEnumerable<Attachment> mailAttachments)
|
||||
{
|
||||
// Format mail message
|
||||
using (var mailMessage = new MailMessage())
|
||||
{
|
||||
mailMessage.From = new MailAddress(fromAddress);
|
||||
mailMessage.Subject = subject;
|
||||
mailMessage.Body = body;
|
||||
mailMessage.IsBodyHtml = isBodyHtml;
|
||||
mailMessage.DeliveryNotificationOptions = DeliveryNotificationOptions;
|
||||
|
||||
if (mailAttachments != null)
|
||||
{
|
||||
foreach (var mailAttachment in mailAttachments)
|
||||
{
|
||||
if (mailAttachment == null)
|
||||
continue;
|
||||
mailMessage.Attachments.Add(mailAttachment);
|
||||
}
|
||||
}
|
||||
|
||||
mailMessage.To.Clear();
|
||||
foreach (var toAddress in toAddresses)
|
||||
{
|
||||
mailMessage.To.Add(new MailAddress(toAddress));
|
||||
}
|
||||
|
||||
if (mailMessage.To.Count > 0)
|
||||
{
|
||||
using (var smptClient = new SmtpClient())
|
||||
{
|
||||
smptClient.Host = MailServer;
|
||||
smptClient.Port = Port;
|
||||
smptClient.DeliveryMethod = SmtpDeliveryMethod.Network;
|
||||
smptClient.EnableSsl = EnableSsl;
|
||||
smptClient.Timeout = Timeout;
|
||||
smptClient.UseDefaultCredentials = UseDefaultCredentials;
|
||||
smptClient.Credentials = Credentials;
|
||||
|
||||
try
|
||||
{
|
||||
smptClient.Send(mailMessage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("An error occurred trying to send an email.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/ServerManager.Common/Utils/FirewallUtils.cs
Normal file
107
src/ServerManager.Common/Utils/FirewallUtils.cs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using WindowsFirewallHelper;
|
||||
using WindowsFirewallHelper.FirewallAPIv2.Rules;
|
||||
|
||||
/*
|
||||
WindowsFirewallHelper
|
||||
A class library to manage the Windows Firewall as well as adding your program to the Windows Firewall Exception list
|
||||
|
||||
=========================================================================
|
||||
USAGE
|
||||
Get an instance of the active firewall using FirewallManager class and
|
||||
use the properties to get the list of firewall rules and profiles.
|
||||
You can also use the methods on this class to add a new rule to the
|
||||
firewall.
|
||||
|
||||
CODE SAMPLE FOR ADDING AN APPLICATION RULE TO THE ACTIVE PROFILE:
|
||||
var rule = FirewallManager.Instance.CreateApplicationRule(FirewallManager.Instance.GetProfile().Type, @"MyApp Rule", FirewallAction.Allow, @"C:\MyApp.exe");
|
||||
rule.Direction = FirewallDirection.Outbound;
|
||||
FirewallManager.Instance.Rules.Add(rule);
|
||||
|
||||
CODE SAMPLE FOR ADDING A PORT RULE TO THE ACTIVE PROFILE:
|
||||
var rule = FirewallManager.Instance.CreatePortRule(FirewallManager.Instance.GetProfile().Type, @"Port 80 - Any Protocol", FirewallAction.Allow, 80, FirewallProtocol.Any);
|
||||
FirewallManager.Instance.Rules.Add(rule);
|
||||
|
||||
CODE SAMPLE TO GET A LIST OF ALL REGISTERED RULES:
|
||||
var allRules = FirewallManager.Instance.Rules.ToArray();
|
||||
|
||||
MORE SAMPLES:
|
||||
Check the Project's Github page at: https://github.com/falahati/WindowsFirewallHelper
|
||||
*/
|
||||
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Code for dealing with firewalls
|
||||
/// </summary>
|
||||
public static class FirewallUtils
|
||||
{
|
||||
public static bool CreateFirewallRules(string exeName, List<int> ports, string ruleName, string description = "")
|
||||
{
|
||||
var firewallManager = FirewallManager.Instance;
|
||||
|
||||
if (firewallManager.IsSupported)
|
||||
{
|
||||
DeleteFirewallRules(exeName);
|
||||
|
||||
// create the TCP rule
|
||||
var rule = firewallManager.CreateApplicationRule(FirewallProfiles.Domain | FirewallProfiles.Private | FirewallProfiles.Public, $"{ruleName} TCP", FirewallAction.Allow, exeName, FirewallProtocol.TCP);
|
||||
if (rule != null)
|
||||
{
|
||||
rule.Direction = FirewallDirection.Inbound;
|
||||
rule.IsEnable = true;
|
||||
rule.LocalPorts = ports.Select(p => (ushort)p).ToArray();
|
||||
|
||||
if (rule is StandardRule)
|
||||
{
|
||||
((StandardRule)rule).Description = description;
|
||||
}
|
||||
firewallManager.Rules.Add(rule);
|
||||
}
|
||||
|
||||
// create the UDP rule
|
||||
rule = firewallManager.CreateApplicationRule(FirewallProfiles.Domain | FirewallProfiles.Private | FirewallProfiles.Public, $"{ruleName} UDP", FirewallAction.Allow, exeName, FirewallProtocol.UDP);
|
||||
if (rule != null)
|
||||
{
|
||||
rule.Direction = FirewallDirection.Inbound;
|
||||
rule.IsEnable = true;
|
||||
rule.LocalPorts = ports.Select(p => (ushort)p).ToArray();
|
||||
|
||||
if (rule is StandardRule)
|
||||
{
|
||||
((StandardRule)rule).Description = description;
|
||||
}
|
||||
firewallManager.Rules.Add(rule);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool DeleteFirewallRules(string exeName)
|
||||
{
|
||||
var firewallManager = FirewallManager.Instance;
|
||||
|
||||
if (firewallManager.IsSupported)
|
||||
{
|
||||
// check for existing rules
|
||||
var rulesToDelete = firewallManager.Rules.Cast<StandardRule>().Where(r => !string.IsNullOrWhiteSpace(r.ApplicationName) && r.ApplicationName.Equals(exeName, StringComparison.OrdinalIgnoreCase)).ToList();
|
||||
|
||||
if (rulesToDelete != null && rulesToDelete.Count > 0)
|
||||
{
|
||||
// delete the existing rules
|
||||
rulesToDelete.ForEach(r => firewallManager.Rules.Remove(r));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/ServerManager.Common/Utils/IOUtils.cs
Normal file
20
src/ServerManager.Common/Utils/IOUtils.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class IOUtils
|
||||
{
|
||||
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool DeleteFile(string name);
|
||||
|
||||
public static bool Unblock(string fileName)
|
||||
{
|
||||
return DeleteFile(fileName + ":Zone.Identifier");
|
||||
}
|
||||
|
||||
public static string NormalizePath(string path) => Path.GetFullPath(new Uri(path).LocalPath).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
172
src/ServerManager.Common/Utils/IniFileUtils.cs
Normal file
172
src/ServerManager.Common/Utils/IniFileUtils.cs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
using ServerManagerTool.Common.Serialization;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class IniFileUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves all the keys and values for the specified section of an initialization file.
|
||||
/// </summary>
|
||||
/// <param name="file">The name of the initialization file.</param>
|
||||
/// <param name="sectionName">The name of the section in the initialization file.</param>
|
||||
/// <returns>A string array containing the key name and value pairs associated with the named section.</returns>
|
||||
public static string[] ReadSection(string file, string sectionName)
|
||||
{
|
||||
if (sectionName == null)
|
||||
return new string[0];
|
||||
|
||||
var iniFile = ReadFromFile(file);
|
||||
return iniFile?.GetSection(sectionName)?.KeysToStringArray() ?? new string[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a string from the specified section in an initialization file.
|
||||
/// </summary>
|
||||
/// <param name="file">The name of the initialization file.</param>
|
||||
/// <param name="sectionName">The name of the section containing the key name.</param>
|
||||
/// <param name="keyName">The name of the key whose associated string is to be retrieved.</param>
|
||||
/// <param name="defaultValue">A default string. If the keyName key cannot be found in the initialization file, the default string is returned. If this parameter is NULL, the default is an empty string, "".</param>
|
||||
/// <returns></returns>
|
||||
public static string ReadValue(string file, string sectionName, string keyName, string defaultValue)
|
||||
{
|
||||
if (sectionName == null || keyName == null)
|
||||
return defaultValue ?? string.Empty;
|
||||
|
||||
var iniFile = ReadFromFile(file);
|
||||
return iniFile?.GetSection(sectionName)?.GetKey(keyName)?.KeyValue ?? defaultValue ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the keys and values for the specified section in an initialization file.
|
||||
/// </summary>
|
||||
/// <param name="file">The name of the initialization file.</param>
|
||||
/// <param name="sectionName">The name of the section in which data is written.</param>
|
||||
/// <param name="keysValuePairs">An array of key names and associated values that are to be written to the named section.</param>
|
||||
/// <returns>True if the function succeeds; otherwise False.</returns>
|
||||
public static bool WriteSection(string file, string sectionName, string[] keysValuePairs)
|
||||
{
|
||||
if (sectionName == null)
|
||||
return false;
|
||||
|
||||
var iniFile = ReadFromFile(file) ?? new IniFile();
|
||||
|
||||
var result = iniFile.WriteSection(sectionName, keysValuePairs);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
return result = SaveToFile(file, iniFile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies a string into the specified section of an initialization file.
|
||||
/// </summary>
|
||||
/// <param name="file">The name of the initialization file.</param>
|
||||
/// <param name="sectionName">The name of the section to which the string will be copied. If the section does not exist, it is created. The name of the section is case-independent; the string can be any combination of uppercase and lowercase letters.</param>
|
||||
/// <param name="keyName">The name of the key to be associated with a string. If the key does not exist in the specified section, it is created. If this parameter is NULL, the entire section, including all entries within the section, is deleted.</param>
|
||||
/// <param name="keyValue">A null-terminated string to be written to the file. If this parameter is NULL, the key pointed to by the keyName parameter is deleted.</param>
|
||||
/// <returns>True if the function succeeds; otherwise False.</returns>
|
||||
public static bool WriteValue(string file, string sectionName, string keyName, string keyValue)
|
||||
{
|
||||
if (sectionName == null)
|
||||
return false;
|
||||
|
||||
var iniFile = ReadFromFile(file) ?? new IniFile();
|
||||
|
||||
var result = iniFile.WriteKey(sectionName, keyName, keyValue);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
return result = SaveToFile(file, iniFile);
|
||||
}
|
||||
|
||||
public static IniFile ReadFromFile(string file)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file))
|
||||
return null;
|
||||
|
||||
if (!File.Exists(file))
|
||||
return new IniFile();
|
||||
|
||||
var iniFile = new IniFile();
|
||||
|
||||
using (StreamReader reader = new StreamReader(file))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = reader.ReadLine();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith(";") || line.StartsWith("#"))
|
||||
continue;
|
||||
|
||||
var sectionName = Regex.Match(line, @"(?<=^\[).*(?=\]$)").Value.Trim();
|
||||
|
||||
var section = iniFile.AddSection(sectionName);
|
||||
if (section != null)
|
||||
continue;
|
||||
|
||||
iniFile.AddKey(line);
|
||||
}
|
||||
|
||||
reader.Close();
|
||||
}
|
||||
|
||||
return iniFile;
|
||||
}
|
||||
|
||||
public static IniFile ReadString(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return new IniFile();
|
||||
|
||||
var iniFile = new IniFile();
|
||||
|
||||
var lines = text.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
|
||||
|
||||
for (int index = 0; index < lines.Length; index++)
|
||||
{
|
||||
var line = lines[index].Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith(";") || line.StartsWith("#"))
|
||||
continue;
|
||||
|
||||
var sectionName = Regex.Match(line, @"(?<=^\[).*(?=\]$)").Value.Trim();
|
||||
|
||||
var section = iniFile.AddSection(sectionName);
|
||||
if (section != null)
|
||||
continue;
|
||||
|
||||
iniFile.AddKey(line);
|
||||
}
|
||||
|
||||
return iniFile;
|
||||
}
|
||||
|
||||
public static bool SaveToFile(string file, IniFile iniFile)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file) || iniFile == null)
|
||||
return false;
|
||||
|
||||
using (StreamWriter writer = new StreamWriter(file, false))
|
||||
{
|
||||
foreach (var section in iniFile.Sections)
|
||||
{
|
||||
writer.WriteLine($"[{section.SectionName}]");
|
||||
|
||||
foreach (var keyString in section.KeysToStringArray())
|
||||
{
|
||||
writer.WriteLine(keyString);
|
||||
}
|
||||
|
||||
writer.WriteLine();
|
||||
}
|
||||
|
||||
writer.Close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
138
src/ServerManager.Common/Utils/JsonUtils.cs
Normal file
138
src/ServerManager.Common/Utils/JsonUtils.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class JsonUtils
|
||||
{
|
||||
public static string Serialize<T>(T value, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (value == null)
|
||||
return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
if (settings != null)
|
||||
return JsonConvert.SerializeObject(value, Formatting.Indented, settings);
|
||||
return JsonConvert.SerializeObject(value, Formatting.Indented);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SerializeToFile<T>(T value, string filename, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (value == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var jsonString = Serialize(value, settings);
|
||||
System.IO.File.WriteAllText(filename, jsonString);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static T Deserialize<T>(string jsonString, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonString))
|
||||
return default(T);
|
||||
|
||||
try
|
||||
{
|
||||
if (settings != null)
|
||||
return JsonConvert.DeserializeObject<T>(jsonString, settings);
|
||||
return JsonConvert.DeserializeObject<T>(jsonString);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public static T DeserializeAnonymousType<T>(string jsonString, T anonTypeObject, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonString))
|
||||
return anonTypeObject;
|
||||
|
||||
try
|
||||
{
|
||||
if (settings != null)
|
||||
return JsonConvert.DeserializeAnonymousType<T>(jsonString, anonTypeObject, settings);
|
||||
return JsonConvert.DeserializeAnonymousType<T>(jsonString, anonTypeObject);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return anonTypeObject;
|
||||
}
|
||||
}
|
||||
|
||||
public static T DeserializeFromFile<T>(string file, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(file))
|
||||
return default(T);
|
||||
|
||||
try
|
||||
{
|
||||
return Deserialize<T>(System.IO.File.ReadAllText(file), settings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public static T DeserializeFromFile<T>(string file, T anonTypeObject, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(file))
|
||||
return anonTypeObject;
|
||||
|
||||
try
|
||||
{
|
||||
return DeserializeAnonymousType<T>(System.IO.File.ReadAllText(file), anonTypeObject, settings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return anonTypeObject;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Populate(string jsonString, object target, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(jsonString) || target == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (settings != null)
|
||||
JsonConvert.PopulateObject(jsonString, target, settings);
|
||||
else
|
||||
JsonConvert.PopulateObject(jsonString, target);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void PopulateFromFile(string file, object target, JsonSerializerSettings settings = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(file) || !System.IO.File.Exists(file) || target == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
Populate(System.IO.File.ReadAllText(file), target, settings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/ServerManager.Common/Utils/MachineUtils.cs
Normal file
60
src/ServerManager.Common/Utils/MachineUtils.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using NLog;
|
||||
using System;
|
||||
using System.DirectoryServices.ActiveDirectory;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class MachineUtils
|
||||
{
|
||||
private const int OS_ANYSERVER = 29;
|
||||
|
||||
[DllImport("shlwapi.dll", SetLastError = true, EntryPoint = "#437")]
|
||||
private static extern bool IsOS(int os);
|
||||
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static bool IsWindowsServer()
|
||||
{
|
||||
return IsOS(OS_ANYSERVER);
|
||||
}
|
||||
|
||||
public static bool IsThisMachineADomainController()
|
||||
{
|
||||
try
|
||||
{
|
||||
Domain domain = Domain.GetCurrentDomain();
|
||||
if (domain == null)
|
||||
return false;
|
||||
|
||||
string thisMachine = $"{Environment.MachineName}.{domain}".ToLower();
|
||||
|
||||
var domainControllers = domain.DomainControllers.OfType<DomainController>();
|
||||
return domainControllers.Any(dc => dc.Name.Equals(thisMachine, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
catch (ActiveDirectoryObjectNotFoundException ex)
|
||||
{
|
||||
_logger.Debug($"{nameof(IsThisMachineADomainController)} checked. {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
catch (ActiveDirectoryOperationException ex)
|
||||
{
|
||||
_logger.Debug($"{nameof(IsThisMachineADomainController)} checked. {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(IsThisMachineADomainController)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsThisMachinePartOfADomain()
|
||||
{
|
||||
ManagementObject manObject = new ManagementObject(string.Format("Win32_ComputerSystem.Name='{0}'", Environment.MachineName));
|
||||
return (bool)manObject["PartOfDomain"];
|
||||
}
|
||||
}
|
||||
}
|
||||
174
src/ServerManager.Common/Utils/NetworkUtils.cs
Normal file
174
src/ServerManager.Common/Utils/NetworkUtils.cs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using ServerManagerTool.Common.Lib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class NetworkUtils
|
||||
{
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static List<NetworkAdapterEntry> GetAvailableIPV4NetworkAdapters()
|
||||
{
|
||||
List<NetworkAdapterEntry> adapters = new List<NetworkAdapterEntry>();
|
||||
|
||||
var interfaces = NetworkInterface.GetAllNetworkInterfaces();
|
||||
|
||||
foreach(var ifc in interfaces)
|
||||
{
|
||||
var ipProperties = ifc.GetIPProperties();
|
||||
if (ipProperties != null)
|
||||
{
|
||||
adapters.AddRange(ipProperties.UnicastAddresses.Select(a => a.Address)
|
||||
.Where(a => a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && !IPAddress.IsLoopback(a))
|
||||
.Select(a => new NetworkAdapterEntry(a, ifc.Description)));
|
||||
}
|
||||
}
|
||||
|
||||
return adapters;
|
||||
}
|
||||
|
||||
public static async Task<Version> GetLatestServerManagerVersion(string url)
|
||||
{
|
||||
using (var webClient = new WebClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
string latestVersion = await webClient.DownloadStringTaskAsync(url);
|
||||
return Version.Parse(latestVersion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(GetLatestServerManagerVersion)} - Exception checking for Server Manager version. {ex.Message}");
|
||||
return new Version();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static NetworkAdapterEntry GetPreferredIP(IEnumerable<NetworkAdapterEntry> adapters)
|
||||
{
|
||||
//
|
||||
// Try for a 192.168. address first
|
||||
//
|
||||
var preferredIp = adapters.FirstOrDefault(a => a.IPAddress.StartsWith("192.168."));
|
||||
if (preferredIp == null)
|
||||
{
|
||||
//
|
||||
// Try a 10.0 address next
|
||||
//
|
||||
preferredIp = adapters.FirstOrDefault(a => a.IPAddress.StartsWith("10.0."));
|
||||
if (preferredIp == null)
|
||||
{
|
||||
//
|
||||
// Sad. Just take the first.
|
||||
//
|
||||
preferredIp = adapters.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return preferredIp;
|
||||
}
|
||||
|
||||
public static string DiscoverPublicIP()
|
||||
{
|
||||
using (var webClient = new WebClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
var publicIP = webClient.DownloadString(CommonConfig.Default.PublicIPCheckUrl);
|
||||
if (IPAddress.TryParse(publicIP, out IPAddress address))
|
||||
{
|
||||
return publicIP;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(DiscoverPublicIP)} - Exception checking for public ip. {ex.Message}");
|
||||
}
|
||||
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> DiscoverPublicIPAsync()
|
||||
{
|
||||
using (var webClient = new WebClient())
|
||||
{
|
||||
try
|
||||
{
|
||||
var publicIP = await webClient.DownloadStringTaskAsync(CommonConfig.Default.PublicIPCheckUrl);
|
||||
if (IPAddress.TryParse(publicIP, out IPAddress address))
|
||||
{
|
||||
return publicIP;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(DiscoverPublicIPAsync)} - Exception checking for public ip. {ex.Message}");
|
||||
}
|
||||
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<bool> CheckServerStatusViaAPI(Uri uri, IPEndPoint endpoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
string jsonString;
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
jsonString = await client.DownloadStringTaskAsync(uri);
|
||||
}
|
||||
|
||||
if (jsonString == null)
|
||||
{
|
||||
_logger.Debug($"Server info request returned null string for {endpoint.Address}:{endpoint.Port}");
|
||||
return false;
|
||||
}
|
||||
|
||||
JObject query = JObject.Parse(jsonString);
|
||||
if (query == null)
|
||||
{
|
||||
_logger.Debug($"Server info request failed to parse for {endpoint.Address}:{endpoint.Port} - '{jsonString}'");
|
||||
return false;
|
||||
}
|
||||
|
||||
var available = query.SelectToken("available");
|
||||
if (available == null)
|
||||
{
|
||||
_logger.Debug($"Server at {endpoint.Address}:{endpoint.Port} returned no availability.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool)available;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Debug($"{nameof(CheckServerStatusViaAPI)} - Failed checking status via API for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task PerformServerCallToAPI(Uri uri, IPEndPoint endpoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var client = new WebClient())
|
||||
{
|
||||
await client.DownloadStringTaskAsync(uri);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Debug($"{nameof(PerformServerCallToAPI)} - Failed calling API for: {endpoint.Address}:{endpoint.Port}. {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
330
src/ServerManager.Common/Utils/ProcessUtils.cs
Normal file
330
src/ServerManager.Common/Utils/ProcessUtils.cs
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
using NLog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class ProcessUtils
|
||||
{
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
// Delegate type to be used as the Handler Routine for SCCH
|
||||
delegate bool ConsoleCtrlDelegate(CtrlTypes CtrlType);
|
||||
|
||||
// Enumerated type for the control messages sent to the handler routine
|
||||
enum CtrlTypes : uint
|
||||
{
|
||||
CTRL_C_EVENT = 0,
|
||||
CTRL_BREAK_EVENT,
|
||||
CTRL_CLOSE_EVENT,
|
||||
CTRL_LOGOFF_EVENT = 5,
|
||||
CTRL_SHUTDOWN_EVENT
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool AttachConsole(uint dwProcessId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
|
||||
private static extern bool FreeConsole();
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate HandlerRoutine, bool Add);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int SetForegroundWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int IsIconic(IntPtr hWnd);
|
||||
|
||||
public static string FIELD_COMMANDLINE = "CommandLine";
|
||||
public static string FIELD_EXECUTABLEPATH = "ExecutablePath";
|
||||
public static string FIELD_PROCESSID = "ProcessId";
|
||||
|
||||
private const int SW_RESTORE = 9;
|
||||
|
||||
private static Mutex _mutex;
|
||||
|
||||
public static string GetCommandLineForProcess(int processId)
|
||||
{
|
||||
var wmiQueryString = $"SELECT {FIELD_COMMANDLINE} FROM Win32_Process WHERE {FIELD_PROCESSID} = {processId}";
|
||||
|
||||
using (var searcher = new ManagementObjectSearcher(wmiQueryString))
|
||||
{
|
||||
using (var results = searcher.Get())
|
||||
{
|
||||
ManagementObject mo = results.Cast<ManagementObject>().FirstOrDefault();
|
||||
if (mo != null)
|
||||
return (string)mo[FIELD_COMMANDLINE];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetMainModuleFilepath(int processId)
|
||||
{
|
||||
var wmiQueryString = $"SELECT {FIELD_EXECUTABLEPATH} FROM Win32_Process WHERE {FIELD_PROCESSID} = {processId}";
|
||||
|
||||
using (var searcher = new ManagementObjectSearcher(wmiQueryString))
|
||||
{
|
||||
using (var results = searcher.Get())
|
||||
{
|
||||
ManagementObject mo = results.Cast<ManagementObject>().FirstOrDefault();
|
||||
if (mo != null)
|
||||
return (string)mo[FIELD_EXECUTABLEPATH];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async Task SendStopAsync(Process process)
|
||||
{
|
||||
if (process == null)
|
||||
return;
|
||||
|
||||
var ts = new TaskCompletionSource<bool>();
|
||||
EventHandler handler = (s, e) => ts.TrySetResult(true);
|
||||
|
||||
try
|
||||
{
|
||||
process.Exited += handler;
|
||||
|
||||
//This does not require the console window to be visible.
|
||||
var result = AttachConsole((uint)process.Id);
|
||||
if (result)
|
||||
{
|
||||
// Disable Ctrl-C handling for our program
|
||||
result = SetConsoleCtrlHandler(null, true);
|
||||
if (result)
|
||||
{
|
||||
result = GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
|
||||
if (result)
|
||||
{
|
||||
FreeConsole();
|
||||
|
||||
// Must wait here. If we don't and re-enable Ctrl-C
|
||||
// handling below too fast, we might terminate ourselves.
|
||||
try
|
||||
{
|
||||
ts.Task.Wait(10000);
|
||||
result = true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
|
||||
//Re-enable Ctrl-C handling or any subsequently started
|
||||
//programs will inherit the disabled state.
|
||||
SetConsoleCtrlHandler(null, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Re-enable Ctrl-C handling or any subsequently started
|
||||
//programs will inherit the disabled state.
|
||||
SetConsoleCtrlHandler(null, false);
|
||||
|
||||
FreeConsole();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeConsole();
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && !process.HasExited)
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
process.Exited -= handler;
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<bool> RunProcessAsync(string file, string arguments, string verb, string workingDirectory, string username, SecureString password, DataReceivedEventHandler outputHandler, CancellationToken cancellationToken, ProcessWindowStyle windowStyle = ProcessWindowStyle.Normal)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file) || !File.Exists(file))
|
||||
throw new FileNotFoundException("The specified file does not exist or could not be found.", file);
|
||||
|
||||
var fileName = Path.GetFileName(file);
|
||||
|
||||
var startInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = file,
|
||||
Arguments = arguments,
|
||||
Verb = verb,
|
||||
UseShellExecute = outputHandler == null && windowStyle == ProcessWindowStyle.Minimized && string.IsNullOrWhiteSpace(username),
|
||||
RedirectStandardOutput = outputHandler != null,
|
||||
CreateNoWindow = outputHandler != null || windowStyle == ProcessWindowStyle.Hidden,
|
||||
WindowStyle = windowStyle,
|
||||
UserName = string.IsNullOrWhiteSpace(username) ? null : username,
|
||||
Password = string.IsNullOrWhiteSpace(username) ? null : password,
|
||||
WorkingDirectory = string.IsNullOrWhiteSpace(workingDirectory) || !Directory.Exists(workingDirectory) ? Path.GetDirectoryName(file) : workingDirectory,
|
||||
};
|
||||
|
||||
var process = Process.Start(startInfo);
|
||||
process.EnableRaisingEvents = true;
|
||||
if (startInfo.RedirectStandardOutput && outputHandler != null)
|
||||
{
|
||||
process.OutputDataReceived += outputHandler;
|
||||
process.BeginOutputReadLine();
|
||||
}
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
using (var cancelRegistration = cancellationToken.Register(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
process.Kill();
|
||||
}
|
||||
finally
|
||||
{
|
||||
tcs.TrySetCanceled();
|
||||
}
|
||||
}))
|
||||
{
|
||||
process.Exited += (s, e) =>
|
||||
{
|
||||
var exitCode = process.ExitCode;
|
||||
|
||||
_logger.Debug($"{nameof(RunProcessAsync)}: filename {fileName}; exitcode = {exitCode}");
|
||||
tcs.TrySetResult(exitCode == 0);
|
||||
process.Close();
|
||||
};
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(RunProcessAsync)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr GetCurrentInstanceWindowHandle()
|
||||
{
|
||||
var hWnd = IntPtr.Zero;
|
||||
var currentProcess = Process.GetCurrentProcess();
|
||||
|
||||
var processes = Process.GetProcessesByName(currentProcess.ProcessName);
|
||||
foreach (var process in processes)
|
||||
{
|
||||
// Get the first instance that is not this instance, has the same process name and was started from the same file name
|
||||
// and location. Also check that the process has a valid window handle in this session to filter out other user's processes.
|
||||
if (process.Id != currentProcess.Id && process.MainModule.FileName == currentProcess.MainModule.FileName && process.MainWindowHandle != IntPtr.Zero)
|
||||
{
|
||||
hWnd = process.MainWindowHandle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
public static bool IsAlreadyRunning()
|
||||
{
|
||||
var assemblyLocation = Assembly.GetEntryAssembly().Location;
|
||||
var name = $"Global::{Path.GetFileName(assemblyLocation)}";
|
||||
|
||||
_mutex = new Mutex(true, name, out bool createdNew);
|
||||
if (createdNew)
|
||||
_mutex.ReleaseMutex();
|
||||
|
||||
return !createdNew;
|
||||
}
|
||||
|
||||
public static bool SwitchToCurrentInstance()
|
||||
{
|
||||
var hWnd = GetCurrentInstanceWindowHandle();
|
||||
if (hWnd == IntPtr.Zero)
|
||||
return false;
|
||||
|
||||
// Restore window if minimised. Do not restore if already in normal or maximised window state, since we don't want to
|
||||
// change the current state of the window.
|
||||
if (IsIconic(hWnd) != 0)
|
||||
ShowWindow(hWnd, SW_RESTORE);
|
||||
|
||||
// Set foreground window.
|
||||
SetForegroundWindow(hWnd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int ProcessorCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var processorCount = Environment.ProcessorCount;
|
||||
//#if DEBUG
|
||||
// processorCount = 40;
|
||||
//#endif
|
||||
return processorCount;
|
||||
}
|
||||
}
|
||||
|
||||
public static BigInteger[] GetProcessorAffinityList()
|
||||
{
|
||||
var processorCount = ProcessorCount;
|
||||
var results = new List<BigInteger>(processorCount + 1);
|
||||
results.Add(BigInteger.Zero); // default - all processors
|
||||
|
||||
for (int index = 0; index < processorCount; index++)
|
||||
{
|
||||
results.Add((BigInteger)Math.Pow(2, index));
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
public static string[] GetProcessPriorityList()
|
||||
{
|
||||
return new string[] { "low", "belownormal", "normal", "abovenormal", "high" };
|
||||
}
|
||||
|
||||
public static bool IsProcessorAffinityValid(BigInteger affinityValue)
|
||||
{
|
||||
if (affinityValue == BigInteger.Zero)
|
||||
return true;
|
||||
|
||||
var maxaffinity = (BigInteger)Math.Pow(2, ProcessorCount);
|
||||
if (affinityValue < BigInteger.Zero || affinityValue > maxaffinity)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsProcessPriorityValid(string priorityValue)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(priorityValue))
|
||||
return false;
|
||||
|
||||
var priorityList = GetProcessPriorityList();
|
||||
if (!priorityList.Contains(priorityValue))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/ServerManager.Common/Utils/ResourceUtils.cs
Normal file
25
src/ServerManager.Common/Utils/ResourceUtils.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class ResourceUtils
|
||||
{
|
||||
public static string GetResourceString(ResourceDictionary resources, string inKey)
|
||||
{
|
||||
if (resources == null)
|
||||
throw new ArgumentNullException(nameof(resources), "parameter cannot be null.");
|
||||
if (string.IsNullOrWhiteSpace(inKey))
|
||||
throw new ArgumentNullException(nameof(inKey), "parameter cannot be null.");
|
||||
|
||||
if (resources.Contains(inKey) && resources[inKey] is string)
|
||||
{
|
||||
var resourceString = resources[inKey].ToString();
|
||||
resourceString = resourceString.Replace("\\r", "\r");
|
||||
resourceString = resourceString.Replace("\\n", "\n");
|
||||
return resourceString;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
src/ServerManager.Common/Utils/ScriptUtils.cs
Normal file
122
src/ServerManager.Common/Utils/ScriptUtils.cs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
using NLog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class ScriptUtils
|
||||
{
|
||||
public static Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
static int NextScriptId = 0;
|
||||
|
||||
public static bool RunElevatedShellScript(string scriptName, string script)
|
||||
{
|
||||
return RunShellScript(scriptName, script, withElevation: true);
|
||||
}
|
||||
|
||||
public static bool RunShellScript(string scriptName, string script, bool withElevation = false, bool waitForExit = true, bool deleteOnExit = true, bool includePID = false)
|
||||
{
|
||||
var scriptNameBase = includePID ? $"{scriptName}_{Process.GetCurrentProcess().Id}" : scriptName;
|
||||
|
||||
string baseScriptPath = Path.Combine(Path.GetTempPath(), $"{scriptNameBase}.cmd");
|
||||
string scriptWrapperPath = Path.Combine(Path.GetTempPath(), $"{scriptNameBase}_wrapper.cmd");
|
||||
|
||||
string scriptLogPath = null;
|
||||
scriptLogPath = Path.ChangeExtension(baseScriptPath, ".out");
|
||||
|
||||
_logger.Debug($"Running Script (Elevation {withElevation}) : {script}");
|
||||
|
||||
var scriptId = NextScriptId++;
|
||||
try
|
||||
{
|
||||
WriteCommandScript(baseScriptPath, script);
|
||||
|
||||
//
|
||||
// Wrap to capture logging (necessary for running administrator scripts from non-admin contexts)
|
||||
//
|
||||
WriteCommandScript(scriptWrapperPath, $"CMD /C {baseScriptPath.AsQuoted()} > {scriptLogPath.AsQuoted()} 2>&1");
|
||||
|
||||
//
|
||||
// Launch the process
|
||||
//
|
||||
ProcessStartInfo psInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = $"{scriptWrapperPath.AsQuoted()}",
|
||||
Verb = withElevation ? "runas" : String.Empty,
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = true,
|
||||
//WindowStyle = ProcessWindowStyle.Hidden,
|
||||
};
|
||||
|
||||
var process = new Process
|
||||
{
|
||||
EnableRaisingEvents = true,
|
||||
StartInfo = psInfo,
|
||||
};
|
||||
|
||||
process.Start();
|
||||
|
||||
//
|
||||
// If we wait, copy the log files when the process is done.
|
||||
//
|
||||
if (waitForExit)
|
||||
{
|
||||
process.WaitForExit();
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Debug($"SCRIPT {scriptId} OUTPUT: {File.ReadAllText(scriptLogPath)}");
|
||||
}
|
||||
catch { }
|
||||
|
||||
return process.ExitCode == 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(RunShellScript)} - Failed to run elevated script. {ex.Message}\r\n{ex.StackTrace}");
|
||||
Debug.WriteLine("Failed to run elevated script: {0}", ex.Message);
|
||||
Debugger.Break();
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//
|
||||
// If we aren't waiting, we can't delete because we will kill the scripts before cmd.exe gets a chance to run them.
|
||||
//
|
||||
if (waitForExit && deleteOnExit)
|
||||
{
|
||||
File.Delete(baseScriptPath);
|
||||
File.Delete(scriptWrapperPath);
|
||||
File.Delete(scriptLogPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteCommandScript(string destinationPath, string script)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// Change to the UTF8 code page
|
||||
builder.AppendLine("chcp 65001");
|
||||
builder.Append(script);
|
||||
|
||||
File.WriteAllText(destinationPath, builder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
|
||||
}
|
||||
|
||||
public static string AsQuoted(this string parameter)
|
||||
{
|
||||
var newValue = parameter;
|
||||
if (!newValue.StartsWith("\""))
|
||||
newValue = "\"" + newValue;
|
||||
if (!newValue.EndsWith("\""))
|
||||
newValue += "\"";
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
288
src/ServerManager.Common/Utils/SecurityUtils.cs
Normal file
288
src/ServerManager.Common/Utils/SecurityUtils.cs
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
using NLog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.DirectoryServices.AccountManagement;
|
||||
using System.IO;
|
||||
using System.Security;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Text;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class SecurityUtils
|
||||
{
|
||||
private const string PasswordChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
private const string GROUP_USERSBUILTIN = @"BUILTIN\Users";
|
||||
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
private static int callCount = 0;
|
||||
|
||||
public static string GeneratePassword(int count)
|
||||
{
|
||||
StringBuilder newPassword = new StringBuilder(count);
|
||||
Random random;
|
||||
unchecked
|
||||
{
|
||||
random = new Random((int)DateTime.Now.Ticks + callCount);
|
||||
callCount++;
|
||||
}
|
||||
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
newPassword.Append(PasswordChars[random.Next(PasswordChars.Length)]);
|
||||
}
|
||||
|
||||
return newPassword.ToString();
|
||||
}
|
||||
|
||||
public static bool IsAdministrator()
|
||||
{
|
||||
WindowsIdentity identity = WindowsIdentity.GetCurrent();
|
||||
WindowsPrincipal principal = new WindowsPrincipal(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
|
||||
public static bool SetDirectoryOwnershipForAllUsers(string directory)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(directory);
|
||||
var security = directoryInfo.GetAccessControl(AccessControlSections.Access);
|
||||
bool result;
|
||||
|
||||
var iFlags = InheritanceFlags.None;
|
||||
|
||||
// *** Add Access Rule to the actual directory itself
|
||||
var accessRule = new FileSystemAccessRule(GROUP_USERSBUILTIN, FileSystemRights.FullControl, iFlags, PropagationFlags.NoPropagateInherit, AccessControlType.Allow);
|
||||
security.ModifyAccessRule(AccessControlModification.Set, accessRule, out result);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
// *** Always allow objects to inherit on a directory
|
||||
iFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
|
||||
|
||||
// *** Add Access rule for the inheritance
|
||||
accessRule = new FileSystemAccessRule(GROUP_USERSBUILTIN, FileSystemRights.FullControl, iFlags, PropagationFlags.InheritOnly, AccessControlType.Allow);
|
||||
security.ModifyAccessRule(AccessControlModification.Add, accessRule, out result);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
directoryInfo.SetAccessControl(security);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// We give it a best-effort here. If we aren't running an enterprise OS, this group may not exist (or be needed.)
|
||||
_logger.Error($"{nameof(SetDirectoryOwnershipForAllUsers)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetFileOwnershipForAllUsers(string file)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file) || !File.Exists(file))
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
var fileInfo = new FileInfo(file);
|
||||
var security = fileInfo.GetAccessControl(AccessControlSections.Access);
|
||||
bool result;
|
||||
|
||||
var iFlags = InheritanceFlags.None;
|
||||
|
||||
// *** Add Access Rule to the actual file itself
|
||||
var accessRule = new FileSystemAccessRule(GROUP_USERSBUILTIN, FileSystemRights.FullControl, iFlags, PropagationFlags.NoPropagateInherit, AccessControlType.Allow);
|
||||
security.ModifyAccessRule(AccessControlModification.Set, accessRule, out result);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
fileInfo.SetAccessControl(security);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// We give it a best-effort here. If we aren't running an enterprise OS, this group may not exist (or be needed.)
|
||||
_logger.Error($"{nameof(SetFileOwnershipForAllUsers)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DoesLocalWindowsAccountExist(string username)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MachineUtils.IsThisMachineADomainController())
|
||||
throw new Exception($"This computer reports to be a domain controller.");
|
||||
|
||||
var context = new PrincipalContext(ContextType.Machine, Environment.MachineName);
|
||||
if (context == null)
|
||||
context = new PrincipalContext(ContextType.Machine);
|
||||
if (context == null)
|
||||
throw new Exception($"Could not create instance of the PrincipalContext ({Environment.MachineName}).");
|
||||
|
||||
var user = UserPrincipal.FindByIdentity(context, IdentityType.Name, username);
|
||||
if (user == null || !(user.Enabled.HasValue && user.Enabled.Value) || user.IsAccountLockedOut())
|
||||
return false;
|
||||
|
||||
var usersGroupSID = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
|
||||
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, IdentityType.Sid, usersGroupSID.Value);
|
||||
if (group == null || !group.Members.Contains(user))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(DoesLocalWindowsAccountExist)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool CreateLocalWindowsAccount(string username, string password, string displayName, string description, bool userCannotChangePassword, bool passwordNeverExpires)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MachineUtils.IsThisMachineADomainController())
|
||||
throw new Exception($"This computer reports to be a domain controller. User cannot be created.");
|
||||
|
||||
var context = new PrincipalContext(ContextType.Machine, Environment.MachineName);
|
||||
if (context == null)
|
||||
context = new PrincipalContext(ContextType.Machine);
|
||||
if (context == null)
|
||||
throw new Exception($"Could not create instance of the PrincipalContext ({Environment.MachineName}).");
|
||||
|
||||
// create the user if it does not exist
|
||||
var user = UserPrincipal.FindByIdentity(context, IdentityType.Name, username);
|
||||
if (user == null)
|
||||
{
|
||||
user = new UserPrincipal(context)
|
||||
{
|
||||
Name = username,
|
||||
DisplayName = displayName,
|
||||
Description = description,
|
||||
};
|
||||
|
||||
if (user == null)
|
||||
throw new Exception($"Could not create new instance of the UserPrincipal ({username}).");
|
||||
}
|
||||
|
||||
user.Enabled = true;
|
||||
user.PasswordNeverExpires = passwordNeverExpires;
|
||||
user.UserCannotChangePassword = userCannotChangePassword;
|
||||
user.SetPassword(password);
|
||||
if (user.IsAccountLockedOut())
|
||||
user.UnlockAccount();
|
||||
user.Save();
|
||||
|
||||
// now add user to "Users" group
|
||||
var usersGroupSID = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
|
||||
if (usersGroupSID == null)
|
||||
throw new Exception($"Could not find instance of the SecurityIdentifier (WellKnownSidType.BuiltinUsersSid).");
|
||||
|
||||
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, IdentityType.Sid, usersGroupSID.Value);
|
||||
if (group == null)
|
||||
throw new Exception($"Could not find instance of the GroupPrincipal (USERS).");
|
||||
|
||||
if (!group.Members.Contains(user))
|
||||
{
|
||||
group.Members.Add(user);
|
||||
group.Save();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(CreateLocalWindowsAccount)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ChangeWindowsAccountPassword(string username, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MachineUtils.IsThisMachineADomainController())
|
||||
throw new Exception($"This computer reports to be a domain controller. User password cannot be changed.");
|
||||
|
||||
var context = new PrincipalContext(ContextType.Machine, Environment.MachineName);
|
||||
if (context == null)
|
||||
context = new PrincipalContext(ContextType.Machine);
|
||||
if (context == null)
|
||||
throw new Exception($"Could not create instance of the PrincipalContext ({Environment.MachineName}).");
|
||||
|
||||
// create the user if it does not exist
|
||||
var user = UserPrincipal.FindByIdentity(context, IdentityType.Name, username);
|
||||
if (user == null)
|
||||
throw new Exception($"Could not find instance of the UserPrincipal ({username}).");
|
||||
|
||||
user.SetPassword(password);
|
||||
user.Save();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ChangeWindowsAccountPassword)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DeleteLocalWindowsAccount(string username)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (MachineUtils.IsThisMachineADomainController())
|
||||
throw new Exception($"This computer reports to be a domain controller. User cannot be deleted.");
|
||||
|
||||
var context = new PrincipalContext(ContextType.Machine, Environment.MachineName);
|
||||
if (context == null)
|
||||
context = new PrincipalContext(ContextType.Machine);
|
||||
if (context == null)
|
||||
throw new Exception($"Could not create instance of the PrincipalContext ({Environment.MachineName}).");
|
||||
|
||||
// create the user if it does not exist
|
||||
var user = UserPrincipal.FindByIdentity(context, IdentityType.Name, username);
|
||||
if (user == null)
|
||||
throw new Exception($"Could not find instance of the UserPrincipal ({username}).");
|
||||
|
||||
// now remove user from "Users" group
|
||||
var usersGroupSID = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
|
||||
if (usersGroupSID != null)
|
||||
{
|
||||
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, IdentityType.Sid, usersGroupSID.Value);
|
||||
if (group != null && group.Members.Contains(user))
|
||||
group.Members.Remove(user);
|
||||
}
|
||||
|
||||
user.Delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(DeleteLocalWindowsAccount)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static SecureString GetSecureString(string sourceString)
|
||||
{
|
||||
var secureString = new SecureString();
|
||||
foreach (var sourceChar in sourceString)
|
||||
{
|
||||
secureString.AppendChar(sourceChar);
|
||||
}
|
||||
return secureString;
|
||||
}
|
||||
}
|
||||
}
|
||||
86
src/ServerManager.Common/Utils/SettingsUtils.cs
Normal file
86
src/ServerManager.Common/Utils/SettingsUtils.cs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
using Newtonsoft.Json;
|
||||
using ServerManagerTool.Common.Lib;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class SettingsUtils
|
||||
{
|
||||
public static void BackupUserConfigSettings(System.Configuration.ApplicationSettingsBase settings, string fileName, string settingsPath, string backupPath)
|
||||
{
|
||||
if (settings == null || string.IsNullOrWhiteSpace(fileName))
|
||||
return;
|
||||
|
||||
var settingsFileName = Path.GetFileNameWithoutExtension(fileName);
|
||||
var settingsFileExt = Path.GetExtension(fileName);
|
||||
var settingsFile = IOUtils.NormalizePath(Path.Combine(settingsPath, $"{fileName}"));
|
||||
|
||||
try
|
||||
{
|
||||
// save the settings file to a json settings file
|
||||
var jsonSettings = new JsonSerializerSettings
|
||||
{
|
||||
ContractResolver = new UserScopedSettingContractResolver(),
|
||||
};
|
||||
JsonUtils.SerializeToFile(settings, settingsFile, jsonSettings);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// do nothing, just exit
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(backupPath))
|
||||
{
|
||||
// create a backup of the settings file
|
||||
var backupFile = IOUtils.NormalizePath(Path.Combine(backupPath, $"{settingsFileName}_{DateTime.UtcNow.ToString("yyyyMMdd_HHmmss")}{settingsFileExt}"));
|
||||
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(backupPath))
|
||||
Directory.CreateDirectory(backupPath);
|
||||
File.Copy(settingsFile, backupFile);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// do nothing, just exit
|
||||
}
|
||||
|
||||
var filesToDelete = new DirectoryInfo(backupPath).GetFiles($"{settingsFileName}_*{settingsFileExt}").Where(f => f.LastWriteTimeUtc.AddDays(7) < DateTime.UtcNow).ToArray();
|
||||
foreach (var fileToDelete in filesToDelete)
|
||||
{
|
||||
try
|
||||
{
|
||||
fileToDelete.Delete();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// do nothing, just exit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void MigrateSettings(System.Configuration.ApplicationSettingsBase settings, string settingsFile)
|
||||
{
|
||||
if (settings == null || string.IsNullOrWhiteSpace(settingsFile) || !File.Exists(settingsFile))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// read the json settings file to a settings file
|
||||
var jsonSettings = new JsonSerializerSettings
|
||||
{
|
||||
//ContractResolver = new UserScopedSettingContractResolver(),
|
||||
};
|
||||
|
||||
JsonUtils.PopulateFromFile(settingsFile, settings, jsonSettings);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// do nothing, just exit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
293
src/ServerManager.Common/Utils/SteamUtils.cs
Normal file
293
src/ServerManager.Common/Utils/SteamUtils.cs
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
using NeXt.Vdf;
|
||||
using NLog;
|
||||
using ServerManagerTool.Common.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class SteamUtils
|
||||
{
|
||||
private const string KEYWORK_QUIT = "+quit";
|
||||
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static WorkshopFileDetailResponse GetSteamModDetails(string appId)
|
||||
{
|
||||
const int MAX_IDS = 100;
|
||||
|
||||
var totalRequests = 0;
|
||||
var requestIndex = 1;
|
||||
|
||||
var response = new WorkshopFileDetailResponse();
|
||||
if (string.IsNullOrWhiteSpace(SteamWebApiKey))
|
||||
return response;
|
||||
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
var httpRequest = WebRequest.Create($"https://api.steampowered.com/IPublishedFileService/QueryFiles/v1/?key={SteamWebApiKey}&format=json&query_type=1&page={requestIndex}&numperpage={MAX_IDS}&appid={appId}&match_all_tags=0&include_recent_votes_only=0&totalonly=0&return_vote_data=0&return_tags=0&return_kv_tags=0&return_previews=0&return_children=0&return_short_description=0&return_for_sale_data=0&return_metadata=1");
|
||||
httpRequest.Timeout = 30000;
|
||||
var httpResponse = httpRequest.GetResponse();
|
||||
var responseString = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd();
|
||||
|
||||
var result = JsonUtils.Deserialize<WorkshopFileDetailResult>(responseString);
|
||||
if (result == null || result.response == null)
|
||||
break;
|
||||
|
||||
if (totalRequests == 0)
|
||||
{
|
||||
totalRequests = 1;
|
||||
response = result.response;
|
||||
|
||||
if (response.total > MAX_IDS)
|
||||
{
|
||||
int remainder;
|
||||
totalRequests = Math.DivRem(response.total, MAX_IDS, out remainder);
|
||||
if (remainder > 0)
|
||||
totalRequests++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.response.publishedfiledetails != null)
|
||||
response.publishedfiledetails.AddRange(result.response.publishedfiledetails);
|
||||
}
|
||||
|
||||
requestIndex++;
|
||||
} while (requestIndex <= totalRequests);
|
||||
|
||||
return response;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(GetSteamModDetails)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
Debug.WriteLine($"ERROR: {nameof(GetSteamModDetails)}\r\n{ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static PublishedFileDetailsResponse GetSteamModDetails(List<string> modIdList)
|
||||
{
|
||||
const int MAX_IDS = 20;
|
||||
|
||||
PublishedFileDetailsResponse response = null;
|
||||
if (string.IsNullOrWhiteSpace(SteamWebApiKey))
|
||||
return response;
|
||||
|
||||
try
|
||||
{
|
||||
if (modIdList == null || modIdList.Count == 0)
|
||||
return new PublishedFileDetailsResponse();
|
||||
|
||||
int remainder;
|
||||
var totalRequests = Math.DivRem(modIdList.Count, MAX_IDS, out remainder);
|
||||
if (remainder > 0)
|
||||
totalRequests++;
|
||||
|
||||
var requestIndex = 0;
|
||||
while (requestIndex < totalRequests)
|
||||
{
|
||||
var count = 0;
|
||||
var postData = "";
|
||||
for (var index = requestIndex * MAX_IDS; count < MAX_IDS && index < modIdList.Count; index++)
|
||||
{
|
||||
postData += $"&publishedfileids[{count}]={modIdList[index]}";
|
||||
count++;
|
||||
}
|
||||
|
||||
postData = $"key={SteamWebApiKey}&format=json&itemcount={count}{postData}";
|
||||
|
||||
var data = Encoding.ASCII.GetBytes(postData);
|
||||
|
||||
var httpRequest = WebRequest.Create("https://api.steampowered.com/ISteamRemoteStorage/GetPublishedFileDetails/v1/");
|
||||
httpRequest.Timeout = 30000;
|
||||
httpRequest.Method = "POST";
|
||||
httpRequest.ContentType = "application/x-www-form-urlencoded";
|
||||
httpRequest.ContentLength = data.Length;
|
||||
|
||||
using (var stream = httpRequest.GetRequestStream())
|
||||
{
|
||||
stream.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
|
||||
var responseString = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd();
|
||||
|
||||
var result = JsonUtils.Deserialize<PublishedFileDetailsResult>(responseString);
|
||||
if (result != null && result.response != null)
|
||||
{
|
||||
if (response == null)
|
||||
response = result.response;
|
||||
else
|
||||
{
|
||||
response.resultcount += result.response.resultcount;
|
||||
response.publishedfiledetails.AddRange(result.response.publishedfiledetails);
|
||||
}
|
||||
}
|
||||
|
||||
requestIndex++;
|
||||
};
|
||||
|
||||
return response ?? new PublishedFileDetailsResponse();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(GetSteamModDetails)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
Debug.WriteLine($"ERROR: {nameof(GetSteamModDetails)}\r\n{ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SteamUserDetailResponse GetSteamUserDetails(List<string> steamIdList)
|
||||
{
|
||||
const int MAX_IDS = 100;
|
||||
|
||||
SteamUserDetailResponse response = null;
|
||||
if (string.IsNullOrWhiteSpace(SteamWebApiKey))
|
||||
return response;
|
||||
|
||||
try
|
||||
{
|
||||
if (steamIdList.Count == 0)
|
||||
return new SteamUserDetailResponse();
|
||||
|
||||
steamIdList = steamIdList.Distinct().ToList();
|
||||
steamIdList = steamIdList.Where(i => long.TryParse(i, out long id)).ToList();
|
||||
|
||||
if (steamIdList.Count == 0)
|
||||
return new SteamUserDetailResponse();
|
||||
|
||||
int remainder;
|
||||
var totalRequests = Math.DivRem(steamIdList.Count, MAX_IDS, out remainder);
|
||||
if (remainder > 0)
|
||||
totalRequests++;
|
||||
|
||||
var requestIndex = 0;
|
||||
while (requestIndex < totalRequests)
|
||||
{
|
||||
var count = 0;
|
||||
var postData = "";
|
||||
var delimiter = "";
|
||||
for (var index = requestIndex * MAX_IDS; count < MAX_IDS && index < steamIdList.Count; index++)
|
||||
{
|
||||
postData += $"{delimiter}{steamIdList[index]}";
|
||||
delimiter = ",";
|
||||
count++;
|
||||
}
|
||||
|
||||
var httpRequest = WebRequest.Create($"https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/?key={SteamWebApiKey}&format=json&steamids={postData}");
|
||||
httpRequest.Timeout = 30000;
|
||||
var httpResponse = (HttpWebResponse)httpRequest.GetResponse();
|
||||
var responseString = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd();
|
||||
|
||||
var result = JsonUtils.Deserialize<SteamUserDetailResult>(responseString);
|
||||
if (result != null && result.response != null)
|
||||
{
|
||||
if (response == null)
|
||||
response = result.response;
|
||||
else
|
||||
{
|
||||
response.players.AddRange(result.response.players);
|
||||
}
|
||||
}
|
||||
|
||||
requestIndex++;
|
||||
}
|
||||
|
||||
return response ?? new SteamUserDetailResponse();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(GetSteamUserDetails)}. {ex.Message}\r\n{ex.StackTrace}");
|
||||
Debug.WriteLine($"ERROR: {nameof(GetSteamUserDetails)}\r\n{ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static SteamCmdAppManifest ReadSteamCmdAppManifestFile(string file)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file) || !File.Exists(file))
|
||||
return null;
|
||||
|
||||
var vdfSerializer = VdfDeserializer.FromFile(file);
|
||||
var vdf = vdfSerializer.Deserialize();
|
||||
|
||||
return SteamCmdManifestDetailsResult.Deserialize(vdf);
|
||||
}
|
||||
|
||||
public static SteamCmdAppWorkshop ReadSteamCmdAppWorkshopFile(string file)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(file) || !File.Exists(file))
|
||||
return null;
|
||||
|
||||
var vdfSerializer = VdfDeserializer.FromFile(file);
|
||||
var vdf = vdfSerializer.Deserialize();
|
||||
|
||||
return SteamCmdWorkshopDetailsResult.Deserialize(vdf);
|
||||
}
|
||||
|
||||
public static string SteamWebApiKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(CommonConfig.Default.SteamAPIKey))
|
||||
return CommonConfig.Default.SteamAPIKey;
|
||||
return CommonConfig.Default.DefaultSteamAPIKey;
|
||||
}
|
||||
}
|
||||
|
||||
public static Process GetSteamProcess()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(CommonConfig.Default.SteamClientFile) || !File.Exists(CommonConfig.Default.SteamClientFile))
|
||||
return null;
|
||||
|
||||
// Find the server process.
|
||||
var expectedPath = IOUtils.NormalizePath(CommonConfig.Default.SteamClientFile);
|
||||
var runningProcesses = Process.GetProcessesByName(CommonConfig.Default.SteamProcessName);
|
||||
|
||||
Process process = null;
|
||||
foreach (var runningProcess in runningProcesses)
|
||||
{
|
||||
var runningPath = ProcessUtils.GetMainModuleFilepath(runningProcess.Id);
|
||||
if (string.Equals(expectedPath, runningPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
process = runningProcess;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
public static string BuildSteamCmdArguments(bool removeQuit, string argumentString)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(argumentString))
|
||||
return argumentString;
|
||||
|
||||
var newArgumentString = argumentString.TrimEnd(' ');
|
||||
|
||||
if (newArgumentString.ToLower().EndsWith(KEYWORK_QUIT) && removeQuit)
|
||||
return newArgumentString.Substring(0, newArgumentString.Length - KEYWORK_QUIT.Length);
|
||||
else if (!newArgumentString.ToLower().EndsWith(KEYWORK_QUIT) && !removeQuit)
|
||||
return newArgumentString += $" {KEYWORK_QUIT}";
|
||||
|
||||
return newArgumentString.TrimEnd(' ');
|
||||
}
|
||||
|
||||
public static string BuildSteamCmdArguments(bool removeQuit, string argumentFormatString, params string[] argumentValues)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(argumentFormatString) || argumentValues == null || argumentValues.Length == 0)
|
||||
return argumentFormatString;
|
||||
|
||||
var argumentString = string.Format(argumentFormatString, argumentValues);
|
||||
return BuildSteamCmdArguments(removeQuit, argumentString);
|
||||
}
|
||||
}
|
||||
}
|
||||
161
src/ServerManager.Common/Utils/StringUtils.cs
Normal file
161
src/ServerManager.Common/Utils/StringUtils.cs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
using ServerManagerTool.Common.Attibutes;
|
||||
using ServerManagerTool.Common.Model;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class StringUtils
|
||||
{
|
||||
public const string DEFAULT_CULTURE_CODE = "en-US";
|
||||
|
||||
public static string GetPropertyValue(object value, PropertyInfo property)
|
||||
{
|
||||
string convertedVal;
|
||||
|
||||
if (property.PropertyType == typeof(float))
|
||||
convertedVal = ((float)value).ToString("0.000000####", CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE));
|
||||
else if (property.PropertyType == typeof(string))
|
||||
convertedVal = $"\"{value}\"";
|
||||
else
|
||||
convertedVal = Convert.ToString(value, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE));
|
||||
|
||||
return convertedVal;
|
||||
}
|
||||
|
||||
public static string GetPropertyValue(object value, PropertyInfo property, BaseIniFileEntryAttribute attribute)
|
||||
{
|
||||
string convertedVal;
|
||||
|
||||
if (property.PropertyType == typeof(int) || property.PropertyType == typeof(NullableValue<int>))
|
||||
convertedVal = Convert.ToString(value, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE));
|
||||
else if (property.PropertyType == typeof(float) || property.PropertyType == typeof(NullableValue<float>))
|
||||
convertedVal = ((float)value).ToString("0.000000####", CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE));
|
||||
else if (property.PropertyType == typeof(bool))
|
||||
{
|
||||
var boolValue = (bool)value;
|
||||
if (attribute.InvertBoolean)
|
||||
boolValue = !boolValue;
|
||||
if (attribute.WriteBooleanAsInteger)
|
||||
convertedVal = boolValue ? "1" : "0";
|
||||
else
|
||||
convertedVal = boolValue.ToString(CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE));
|
||||
}
|
||||
else
|
||||
convertedVal = Convert.ToString(value, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE));
|
||||
|
||||
return convertedVal;
|
||||
}
|
||||
|
||||
public static void SetPropertyValue(string value, object obj, PropertyInfo property)
|
||||
{
|
||||
if (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?))
|
||||
{
|
||||
bool boolValue;
|
||||
bool.TryParse(value, out boolValue);
|
||||
property.SetValue(obj, boolValue);
|
||||
}
|
||||
else if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
|
||||
{
|
||||
int intValue;
|
||||
int.TryParse(value, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out intValue);
|
||||
property.SetValue(obj, intValue);
|
||||
}
|
||||
else if (property.PropertyType == typeof(float) || property.PropertyType == typeof(float?))
|
||||
{
|
||||
var tempValue = value.Replace("f", "");
|
||||
|
||||
float floatValue;
|
||||
float.TryParse(tempValue, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out floatValue);
|
||||
property.SetValue(obj, floatValue);
|
||||
}
|
||||
else if (property.PropertyType == typeof(NullableValue<int>))
|
||||
{
|
||||
int intValue;
|
||||
int.TryParse(value, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out intValue);
|
||||
var field = property.GetValue(obj) as NullableValue<int>;
|
||||
property.SetValue(obj, field.SetValue(true, intValue));
|
||||
}
|
||||
else if (property.PropertyType == typeof(NullableValue<float>))
|
||||
{
|
||||
var tempValue = value.Replace("f", "");
|
||||
|
||||
float floatValue;
|
||||
float.TryParse(tempValue, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out floatValue);
|
||||
var field = property.GetValue(obj) as NullableValue<float>;
|
||||
property.SetValue(obj, field.SetValue(true, floatValue));
|
||||
}
|
||||
else if (property.PropertyType.IsSubclassOf(typeof(AggregateIniValue)))
|
||||
{
|
||||
var field = property.GetValue(obj) as AggregateIniValue;
|
||||
field?.InitializeFromINIValue(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var convertedValue = Convert.ChangeType(value, property.PropertyType, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE));
|
||||
if (convertedValue is string)
|
||||
convertedValue = (convertedValue as string).Trim('"');
|
||||
property.SetValue(obj, convertedValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SetPropertyValue(string value, object obj, PropertyInfo property, BaseIniFileEntryAttribute attribute)
|
||||
{
|
||||
if (property.PropertyType == typeof(bool) || property.PropertyType == typeof(bool?))
|
||||
{
|
||||
bool boolValue;
|
||||
if (attribute.WriteBooleanAsInteger)
|
||||
boolValue = value.Equals("1", StringComparison.OrdinalIgnoreCase);
|
||||
else
|
||||
bool.TryParse(value, out boolValue);
|
||||
if (attribute.InvertBoolean)
|
||||
boolValue = !boolValue;
|
||||
property.SetValue(obj, boolValue);
|
||||
return true;
|
||||
}
|
||||
if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
|
||||
{
|
||||
int intValue;
|
||||
int.TryParse(value, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out intValue);
|
||||
property.SetValue(obj, intValue);
|
||||
return true;
|
||||
}
|
||||
if (property.PropertyType == typeof(float) || property.PropertyType == typeof(float?))
|
||||
{
|
||||
var tempValue = value.Replace("f", "");
|
||||
|
||||
float floatValue;
|
||||
float.TryParse(tempValue, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out floatValue);
|
||||
property.SetValue(obj, floatValue);
|
||||
return true;
|
||||
}
|
||||
if (property.PropertyType == typeof(NullableValue<int>))
|
||||
{
|
||||
int intValue;
|
||||
int.TryParse(value, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out intValue);
|
||||
var field = property.GetValue(obj) as NullableValue<int>;
|
||||
property.SetValue(obj, field.SetValue(true, intValue));
|
||||
return true;
|
||||
}
|
||||
if (property.PropertyType == typeof(NullableValue<float>))
|
||||
{
|
||||
var tempValue = value.Replace("f", "");
|
||||
|
||||
float floatValue;
|
||||
float.TryParse(tempValue, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, CultureInfo.GetCultureInfo(DEFAULT_CULTURE_CODE), out floatValue);
|
||||
var field = property.GetValue(obj) as NullableValue<float>;
|
||||
property.SetValue(obj, field.SetValue(true, floatValue));
|
||||
return true;
|
||||
}
|
||||
if (property.PropertyType.IsSubclassOf(typeof(AggregateIniValue)))
|
||||
{
|
||||
var field = property.GetValue(obj) as AggregateIniValue;
|
||||
field?.InitializeFromINIValue(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
707
src/ServerManager.Common/Utils/TaskSchedulerUtils.cs
Normal file
707
src/ServerManager.Common/Utils/TaskSchedulerUtils.cs
Normal file
|
|
@ -0,0 +1,707 @@
|
|||
using Microsoft.Win32.TaskScheduler;
|
||||
using NLog;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class TaskSchedulerUtils
|
||||
{
|
||||
private const string PREFIX_BACKUP = "AutoBackup";
|
||||
private const string PREFIX_SHUTDOWN = "AutoShutdown";
|
||||
private const string PREFIX_START = "AutoStart";
|
||||
private const string PREFIX_UPDATE = "AutoUpdate";
|
||||
|
||||
private const int EXECUTION_TIME_LIMIT = 3;
|
||||
|
||||
public enum ShutdownType
|
||||
{
|
||||
Shutdown1,
|
||||
Shutdown2,
|
||||
}
|
||||
|
||||
public enum TaskType
|
||||
{
|
||||
AutoBackup,
|
||||
AutoShutdown,
|
||||
AutoStart,
|
||||
AutoUpdate,
|
||||
}
|
||||
|
||||
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
static TaskSchedulerUtils()
|
||||
{
|
||||
TaskFolder = "ServerManager";
|
||||
}
|
||||
|
||||
public static string TaskFolder
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public static string ComputeKey(string folder)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var hashAlgo = MD5.Create())
|
||||
{
|
||||
var hashStr = Encoding.UTF8.GetBytes(folder);
|
||||
var hash = hashAlgo.ComputeHash(hashStr);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var b in hash)
|
||||
{
|
||||
// can be "x2" if you want lowercase
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
// Exception has been thrown by the target of an invocation.
|
||||
// This error message seems to occur when using MD5 hash algorithm on an environment where FIPS is enabled.
|
||||
// Swallow the exception and allow the SHA1 algorithm to be used.
|
||||
_logger.Debug($"Unable to calculate the ComputeKey (MD5).\r\n{ex.Message}.");
|
||||
}
|
||||
|
||||
// An error occurred using the MD5 hash, try using SHA1 instead.
|
||||
using (var hashAlgo = SHA1.Create())
|
||||
{
|
||||
var hashStr = Encoding.UTF8.GetBytes(folder);
|
||||
var hash = hashAlgo.ComputeHash(hashStr);
|
||||
|
||||
var sb = new StringBuilder(hash.Length * 2);
|
||||
foreach (byte b in hash)
|
||||
{
|
||||
// can be "x2" if you want lowercase
|
||||
sb.Append(b.ToString("x2"));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetScheduleTaskInformation(TaskType taskType, string taskKey, string taskSuffix)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(taskType, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
if (taskFolder == null)
|
||||
return null;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return null;
|
||||
|
||||
return task.Xml;
|
||||
}
|
||||
|
||||
public static string GetScheduledTaskName(TaskType taskType, string taskKey, string taskSuffix)
|
||||
{
|
||||
var taskName = string.Empty;
|
||||
|
||||
switch (taskType)
|
||||
{
|
||||
case TaskType.AutoBackup:
|
||||
taskName = $"{PREFIX_BACKUP}_{taskKey}";
|
||||
break;
|
||||
|
||||
case TaskType.AutoShutdown:
|
||||
taskName = $"{PREFIX_SHUTDOWN}_{taskKey}";
|
||||
break;
|
||||
|
||||
case TaskType.AutoStart:
|
||||
taskName = $"{PREFIX_START}_{taskKey}";
|
||||
break;
|
||||
|
||||
case TaskType.AutoUpdate:
|
||||
taskName = $"{PREFIX_UPDATE}_{taskKey}";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(taskName) && !string.IsNullOrWhiteSpace(taskSuffix))
|
||||
taskName += $"_{taskSuffix}";
|
||||
|
||||
return taskName;
|
||||
}
|
||||
|
||||
public static void RunAutoBackup(string taskKey, string taskSuffix)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoBackup, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
if (taskFolder == null)
|
||||
return;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return;
|
||||
|
||||
task.Run();
|
||||
}
|
||||
|
||||
public static void RunAutoUpdate(string taskKey, string taskSuffix)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoUpdate, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
if (taskFolder == null)
|
||||
return;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return;
|
||||
|
||||
task.Run();
|
||||
}
|
||||
|
||||
public static bool ScheduleAutoBackup(string taskKey, string taskSuffix, string command, int autoBackupPeriod)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoBackup, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
|
||||
if (autoBackupPeriod > 0)
|
||||
{
|
||||
// create the task folder
|
||||
if (taskFolder == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
taskFolder = TaskService.Instance.RootFolder.CreateFolder(TaskFolder, null, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoBackup)} - Unable to create the Server Manager task folder. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (taskFolder == null)
|
||||
return false;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
var taskDefinition = task?.Definition ?? TaskService.Instance.NewTask();
|
||||
|
||||
if (taskDefinition == null)
|
||||
return false;
|
||||
|
||||
Version.TryParse(AppUtils.GetDeployedVersion(), out Version appVersion);
|
||||
|
||||
taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken;
|
||||
taskDefinition.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
|
||||
taskDefinition.RegistrationInfo.Description = "Server Auto-Backup";
|
||||
taskDefinition.RegistrationInfo.Source = "Server Manager";
|
||||
taskDefinition.RegistrationInfo.Version = appVersion;
|
||||
|
||||
taskDefinition.Settings.ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT);
|
||||
taskDefinition.Settings.Priority = ProcessPriorityClass.Normal;
|
||||
|
||||
// Add/Edit the trigger that will fire every x minutes
|
||||
var triggers = taskDefinition.Triggers.OfType<TimeTrigger>();
|
||||
if (triggers.Count() == 0)
|
||||
{
|
||||
var trigger = new TimeTrigger
|
||||
{
|
||||
StartBoundary = DateTime.Today.AddHours(DateTime.Now.Hour + 1),
|
||||
ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT),
|
||||
Repetition = { Interval = TimeSpan.FromMinutes(autoBackupPeriod) },
|
||||
};
|
||||
taskDefinition.Triggers.Add(trigger);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var trigger in triggers)
|
||||
{
|
||||
trigger.Repetition.Interval = TimeSpan.FromMinutes(autoBackupPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an action that will launch whenever the trigger fires
|
||||
taskDefinition.Actions.Clear();
|
||||
var action = new ExecAction
|
||||
{
|
||||
Path = command,
|
||||
Arguments = Constants.ARG_AUTOBACKUP
|
||||
};
|
||||
taskDefinition.Actions.Add(action);
|
||||
|
||||
try
|
||||
{
|
||||
task = taskFolder.RegisterTaskDefinition(taskName, taskDefinition, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.InteractiveToken);
|
||||
return task != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoBackup)} - Unable to create the ScheduleAutoBackup task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (taskFolder == null)
|
||||
return true;
|
||||
|
||||
// Retrieve the task to be deleted
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
// Delete the task
|
||||
taskFolder.DeleteTask(taskName, false);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoBackup)} - Unable to delete the ScheduleAutoBackup task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ScheduleAutoShutdown(string taskKey, string taskSuffix, string command, TimeSpan? restartTime, string profileName, ShutdownType type)
|
||||
{
|
||||
return ScheduleAutoShutdown(taskKey, taskSuffix, command, restartTime, DaysOfTheWeek.AllDays, profileName, type);
|
||||
}
|
||||
|
||||
public static bool ScheduleAutoShutdown(string taskKey, string taskSuffix, string command, TimeSpan? restartTime, DaysOfTheWeek daysOfTheWeek, string profileName, ShutdownType type)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoShutdown, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
|
||||
if (restartTime.HasValue)
|
||||
{
|
||||
// create the task folder
|
||||
if (taskFolder == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
taskFolder = TaskService.Instance.RootFolder.CreateFolder(TaskFolder, null, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoShutdown)} - Unable to create the Server Manager task folder. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (taskFolder == null)
|
||||
return false;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
var taskDefinition = task?.Definition ?? TaskService.Instance.NewTask();
|
||||
|
||||
if (taskDefinition == null)
|
||||
return false;
|
||||
|
||||
Version.TryParse(AppUtils.GetDeployedVersion(), out Version appVersion);
|
||||
|
||||
taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken;
|
||||
taskDefinition.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
|
||||
taskDefinition.RegistrationInfo.Description = $"Server Auto-Shutdown - {profileName}";
|
||||
taskDefinition.RegistrationInfo.Source = "Server Manager";
|
||||
taskDefinition.RegistrationInfo.Version = appVersion;
|
||||
|
||||
taskDefinition.Settings.ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT);
|
||||
taskDefinition.Settings.Priority = ProcessPriorityClass.Normal;
|
||||
|
||||
// Add/Edit the trigger that will fire every day at the specified restart time
|
||||
var triggers = taskDefinition.Triggers.OfType<WeeklyTrigger>().ToList();
|
||||
if (triggers.Count == 0)
|
||||
{
|
||||
var trigger = new WeeklyTrigger
|
||||
{
|
||||
StartBoundary = DateTime.Today.Add(restartTime.Value),
|
||||
ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT),
|
||||
DaysOfWeek = daysOfTheWeek,
|
||||
};
|
||||
taskDefinition.Triggers.Add(trigger);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var trigger in triggers)
|
||||
{
|
||||
trigger.StartBoundary = DateTime.Today.Add(restartTime.Value);
|
||||
trigger.DaysOfWeek = daysOfTheWeek;
|
||||
}
|
||||
}
|
||||
|
||||
// remove any old triggers
|
||||
var oldTriggers = taskDefinition.Triggers.OfType<DailyTrigger>().ToList();
|
||||
if (oldTriggers.Count > 0)
|
||||
{
|
||||
foreach (var oldTrigger in oldTriggers)
|
||||
{
|
||||
taskDefinition.Triggers.Remove(oldTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an action that will launch whenever the trigger fires
|
||||
var arguments = string.Empty;
|
||||
switch (type)
|
||||
{
|
||||
case ShutdownType.Shutdown1:
|
||||
arguments = Constants.ARG_AUTOSHUTDOWN1;
|
||||
break;
|
||||
case ShutdownType.Shutdown2:
|
||||
arguments = Constants.ARG_AUTOSHUTDOWN2;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
taskDefinition.Actions.Clear();
|
||||
var action = new ExecAction
|
||||
{
|
||||
Path = command,
|
||||
Arguments = $"{arguments}{taskKey}"
|
||||
};
|
||||
taskDefinition.Actions.Add(action);
|
||||
|
||||
try
|
||||
{
|
||||
task = taskFolder.RegisterTaskDefinition(taskName, taskDefinition, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.InteractiveToken);
|
||||
return task != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoShutdown)} - Unable to create the ScheduleAutoShutdown task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (taskFolder == null)
|
||||
return true;
|
||||
|
||||
// Retrieve the task to be deleted
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
// Delete the task
|
||||
taskFolder.DeleteTask(taskName, false);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoShutdown)} - Unable to delete the ScheduleAutoShutdown task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ScheduleAutoStart(string taskKey, string taskSuffix, bool enableAutoStart, string command, string profileName, bool onBoot)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoStart, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
|
||||
if (enableAutoStart)
|
||||
{
|
||||
// create the task folder
|
||||
if (taskFolder == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
taskFolder = TaskService.Instance.RootFolder.CreateFolder(TaskFolder, null, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoStart)} - Unable to create the Server Manager task folder. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (taskFolder == null)
|
||||
return false;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
var taskDefinition = task?.Definition ?? TaskService.Instance.NewTask();
|
||||
|
||||
if (taskDefinition == null)
|
||||
return false;
|
||||
|
||||
Version.TryParse(AppUtils.GetDeployedVersion(), out Version appVersion);
|
||||
|
||||
taskDefinition.Principal.LogonType = TaskLogonType.ServiceAccount;
|
||||
taskDefinition.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
|
||||
taskDefinition.RegistrationInfo.Description = $"Server Auto-Start - {profileName}";
|
||||
taskDefinition.RegistrationInfo.Source = "Server Manager";
|
||||
taskDefinition.RegistrationInfo.Version = appVersion;
|
||||
|
||||
taskDefinition.Settings.ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT);
|
||||
taskDefinition.Settings.Priority = ProcessPriorityClass.Normal;
|
||||
|
||||
// Add a trigger that will fire after the machine has started
|
||||
if (onBoot)
|
||||
{
|
||||
var triggers = taskDefinition.Triggers.OfType<BootTrigger>();
|
||||
if (triggers.Count() == 0)
|
||||
{
|
||||
var trigger = new BootTrigger
|
||||
{
|
||||
Delay = TimeSpan.FromMinutes(1),
|
||||
ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT)
|
||||
};
|
||||
taskDefinition.Triggers.Add(trigger);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var trigger in triggers)
|
||||
{
|
||||
trigger.Delay = TimeSpan.FromMinutes(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var triggers = taskDefinition.Triggers.OfType<LogonTrigger>();
|
||||
if (triggers.Count() == 0)
|
||||
{
|
||||
var trigger = new LogonTrigger
|
||||
{
|
||||
Delay = TimeSpan.FromMinutes(1),
|
||||
ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT)
|
||||
};
|
||||
taskDefinition.Triggers.Add(trigger);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var trigger in triggers)
|
||||
{
|
||||
trigger.Delay = TimeSpan.FromMinutes(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create an action that will launch whenever the trigger fires
|
||||
taskDefinition.Actions.Clear();
|
||||
var action = new ExecAction
|
||||
{
|
||||
Path = command,
|
||||
Arguments = string.Empty
|
||||
};
|
||||
taskDefinition.Actions.Add(action);
|
||||
|
||||
try
|
||||
{
|
||||
task = taskFolder.RegisterTaskDefinition(taskName, taskDefinition, TaskCreation.CreateOrUpdate, "SYSTEM", null, TaskLogonType.ServiceAccount);
|
||||
return task != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoStart)} - Unable to create the ScheduleAutoStart task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (taskFolder == null)
|
||||
return true;
|
||||
|
||||
// Retrieve the task to be deleted
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
// Delete the task
|
||||
taskFolder.DeleteTask(taskName, false);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoStart)} - Unable to delete the ScheduleAutoStart task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ScheduleAutoUpdate(string taskKey, string taskSuffix, string command, int autoUpdatePeriod)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoUpdate, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
|
||||
if (autoUpdatePeriod > 0)
|
||||
{
|
||||
|
||||
// create the task folder
|
||||
if (taskFolder == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
taskFolder = TaskService.Instance.RootFolder.CreateFolder(TaskFolder, null, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoUpdate)} - Unable to create the Server Manager task folder. {ex.Message}\r\n{ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (taskFolder == null)
|
||||
return false;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
var taskDefinition = task?.Definition ?? TaskService.Instance.NewTask();
|
||||
|
||||
if (taskDefinition == null)
|
||||
return false;
|
||||
|
||||
Version.TryParse(AppUtils.GetDeployedVersion(), out Version appVersion);
|
||||
|
||||
taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken;
|
||||
taskDefinition.Principal.RunLevel = TaskRunLevel.Highest;
|
||||
|
||||
taskDefinition.RegistrationInfo.Description = "Server Auto-Update";
|
||||
taskDefinition.RegistrationInfo.Source = "Server Manager";
|
||||
taskDefinition.RegistrationInfo.Version = appVersion;
|
||||
|
||||
taskDefinition.Settings.ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT);
|
||||
taskDefinition.Settings.Priority = ProcessPriorityClass.Normal;
|
||||
|
||||
// Add/Edit the trigger that will fire every x minutes
|
||||
var triggers = taskDefinition.Triggers.OfType<TimeTrigger>();
|
||||
if (triggers.Count() == 0)
|
||||
{
|
||||
var trigger = new TimeTrigger
|
||||
{
|
||||
StartBoundary = DateTime.Today.AddHours(DateTime.Now.Hour + 1),
|
||||
ExecutionTimeLimit = TimeSpan.FromHours(EXECUTION_TIME_LIMIT),
|
||||
Repetition = { Interval = TimeSpan.FromMinutes(autoUpdatePeriod) },
|
||||
};
|
||||
taskDefinition.Triggers.Add(trigger);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var trigger in triggers)
|
||||
{
|
||||
trigger.Repetition.Interval = TimeSpan.FromMinutes(autoUpdatePeriod);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an action that will launch whenever the trigger fires
|
||||
taskDefinition.Actions.Clear();
|
||||
var action = new ExecAction
|
||||
{
|
||||
Path = command,
|
||||
Arguments = Constants.ARG_AUTOUPDATE
|
||||
};
|
||||
taskDefinition.Actions.Add(action);
|
||||
|
||||
try
|
||||
{
|
||||
task = taskFolder.RegisterTaskDefinition(taskName, taskDefinition, TaskCreation.CreateOrUpdate, null, null, TaskLogonType.InteractiveToken);
|
||||
return task != null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoUpdate)} - Unable to create the ScheduleAutoUpdate task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (taskFolder == null)
|
||||
return true;
|
||||
|
||||
// Retrieve the task to be deleted
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
// Delete the task
|
||||
taskFolder.DeleteTask(taskName, false);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"{nameof(ScheduleAutoUpdate)} - Unable to delete the ScheduleAutoUpdate task. {ex.Message}\r\n{ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void SetAutoBackupState(string taskKey, string taskSuffix, bool? enable)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoBackup, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
if (taskFolder == null)
|
||||
return;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return;
|
||||
|
||||
if (task.State == TaskState.Disabled || task.State == TaskState.Ready)
|
||||
{
|
||||
task.Definition.Settings.Enabled = enable ?? !task.Enabled;
|
||||
task.RegisterChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetAutoUpdateState(string taskKey, string taskSuffix, bool? enable)
|
||||
{
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoUpdate, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
if (taskFolder == null)
|
||||
return;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return;
|
||||
|
||||
if (task.State == TaskState.Disabled || task.State == TaskState.Ready)
|
||||
{
|
||||
task.Definition.Settings.Enabled = enable ?? !task.Enabled;
|
||||
task.RegisterChanges();
|
||||
}
|
||||
}
|
||||
|
||||
public static TaskState TaskStateAutoBackup(string taskKey, string taskSuffix, out DateTime nextRunTime)
|
||||
{
|
||||
nextRunTime = DateTime.MinValue;
|
||||
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoBackup, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
if (taskFolder == null)
|
||||
return TaskState.Unknown;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return TaskState.Unknown;
|
||||
|
||||
nextRunTime = task.NextRunTime;
|
||||
return task.State;
|
||||
}
|
||||
|
||||
public static TaskState TaskStateAutoUpdate(string taskKey, string taskSuffix, out DateTime nextRunTime)
|
||||
{
|
||||
nextRunTime = DateTime.MinValue;
|
||||
|
||||
var taskName = GetScheduledTaskName(TaskType.AutoUpdate, taskKey, taskSuffix);
|
||||
var taskFolder = TaskService.Instance.RootFolder.SubFolders.Exists(TaskFolder) ? TaskService.Instance.RootFolder.SubFolders[TaskFolder] : null;
|
||||
if (taskFolder == null)
|
||||
return TaskState.Unknown;
|
||||
|
||||
var task = taskFolder.Tasks.Exists(taskName) ? taskFolder.Tasks[taskName] : null;
|
||||
if (task == null)
|
||||
return TaskState.Unknown;
|
||||
|
||||
nextRunTime = task.NextRunTime;
|
||||
return task.State;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/ServerManager.Common/Utils/TaskUtils.cs
Normal file
60
src/ServerManager.Common/Utils/TaskUtils.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class TaskUtils
|
||||
{
|
||||
public static void DoNotWait(this Task task)
|
||||
{
|
||||
// Do nothing, let the task continue. Eliminates compiler warning about non-awaited tasks in an async method.
|
||||
}
|
||||
|
||||
public static async Task RunOnUIThreadAsync(Action action)
|
||||
{
|
||||
var app = Application.Current;
|
||||
if (app != null)
|
||||
{
|
||||
await app.Dispatcher.InvokeAsync(action);
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly Task FinishedTask = Task.FromResult(true);
|
||||
|
||||
public static async Task TimeoutAfterAsync(this Task task, int millisecondsDelay)
|
||||
{
|
||||
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
|
||||
{
|
||||
var completedTask = await Task.WhenAny(task, Task.Delay(millisecondsDelay, timeoutCancellationTokenSource.Token));
|
||||
if (completedTask == task)
|
||||
{
|
||||
timeoutCancellationTokenSource.Cancel();
|
||||
await task; // Very important in order to propagate exceptions
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TimeoutException("The operation has timed out.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<TResult> TimeoutAfterAsync<TResult>(this Task<TResult> task, int millisecondsDelay)
|
||||
{
|
||||
using (var timeoutCancellationTokenSource = new CancellationTokenSource())
|
||||
{
|
||||
var completedTask = await Task.WhenAny(task, Task.Delay(millisecondsDelay, timeoutCancellationTokenSource.Token));
|
||||
if (completedTask == task)
|
||||
{
|
||||
timeoutCancellationTokenSource.Cancel();
|
||||
return await task; // Very important in order to propagate exceptions
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TimeoutException("The operation has timed out.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/ServerManager.Common/Utils/VersionFeedUtils.cs
Normal file
54
src/ServerManager.Common/Utils/VersionFeedUtils.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using ServerManagerTool.Common.Model;
|
||||
using System;
|
||||
using System.ServiceModel.Syndication;
|
||||
using System.Xml;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class VersionFeedUtils
|
||||
{
|
||||
public static VersionFeed LoadVersionFeed(string inputUri, string currentVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
var reader = XmlReader.Create(inputUri);
|
||||
var feed = SyndicationFeed.Load(reader);
|
||||
|
||||
var versionFeed = new VersionFeed
|
||||
{
|
||||
Id = feed.Id,
|
||||
Title = feed.Title?.Text,
|
||||
SubTitle = feed.Description?.Text,
|
||||
Link = feed.Links?[0].Uri,
|
||||
Updated = feed.LastUpdatedTime.ToLocalTime(),
|
||||
};
|
||||
|
||||
//Loop through all items in the SyndicationFeed
|
||||
foreach (var item in feed.Items)
|
||||
{
|
||||
var textContent = item.Content as TextSyndicationContent;
|
||||
|
||||
var versionFeedEntry = new VersionFeedEntry
|
||||
{
|
||||
Id = item.Id,
|
||||
Title = item.Title?.Text,
|
||||
Summary = item.Summary?.Text,
|
||||
Link = item.Links?[0].Uri,
|
||||
Updated = item.LastUpdatedTime.ToLocalTime(),
|
||||
Content = textContent?.Text,
|
||||
Author = item.Authors?[0].Name,
|
||||
|
||||
IsCurrent = (item.Summary?.Text ?? string.Empty).Equals(currentVersion),
|
||||
};
|
||||
versionFeed.Entries.Add(versionFeedEntry);
|
||||
}
|
||||
|
||||
return versionFeed;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return new VersionFeed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/ServerManager.Common/Utils/VersionUtils.cs
Normal file
28
src/ServerManager.Common/Utils/VersionUtils.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class VersionUtils
|
||||
{
|
||||
public static Version GetVersionFromFile(string versionFile)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(versionFile) && File.Exists(versionFile))
|
||||
{
|
||||
var fileValue = File.ReadAllText(versionFile);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(fileValue))
|
||||
{
|
||||
string versionString = fileValue.ToString();
|
||||
if (versionString.IndexOf('.') == -1)
|
||||
versionString = versionString + ".0";
|
||||
|
||||
if (Version.TryParse(versionString, out Version version))
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
return new Version(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
122
src/ServerManager.Common/Utils/WindowUtils.cs
Normal file
122
src/ServerManager.Common/Utils/WindowUtils.cs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class WindowUtils
|
||||
{
|
||||
public static void RemoveDefaultResourceDictionary(Window window, string defaultDictionary)
|
||||
{
|
||||
if (window == null)
|
||||
return;
|
||||
|
||||
var dictToRemove = window.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(defaultDictionary));
|
||||
if (dictToRemove != null)
|
||||
{
|
||||
window.Resources.MergedDictionaries.Remove(dictToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveDefaultResourceDictionary(UserControl control, string defaultDictionary)
|
||||
{
|
||||
if (control == null)
|
||||
return;
|
||||
|
||||
var dictToRemove = control.Resources.MergedDictionaries.FirstOrDefault(d => d.Source.OriginalString.Contains(defaultDictionary));
|
||||
if (dictToRemove != null)
|
||||
{
|
||||
control.Resources.MergedDictionaries.Remove(dictToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a parent of a given item on the visual tree.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the queried item.</typeparam>
|
||||
/// <param name="child">A direct or indirect child of the queried item.</param>
|
||||
/// <returns>The first parent item that matches the submitted type parameter. If not matching item can be found, a null reference is being returned.</returns>
|
||||
public static T TryFindParent<T>(DependencyObject child)
|
||||
where T : DependencyObject
|
||||
{
|
||||
//get parent item
|
||||
DependencyObject parentObject = GetParentObject(child);
|
||||
|
||||
//we've reached the end of the tree
|
||||
if (parentObject == null) return null;
|
||||
|
||||
//check if the parent matches the type we're looking for
|
||||
T parent = parentObject as T;
|
||||
if (parent != null)
|
||||
return parent;
|
||||
|
||||
//use recursion to proceed with next level
|
||||
return TryFindParent<T>(parentObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is an alternative to WPF's <see cref="VisualTreeHelper.GetParent"/> method, which also supports content elements.
|
||||
/// Do note, that for content element, this method falls back to the logical tree of the element.
|
||||
/// </summary>
|
||||
/// <param name="child">The item to be processed.</param>
|
||||
/// <returns>The submitted item's parent, if available. Otherwise null.</returns>
|
||||
public static DependencyObject GetParentObject(DependencyObject child)
|
||||
{
|
||||
if (child == null) return null;
|
||||
var contentElement = child as ContentElement;
|
||||
|
||||
if (contentElement != null)
|
||||
{
|
||||
var parent = ContentOperations.GetParent(contentElement);
|
||||
if (parent != null) return parent;
|
||||
|
||||
var fce = contentElement as FrameworkContentElement;
|
||||
return fce?.Parent;
|
||||
}
|
||||
|
||||
//if it's not a ContentElement, rely on VisualTreeHelper
|
||||
return VisualTreeHelper.GetParent(child);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively processes a given dependency object and all its children, and updates sources of all objects that use a binding expression on a given property.
|
||||
/// </summary>
|
||||
/// <param name="obj">The dependency object that marks a starting point. This could be a dialog window or a panel control that hosts bound controls.</param>
|
||||
/// <param name="properties">The properties to be updated if
|
||||
/// <paramref name="obj"/> or one of its childs provide it along with a binding expression.</param>
|
||||
public static void UpdateBindingSources(DependencyObject obj, params DependencyProperty[] properties)
|
||||
{
|
||||
foreach (var depProperty in properties)
|
||||
{
|
||||
//check whether the submitted object provides a bound property that matches the property parameters
|
||||
var be = BindingOperations.GetBindingExpression(obj, depProperty);
|
||||
be?.UpdateSource();
|
||||
}
|
||||
|
||||
int count = VisualTreeHelper.GetChildrenCount(obj);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
//process child items recursively
|
||||
var childObject = VisualTreeHelper.GetChild(obj, i);
|
||||
UpdateBindingSources(childObject, properties);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to locate a given item within the visual tree, starting with the dependency object at a given position.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the element to be found on the visual tree of the element at the given location.</typeparam>
|
||||
/// <param name="reference">The main element which is used to perform hit testing.</param>
|
||||
/// <param name="point">The position to be evaluated on the origin.</param>
|
||||
public static T TryFindFromPoint<T>(UIElement reference, Point point)
|
||||
where T : DependencyObject
|
||||
{
|
||||
var element = reference.InputHitTest(point) as DependencyObject;
|
||||
if (element == null) return null;
|
||||
if (element is T) return (T)element;
|
||||
return TryFindParent<T>(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
182
src/ServerManager.Common/Utils/ZipUtils.cs
Normal file
182
src/ServerManager.Common/Utils/ZipUtils.cs
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
using Ionic.Zip;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace ServerManagerTool.Common.Utils
|
||||
{
|
||||
public static class ZipUtils
|
||||
{
|
||||
public static bool DoesFileExist(string zipFile, string entryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zipFile))
|
||||
throw new ArgumentNullException(nameof(zipFile));
|
||||
if (string.IsNullOrWhiteSpace(entryName))
|
||||
throw new ArgumentNullException(nameof(entryName));
|
||||
|
||||
if (!File.Exists(zipFile))
|
||||
throw new FileNotFoundException();
|
||||
|
||||
using (var zip = ZipFile.Read(zipFile))
|
||||
{
|
||||
return zip.Entries.Any(e => Path.GetFileName(e.FileName).Equals(entryName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
public static int ExtractAFile(string zipFile, string entryName, string destinationPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zipFile))
|
||||
throw new ArgumentNullException(nameof(zipFile));
|
||||
if (string.IsNullOrWhiteSpace(entryName))
|
||||
throw new ArgumentNullException(nameof(entryName));
|
||||
if (string.IsNullOrWhiteSpace(destinationPath))
|
||||
throw new ArgumentNullException(nameof(destinationPath));
|
||||
|
||||
if (!File.Exists(zipFile))
|
||||
throw new FileNotFoundException();
|
||||
if (!Directory.Exists(destinationPath))
|
||||
Directory.CreateDirectory(destinationPath);
|
||||
|
||||
using (var zip = ZipFile.Read(zipFile))
|
||||
{
|
||||
var selection = zip.Entries.Where(e => Path.GetFileName(e.FileName).Equals(entryName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
foreach (var entry in selection)
|
||||
{
|
||||
entry.Extract(destinationPath, ExtractExistingFileAction.OverwriteSilently);
|
||||
}
|
||||
|
||||
return selection.Count();
|
||||
}
|
||||
}
|
||||
|
||||
public static int ExtractAllFiles(string zipFile, string destinationPath)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zipFile))
|
||||
throw new ArgumentNullException(nameof(zipFile));
|
||||
if (string.IsNullOrWhiteSpace(destinationPath))
|
||||
throw new ArgumentNullException(nameof(destinationPath));
|
||||
|
||||
if (!Directory.Exists(destinationPath))
|
||||
Directory.CreateDirectory(destinationPath);
|
||||
|
||||
using (var zip = ZipFile.Read(zipFile))
|
||||
{
|
||||
zip.ExtractAll(destinationPath, ExtractExistingFileAction.OverwriteSilently);
|
||||
|
||||
return zip.Entries.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdateFiles(string zipFile, string[] filesToZip, string comment = "", bool preserveDirHierarchy = true, string directoryPathInArchive = "")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zipFile))
|
||||
throw new ArgumentNullException(nameof(zipFile));
|
||||
if (filesToZip == null || filesToZip.Length == 0)
|
||||
return;
|
||||
|
||||
if (!File.Exists(zipFile))
|
||||
{
|
||||
ZipFiles(zipFile, filesToZip, comment, preserveDirHierarchy, directoryPathInArchive);
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var zip = ZipFile.Read(zipFile))
|
||||
{
|
||||
zip.AddFiles(filesToZip.Where(f => !string.IsNullOrWhiteSpace(f) && File.Exists(f)), preserveDirHierarchy, directoryPathInArchive);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(comment))
|
||||
zip.Comment = comment;
|
||||
|
||||
zip.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ZipAFile(string zipFile, string entryName, string content)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zipFile))
|
||||
throw new ArgumentNullException(nameof(zipFile));
|
||||
if (string.IsNullOrWhiteSpace(entryName))
|
||||
throw new ArgumentNullException(nameof(entryName));
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
throw new ArgumentNullException(nameof(content));
|
||||
|
||||
if (!File.Exists(zipFile))
|
||||
{
|
||||
using (var zip = new ZipFile())
|
||||
{
|
||||
zip.AddEntry(entryName, content);
|
||||
|
||||
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
|
||||
|
||||
zip.Save(zipFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var zip = ZipFile.Read(zipFile))
|
||||
{
|
||||
zip.AddEntry(entryName, content);
|
||||
|
||||
zip.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ZipAFile(string zipFile, string entryName, string content, string comment)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zipFile))
|
||||
throw new ArgumentNullException(nameof(zipFile));
|
||||
if (string.IsNullOrWhiteSpace(entryName))
|
||||
throw new ArgumentNullException(nameof(entryName));
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
throw new ArgumentNullException(nameof(content));
|
||||
|
||||
if (!File.Exists(zipFile))
|
||||
{
|
||||
using (var zip = new ZipFile())
|
||||
{
|
||||
zip.AddEntry(entryName, File.ReadAllBytes(content));
|
||||
|
||||
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
|
||||
if (!string.IsNullOrWhiteSpace(comment))
|
||||
zip.Comment = comment;
|
||||
|
||||
zip.Save(zipFile);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var zip = ZipFile.Read(zipFile))
|
||||
{
|
||||
zip.AddEntry(entryName, File.ReadAllBytes(content));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(comment))
|
||||
zip.Comment = comment;
|
||||
|
||||
zip.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ZipFiles(string zipFile, string[] filesToZip, string comment = "", bool preserveDirHierarchy = true, string directoryPathInArchive = "")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(zipFile))
|
||||
throw new ArgumentNullException(nameof(zipFile));
|
||||
if (filesToZip == null || filesToZip.Length == 0)
|
||||
throw new ArgumentNullException(nameof(filesToZip));
|
||||
|
||||
using (var zip = new ZipFile(zipFile))
|
||||
{
|
||||
zip.AddFiles(filesToZip.Where(f => !string.IsNullOrWhiteSpace(f) && File.Exists(f)), preserveDirHierarchy, directoryPathInArchive);
|
||||
|
||||
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
|
||||
if (!string.IsNullOrWhiteSpace(comment))
|
||||
zip.Comment = comment;
|
||||
|
||||
zip.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue