7
// Copyright (C) 2007 Novell, Inc (http://www.novell.com)
9
// Permission is hereby granted, free of charge, to any person obtaining
10
// a copy of this software and associated documentation files (the
11
// "Software"), to deal in the Software without restriction, including
12
// without limitation the rights to use, copy, modify, merge, publish,
13
// distribute, sublicense, and/or sell copies of the Software, and to
14
// permit persons to whom the Software is furnished to do so, subject to
15
// the following conditions:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
using System.Collections;
33
using System.Xml.Serialization;
34
using System.Reflection;
35
using System.Diagnostics;
36
using System.Collections.Specialized;
39
using ICSharpCode.SharpZipLib.Zip;
41
using Mono.Addins.Description;
42
using System.Collections.Generic;
45
namespace Mono.Addins.Setup
47
internal class AddinPackage: Package
53
bool disablingOnUninstall;
54
bool uninstallingLoaded;
59
public AddinHeader Addin {
63
public override string Name {
64
get { return info.Name + " v" + info.Version; }
67
public static AddinPackage PackageFromRepository (AddinRepositoryEntry repAddin)
69
AddinPackage pack = new AddinPackage ();
70
pack.info = (AddinInfo) repAddin.Addin;
71
pack.url = new Uri (new Uri (repAddin.RepositoryUrl), repAddin.Url).ToString ();
75
public static AddinPackage PackageFromFile (string file)
77
AddinPackage pack = new AddinPackage ();
78
pack.info = ReadAddinInfo (file);
83
public static AddinPackage FromInstalledAddin (Addin sinfo)
85
AddinPackage pack = new AddinPackage ();
86
pack.info = AddinInfo.ReadFromDescription (sinfo.Description);
90
static AddinInfo ReadAddinInfo (string file)
92
ZipFile zfile = new ZipFile (file);
93
foreach (ZipEntry ze in zfile) {
94
if (ze.Name == "addin.info") {
95
using (Stream s = zfile.GetInputStream (ze)) {
96
return AddinInfo.ReadFromAddinFile (new StreamReader (s));
100
throw new InstallException ("Addin configuration file not found in package.");
103
internal override bool IsUpgradeOf (Package p)
105
AddinPackage ap = p as AddinPackage;
106
if (ap == null) return false;
107
return info.SupportsVersion (ap.info.Version);
110
public override bool Equals (object ob)
112
AddinPackage ap = ob as AddinPackage;
113
if (ap == null) return false;
114
return ap.info.Id == info.Id && ap.info.Version == info.Version;
117
public override int GetHashCode ()
119
return (info.Id + info.Version).GetHashCode ();
122
internal override void PrepareInstall (IProgressMonitor monitor, AddinStore service)
124
if (service.Registry.IsRegisteredForUninstall (info.Id))
125
throw new InstallException ("The addin " + info.Name + " v" + info.Version + " is scheduled for uninstallation. Please restart the application before trying to install it again.");
126
if (service.Registry.GetAddin (Mono.Addins.Addin.GetFullId (info.Namespace, info.Id, info.Version), true) != null)
127
throw new InstallException ("The addin " + info.Name + " v" + info.Version + " is already installed.");
130
packFile = service.DownloadFile (monitor, url);
132
tempFolder = CreateTempFolder ();
135
using (FileStream fs = new FileStream (packFile, FileMode.Open, FileAccess.Read)) {
136
ZipFile zip = new ZipFile (fs);
137
foreach (ZipEntry entry in zip) {
138
string path = Path.Combine (tempFolder, entry.Name);
139
string dir = Path.GetDirectoryName (path);
140
if (!Directory.Exists (dir))
141
Directory.CreateDirectory (dir);
143
byte[] buffer = new byte [8192];
145
Stream inStream = zip.GetInputStream (entry);
146
Stream outStream = null;
148
outStream = File.Create (path);
149
while ((n = inStream.Read (buffer, 0, buffer.Length)) > 0)
150
outStream.Write (buffer, 0, n);
153
if (outStream != null)
159
foreach (string s in Directory.GetFiles (tempFolder)) {
160
if (Path.GetFileName (s) == "addin.info") {
166
if (configFile == null)
167
throw new InstallException ("Add-in information file not found in package.");
170
internal override void CommitInstall (IProgressMonitor monitor, AddinStore service)
172
service.RegisterAddin (monitor, info, tempFolder);
176
internal override void RollbackInstall (IProgressMonitor monitor, AddinStore service)
179
iaddin = service.Registry.GetAddin (info.Id);
181
CommitUninstall (monitor, service);
185
internal override void EndInstall (IProgressMonitor monitor, AddinStore service)
187
if (url != null && packFile != null)
188
File.Delete (packFile);
189
if (tempFolder != null)
190
Directory.Delete (tempFolder, true);
193
internal override void Resolve (IProgressMonitor monitor, AddinStore service, PackageCollection toInstall, PackageCollection toUninstall, PackageCollection installedRequired, DependencyCollection unresolved)
195
Addin ia = service.Registry.GetAddin (Mono.Addins.Addin.GetIdName (info.Id));
198
Package p = AddinPackage.FromInstalledAddin (ia);
199
if (!toUninstall.Contains (p))
202
if (!info.SupportsVersion (ia.Version)) {
204
// This addin breaks the api of the currently installed one,
205
// it has to be removed, together with all dependencies
207
Addin[] ainfos = service.GetDependentAddins (info.Id, true);
208
foreach (Addin ainfo in ainfos) {
209
p = AddinPackage.FromInstalledAddin (ainfo);
210
if (!toUninstall.Contains (p))
216
foreach (Dependency dep in info.Dependencies) {
217
service.ResolveDependency (monitor, dep, this, toInstall, toUninstall, installedRequired, unresolved);
221
internal override void PrepareUninstall (IProgressMonitor monitor, AddinStore service)
223
iaddin = service.Registry.GetAddin (info.Id, true);
225
throw new InstallException (string.Format ("The add-in '{0}' is not installed.", info.Name));
227
AddinDescription conf = iaddin.Description;
229
if (!File.Exists (iaddin.AddinFile)) {
230
monitor.ReportWarning (string.Format ("The add-in '{0}' is scheduled for uninstalling, but the add-in file could not be found.", info.Name));
234
// The add-in is a core application add-in. It can't be uninstalled, so it will be disabled.
235
if (!service.IsUserAddin (iaddin.AddinFile)) {
236
disablingOnUninstall = true;
240
// If the add-in assemblies are loaded, or if there is any file with a write lock, delay the uninstallation
241
HashSet<string> files = new HashSet<string> (GetInstalledFiles (conf));
242
if (AddinManager.CheckAssembliesLoaded (files) || files.Any (f => HasWriteLock (f))) {
243
uninstallingLoaded = true;
247
if (!service.HasWriteAccess (iaddin.AddinFile))
248
throw new InstallException (AddinStore.GetUninstallErrorNoRoot (info));
250
foreach (string path in GetInstalledFiles (conf)) {
251
if (!service.HasWriteAccess (path))
252
throw new InstallException (AddinStore.GetUninstallErrorNoRoot (info));
255
tempFolder = CreateTempFolder ();
256
CopyAddinFiles (monitor, conf, iaddin.AddinFile, tempFolder);
259
bool HasWriteLock (string file)
261
if (!File.Exists (file))
264
File.OpenWrite (file).Close ();
271
IEnumerable<string> GetInstalledFiles (AddinDescription conf)
273
string basePath = Path.GetDirectoryName (conf.AddinFile);
274
foreach (string relPath in conf.AllFiles) {
275
string afile = Path.Combine (basePath, relPath);
276
if (File.Exists (afile))
279
foreach (var p in conf.Properties) {
282
file = Path.Combine (basePath, p.Value);
283
if (!File.Exists (file))
293
internal override void CommitUninstall (IProgressMonitor monitor, AddinStore service)
295
if (disablingOnUninstall) {
296
disablingOnUninstall = false;
297
service.Registry.DisableAddin (info.Id);
301
AddinDescription conf = iaddin.Description;
303
string basePath = Path.GetDirectoryName (conf.AddinFile);
305
if (uninstallingLoaded) {
306
List<string> files = new List<string> ();
307
files.Add (iaddin.AddinFile);
308
foreach (string f in GetInstalledFiles (conf))
310
service.Registry.RegisterForUninstall (info.Id, files);
314
if (tempFolder == null)
317
monitor.Log.WriteLine ("Uninstalling " + info.Name + " v" + info.Version);
319
foreach (string path in GetInstalledFiles (conf))
322
File.Delete (iaddin.AddinFile);
324
RecDeleteDir (monitor, basePath);
326
monitor.Log.WriteLine ("Done");
329
void RecDeleteDir (IProgressMonitor monitor, string path)
331
if (Directory.GetFiles (path).Length != 0)
334
foreach (string dir in Directory.GetDirectories (path))
335
RecDeleteDir (monitor, dir);
338
Directory.Delete (path);
340
monitor.ReportWarning ("Directory " + path + " could not be deleted.");
344
internal override void RollbackUninstall (IProgressMonitor monitor, AddinStore service)
346
disablingOnUninstall = false;
347
if (tempFolder != null) {
348
AddinDescription conf = iaddin.Description;
349
string configFile = Path.Combine (tempFolder, Path.GetFileName (iaddin.AddinFile));
351
string addinDir = Path.GetDirectoryName (iaddin.AddinFile);
352
CopyAddinFiles (monitor, conf, configFile, addinDir);
356
internal override void EndUninstall (IProgressMonitor monitor, AddinStore service)
358
if (tempFolder != null)
359
Directory.Delete (tempFolder, true);
363
void CopyAddinFiles (IProgressMonitor monitor, AddinDescription conf, string configFile, string destPath)
365
if (!Directory.Exists (destPath))
366
Directory.CreateDirectory (destPath);
368
string dfile = Path.Combine (destPath, Path.GetFileName (configFile));
369
if (File.Exists (dfile))
372
File.Copy (configFile, dfile);
374
string basePath = Path.GetDirectoryName (configFile);
376
foreach (string relPath in conf.AllFiles) {
377
string path = Path.Combine (basePath, relPath);
378
if (!File.Exists (path))
381
string destf = Path.Combine (destPath, Path.GetDirectoryName (relPath));
382
if (!Directory.Exists (destf))
383
Directory.CreateDirectory (destf);
385
dfile = Path.Combine (destPath, relPath);
386
if (File.Exists (dfile))
389
File.Copy (path, dfile);