mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-10-27 11:01:16 +00:00
Replaces the single CriminalIP API integration with a new IP query system supporting both CriminalIP and AbuseIPDB providers. Updates configuration keys, resource files, and the editor UI to handle multiple API endpoints and keys. Refactors backend logic to aggregate and return results from both providers in a unified XML format, and updates the frontend to parse and display these results. Adds improved error handling and logging for IP query and WHOIS operations.
202 lines
7.4 KiB
C#
202 lines
7.4 KiB
C#
// Completion.cs
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2025 Catswords OSS and WelsonJS Contributors
|
|
// https://github.com/gnh1201/welsonjs
|
|
//
|
|
using Microsoft.Win32;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Xml.Linq;
|
|
using System.Net.Http;
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace WelsonJS.Launcher.ResourceTools
|
|
{
|
|
public class Completion : IResourceTool
|
|
{
|
|
private readonly ResourceServer Server;
|
|
private readonly HttpClient _httpClient;
|
|
private readonly ICompatibleLogger _logger;
|
|
private const string Prefix = "completion/";
|
|
private readonly ConcurrentBag<string> DiscoveredExecutables = new ConcurrentBag<string>();
|
|
|
|
public Completion(ResourceServer server, HttpClient httpClient, ICompatibleLogger logger)
|
|
{
|
|
Server = server;
|
|
|
|
_httpClient = httpClient;
|
|
_logger = logger;
|
|
|
|
Task.Run(async () => await SafeDiscoverAsync(DiscoverFromInstalledSoftware));
|
|
Task.Run(async () => await SafeDiscoverAsync(DiscoverFromPathVariable));
|
|
Task.Run(async () => await SafeDiscoverAsync(DiscoverFromProgramDirectories));
|
|
}
|
|
|
|
public bool CanHandle(string path)
|
|
{
|
|
return path.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public async Task HandleAsync(HttpListenerContext context, string path)
|
|
{
|
|
string word = path.Substring(Prefix.Length);
|
|
|
|
try
|
|
{
|
|
List<CompletionItem> completionItems = DiscoveredExecutables
|
|
.Where(exec => exec.IndexOf(word, 0, StringComparison.OrdinalIgnoreCase) > -1)
|
|
.Take(100) // Limit the number of results
|
|
.Select(exec => new CompletionItem
|
|
{
|
|
Label = Path.GetFileName(exec),
|
|
Kind = "Text",
|
|
Documentation = $"An executable file: {exec}",
|
|
InsertText = exec
|
|
})
|
|
.ToList();
|
|
|
|
XElement response = new XElement("suggestions",
|
|
completionItems.Select(item => new XElement("item",
|
|
new XElement("label", item.Label),
|
|
new XElement("kind", item.Kind),
|
|
new XElement("documentation", item.Documentation),
|
|
new XElement("insertText", item.InsertText)
|
|
))
|
|
);
|
|
|
|
await Server.ServeResource(context, response.ToString(), "application/xml");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await Server.ServeResource(context, $"<error>Failed to try completion. {ex.Message}</error>", "application/xml", 500);
|
|
}
|
|
}
|
|
|
|
private void DiscoverFromInstalledSoftware()
|
|
{
|
|
const string registryPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
|
|
|
|
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(registryPath))
|
|
{
|
|
if (key == null) return;
|
|
|
|
foreach (string subKeyName in key.GetSubKeyNames())
|
|
{
|
|
RegistryKey subKey = key.OpenSubKey(subKeyName);
|
|
if (subKey == null) continue;
|
|
|
|
using (subKey)
|
|
{
|
|
string installLocation = subKey.GetValue("InstallLocation") as string;
|
|
if (!string.IsNullOrEmpty(installLocation))
|
|
{
|
|
SearchAllExecutables(installLocation);
|
|
}
|
|
|
|
string uninstallString = subKey.GetValue("UninstallString") as string;
|
|
if (!string.IsNullOrEmpty(uninstallString))
|
|
{
|
|
var match = Regex.Match(uninstallString, @"(?<=""|^)([a-zA-Z]:\\[^""]+\.exe)", RegexOptions.IgnoreCase);
|
|
if (match.Success && File.Exists(match.Value))
|
|
{
|
|
AddDiscoveredExecutables(new List<string> { match.Value });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DiscoverFromPathVariable() {
|
|
var paths = (Environment.GetEnvironmentVariable("PATH") ?? string.Empty)
|
|
.Split(';')
|
|
.Select(p => p.Trim())
|
|
.Where(p => !string.IsNullOrEmpty(p));
|
|
|
|
paths.ToList().ForEach(x => SearchAllExecutables(x, SearchOption.TopDirectoryOnly));
|
|
}
|
|
|
|
private void DiscoverFromProgramDirectories()
|
|
{
|
|
string windir = Environment.GetEnvironmentVariable("WINDIR");
|
|
string programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
|
|
string userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
|
|
|
var paths = new[]
|
|
{
|
|
// Default directory
|
|
Path.Combine(Program.GetAppDataPath(), "bin"),
|
|
|
|
// Standard program installation directories
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
|
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
|
|
|
|
// .NET Framework directories
|
|
Path.Combine(windir, "Microsoft.NET", "Framework"),
|
|
Path.Combine(windir, "Microsoft.NET", "Framework64"),
|
|
|
|
// Chocolatey package directory
|
|
Path.Combine(programData, "chocolatey", "lib"),
|
|
|
|
// Scoop apps directory
|
|
Path.Combine(userProfile, "scoop", "apps")
|
|
};
|
|
|
|
paths.ToList().ForEach(x => SearchAllExecutables(x));
|
|
}
|
|
|
|
private void SearchAllExecutables(string path, SearchOption searchOption = SearchOption.AllDirectories, int maxFiles = 1000)
|
|
{
|
|
if (!Directory.Exists(path))
|
|
{
|
|
_logger.Info("Directory does not exist: {0}", path);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
var executableFiles = Directory.GetFiles(path, "*.exe", searchOption)
|
|
.Take(maxFiles)
|
|
.OrderByDescending(f => new FileInfo(f).Length)
|
|
.ToList();
|
|
|
|
AddDiscoveredExecutables(executableFiles);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Info("Error enumerating executables in '{0}': {1}", path, ex.Message);
|
|
}
|
|
}
|
|
|
|
private void AddDiscoveredExecutables(List<string> executableFiles)
|
|
{
|
|
executableFiles.ForEach(x => DiscoveredExecutables.Add(x));
|
|
}
|
|
|
|
private async Task SafeDiscoverAsync(Action discoveryMethod)
|
|
{
|
|
try
|
|
{
|
|
await Task.Run(discoveryMethod);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error($"Discovery failed: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
public class CompletionItem
|
|
{
|
|
public string Label { get; set; }
|
|
public string Kind { get; set; }
|
|
public string Documentation { get; set; }
|
|
public string InsertText { get; set; }
|
|
}
|
|
}
|