First Commit

This commit is contained in:
Donny 2024-08-30 14:27:20 -06:00
parent 071616b426
commit 70efdc32dc
14 changed files with 1306 additions and 0 deletions

111
.gitignore vendored Normal file
View File

@ -0,0 +1,111 @@
# Ignore Visual Studio temporary files, build results, and
# files generated by popular Visual Studio add-ons.
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (Mono)
mono_crash.*
# Auto-generated files
*.vspscc
*.vssscc
# Build results
[Dd]ebug/
[Rr]elease/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Roslyn cache directories
.vscode/
.vscode-test/
.vscode-oss/
# NuGet Packages
*.nupkg
*.snupkg
.nuget/
packages/
paket-files/
.nuget/
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# VS Code directories
.vscode/
.history/
# Rider
.idea/
# JetBrains Rider config directory
.idea/
# Rider's default project-specific files
.idea/
.idea/.*
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# Azure Web Apps
site/wwwroot/
site/deployment/
site/extensions/
# Entity Framework Migrations
Migrations/
# Publish directory
.publish/
# Web config
web.config
app.config
# Certificates
*.pfx
*.cer
*.crt
# Temporary files
*.tmp
*.temp
# Log files
*.log
*.tlog
# Add this section to ignore the MPELicenseTracker folder
MPELicenseTracker/
# Custom configuration files
*.json
*.env
# Ignore all files and directories starting with a dot
.*
!.gitignore
# Optional: Ignore the Visual Studio Code workspace file
*.code-workspace

29
Installer1.cs Normal file
View File

@ -0,0 +1,29 @@
// Decompiled with JetBrains decompiler
// Type: LicenseTrackerService.Installer1
// Assembly: LicenseTrackerService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 190801E4-3027-41A4-B03A-CBAE9DAB23BA
// Assembly location: C:\MPE\MPELicenseTracker\LicenseTrackerService.exe
using System.ComponentModel;
using System.Configuration.Install;
#nullable disable
namespace LicenseTrackerService
{
[RunInstaller(true)]
public class Installer1 : Installer
{
private IContainer components = (IContainer) null;
public Installer1() => this.InitializeComponent();
protected override void Dispose(bool disposing)
{
if (disposing && this.components != null)
this.components.Dispose();
base.Dispose(disposing);
}
private void InitializeComponent() => this.components = (IContainer) new System.ComponentModel.Container();
}
}

25
MPELicenseAgent.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.35013.160
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MPELicenseAgent", "MPELicenseAgent\MPELicenseAgent.csproj", "{92405B10-B07E-4794-BC0D-230A599048F8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{92405B10-B07E-4794-BC0D-230A599048F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{92405B10-B07E-4794-BC0D-230A599048F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92405B10-B07E-4794-BC0D-230A599048F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92405B10-B07E-4794-BC0D-230A599048F8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {40E80C92-E24A-4BB1-AAB7-A4AF03AC2A06}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{92405B10-B07E-4794-BC0D-230A599048F8}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>MPELicenseAgent</RootNamespace>
<AssemblyName>MPELicenseAgent</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ProjectInstaller.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ProjectInstaller.Designer.cs">
<DependentUpon>ProjectInstaller.cs</DependentUpon>
</Compile>
<Compile Include="Service1.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Service1.Designer.cs">
<DependentUpon>Service1.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ProjectInstaller.resx">
<DependentUpon>ProjectInstaller.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace MPELicenseAgent
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service1()
};
ServiceBase.Run(ServicesToRun);
}
}
}

View File

