Imports System.IO Imports System.Net Imports System.Net.Http Imports ModpackUpdater.Model Imports WebDav Public Class UpdateInstaller Public Event InstallProgessUpdated(result As UpdateCheckResult, processedSyncs As Integer) Public Event CheckingProgressUpdated(toCheck As Integer, processed As Integer) Private ReadOnly updateConfig As ModpackConfig Private ReadOnly localPath As String Private webdavClient As WebDavClient = Nothing Private httpClient As New HttpClient Public Sub New(updateConfig As ModpackConfig, localPath As String) Me.updateConfig = updateConfig Me.localPath = localPath End Sub Protected Overrides Sub Finalize() httpClient.Dispose() MyBase.Finalize() End Sub Private Function CreateClient() As WebDavClient If webdavClient Is Nothing Then Dim params As New WebDavClientParams With { .BaseAddress = New Uri(updateConfig.WebdavURL), .Credentials = New NetworkCredential(updateConfig.WebdavUsername, updateConfig.WebdavPassword) } webdavClient = New WebDavClient(params) End If Return webdavClient End Function Private Async Function DownloadUpdateInfos() As Task(Of UpdateInfos) Dim content As String = Await httpClient.GetStringAsync(updateConfig.UpdateUrl) Return UpdateInfos.Parse(content) End Function Public Async Function CheckForUpdates(ignoreRevmoedFiles As Boolean) As Task(Of UpdateCheckResult) Dim infos As UpdateInfos = Await DownloadUpdateInfos() Dim result As New UpdateCheckResult If infos IsNot Nothing AndAlso infos.Updates.Any Then Dim updatesOrderes = infos.Updates.OrderByDescending(Function(n) n.Version) result.LatestVersion = updatesOrderes.First.Version If ModpackInfo.HasModpackInfo(localPath) Then Dim modpackInfo As ModpackInfo = ModpackInfo.Load(localPath) result.CurrentVersion = modpackInfo.Version Dim checkingVersionIndex As Integer = 0 Dim checkingVersion As UpdateInfo = updatesOrderes(checkingVersionIndex) Do While checkingVersion IsNot Nothing AndAlso checkingVersion.Version > result.CurrentVersion Dim actionsToAdd As New List(Of UpdateAction) For Each action In checkingVersion.Actions If Not result.UpdateActions.Any(Function(n) n.DestPath = action.DestPath) Then actionsToAdd.Add(action) End If Next result.UpdateActions.InsertRange(0, actionsToAdd) checkingVersionIndex += 1 checkingVersion = updatesOrderes.ElementAtOrDefault(checkingVersionIndex) Loop Else Await CheckForUpdatesLegacy(result, ignoreRevmoedFiles) End If End If Return result End Function Private Async Function CheckForUpdatesLegacy(result As UpdateCheckResult, ignoreRevmoedFiles As Boolean) As Task Dim client As WebDavClient = CreateClient() Dim resTopFolder As WebDavResource = Nothing Dim checkedFiles = New List(Of String)() Dim responseSuccessfull As Boolean = False result.CurrentVersion = New Version("0.0.0.0") result.IsLegacy = True 'Check for new & updated files Dim response = Await client.Propfind(String.Empty, New PropfindParameters() With {.ApplyTo = ApplyTo.Propfind.ResourceAndAncestors}) If resTopFolder Is Nothing AndAlso response.IsSuccessful AndAlso response.Resources.Any() Then resTopFolder = response.Resources.ElementAtOrDefault(0) responseSuccessfull = True End If If responseSuccessfull AndAlso response.Resources.Count > 1 Then Dim i As Integer = 1 Dim loopTo As Integer = response.Resources.Count - 1 Do While i <= loopTo Dim res = response.Resources.ElementAtOrDefault(i) Dim isFolder As Boolean = res.Uri.EndsWith("/") If Not isFolder Then Dim syncAction? As UpdateSyncAction = Nothing Dim localFile As String Dim remoteFile As String 'Get remote file path remoteFile = res.Uri 'Get local file path localFile = Path.Combine(localPath, Uri.UnescapeDataString(res.Uri.Substring(resTopFolder.Uri.Length)).Replace("/", "\")) 'Check action If File.Exists(localFile) Then If File.GetLastWriteTime(localFile) < res.LastModifiedDate = True Then syncAction = UpdateSyncAction.UpdatedFile End If Else syncAction = UpdateSyncAction.NewFile End If 'Add to list checkedFiles.Add(localFile) If syncAction IsNot Nothing Then result.SyncFiles.Add(New UpdateSyncFile(syncAction, localFile, remoteFile)) End If End If i += 1 RaiseEvent CheckingProgressUpdated(loopTo, i) Loop End If 'Find all old files to remove If responseSuccessfull Then Dim allLocalFiles = Directory.GetFiles(localPath, "*", SearchOption.AllDirectories) For Each lf As String In allLocalFiles Dim isKnown As Boolean = False For Each checkedFile As String In checkedFiles If Not isKnown AndAlso If(checkedFile, "") = If(lf, "") Then isKnown = True End If Next If Not isKnown Then result.SyncFiles.Add(New UpdateSyncFile(If(ignoreRevmoedFiles, UpdateSyncAction.None, UpdateSyncAction.RemovedFile), lf, String.Empty)) End If Next End If End Function Public Async Function InstallUpdates(checkResult As UpdateCheckResult, Optional ignoreActions As UpdateSyncAction = UpdateSyncAction.None) As Task(Of Boolean?) Dim isSuccessfully As Boolean Dim modpackInfo As ModpackInfo If ModpackInfo.HasModpackInfo(localPath) Then modpackInfo = ModpackInfo.Load(localPath) Else modpackInfo = New ModpackInfo End If If checkResult.IsLegacy Then isSuccessfully = Await InstallUpdatesLegacy(checkResult, ignoreActions) Else Dim processed As Integer = 0 For Each action As UpdateAction In checkResult.UpdateActions Dim destFilePath As String = Path.Combine(localPath, action.DestPath) Select Case action.Type Case UpdateActionType.Update Directory.CreateDirectory(Path.GetDirectoryName(destFilePath)) Dim sRemote As Stream = Await httpClient.GetStreamAsync(action.DownloadUrl) Dim fs As New FileStream(destFilePath, FileMode.Create, FileAccess.ReadWrite) Await sRemote.CopyToAsync(fs) sRemote.Close() fs.Close() Case UpdateActionType.Delete If action.IsDirectory Then If Directory.Exists(destFilePath) Then Directory.Delete(destFilePath, True) End If Else If File.Exists(destFilePath) Then File.Delete(destFilePath) End If End If Case UpdateActionType.Copy Dim srcFilePath As String = Path.Combine(localPath, action.SrcPath) If File.Exists(srcFilePath) Then Directory.CreateDirectory(Path.GetDirectoryName(destFilePath)) File.Copy(srcFilePath, destFilePath, True) End If Case UpdateActionType.Move Dim srcFilePath As String = Path.Combine(localPath, action.SrcPath) If File.Exists(srcFilePath) Then Directory.CreateDirectory(Path.GetDirectoryName(destFilePath)) File.Move(srcFilePath, destFilePath, True) End If End Select processed += 1 RaiseEvent InstallProgessUpdated(checkResult, processed) Next isSuccessfully = True End If If isSuccessfully Then modpackInfo.Version = checkResult.LatestVersion modpackInfo.Save(localPath) End If Return isSuccessfully End Function Private Async Function InstallUpdatesLegacy(checkResult As UpdateCheckResult, Optional ignoreActions As UpdateSyncAction = UpdateSyncAction.None) As Task(Of Boolean?) Dim client As WebDavClient = CreateClient() Dim success As Boolean? = False Dim processed As Integer = 0 'Init process update RaiseEvent InstallProgessUpdated(checkResult, processed) For Each syncFile In checkResult.SyncFiles If syncFile.SyncAction <> (ignoreActions And syncFile.SyncAction) Then If syncFile.SyncAction = UpdateSyncAction.UpdatedFile OrElse syncFile.SyncAction = UpdateSyncAction.RemovedFile Then If File.Exists(syncFile.LocalFile) Then File.Delete(syncFile.LocalFile) End If End If If syncFile.SyncAction = UpdateSyncAction.UpdatedFile OrElse syncFile.SyncAction = UpdateSyncAction.NewFile Then Dim response = Await client.GetProcessedFile(syncFile.RemoveFile) Dim dirParent As New DirectoryInfo(Path.GetDirectoryName(syncFile.LocalFile)) If response.IsSuccessful Then If Not dirParent.Exists Then dirParent.Create() End If Dim fs As New FileStream(syncFile.LocalFile, FileMode.Create, FileAccess.Write) response.Stream.CopyTo(fs) fs.Close() success = True End If End If End If processed += 1 RaiseEvent InstallProgessUpdated(checkResult, processed) Next Return success End Function End Class