You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
675 lines
18 KiB
675 lines
18 KiB
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) |
|
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) |
|
|
|
using System; |
|
using System.Collections.Generic; |
|
using System.IO; |
|
using System.Linq; |
|
using ICSharpCode.Core; |
|
using ICSharpCode.SharpDevelop; |
|
using ICSharpCode.SharpZipLib.Zip; |
|
using NuGet; |
|
|
|
namespace ICSharpCode.AddInManager2.Model |
|
{ |
|
/// <summary> |
|
/// Helper class for AddIn setup operations. |
|
/// </summary> |
|
public class AddInSetup : IAddInSetup |
|
{ |
|
private IAddInManagerEvents _events = null; |
|
private INuGetPackageManager _nuGet = null; |
|
|
|
private List<ManagedAddIn> _addInsMarkedForInstall; |
|
|
|
public AddInSetup(IAddInManagerEvents events, INuGetPackageManager nuGet) |
|
{ |
|
_events = events; |
|
_nuGet = nuGet; |
|
|
|
_addInsMarkedForInstall = new List<ManagedAddIn>(); |
|
|
|
// Register event handlers |
|
_events.AddInPackageDownloaded += events_AddInPackageDownloaded; |
|
} |
|
|
|
public IEnumerable<ManagedAddIn> AddInsWithMarkedForInstallation |
|
{ |
|
get |
|
{ |
|
return SD.AddInTree.AddIns.Select(a => new ManagedAddIn(a) { IsTemporary = false, IsUpdate = false }) |
|
.GroupJoin( |
|
_addInsMarkedForInstall, |
|
installedAddIn => installedAddIn.AddIn.Manifest.PrimaryIdentity, |
|
markedAddIn => markedAddIn.AddIn.Manifest.PrimaryIdentity, |
|
(installedAddIn, e) => e.ElementAtOrDefault(0) ?? installedAddIn); |
|
} |
|
} |
|
|
|
private void events_AddInPackageDownloaded(object sender, PackageOperationEventArgs e) |
|
{ |
|
try |
|
{ |
|
InstallAddIn(e.Package, e.InstallPath); |
|
} |
|
catch (Exception ex) |
|
{ |
|
_events.OnAddInOperationError(new AddInExceptionEventArgs(ex)); |
|
} |
|
} |
|
|
|
private AddIn LoadAddInFromZip(ZipFile file) |
|
{ |
|
AddIn resultAddIn = null; |
|
ZipEntry addInEntry = null; |
|
foreach (ZipEntry entry in file) |
|
{ |
|
if (entry.Name.EndsWith(".addin")) |
|
{ |
|
if (addInEntry != null) |
|
{ |
|
throw new AddInLoadException("The package may only contain one .addin file."); |
|
} |
|
addInEntry = entry; |
|
} |
|
} |
|
if (addInEntry == null) |
|
{ |
|
throw new AddInLoadException("The package must contain one .addin file."); |
|
} |
|
using (Stream s = file.GetInputStream(addInEntry)) |
|
{ |
|
using (StreamReader r = new StreamReader(s)) |
|
{ |
|
resultAddIn = AddIn.Load(SD.AddInTree, r); |
|
} |
|
} |
|
|
|
return resultAddIn; |
|
} |
|
|
|
public AddIn InstallAddIn(string archiveFileName) |
|
{ |
|
if (archiveFileName != null) |
|
{ |
|
// Try to load the *.sdaddin file as ZIP archive |
|
AddIn addIn = null; |
|
ZipFile zipFile = null; |
|
try |
|
{ |
|
zipFile = new ZipFile(archiveFileName); |
|
addIn = LoadAddInFromZip(zipFile); |
|
} |
|
catch (Exception ex) |
|
{ |
|
// Report the exception |
|
_events.OnAddInOperationError(new AddInExceptionEventArgs(ex)); |
|
return null; |
|
} |
|
finally |
|
{ |
|
if (zipFile != null) |
|
{ |
|
zipFile.Close(); |
|
} |
|
} |
|
|
|
if (addIn != null) |
|
{ |
|
if ((addIn.Manifest == null) || (addIn.Manifest.PrimaryIdentity == null)) |
|
{ |
|
_events.OnAddInOperationError( |
|
new AddInExceptionEventArgs( |
|
new AddInLoadException(SD.ResourceService.GetString("AddInManager.AddInMustHaveIdentity")))); |
|
return null; |
|
} |
|
|
|
// Try to find this AddIn in current registry |
|
string identity = addIn.Manifest.PrimaryIdentity; |
|
AddIn foundAddIn = null; |
|
foreach (AddIn treeAddIn in SD.AddInTree.AddIns) |
|
{ |
|
if (treeAddIn.Manifest.Identities.ContainsKey(identity)) |
|
{ |
|
foundAddIn = treeAddIn; |
|
break; |
|
} |
|
} |
|
|
|
// Prevent this AddIn from being uninstalled, if marked for deinstallation |
|
foreach (string installedIdentity in addIn.Manifest.Identities.Keys) |
|
{ |
|
ICSharpCode.Core.AddInManager.AbortRemoveUserAddInOnNextStart(installedIdentity); |
|
} |
|
|
|
// Create target directory for AddIn in user profile & copy package contents there |
|
CopyAddInFromZip(addIn, archiveFileName); |
|
|
|
// Install the AddIn using manifest |
|
if (foundAddIn != null) |
|
{ |
|
addIn.Action = AddInAction.Update; |
|
} |
|
else |
|
{ |
|
addIn.Action = AddInAction.Install; |
|
((AddInTreeImpl)SD.AddInTree).InsertAddIn(addIn); |
|
} |
|
|
|
// Mark this AddIn |
|
ManagedAddIn markedAddIn = new ManagedAddIn(addIn) |
|
{ |
|
IsTemporary = true, |
|
IsUpdate = (foundAddIn != null), |
|
OldVersion = (foundAddIn != null) ? foundAddIn.Version : null |
|
}; |
|
_addInsMarkedForInstall.Add(markedAddIn); |
|
|
|
// Successful installation |
|
AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); |
|
_events.OnAddInInstalled(eventArgs); |
|
_events.OnAddInStateChanged(eventArgs); |
|
|
|
return addIn; |
|
} |
|
else |
|
{ |
|
// This is not a valid SharpDevelop AddIn package! |
|
// TODO Show a message to user! |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
public AddIn InstallAddIn(IPackage package, string packageDirectory) |
|
{ |
|
// Lookup for .addin file in package output |
|
var addInManifestFile = Directory.EnumerateFiles(packageDirectory, "*.addin", SearchOption.TopDirectoryOnly).FirstOrDefault(); |
|
if (addInManifestFile != null) |
|
{ |
|
AddIn addIn = AddIn.Load(SD.AddInTree, addInManifestFile); |
|
if (addIn.Manifest.PrimaryIdentity == null) |
|
{ |
|
_events.OnAddInOperationError( |
|
new AddInExceptionEventArgs( |
|
new AddInLoadException(SD.ResourceService.GetString("AddInManager.AddInMustHaveIdentity")))); |
|
return null; |
|
} |
|
|
|
// Try to find this AddIn in current registry |
|
string identity = addIn.Manifest.PrimaryIdentity; |
|
AddIn foundAddIn = null; |
|
foreach (AddIn treeAddIn in SD.AddInTree.AddIns) |
|
{ |
|
if (treeAddIn.Manifest.Identities.ContainsKey(identity)) |
|
{ |
|
foundAddIn = treeAddIn; |
|
break; |
|
} |
|
} |
|
|
|
// Prevent this AddIn from being uninstalled, if marked for deinstallation |
|
foreach (string installedIdentity in addIn.Manifest.Identities.Keys) |
|
{ |
|
ICSharpCode.Core.AddInManager.AbortRemoveUserAddInOnNextStart(installedIdentity); |
|
} |
|
|
|
// Create target directory for AddIn in user profile & copy package contents there |
|
CopyAddInFromPackage(addIn, packageDirectory); |
|
|
|
// Install the AddIn using manifest |
|
if (foundAddIn != null) |
|
{ |
|
addIn.Action = AddInAction.Update; |
|
} |
|
else |
|
{ |
|
addIn.Action = AddInAction.Install; |
|
((AddInTreeImpl)SD.AddInTree).InsertAddIn(addIn); |
|
} |
|
|
|
// Mark this AddIn |
|
ManagedAddIn markedAddIn = new ManagedAddIn(addIn) |
|
{ |
|
IsTemporary = true, |
|
IsUpdate = (foundAddIn != null), |
|
OldVersion = (foundAddIn != null) ? foundAddIn.Version : null |
|
}; |
|
_addInsMarkedForInstall.Add(markedAddIn); |
|
|
|
// Successful installation |
|
AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); |
|
_events.OnAddInInstalled(eventArgs); |
|
_events.OnAddInStateChanged(eventArgs); |
|
|
|
return addIn; |
|
} |
|
else |
|
{ |
|
// This is not a valid SharpDevelop AddIn package! |
|
// TODO Throw something. |
|
} |
|
|
|
return null; |
|
} |
|
|
|
private bool CopyAddInFromZip(AddIn addIn, string zipFile) |
|
{ |
|
if ((addIn != null) && (addIn.Manifest != null)) |
|
{ |
|
try |
|
{ |
|
string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, |
|
addIn.Manifest.PrimaryIdentity); |
|
if (Directory.Exists(targetDir)) |
|
{ |
|
Directory.Delete(targetDir, true); |
|
} |
|
Directory.CreateDirectory(targetDir); |
|
FastZip fastZip = new FastZip(); |
|
fastZip.CreateEmptyDirectories = true; |
|
fastZip.ExtractZip(zipFile, targetDir, null); |
|
|
|
return true; |
|
} |
|
catch (Exception) |
|
{ |
|
return false; |
|
} |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
private bool CopyAddInFromPackage(AddIn addIn, string packageDirectory) |
|
{ |
|
try |
|
{ |
|
string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, |
|
addIn.Manifest.PrimaryIdentity); |
|
if (Directory.Exists(targetDir)) |
|
{ |
|
Directory.Delete(targetDir, true); |
|
} |
|
Directory.CreateDirectory(targetDir); |
|
var packageContentsFiles = Directory.EnumerateFiles(packageDirectory, "*.*", SearchOption.TopDirectoryOnly); |
|
if (packageContentsFiles != null) |
|
{ |
|
foreach (var file in packageContentsFiles) |
|
{ |
|
// Don't copy the .nupkg file |
|
FileInfo fileInfo = new FileInfo(file); |
|
if (fileInfo.Extension != ".nupkg") |
|
{ |
|
File.Copy(file, Path.Combine(targetDir, fileInfo.Name)); |
|
} |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
catch (Exception) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
public void CancelUpdate(AddIn addIn) |
|
{ |
|
if (addIn != null) |
|
{ |
|
CancelPendingUpdate(addIn); |
|
|
|
// If there is also a NuGet package installed for this, delete it as well |
|
IPackage addInPackage = GetNuGetPackageForAddIn(addIn, true); |
|
if (addInPackage != null) |
|
{ |
|
_nuGet.Packages.UninstallPackage(addInPackage, true, false); |
|
} |
|
|
|
AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); |
|
eventArgs.PreviousVersionRemains = true; |
|
_events.OnAddInUninstalled(eventArgs); |
|
_events.OnAddInStateChanged(eventArgs); |
|
} |
|
} |
|
|
|
private void CancelPendingUpdate(AddIn addIn) |
|
{ |
|
if ((addIn != null) && (addIn.Manifest != null)) |
|
{ |
|
foreach (string identity in addIn.Manifest.Identities.Keys) |
|
{ |
|
// Delete from installation temp (if installation or update is pending) |
|
string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.AddInInstallTemp, identity); |
|
if (Directory.Exists(targetDir)) |
|
{ |
|
Directory.Delete(targetDir, true); |
|
} |
|
} |
|
|
|
_addInsMarkedForInstall.RemoveAll(markedAddIn => markedAddIn.AddIn == addIn); |
|
} |
|
} |
|
|
|
public void CancelInstallation(AddIn addIn) |
|
{ |
|
if (addIn != null) |
|
{ |
|
_addInsMarkedForInstall.RemoveAll(markedAddIn => markedAddIn.AddIn == addIn); |
|
UninstallAddIn(addIn); |
|
|
|
// If there is also a NuGet package installed for this, delete it as well |
|
IPackage addInPackage = GetNuGetPackageForAddIn(addIn, true); |
|
if (addInPackage != null) |
|
{ |
|
_nuGet.Packages.UninstallPackage(addInPackage, true, false); |
|
} |
|
} |
|
} |
|
|
|
public void CancelUninstallation(AddIn addIn) |
|
{ |
|
if ((addIn != null) && (addIn.Manifest != null)) |
|
{ |
|
// Abort uninstallation of this AddIn |
|
foreach (string identity in addIn.Manifest.Identities.Keys) |
|
{ |
|
ICSharpCode.Core.AddInManager.AbortRemoveUserAddInOnNextStart(identity); |
|
} |
|
ICSharpCode.Core.AddInManager.Enable(new AddIn[] { addIn }); |
|
_events.OnAddInStateChanged(new AddInInstallationEventArgs(addIn)); |
|
} |
|
} |
|
|
|
public void UninstallAddIn(AddIn addIn) |
|
{ |
|
if ((addIn != null) && (addIn.Manifest != null)) |
|
{ |
|
List<AddIn> addInList = new List<AddIn>(); |
|
addInList.Add(addIn); |
|
ICSharpCode.Core.AddInManager.RemoveExternalAddIns(addInList); |
|
|
|
CancelPendingUpdate(addIn); |
|
foreach (string identity in addIn.Manifest.Identities.Keys) |
|
{ |
|
// Remove the user AddIn |
|
string targetDir = Path.Combine(ICSharpCode.Core.AddInManager.UserAddInPath, identity); |
|
if (Directory.Exists(targetDir)) |
|
{ |
|
if (!addIn.Enabled) |
|
{ |
|
try |
|
{ |
|
Directory.Delete(targetDir, true); |
|
continue; |
|
} |
|
catch |
|
{ |
|
// TODO Throw something? |
|
} |
|
} |
|
|
|
ICSharpCode.Core.AddInManager.RemoveUserAddInOnNextStart(identity); |
|
} |
|
} |
|
|
|
// Successfully uninstalled |
|
AddInInstallationEventArgs eventArgs = new AddInInstallationEventArgs(addIn); |
|
_events.OnAddInUninstalled(eventArgs); |
|
_events.OnAddInStateChanged(eventArgs); |
|
} |
|
} |
|
|
|
public void SwitchAddInActivation(AddIn addIn) |
|
{ |
|
if (addIn != null) |
|
{ |
|
// Decide whether to enable or to disable the AddIn |
|
bool disable = addIn.Enabled; |
|
if (addIn.Action == AddInAction.Disable) |
|
{ |
|
disable = false; |
|
} |
|
else if (addIn.Action == AddInAction.Enable) |
|
{ |
|
disable = true; |
|
} |
|
|
|
if (disable) |
|
{ |
|
ICSharpCode.Core.AddInManager.Disable(new AddIn[] { addIn }); |
|
} |
|
else |
|
{ |
|
ICSharpCode.Core.AddInManager.Enable(new AddIn[] { addIn }); |
|
} |
|
|
|
_events.OnAddInStateChanged(new AddInInstallationEventArgs(addIn)); |
|
} |
|
} |
|
|
|
public AddIn GetInstalledAddInByIdentity(string identity) |
|
{ |
|
if (!String.IsNullOrEmpty(identity)) |
|
{ |
|
return SD.AddInTree.AddIns |
|
.Where(a => (a.Manifest != null) && a.Manifest.Identities.ContainsKey(identity)) |
|
.FirstOrDefault(); |
|
} |
|
else |
|
{ |
|
return null; |
|
} |
|
} |
|
|
|
public bool IsAddInInstalled(AddIn addIn) |
|
{ |
|
if (addIn == null) |
|
{ |
|
// Without a valid AddIn instance we can't do anything... |
|
return false; |
|
} |
|
|
|
string identity = null; |
|
if (addIn.Manifest != null) |
|
{ |
|
identity = addIn.Manifest.PrimaryIdentity; |
|
} |
|
return (SD.AddInTree.AddIns |
|
.Where(a => (addIn == a) || ((identity != null) && (a.Manifest != null) && a.Manifest.Identities.ContainsKey(identity))) |
|
.FirstOrDefault() != null); |
|
} |
|
|
|
public bool IsAddInPreinstalled(AddIn addIn) |
|
{ |
|
if (addIn != null) |
|
{ |
|
return |
|
String.Equals(addIn.Properties["addInManagerHidden"], "preinstalled", StringComparison.OrdinalIgnoreCase) |
|
&& FileUtility.IsBaseDirectory(FileUtility.ApplicationRootPath, addIn.FileName); |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
public IPackage GetNuGetPackageForAddIn(AddIn addIn, bool getLatest) |
|
{ |
|
if (addIn == null) |
|
{ |
|
throw new ArgumentNullException("addIn"); |
|
} |
|
|
|
IPackage package = null; |
|
string nuGetPackageID = null; |
|
if (addIn.Properties.Contains("nuGetPackageID")) |
|
{ |
|
nuGetPackageID = addIn.Properties["nuGetPackageID"]; |
|
} |
|
string primaryIdentity = null; |
|
if (addIn.Manifest != null) |
|
{ |
|
primaryIdentity = addIn.Manifest.PrimaryIdentity; |
|
} |
|
|
|
if (!String.IsNullOrEmpty(nuGetPackageID)) |
|
{ |
|
// Find installed package with mapped NuGet package ID |
|
var matchingPackages = _nuGet.Packages.LocalRepository.GetPackages() |
|
.Where(p => (p.Id == primaryIdentity) || (p.Id == nuGetPackageID)) |
|
.OrderBy(p => p.Version); |
|
if (getLatest) |
|
{ |
|
// Return latest package version |
|
package = matchingPackages.LastOrDefault(); |
|
} |
|
else |
|
{ |
|
// Return oldest installed package version |
|
package = matchingPackages.FirstOrDefault(); |
|
} |
|
} |
|
|
|
return package; |
|
} |
|
|
|
public AddIn GetAddInForNuGetPackage(IPackage package) |
|
{ |
|
return GetAddInForNuGetPackage(package, false); |
|
} |
|
|
|
public AddIn GetAddInForNuGetPackage(IPackage package, bool withAddInsMarkedForInstallation) |
|
{ |
|
if (withAddInsMarkedForInstallation) |
|
{ |
|
return GetInstalledOrMarkedAddInForNuGetPackage(package); |
|
} |
|
else |
|
{ |
|
return GetInstalledAddInForNuGetPackage(package); |
|
} |
|
} |
|
|
|
private AddIn GetInstalledAddInForNuGetPackage(IPackage package) |
|
{ |
|
if (package == null) |
|
{ |
|
throw new ArgumentNullException("package"); |
|
} |
|
|
|
AddIn foundAddIn = SD.AddInTree.AddIns.Where( |
|
a => ((a.Manifest != null) && (a.Manifest.PrimaryIdentity == package.Id)) |
|
|| (a.Properties.Contains("nuGetPackageID") && (a.Properties["nuGetPackageID"] == package.Id))) |
|
.FirstOrDefault(); |
|
|
|
return foundAddIn; |
|
} |
|
|
|
private AddIn GetInstalledOrMarkedAddInForNuGetPackage(IPackage package) |
|
{ |
|
if (package == null) |
|
{ |
|
throw new ArgumentNullException("package"); |
|
} |
|
|
|
ManagedAddIn foundAddIn = AddInsWithMarkedForInstallation.Where( |
|
a => ((a.AddIn.Manifest != null) && (a.AddIn.Manifest.PrimaryIdentity == package.Id)) |
|
|| (a.AddIn.Properties.Contains("nuGetPackageID") && (a.AddIn.Properties["nuGetPackageID"] == package.Id))) |
|
.FirstOrDefault(); |
|
|
|
return (foundAddIn != null) ? foundAddIn.AddIn : null; |
|
} |
|
|
|
public IEnumerable<ManagedAddIn> GetDependentAddIns(AddIn addIn) |
|
{ |
|
if ((addIn != null) && (addIn.Manifest != null) && (addIn.Manifest.PrimaryIdentity != null)) |
|
{ |
|
// Get all AddIns which are dependent from given AddIn |
|
var dependentAddIns = AddInsWithMarkedForInstallation.Where( |
|
a => (a.AddIn.Manifest != null) && a.AddIn.Manifest.Dependencies.Any( |
|
reference => reference.Name == addIn.Manifest.PrimaryIdentity)); |
|
return dependentAddIns; |
|
} |
|
|
|
return null; |
|
} |
|
|
|
public void RemoveUnreferencedNuGetPackages() |
|
{ |
|
// Get list of installed NuGet packages |
|
var localRepositoryPackages = _nuGet.Packages.LocalRepository.GetPackages(); |
|
if (localRepositoryPackages != null) |
|
{ |
|
List<IPackage> installedNuGetPackages = new List<IPackage>(localRepositoryPackages); |
|
foreach (var installedPackage in installedNuGetPackages) |
|
{ |
|
bool removeThisPackage = false; |
|
AddIn addIn = GetAddInForNuGetPackage(installedPackage); |
|
if (addIn == null) |
|
{ |
|
// There is no AddIn for this package -> remove it |
|
removeThisPackage = true; |
|
} |
|
else |
|
{ |
|
// Try to get the most recent (= with highest version) package for this AddIn |
|
IPackage latestPackage = installedNuGetPackages |
|
.Where(p => p.Id == installedPackage.Id) |
|
.OrderBy(p => p.Version) |
|
.LastOrDefault(); |
|
if (latestPackage.Version != installedPackage.Version) |
|
{ |
|
// This is not the most recent installed package for this AddIn -> remove it |
|
removeThisPackage = true; |
|
} |
|
} |
|
|
|
if (removeThisPackage) |
|
{ |
|
// We decided to remove this package |
|
LoggingService.InfoFormatted("Removing unreferenced NuGet package {0} {1}.", |
|
installedPackage.Id, installedPackage.Version.ToString()); |
|
_nuGet.Packages.UninstallPackage(installedPackage, true, false); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// private void LoadMappings() |
|
// { |
|
// var savedMappings = PropertyService.Get<string[]>("AddInManager2.AddInPackageMappings", null); |
|
// if (savedMappings != null) |
|
// { |
|
// foreach (var mapping in savedMappings) |
|
// { |
|
// string[] mappingParts = mapping.Split(new char[] { '|' }, 2); |
|
// if ((mappingParts != null) && (mappingParts.Length == 2)) |
|
// { |
|
// _addInToNuGetMapping[mappingParts[0]] = mappingParts[1]; |
|
// } |
|
// } |
|
// } |
|
// else |
|
// { |
|
// // Save empty mapping |
|
// SaveMappings(); |
|
// } |
|
// } |
|
// |
|
// private void SaveMappings() |
|
// { |
|
// List<string> mappingList = new List<string>(); |
|
// foreach (var mapping in _addInToNuGetMapping) |
|
// { |
|
// mappingList.Add(mapping.Key + "|" + mapping.Value); |
|
// } |
|
// |
|
// PropertyService.Set<string[]>("AddInManager2.AddInPackageMappings", mappingList.ToArray()); |
|
// } |
|
} |
|
}
|
|
|