@ -0,0 +1,68 @@
using System.Configuration.Install;
using System.ServiceProcess;
namespace MPELicenseAgent
{
partial class ProjectInstaller
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; // Runs the service as the logged in user
this.serviceProcessInstaller1.Password = (string)null;
this.serviceProcessInstaller1.Username = (string)null;
//
// serviceInstaller1
//
this.serviceInstaller1.Description = "Used to track MPE license usage";
this.serviceInstaller1.DisplayName = "MPE License Tracker";
this.serviceInstaller1.ServiceName = "MPE License Tracker";
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic; //Make sure it starts automatically
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.serviceInstaller1});
}
#endregion
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
private System.ServiceProcess.ServiceInstaller serviceInstaller1;
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;
using System.Threading.Tasks;
namespace MPELicenseAgent
{
[RunInstaller(true)]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
AfterInstall += serviceInstaller1_AfterInstall;
InitializeComponent();
}
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
using (ServiceController serviceController = new ServiceController(this.serviceInstaller1.ServiceName))
{
if (serviceController.Status.Equals((object)ServiceControllerStatus.Running) || serviceController.Status.Equals((object)ServiceControllerStatus.StartPending))
serviceController.Stop();
serviceController.WaitForStatus(ServiceControllerStatus.Stopped);
serviceController.Start(); // Starts the service
serviceController.WaitForStatus(ServiceControllerStatus.Running);
// Automatic restarts ("Recovery" tab in service properties is sec via the command): +
// sc.exe failure "MPE License Tracker" reset= 0 actions= restart/60000
}
}
}
}

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="serviceProcessInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="serviceInstaller1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>194, 17</value>
</metadata>
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MPELicenseAgent")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("MPE Engineering Ltd")]
[assembly: AssemblyProduct("MPELicenseAgent")]
[assembly: AssemblyCopyright("Copyright © MPE Engineering Ltd 2024")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("92405b10-b07e-4794-bc0d-230a599048f8")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

37
MPELicenseAgent/Service1.Designer.cs generated Normal file
View File

@ -0,0 +1,37 @@
namespace MPELicenseAgent
{
partial class Service1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.ServiceName = "Service1";
}
#endregion
}
}

338
MPELicenseAgent/Service1.cs Normal file
View File

