using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; using GitLabApiClient; using GitLabApiClient.Models.Trees.Responses; using Saradomin.Model; using Saradomin.View.Windows; namespace Saradomin.Infrastructure.Services { public class PluginDownloadService : IPluginDownloadService { private const string GroupName = "2009scape"; private const string ProjectName = "Tools/client-plugins"; private const string BranchName = "master"; private readonly GitLabClient _gitLabClient; private static IList _cachedQuery; public PluginDownloadService() { _gitLabClient = new GitLabClient("https://gitlab.com"); } private async Task PerformInitialQuery() { _cachedQuery = await _gitLabClient.Trees.GetAsync($"{GroupName}/{ProjectName}", o => { o.Recursive = true; o.Reference = BranchName; } ); } public async Task> FetchPluginMetadataPaths() { if (_cachedQuery == null) await PerformInitialQuery(); return _cachedQuery .Where(x => x.Type == "blob" && x.Path.Contains("properties")) .Select(x => x.Path) .ToList(); } public async Task> FetchFileListForPlugin(string pluginName) { if (_cachedQuery == null) await PerformInitialQuery(); return _cachedQuery .Where(x => x.Path.Contains(pluginName) && x.Type == "blob") .Select(x => x.Path) .ToList(); } public async Task DownloadPluginFiles(string pluginName, string pluginRepositoryPath) { var directoryPath = Path.Combine(pluginRepositoryPath, pluginName); if (!Directory.Exists(directoryPath)) { Directory.CreateDirectory(directoryPath); } else { foreach (var file in Directory.EnumerateFiles(directoryPath)) { File.Delete(file); } } var pluginFiles = await FetchFileListForPlugin(pluginName); foreach (var filePath in pluginFiles) { var file = await _gitLabClient.Files.GetAsync($"{GroupName}/{ProjectName}", filePath, BranchName); var downloadDirectoryPath = Path.Combine(pluginRepositoryPath, Path.GetDirectoryName(filePath)!); var downloadFilePath = Path.Combine(pluginRepositoryPath, filePath); Directory.CreateDirectory(downloadDirectoryPath); await File.WriteAllBytesAsync( downloadFilePath, Convert.FromBase64String(file.Content) ); } } public async Task > GetAllMetadata (string pluginRepositoryPath, bool isUpdateCheck, bool writePersistentUpdateFlag) { try { var metadataPaths = await FetchPluginMetadataPaths(); var infos = new List(); foreach (var path in metadataPaths) { infos.Add(await ProcessMetadataPath(path, pluginRepositoryPath, isUpdateCheck, writePersistentUpdateFlag)); } return isUpdateCheck ? infos.Where(x => x.UpdateAvailable).ToList() : infos; } catch (HttpRequestException e) { return new List(); } } private async Task ProcessMetadataPath(string path, string pluginRepositoryPath, bool isUpdateCheck, bool writePersistentUpdateFlag) { var pluginName = path.Split("/")[0]; var pluginFolder = Path.Combine(pluginRepositoryPath, pluginName); if (!Directory.Exists(pluginFolder)) Directory.CreateDirectory(pluginFolder); var filePath = Path.Combine(pluginFolder, "plugin.properties"); var fileContent = ""; var updateContent = ""; if (!File.Exists(filePath) || isUpdateCheck) { var file = await _gitLabClient.Files.GetAsync($"{GroupName}/{ProjectName}", path, BranchName); if (isUpdateCheck && File.Exists(filePath)) { fileContent = await File.ReadAllTextAsync(filePath); updateContent = file.ContentDecoded; } else if (isUpdateCheck) fileContent = updateContent = file.ContentDecoded; else fileContent = file.ContentDecoded; if (!isUpdateCheck) { await File.WriteAllBytesAsync( filePath, Convert.FromBase64String(file.Content) ); } } else if (File.Exists(filePath)) { fileContent = await File.ReadAllTextAsync(filePath); } var info = ParseInfo(pluginName, fileContent); var updatedInfo = !string.IsNullOrEmpty(updateContent) ? ParseInfo(pluginName, updateContent) : info; if (isUpdateCheck && updatedInfo.Version != info.Version) { if (writePersistentUpdateFlag) await File.WriteAllTextAsync(filePath, fileContent + "\r\nUPDATEAVAILABLE=1"); info.UpdateAvailable = true; } info.Installed = File.Exists(Path.Combine(pluginFolder, "plugin.class")); return info; } public static PluginInfo ParseInfo(string pluginName, string text) { var lines = new Regex("\r\n|\r|\n").Split(text); var parsedData = new Dictionary(); var lastKey = ""; foreach (var lineInfo in lines.Select(t => t.Split("="))) { if (lineInfo.Length > 1) { parsedData.Add (lineInfo[0], lineInfo[1].Trim('\'').Trim()); lastKey = lineInfo[0]; } else { parsedData[lastKey] += lineInfo[0]; } } var info = new PluginInfo(pluginName); foreach (var (key, value) in parsedData) { switch (key.ToLower()) { case "author": info.Author = value; break; case "description": info.Description = value.Replace("\\", System.Environment.NewLine); break; case "version": info.Version = value; break; case "updateavailable": info.UpdateAvailable = true; break; } } return info; } } }