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;
31
using System.Collections.Specialized;
34
using System.Xml.Serialization;
35
using System.Reflection;
36
using System.Diagnostics;
38
using System.Runtime.Serialization;
39
using System.Runtime.Serialization.Formatters.Binary;
41
using ICSharpCode.SharpZipLib.Zip;
43
using Mono.Addins.Setup.ProgressMonitoring;
44
using Mono.Addins.Description;
45
using Mono.Addins.Serialization;
46
using System.Collections.Generic;
49
namespace Mono.Addins.Setup
51
internal class AddinStore
55
public AddinStore (SetupService service)
57
this.service = service;
60
internal void ResetCachedData ()
64
public AddinRegistry Registry {
65
get { return service.Registry; }
68
public bool Install (IProgressStatus statusMonitor, params string[] files)
70
Package[] packages = new Package [files.Length];
71
for (int n=0; n<files.Length; n++)
72
packages [n] = AddinPackage.FromFile (files [n]);
74
return Install (statusMonitor, packages);
77
public bool Install (IProgressStatus statusMonitor, params AddinRepositoryEntry[] addins)
79
Package[] packages = new Package [addins.Length];
80
for (int n=0; n<addins.Length; n++)
81
packages [n] = AddinPackage.FromRepository (addins [n]);
83
return Install (statusMonitor, packages);
86
internal bool Install (IProgressStatus monitor, params Package[] packages)
88
PackageCollection packs = new PackageCollection ();
89
packs.AddRange (packages);
90
return Install (monitor, packs);
93
internal bool Install (IProgressStatus statusMonitor, PackageCollection packs)
95
// Make sure the registry is up to date
96
service.Registry.Update (statusMonitor);
98
IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor);
100
PackageCollection toUninstall;
101
DependencyCollection unresolved;
102
if (!ResolveDependencies (monitor, packs, out toUninstall, out unresolved)) {
103
monitor.ReportError ("Not all dependencies could be resolved.", null);
107
ArrayList prepared = new ArrayList ();
108
ArrayList uninstallPrepared = new ArrayList ();
109
bool rollback = false;
111
monitor.BeginTask ("Installing add-ins...", 100);
115
monitor.BeginStepTask ("Initializing installation", toUninstall.Count + packs.Count + 1, 75);
117
foreach (Package mpack in toUninstall) {
119
mpack.PrepareUninstall (monitor, this);
120
uninstallPrepared.Add (mpack);
121
if (monitor.IsCancelRequested)
122
throw new InstallException ("Installation cancelled.");
124
} catch (Exception ex) {
125
ReportException (monitor, ex);
133
foreach (Package mpack in packs) {
135
mpack.PrepareInstall (monitor, this);
136
if (monitor.IsCancelRequested)
137
throw new InstallException ("Installation cancelled.");
138
prepared.Add (mpack);
140
} catch (Exception ex) {
141
ReportException (monitor, ex);
149
monitor.BeginStepTask ("Installing", toUninstall.Count + packs.Count + 1, 20);
154
foreach (Package mpack in toUninstall) {
156
mpack.CommitUninstall (monitor, this);
157
if (monitor.IsCancelRequested)
158
throw new InstallException ("Installation cancelled.");
160
} catch (Exception ex) {
161
ReportException (monitor, ex);
171
foreach (Package mpack in packs) {
173
mpack.CommitInstall (monitor, this);
174
if (monitor.IsCancelRequested)
175
throw new InstallException ("Installation cancelled.");
177
} catch (Exception ex) {
178
ReportException (monitor, ex);
187
// Rollback if failed
189
if (monitor.IsCancelRequested)
190
monitor = new NullProgressMonitor ();
193
monitor.BeginStepTask ("Finishing installation", (prepared.Count + uninstallPrepared.Count)*2 + 1, 5);
195
foreach (Package mpack in prepared) {
197
mpack.RollbackInstall (monitor, this);
199
} catch (Exception ex) {
200
ReportException (monitor, ex);
204
foreach (Package mpack in uninstallPrepared) {
206
mpack.RollbackUninstall (monitor, this);
208
} catch (Exception ex) {
209
ReportException (monitor, ex);
213
monitor.BeginStepTask ("Finishing installation", prepared.Count + uninstallPrepared.Count + 1, 5);
217
foreach (Package mpack in prepared) {
219
mpack.EndInstall (monitor, this);
221
} catch (Exception ex) {
222
monitor.Log.WriteLine (ex);
228
foreach (Package mpack in uninstallPrepared) {
230
mpack.EndUninstall (monitor, this);
232
} catch (Exception ex) {
233
monitor.Log.WriteLine (ex);
237
// Update the extension maps
238
service.Registry.Update (statusMonitor);
244
service.SaveConfiguration ();
250
void ReportException (IProgressMonitor statusMonitor, Exception ex)
252
if (ex is InstallException)
253
statusMonitor.ReportError (ex.Message, null);
255
statusMonitor.ReportError (null, ex);
258
public void Uninstall (IProgressStatus statusMonitor, string id)
260
Uninstall (statusMonitor, new string[] { id });
263
public void Uninstall (IProgressStatus statusMonitor, IEnumerable<string> ids)
265
IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor);
266
monitor.BeginTask ("Uninstalling add-ins", ids.Count ());
268
foreach (string id in ids) {
269
bool rollback = false;
270
ArrayList toUninstall = new ArrayList ();
271
ArrayList uninstallPrepared = new ArrayList ();
273
Addin ia = service.Registry.GetAddin (id);
275
throw new InstallException ("The add-in '" + id + "' is not installed.");
277
toUninstall.Add (AddinPackage.FromInstalledAddin (ia));
279
Addin[] deps = GetDependentAddins (id, true);
280
foreach (Addin dep in deps)
281
toUninstall.Add (AddinPackage.FromInstalledAddin (dep));
283
monitor.BeginTask ("Deleting files", toUninstall.Count*2 + uninstallPrepared.Count + 1);
287
foreach (Package mpack in toUninstall) {
289
mpack.PrepareUninstall (monitor, this);
291
uninstallPrepared.Add (mpack);
292
} catch (Exception ex) {
293
ReportException (monitor, ex);
302
foreach (Package mpack in toUninstall) {
304
mpack.CommitUninstall (monitor, this);
306
} catch (Exception ex) {
307
ReportException (monitor, ex);
314
// Rollback if failed
317
monitor.BeginTask ("Rolling back uninstall", uninstallPrepared.Count);
318
foreach (Package mpack in uninstallPrepared) {
320
mpack.RollbackUninstall (monitor, this);
321
} catch (Exception ex) {
322
ReportException (monitor, ex);
331
foreach (Package mpack in uninstallPrepared) {
333
mpack.EndUninstall (monitor, this);
335
} catch (Exception ex) {
336
monitor.Log.WriteLine (ex);
344
// Update the extension maps
345
service.Registry.Update (statusMonitor);
349
service.SaveConfiguration ();
353
public Addin[] GetDependentAddins (string id, bool recursive)
355
ArrayList list = new ArrayList ();
356
FindDependentAddins (list, id, recursive);
357
return (Addin[]) list.ToArray (typeof (Addin));
360
void FindDependentAddins (ArrayList list, string id, bool recursive)
362
foreach (Addin iaddin in service.Registry.GetAddins ()) {
363
if (list.Contains (iaddin))
365
foreach (Dependency dep in iaddin.Description.MainModule.Dependencies) {
366
AddinDependency adep = dep as AddinDependency;
367
if (adep != null && adep.AddinId == id) {
370
FindDependentAddins (list, iaddin.Id, true);
376
public bool ResolveDependencies (IProgressStatus statusMonitor, AddinRepositoryEntry[] addins, out PackageCollection resolved, out PackageCollection toUninstall, out DependencyCollection unresolved)
378
resolved = new PackageCollection ();
379
for (int n=0; n<addins.Length; n++)
380
resolved.Add (AddinPackage.FromRepository (addins [n]));
381
return ResolveDependencies (statusMonitor, resolved, out toUninstall, out unresolved);
384
public bool ResolveDependencies (IProgressStatus statusMonitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved)
386
IProgressMonitor monitor = ProgressStatusMonitor.GetProgressMonitor (statusMonitor);
387
return ResolveDependencies (monitor, packages, out toUninstall, out unresolved);
390
internal bool ResolveDependencies (IProgressMonitor monitor, PackageCollection packages, out PackageCollection toUninstall, out DependencyCollection unresolved)
392
PackageCollection requested = new PackageCollection();
393
requested.AddRange (packages);
395
unresolved = new DependencyCollection ();
396
toUninstall = new PackageCollection ();
397
PackageCollection installedRequired = new PackageCollection ();
399
for (int n=0; n<packages.Count; n++) {
400
Package p = packages [n];
401
p.Resolve (monitor, this, packages, toUninstall, installedRequired, unresolved);
404
if (unresolved.Count != 0) {
405
foreach (Dependency dep in unresolved)
406
monitor.ReportError (string.Format ("The package '{0}' could not be found in any repository", dep.Name), null);
410
// Check that we are not uninstalling packages that are required
411
// by packages being installed.
413
foreach (Package p in installedRequired) {
414
if (toUninstall.Contains (p)) {
415
// Only accept to uninstall this package if we are
416
// going to install a newer version.
417
bool foundUpgrade = false;
418
foreach (Package tbi in packages)
419
if (tbi.Equals (p) || tbi.IsUpgradeOf (p)) {
428
// Check that we are not trying to uninstall from a directory from
429
// which we don't have write permissions
431
foreach (Package p in toUninstall) {
432
AddinPackage ap = p as AddinPackage;
434
Addin ia = service.Registry.GetAddin (ap.Addin.Id);
435
if (File.Exists (ia.AddinFile) && !HasWriteAccess (ia.AddinFile) && IsUserAddin (ia.AddinFile)) {
436
monitor.ReportError (GetUninstallErrorNoRoot (ap.Addin), null);
442
// Check that we are not installing two versions of the same addin
444
PackageCollection resolved = new PackageCollection();
445
resolved.AddRange (packages);
449
for (int n=0; n<packages.Count; n++) {
450
AddinPackage ap = packages [n] as AddinPackage;
451
if (ap == null) continue;
453
for (int k=n+1; k<packages.Count; k++) {
454
AddinPackage otherap = packages [k] as AddinPackage;
455
if (otherap == null) continue;
457
if (ap.Addin.Id == otherap.Addin.Id) {
458
if (ap.IsUpgradeOf (otherap)) {
459
if (requested.Contains (otherap)) {
460
monitor.ReportError ("Can't install two versions of the same add-in: '" + ap.Addin.Name + "'.", null);
463
packages.RemoveAt (k);
465
} else if (otherap.IsUpgradeOf (ap)) {
466
if (requested.Contains (ap)) {
467
monitor.ReportError ("Can't install two versions of the same add-in: '" + ap.Addin.Name + "'.", null);
470
packages.RemoveAt (n);
475
monitor.ReportError ("Can't install two versions of the same add-in: '" + ap.Addin.Name + "'.", null);
482
// Don't allow installing add-ins which are scheduled for uninstall
484
foreach (Package p in packages) {
485
AddinPackage ap = p as AddinPackage;
486
if (ap != null && Registry.IsRegisteredForUninstall (ap.Addin.Id)) {
488
monitor.ReportError ("The addin " + ap.Addin.Name + " v" + ap.Addin.Version + " is scheduled for uninstallation. Please restart the application before trying to re-install it.", null);
495
internal void ResolveDependency (IProgressMonitor monitor, Dependency dep, AddinPackage parentPackage, PackageCollection toInstall, PackageCollection toUninstall, PackageCollection installedRequired, DependencyCollection unresolved)
497
AddinDependency adep = dep as AddinDependency;
501
string nsid = Addin.GetFullId (parentPackage.Addin.Namespace, adep.AddinId, null);
503
foreach (Package p in toInstall) {
504
AddinPackage ap = p as AddinPackage;
506
if (Addin.GetIdName (ap.Addin.Id) == nsid && ((AddinInfo)ap.Addin).SupportsVersion (adep.Version))
511
ArrayList addins = new ArrayList ();
512
addins.AddRange (service.Registry.GetAddins ());
513
addins.AddRange (service.Registry.GetAddinRoots ());
515
foreach (Addin addin in addins) {
516
if (Addin.GetIdName (addin.Id) == nsid && addin.SupportsVersion (adep.Version)) {
517
AddinPackage p = AddinPackage.FromInstalledAddin (addin);
518
if (!installedRequired.Contains (p))
519
installedRequired.Add (p);
524
AddinRepositoryEntry[] avaddins = service.Repositories.GetAvailableAddins ();
525
foreach (PackageRepositoryEntry avAddin in avaddins) {
526
if (Addin.GetIdName (avAddin.Addin.Id) == nsid && ((AddinInfo)avAddin.Addin).SupportsVersion (adep.Version)) {
527
toInstall.Add (AddinPackage.FromRepository (avAddin));
531
unresolved.Add (adep);
534
internal string GetAddinDirectory (AddinInfo info)
536
return Path.Combine (service.InstallDirectory, info.Id.Replace (',','.'));
539
internal void RegisterAddin (IProgressMonitor monitor, AddinInfo info, string sourceDir)
541
monitor.Log.WriteLine ("Installing " + info.Name + " v" + info.Version);
542
string addinDir = GetAddinDirectory (info);
543
if (!Directory.Exists (addinDir))
544
Directory.CreateDirectory (addinDir);
545
CopyDirectory (sourceDir, addinDir);
550
void CopyDirectory (string src, string dest)
552
CopyDirectory (src, dest, "");
555
void CopyDirectory (string src, string dest, string subdir)
557
string destDir = Path.Combine (dest, subdir);
559
if (!Directory.Exists (destDir))
560
Directory.CreateDirectory (destDir);
562
foreach (string file in Directory.GetFiles (src)) {
563
if (Path.GetFileName (file) != "addin.info")
564
File.Copy (file, Path.Combine (destDir, Path.GetFileName (file)), true);
567
foreach (string dir in Directory.GetDirectories (src))
568
CopyDirectory (dir, dest, Path.Combine (subdir, Path.GetFileName (dir)));
571
internal object DownloadObject (IProgressMonitor monitor, string url, Type type)
575
file = DownloadFile (monitor, url);
576
return ReadObject (file, type);
583
static XmlSerializer GetSerializer (Type type)
585
if (type == typeof(AddinSystemConfiguration))
586
return new AddinSystemConfigurationSerializer ();
587
else if (type == typeof(Repository))
588
return new RepositorySerializer ();
590
return new XmlSerializer (type);
593
internal static object ReadObject (string file, Type type)
595
if (!File.Exists (file))
598
StreamReader r = new StreamReader (file);
600
XmlSerializer ser = GetSerializer (type);
601
return ser.Deserialize (r);
609
internal static void WriteObject (string file, object obj)
611
string dir = Path.GetDirectoryName (file);
612
if (!Directory.Exists (dir))
613
Directory.CreateDirectory (dir);
614
StreamWriter s = new StreamWriter (file);
616
XmlSerializer ser = GetSerializer (obj.GetType());
617
ser.Serialize (s, obj);
621
if (File.Exists (file))
627
internal string DownloadFile (IProgressMonitor monitor, string url)
629
if (url.StartsWith ("file://")) {
630
string tmpfile = Path.GetTempFileName ();
631
string path = new Uri (url).LocalPath;
632
File.Delete (tmpfile);
633
File.Copy (path, tmpfile);
637
monitor.BeginTask ("Requesting " + url, 2);
638
HttpWebRequest req = (HttpWebRequest) WebRequest.Create (url);
639
req.Headers ["Pragma"] = "no-cache";
640
HttpWebResponse resp = (HttpWebResponse) req.GetResponse ();
643
monitor.BeginTask ("Downloading " + url, (int) resp.ContentLength);
645
string file = Path.GetTempFileName ();
646
FileStream fs = null;
649
fs = new FileStream (file, FileMode.Create, FileAccess.Write);
650
s = req.GetResponse ().GetResponseStream ();
651
byte[] buffer = new byte [4096];
654
while ((n = s.Read (buffer, 0, buffer.Length)) != 0) {
656
fs.Write (buffer, 0, n);
657
if (monitor.IsCancelRequested)
658
throw new InstallException ("Installation cancelled.");
676
internal bool HasWriteAccess (string file)
678
FileInfo f = new FileInfo (file);
679
return !f.Exists || !f.IsReadOnly;
682
internal bool IsUserAddin (string addinFile)
684
string installPath = service.InstallDirectory;
685
if (installPath [installPath.Length - 1] != Path.DirectorySeparatorChar)
686
installPath += Path.DirectorySeparatorChar;
687
return Path.GetFullPath (addinFile).StartsWith (installPath);
690
internal static string GetUninstallErrorNoRoot (AddinHeader ainfo)
692
return string.Format ("The add-in '{0} v{1}' can't be uninstalled with the current user permissions.", ainfo.Name, ainfo.Version);