@ -0,0 +1,338 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Net.Http;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace MPELicenseAgent
{
public partial class Service1 : ServiceBase
{
private string homeBaseAddress = "apilicenses.mpe.ca";
private string apiKey = "&v94gt8ZHFTTTeuT";
private string[] requestedPrograms = new string[30];
private Timer pollRateTimer;
private int pollRate;
private bool canPhoneHome = false;
public Service1()
{
InitializeComponent();
}
// Function that starts when the service is run
protected override async void OnStart(string[] args)
{
this.WriteToFile("\n*******************************************************\nMPE License Tracker\nMade by: Donavon McDowell\n");
try
{
await this.getHealth();
if (this.canPhoneHome)
{
this.WriteToFile(DateTime.Now.ToString() + " Connected to home base " + this.homeBaseAddress);
await this.getPollRate();
await this.getRequestedPrograms();
await this.getClientPrograms();
}
}
catch (Exception ex1)
{
Exception ex = ex1;
}
this.pollRateTimer = new Timer();
this.pollRateTimer.Interval = (double)this.pollRate;
this.pollRateTimer.Elapsed += new ElapsedEventHandler(this.OnElapsedTime);
this.pollRateTimer.Start();
}
protected override void OnStop()
{
if (this.pollRateTimer == null)
return;
this.pollRateTimer.Stop();
this.pollRateTimer.Dispose();
}
// Params: STRING data
// Description: Takes a string and writes it to an output / log file
// Location: Where the exe file for this service exists
public void WriteToFile(string data)
{
string str1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
if (!Directory.Exists(str1))
Directory.CreateDirectory(str1);
string str2 = Path.Combine(str1, "ServiceLog.txt");
if (File.Exists(str2) && new FileInfo(str2).Length / 1024L > 100L)
{
File.Delete(str2);
using (StreamWriter text = File.CreateText(str2))
text.WriteLine(data);
}
else
{
using (StreamWriter streamWriter = File.AppendText(str2))
streamWriter.WriteLine(data);
}
}
private async void OnElapsedTime(object source, ElapsedEventArgs e)
{
await this.getHealth();
if (!this.canPhoneHome)
return;
await this.getRequestedPrograms();
await this.getPollRate();
await this.getClientPrograms();
this.pollRateTimer.Interval = (double)this.pollRate;
}
// Checks the health of the api, if there are any issues it will wait and probe again in a little bit
// This is used so that we don't DDOS the reverse proxy
private async Task getHealth()
{
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/healthcheck";
HttpMethod method = HttpMethod.Get;
HttpClient httpClient = new HttpClient()
{
Timeout = TimeSpan.FromSeconds(4.0)
};
try
{
HttpResponseMessage response = await httpClient.GetAsync(baseUrl + endpoint);
if (response.IsSuccessStatusCode)
{
string responses = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(responses))
{
this.canPhoneHome = true;
}
else
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " Disconnected from home base " + this.homeBaseAddress);
}
responses = (string)null;
}
else
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " Disconnected from home base " + this.homeBaseAddress);
}
response = (HttpResponseMessage)null;
baseUrl = (string)null;
endpoint = (string)null;
method = (HttpMethod)null;
httpClient = (HttpClient)null;
}
catch (TaskCanceledException ex)
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " [Error]: Connection timed out while trying to phone home to " + this.homeBaseAddress);
baseUrl = (string)null;
endpoint = (string)null;
method = (HttpMethod)null;
httpClient = (HttpClient)null;
}
catch (Exception ex)
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " [Error]: There was an error in the getHealth() function. " + ex.Message);
baseUrl = (string)null;
endpoint = (string)null;
method = (HttpMethod)null;
httpClient = (HttpClient)null;
}
}
// This talks to the backend and gets all of the programs that we are interested in monitoring
private async Task getRequestedPrograms()
{
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/programs";
HttpMethod method = HttpMethod.Get;
try
{
string responses = await Service1.ApiClient.hitApi(baseUrl, endpoint, method);
string cleanedResponses = responses.Trim('[', ']');
string[] responseArray = cleanedResponses.Split(new string[1]
{
"\",\""
}, StringSplitOptions.None);
responseArray[0] = responseArray[0].Trim('"');
responseArray[responseArray.Length - 1] = responseArray[responseArray.Length - 1].Trim('"');
this.requestedPrograms = responseArray;
responses = (string)null;
cleanedResponses = (string)null;
responseArray = (string[])null;
baseUrl = (string)null;
endpoint = (string)null;
method = (HttpMethod)null;
}
catch (Exception ex)
{
this.WriteToFile(DateTime.Now.ToString() + " [Error]: There was an error in the getRequestedPrograms() function. " + ex?.ToString());
baseUrl = (string)null;
endpoint = (string)null;
method = (HttpMethod)null;
}
}
// This looks at the computer itself and determines any applications that match our requested programs
private async Task getClientPrograms()
{
Process[] processes = Process.GetProcesses();
string[] matchingProcesses = processes
.Select(p => p.ProcessName)
.Where(pn => this.requestedPrograms.Contains(pn, StringComparer.OrdinalIgnoreCase))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
foreach (string processName in matchingProcesses)
{
Process[] processList = processes.Where(p => p.ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase)).ToArray();
foreach (Process process in processList)
{
string userName = GetProcessOwner(process.Id);
await this.sendMatchingPrograms(processName, userName);
}
}
processes = null;
matchingProcesses = null;
}
// Method to get the username of the process owner
private string GetProcessOwner(int processId)
{
try
{
string query = "Select * From Win32_Process Where ProcessID = " + processId;
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
using (ManagementObjectCollection processList = searcher.Get())
{
foreach (ManagementObject obj in processList)
{
object[] argList = new object[2] { string.Empty, string.Empty };
int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnVal == 0)
{
return argList[0]?.ToString(); // Return the username
}
}
}
}
catch
{
// Handle any exceptions
}
return "Unknown"; // Return 'Unknown' if unable to retrieve username
}
// This actually sends the matching applications to the backend
// This actually sends the matching applications to the backend
private async Task sendMatchingPrograms(string applicationName, string userName)
{
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/currentPrograms";
HttpMethod method = HttpMethod.Post;
try
{
var payload = new
{
applicationName = applicationName,
machineName = userName, // Replacing currentMachineName with userName
apiKey = this.apiKey
};
string response = await Service1.ApiClient.hitApi(baseUrl, endpoint, method, (object)payload);
Console.WriteLine("Response from server: " + response);
}
catch (HttpRequestException ex)
{
Console.WriteLine("Network error: " + ex.Message);
}
catch (JsonException ex)
{
Console.WriteLine("JSON parsing error: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
private async Task getPollRate()
{
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/pollrate";
HttpMethod method = HttpMethod.Get;
try
{
string response = await Service1.ApiClient.hitApi(baseUrl, endpoint, method);
JObject jsonObject = JObject.Parse(response);
string pollrateString = (string)jsonObject["pollrate"];
this.pollRate = int.Parse(pollrateString);
response = (string)null;
jsonObject = (JObject)null;
pollrateString = (string)null;
baseUrl = (string)null;
endpoint = (string)null;
method = (HttpMethod)null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
baseUrl = (string)null;
endpoint = (string)null;
method = (HttpMethod)null;
}
}
public static class ApiClient
{
public static async Task<string> hitApi(
string baseUrl,
string endpoint,
HttpMethod method,
object data = null)
{
string str;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
HttpResponseMessage response;
if (method == HttpMethod.Get)
{
response = await client.GetAsync(endpoint);
}
else
{
if (!(method == HttpMethod.Post))
throw new NotSupportedException(string.Format("HTTP method {0} is not supported.", (object)method));
string jsonData = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");
response = await client.PostAsync(endpoint, (HttpContent)content);
jsonData = (string)null;
content = (StringContent)null;
}
response.EnsureSuccessStatusCode();
str = await response.Content.ReadAsStringAsync();
}
return str;
}
}
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
</packages>

64
ProjectInstaller.cs Normal file
View File

@ -0,0 +1,64 @@
// Decompiled with JetBrains decompiler
// Type: LicenseTrackerService.ProjectInstaller
// Assembly: LicenseTrackerService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 190801E4-3027-41A4-B03A-CBAE9DAB23BA
// Assembly location: C:\MPE\MPELicenseTracker\LicenseTrackerService.exe
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
#nullable disable
namespace LicenseTrackerService
{
[RunInstaller(true)]
public class ProjectInstaller : Installer
{
private IContainer components = (IContainer) null;
private ServiceProcessInstaller serviceProcessInstaller1;
private ServiceInstaller serviceInstaller1;
public ProjectInstaller()
{
this.InitializeComponent();
this.serviceInstaller1.StartType = ServiceStartMode.Automatic;
this.AfterInstall += new InstallEventHandler(this.serviceInstaller1_AfterInstall);
}
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
using (ServiceController serviceController = new ServiceController(this.serviceInstaller1.ServiceName))
{
if (serviceController.Status.Equals((object) ServiceControllerStatus.Running) || serviceController.Status.Equals((object) ServiceControllerStatus.StartPending))
serviceController.Stop();
serviceController.WaitForStatus(ServiceControllerStatus.Stopped);
serviceController.Start();
serviceController.WaitForStatus(ServiceControllerStatus.Running);
}
}
protected override void Dispose(bool disposing)
{
if (disposing && this.components != null)
this.components.Dispose();
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new ServiceProcessInstaller();
this.serviceInstaller1 = new ServiceInstaller();
this.serviceProcessInstaller1.Account = ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = (string) null;
this.serviceProcessInstaller1.Username = (string) null;
this.serviceInstaller1.Description = "Used to track license usage";
this.serviceInstaller1.DisplayName = "MPE License Tracker";
this.serviceInstaller1.ServiceName = "MPELicenseTracker";
this.Installers.AddRange(new Installer[2]
{
(Installer) this.serviceProcessInstaller1,
(Installer) this.serviceInstaller1
});
}
}
}

326
Service1.cs Normal file
View File

@ -0,0 +1,326 @@
// Decompiled with JetBrains decompiler
// Type: LicenseTrackerService.Service1
// Assembly: LicenseTrackerService, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 190801E4-3027-41A4-B03A-CBAE9DAB23BA
// Assembly location: C:\MPE\MPELicenseTracker\LicenseTrackerService.exe
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
#nullable disable
namespace LicenseTrackerService
{
public class Service1 : ServiceBase
{
private string homeBaseAddress = "apilicenses.mpe.ca";
private string apiKey = "&v94gt8ZHFTTTeuT";
private string[] requestedPrograms = new string[15];
private Timer pollRateTimer;
private int pollRate;
private bool canPhoneHome = false;
private IContainer components = (IContainer) null;
public Service1() => this.InitializeComponent();
protected override async void OnStart(string[] args)
{
this.WriteToFile("\n*******************************************************\nMPE License Tracker\nMade by: Donavon McDowell\n");
try
{
await this.getHealth();
if (this.canPhoneHome)
{
this.WriteToFile(DateTime.Now.ToString() + " Connected to home base " + this.homeBaseAddress);
await this.getPollRate();
await this.getRequestedPrograms();
await this.getClientPrograms();
}
}
catch (Exception ex1)
{
Exception ex = ex1;
}
this.pollRateTimer = new Timer();
this.pollRateTimer.Interval = (double) this.pollRate;
this.pollRateTimer.Elapsed += new ElapsedEventHandler(this.OnElapsedTime);
this.pollRateTimer.Start();
}
protected override void OnStop()
{
if (this.pollRateTimer == null)
return;
this.pollRateTimer.Stop();
this.pollRateTimer.Dispose();
}
private async void OnElapsedTime(object source, ElapsedEventArgs e)
{
await this.getHealth();
if (!this.canPhoneHome)
return;
await this.getRequestedPrograms();
await this.getPollRate();
await this.getClientPrograms();
this.pollRateTimer.Interval = (double) this.pollRate;
}
private async Task getHealth()
{
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/healthcheck";
HttpMethod method = HttpMethod.Get;
HttpClient httpClient = new HttpClient()
{
Timeout = TimeSpan.FromSeconds(4.0)
};
try
{
HttpResponseMessage response = await httpClient.GetAsync(baseUrl + endpoint);
if (response.IsSuccessStatusCode)
{
string responses = await response.Content.ReadAsStringAsync();
if (!string.IsNullOrEmpty(responses))
{
this.canPhoneHome = true;
}
else
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " Disconnected from home base " + this.homeBaseAddress);
}
responses = (string) null;
}
else
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " Disconnected from home base " + this.homeBaseAddress);
}
response = (HttpResponseMessage) null;
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
httpClient = (HttpClient) null;
}
catch (TaskCanceledException ex)
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " [Error]: Connection timed out while trying to phone home to " + this.homeBaseAddress);
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
httpClient = (HttpClient) null;
}
catch (Exception ex)
{
this.canPhoneHome = false;
this.WriteToFile(DateTime.Now.ToString() + " [Error]: There was an error in the getHealth() function. " + ex.Message);
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
httpClient = (HttpClient) null;
}
}
private async Task getRequestedPrograms()
{
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/programs";
HttpMethod method = HttpMethod.Get;
try
{
string responses = await Service1.ApiClient.hitApi(baseUrl, endpoint, method);
string cleanedResponses = responses.Trim('[', ']');
string[] responseArray = cleanedResponses.Split(new string[1]
{
"\",\""
}, StringSplitOptions.None);
responseArray[0] = responseArray[0].Trim('"');
responseArray[responseArray.Length - 1] = responseArray[responseArray.Length - 1].Trim('"');
this.requestedPrograms = responseArray;
responses = (string) null;
cleanedResponses = (string) null;
responseArray = (string[]) null;
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
catch (Exception ex)
{
this.WriteToFile(DateTime.Now.ToString() + " [Error]: There was an error in the getRequestedPrograms() function. " + ex?.ToString());
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
}
private async Task getClientPrograms()
{
Process[] processes = Process.GetProcesses();
string[] matchingProcesses = ((IEnumerable<Process>) processes).Select<Process, string>((Func<Process, string>) (p => p.ProcessName)).Where<string>((Func<string, bool>) (pn => ((IEnumerable<string>) this.requestedPrograms).Contains<string>(pn, (IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase))).Distinct<string>((IEqualityComparer<string>) StringComparer.OrdinalIgnoreCase).ToArray<string>();
string[] strArray = matchingProcesses;
for (int index = 0; index < strArray.Length; ++index)
{
string processName = strArray[index];
await this.sendMatchingPrograms(processName);
processName = (string) null;
}
strArray = (string[]) null;
processes = (Process[]) null;
matchingProcesses = (string[]) null;
}
private async Task sendMatchingPrograms(string applicationName)
{
string currentMachineName = Environment.MachineName;
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/currentPrograms";
HttpMethod method = HttpMethod.Post;
try
{
var payload = new
{
applicationName = applicationName,
machineName = currentMachineName,
apiKey = this.apiKey
};
string response = await Service1.ApiClient.hitApi(baseUrl, endpoint, method, (object) payload);
Console.WriteLine("Response from server: " + response);
payload = null;
response = (string) null;
currentMachineName = (string) null;
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
catch (HttpRequestException ex)
{
Console.WriteLine("Network error: " + ex.Message);
currentMachineName = (string) null;
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
catch (JsonException ex)
{
Console.WriteLine("JSON parsing error: " + ex.Message);
currentMachineName = (string) null;
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
currentMachineName = (string) null;
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
}
private async Task getPollRate()
{
string baseUrl = "https://" + this.homeBaseAddress;
string endpoint = "/api/pollrate";
HttpMethod method = HttpMethod.Get;
try
{
string response = await Service1.ApiClient.hitApi(baseUrl, endpoint, method);
JObject jsonObject = JObject.Parse(response);
string pollrateString = (string) jsonObject["pollrate"];
this.pollRate = int.Parse(pollrateString);
response = (string) null;
jsonObject = (JObject) null;
pollrateString = (string) null;
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
baseUrl = (string) null;
endpoint = (string) null;
method = (HttpMethod) null;
}
}
public void WriteToFile(string data)
{
string str1 = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
if (!Directory.Exists(str1))
Directory.CreateDirectory(str1);
string str2 = Path.Combine(str1, "ServiceLog.txt");
if (File.Exists(str2) && new FileInfo(str2).Length / 1024L > 100L)
{
File.Delete(str2);
using (StreamWriter text = File.CreateText(str2))
text.WriteLine(data);
}
else
{
using (StreamWriter streamWriter = File.AppendText(str2))
streamWriter.WriteLine(data);
}
}
protected override void Dispose(bool disposing)
{
if (disposing && this.components != null)
this.components.Dispose();
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.components = (IContainer) new System.ComponentModel.Container();
this.ServiceName = nameof (Service1);
}
public static class ApiClient
{
public static async Task<string> hitApi(
string baseUrl,
string endpoint,
HttpMethod method,
object data = null)
{
string str;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
HttpResponseMessage response;
if (method == HttpMethod.Get)
{
response = await client.GetAsync(endpoint);
}
else
{
if (!(method == HttpMethod.Post))
throw new NotSupportedException(string.Format("HTTP method {0} is not supported.", (object) method));
string jsonData = JsonConvert.SerializeObject(data);
StringContent content = new StringContent(jsonData, Encoding.UTF8, "application/json");
response = await client.PostAsync(endpoint, (HttpContent) content);
jsonData = (string) null;
content = (StringContent) null;
}
response.EnsureSuccessStatusCode();
str = await response.Content.ReadAsStringAsync();
}
return str;
}
}
}
}