405 lines
14 KiB
C#
405 lines
14 KiB
C#
using global::System.IO;
|
|
using global::Newtonsoft.Json.Linq;
|
|
using WebDav;
|
|
using System.Net;
|
|
using System;
|
|
using System.Threading.Tasks;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using static Microsoft.VisualBasic.CompilerServices.LikeOperator;
|
|
using Microsoft.VisualBasic;
|
|
|
|
namespace Pilz.Updating.Administration
|
|
{
|
|
public class UpdateServerManager
|
|
{
|
|
private const string PKG_SEARCHTEXT = "pkg*.*.*.*.zip";
|
|
private const string PKG_FILENAME_TEMPLATE = "pkg{0}{1}.zip";
|
|
private const string PKG_FILENAME_ALPHADEFINITION = "a";
|
|
private const string PKG_FILENAME_BETADEFINITION = "b";
|
|
private const string PKG_FILENAME_RCDEFINITION = "rc";
|
|
private const string PKG_FILENAME_RELEASEDEFINITION = "r";
|
|
|
|
private WebDavClient client;
|
|
public UpdateInfo UpdateInfo { get; private set; }
|
|
public UpdateServerConfig Config { get; private set; }
|
|
public bool IsReady { get; private set; }
|
|
|
|
public UpdateServerManager(UpdateServerConfig config)
|
|
{
|
|
GenerateClient(config);
|
|
NewInfo();
|
|
}
|
|
|
|
public bool GenerateClient(UpdateServerConfig config)
|
|
{
|
|
bool success;
|
|
|
|
try
|
|
{
|
|
// Create client params
|
|
var clientparams = new WebDavClientParams()
|
|
{
|
|
BaseAddress = new Uri(config.ServerAdress),
|
|
Credentials = new NetworkCredential(config.Username, config.Password),
|
|
UseProxy = false
|
|
};
|
|
|
|
// Create client
|
|
client = new WebDavClient(clientparams);
|
|
|
|
// Remember config
|
|
Config = config;
|
|
|
|
success = true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
success = false;
|
|
Config = null;
|
|
client = null;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
public async Task<bool> LoadInfoFromServer()
|
|
{
|
|
bool success;
|
|
|
|
try
|
|
{
|
|
var response = await client.GetProcessedFile(Config.UpdateInfoFilename);
|
|
|
|
if (response.IsSuccessful)
|
|
{
|
|
var sr = new StreamReader(response.Stream);
|
|
var raw = await sr.ReadToEndAsync();
|
|
sr.Close();
|
|
UpdateInfo = JObject.Parse(raw).ToObject<UpdateInfo>();
|
|
}
|
|
|
|
success = true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
public async Task<bool> SaveInfoToServer()
|
|
{
|
|
bool success;
|
|
|
|
try
|
|
{
|
|
// Remove configs of non-existing packages
|
|
await ClearUpdateInfo();
|
|
|
|
// Update Packagelinks
|
|
UpdatePackageLinks();
|
|
|
|
// Write
|
|
var raw = UpdateInfo.ToString();
|
|
var ms = new MemoryStream();
|
|
var sw = new StreamWriter(ms);
|
|
await sw.WriteAsync(raw);
|
|
await sw.FlushAsync();
|
|
|
|
// Upload
|
|
ms.Position = 0;
|
|
await client.PutFile(Config.UpdateInfoFilename, ms);
|
|
|
|
ms.Close();
|
|
success = true;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
success = false;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
public void LoadInfoFromFile(string filePath)
|
|
{
|
|
UpdateInfo = JObject.Parse(File.ReadAllText(filePath)).ToObject<UpdateInfo>();
|
|
}
|
|
|
|
public async Task SaveInfoToFile(string filePath)
|
|
{
|
|
// Remove configs of non-existing packages
|
|
await ClearUpdateInfo();
|
|
|
|
// Update Packagelinks
|
|
UpdatePackageLinks();
|
|
|
|
// Write
|
|
File.WriteAllText(filePath, UpdateInfo.ToString());
|
|
}
|
|
|
|
public void NewInfo()
|
|
{
|
|
UpdateInfo = new UpdateInfo();
|
|
}
|
|
|
|
private async Task ClearUpdateInfo()
|
|
{
|
|
var pkgs = await GetUpdatePackagesList();
|
|
var infosToRemove = new List<UpdatePackageInfo>();
|
|
|
|
// Find non-existing packages
|
|
foreach (var info in UpdateInfo.Packages)
|
|
{
|
|
if (!pkgs.Where((n) => n == info.Version).Any())
|
|
{
|
|
infosToRemove.Add(info);
|
|
}
|
|
}
|
|
|
|
// Remove found packages
|
|
foreach (var info in infosToRemove)
|
|
UpdateInfo.Packages.Remove(info);
|
|
}
|
|
|
|
private void UpdatePackageLinks()
|
|
{
|
|
foreach (var info in UpdateInfo.Packages)
|
|
UpdatePackageLink(info);
|
|
}
|
|
|
|
private void UpdatePackageLink(UpdatePackageInfo info)
|
|
{
|
|
info.Packagelink = Config.PublicPackageBaseURL + BuildPackageFilename(info.Version);
|
|
}
|
|
|
|
public async Task<IEnumerable<ApplicationVersion>> GetUpdatePackagesList()
|
|
{
|
|
var pkgs = new List<ApplicationVersion>();
|
|
var response = await client.Propfind(string.Empty);
|
|
|
|
if (response.IsSuccessful)
|
|
{
|
|
foreach (var resource in response.Resources)
|
|
{
|
|
var fileName = Path.GetFileName(resource.Uri);
|
|
if (!string.IsNullOrEmpty(fileName) && fileName.ToLower() != Config.UpdateInfoFilename && LikeString(fileName, "pkg*.*.*.*.zip", CompareMethod.Text))
|
|
{
|
|
var appVersion = new ApplicationVersion();
|
|
bool allowAdd = true;
|
|
|
|
fileName = Path.GetFileNameWithoutExtension(fileName);
|
|
fileName = fileName.Substring(3);
|
|
|
|
// Get alpha/beta/rc value
|
|
{
|
|
int indexAlpha, indexBeta, indexRC, indexRelease;
|
|
indexAlpha = fileName.IndexOf(PKG_FILENAME_ALPHADEFINITION);
|
|
indexBeta = fileName.IndexOf(PKG_FILENAME_BETADEFINITION);
|
|
indexRC = fileName.IndexOf(PKG_FILENAME_RCDEFINITION);
|
|
indexRelease = fileName.IndexOf(PKG_FILENAME_RELEASEDEFINITION);
|
|
|
|
int indexDef;
|
|
string pkgFilenameDef;
|
|
if (indexAlpha > -1)
|
|
{
|
|
indexDef = indexAlpha;
|
|
pkgFilenameDef = PKG_FILENAME_ALPHADEFINITION;
|
|
}
|
|
else if (indexBeta > -1)
|
|
{
|
|
indexDef = indexBeta;
|
|
pkgFilenameDef = PKG_FILENAME_BETADEFINITION;
|
|
}
|
|
else if (indexRC > -1)
|
|
{
|
|
indexDef = indexRC;
|
|
pkgFilenameDef = PKG_FILENAME_RCDEFINITION;
|
|
}
|
|
else if (indexRelease > -1)
|
|
{
|
|
indexDef = indexRelease;
|
|
pkgFilenameDef = PKG_FILENAME_RELEASEDEFINITION;
|
|
}
|
|
else
|
|
{
|
|
indexDef = -1;
|
|
pkgFilenameDef = null;
|
|
}
|
|
|
|
if (indexDef > -1)
|
|
{
|
|
// Get def from filename
|
|
var def = fileName.Substring(indexDef);
|
|
fileName = fileName.Remove(indexDef);
|
|
|
|
// Get channel
|
|
switch (pkgFilenameDef)
|
|
{
|
|
case PKG_FILENAME_ALPHADEFINITION:
|
|
appVersion.Channel = Channels.Alpha;
|
|
break;
|
|
case PKG_FILENAME_BETADEFINITION:
|
|
appVersion.Channel = Channels.Beta;
|
|
break;
|
|
case PKG_FILENAME_RCDEFINITION:
|
|
appVersion.Channel = Channels.PreRelease;
|
|
break;
|
|
case PKG_FILENAME_RELEASEDEFINITION:
|
|
appVersion.Channel = Channels.Stable;
|
|
break;
|
|
}
|
|
|
|
// Get build
|
|
var defBuild = def.Substring(pkgFilenameDef.Length);
|
|
appVersion.Build = Convert.ToInt32(defBuild);
|
|
}
|
|
else
|
|
{
|
|
// Set to default
|
|
appVersion.Build = 1;
|
|
appVersion.Channel = Channels.Stable;
|
|
}
|
|
}
|
|
|
|
// Get version
|
|
if (Version.TryParse(fileName, out Version version))
|
|
appVersion.Version = version;
|
|
else
|
|
allowAdd = false;
|
|
|
|
if (allowAdd)
|
|
pkgs.Add(appVersion);
|
|
}
|
|
}
|
|
}
|
|
|
|
return pkgs;
|
|
}
|
|
|
|
public async Task<bool> DeletePackage(ApplicationVersion version)
|
|
{
|
|
var fileName = BuildPackageFilename(version);
|
|
var response = await client.Delete(fileName);
|
|
return response.IsSuccessful;
|
|
}
|
|
|
|
public async Task<bool> UploadPackage(string filePath, ApplicationVersion version)
|
|
{
|
|
bool success;
|
|
var fileName = BuildPackageFilename(version);
|
|
|
|
// Upload
|
|
var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
|
|
var response = await client.PutFile(fileName, fs);
|
|
fs.Close();
|
|
success = response.IsSuccessful;
|
|
|
|
// Generate public downloadlink
|
|
if (success)
|
|
{
|
|
var pkgInfo = GetOrCreateUpdatePackageInfo(version);
|
|
pkgInfo.Packagelink = Config.PublicPackageBaseURL + fileName;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
private string BuildPackageFilename(ApplicationVersion version)
|
|
{
|
|
// Build channel/build definition of filename
|
|
string def = string.Empty;
|
|
switch (version.Channel)
|
|
{
|
|
case Channels.Alpha:
|
|
def = PKG_FILENAME_ALPHADEFINITION + version.Build;
|
|
break;
|
|
case Channels.Stable:
|
|
if (version.Build != 1)
|
|
def = PKG_FILENAME_RELEASEDEFINITION + version.Build;
|
|
break;
|
|
case Channels.PreRelease:
|
|
def = PKG_FILENAME_RCDEFINITION + version.Build;
|
|
break;
|
|
case Channels.Beta:
|
|
def = PKG_FILENAME_BETADEFINITION + version.Build;
|
|
break;
|
|
}
|
|
|
|
// Build filename
|
|
var fileName = string.Format(PKG_FILENAME_TEMPLATE, version.Version, def);
|
|
|
|
return fileName;
|
|
}
|
|
|
|
private UpdatePackageInfo GetOrCreateUpdatePackageInfo(ApplicationVersion version)
|
|
{
|
|
var info = GetUpdatePackageInfo(version);
|
|
|
|
if (info == null)
|
|
info = CreateUpdatePackageInfo(version);
|
|
|
|
return info;
|
|
}
|
|
|
|
public UpdatePackageInfo GetUpdatePackageInfo(ApplicationVersion version)
|
|
{
|
|
return UpdateInfo.Packages.FirstOrDefault((n) => n.Version == version);
|
|
}
|
|
|
|
private UpdatePackageInfo CreateUpdatePackageInfo(ApplicationVersion version)
|
|
{
|
|
var info = new UpdatePackageInfo()
|
|
{
|
|
Version = version
|
|
};
|
|
UpdateInfo.Packages.Add(info);
|
|
return info;
|
|
}
|
|
|
|
public (string name, string description, UpdateNotesContentType descriptionType) GetPackageDescription(ApplicationVersion version)
|
|
{
|
|
var pkg = GetUpdatePackageInfo(version);
|
|
if (pkg is object)
|
|
return (pkg.Name, pkg.Notes.Content, pkg.Notes.ContentType);
|
|
else
|
|
return default;
|
|
}
|
|
|
|
public void SetPackageDescription(ApplicationVersion version, string name, string description, UpdateNotesContentType descriptionType)
|
|
{
|
|
var pkg = GetOrCreateUpdatePackageInfo(version);
|
|
if (pkg is object)
|
|
{
|
|
pkg.Name = name;
|
|
pkg.Notes.Content = description;
|
|
pkg.Notes.ContentType = descriptionType;
|
|
}
|
|
}
|
|
|
|
public async Task<bool> ChangePackageVersion(ApplicationVersion currentVersion, ApplicationVersion newVersion)
|
|
{
|
|
bool success = false;
|
|
|
|
// Get file names
|
|
var currentFilename = BuildPackageFilename(currentVersion);
|
|
var newFilename = BuildPackageFilename(newVersion);
|
|
|
|
// Move
|
|
var response = await client.Move(currentFilename, newFilename);
|
|
|
|
// Change package info version, if exists
|
|
if (response.IsSuccessful)
|
|
{
|
|
var pkg = GetUpdatePackageInfo(currentVersion);
|
|
if (pkg is object)
|
|
pkg.Version = newVersion;
|
|
success = true;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
}
|
|
} |