licensetrackeragent/MPELicenseAgent/Service1.cs

339 lines
13 KiB
C#
Raw Permalink Normal View History

2024-08-30 14:27:20 -06:00
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;
}
}
}
}