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.
31
using System.Threading;
32
using System.Collections;
33
using System.Collections.Specialized;
36
using System.Reflection;
37
using Mono.Addins.Description;
38
using System.Collections.Generic;
41
namespace Mono.Addins.Database
45
public const string GlobalDomain = "global";
46
public const string UnknownDomain = "unknown";
48
public const string VersionTag = "001";
50
List<Addin> allSetupInfos;
51
List<Addin> addinSetupInfos;
52
List<Addin> rootSetupInfos;
53
internal static bool RunningSetupProcess;
54
bool fatalDatabseError;
55
Hashtable cachedAddinSetupInfos = new Hashtable ();
56
AddinScanResult currentScanResult;
57
AddinHostIndex hostIndex;
58
FileDatabase fileDatabase;
60
DatabaseConfiguration config = null;
61
AddinRegistry registry;
63
AddinEngine addinEngine;
64
AddinFileSystemExtension fs = new AddinFileSystemExtension ();
65
List<object> extensions = new List<object> ();
67
public AddinDatabase (AddinEngine addinEngine, AddinRegistry registry)
69
this.addinEngine = addinEngine;
70
this.registry = registry;
71
addinDbDir = Path.Combine (registry.AddinCachePath, "addin-db-" + VersionTag);
72
fileDatabase = new FileDatabase (AddinDbDir);
76
get { return addinDbDir; }
79
public AddinFileSystemExtension FileSystem {
83
public string AddinCachePath {
84
get { return Path.Combine (AddinDbDir, "addin-data"); }
87
public string AddinFolderCachePath {
88
get { return Path.Combine (AddinDbDir, "addin-dir-data"); }
91
public string AddinPrivateDataPath {
92
get { return Path.Combine (AddinDbDir, "addin-priv-data"); }
95
public string HostsPath {
96
get { return Path.Combine (AddinDbDir, "hosts"); }
99
string HostIndexFile {
100
get { return Path.Combine (AddinDbDir, "host-index"); }
104
get { return Path.Combine (AddinDbDir, "config.xml"); }
107
internal bool IsGlobalRegistry {
109
return registry.RegistryPath == AddinRegistry.GlobalRegistryPath;
113
public AddinRegistry Registry {
115
return this.registry;
121
if (Directory.Exists (AddinCachePath))
122
Directory.Delete (AddinCachePath, true);
123
if (Directory.Exists (AddinFolderCachePath))
124
Directory.Delete (AddinFolderCachePath, true);
127
public void CopyExtensions (AddinDatabase other)
129
foreach (object o in other.extensions)
130
RegisterExtension (o);
133
public void RegisterExtension (object extension)
135
extensions.Add (extension);
136
if (extension is AddinFileSystemExtension)
137
fs = (AddinFileSystemExtension) extension;
139
throw new NotSupportedException ();
142
public void UnregisterExtension (object extension)
144
extensions.Remove (extension);
145
if ((extension as AddinFileSystemExtension) == fs)
146
fs = new AddinFileSystemExtension ();
148
throw new InvalidOperationException ();
151
public ExtensionNodeSet FindNodeSet (string domain, string addinId, string id)
153
return FindNodeSet (domain, addinId, id, new Hashtable ());
156
ExtensionNodeSet FindNodeSet (string domain, string addinId, string id, Hashtable visited)
158
if (visited.Contains (addinId))
160
visited.Add (addinId, addinId);
161
Addin addin = GetInstalledAddin (domain, addinId, true, false);
164
AddinDescription desc = addin.Description;
167
foreach (ExtensionNodeSet nset in desc.ExtensionNodeSets)
171
// Not found in the add-in. Look on add-ins on which it depends
173
foreach (Dependency dep in desc.MainModule.Dependencies) {
174
AddinDependency adep = dep as AddinDependency;
175
if (adep == null) continue;
177
string aid = Addin.GetFullId (desc.Namespace, adep.AddinId, adep.Version);
178
ExtensionNodeSet nset = FindNodeSet (domain, aid, id, visited);
185
public IEnumerable<Addin> GetInstalledAddins (string domain, AddinSearchFlagsInternal flags)
188
domain = registry.CurrentDomain;
190
// Get the cached list if the add-in list has already been loaded.
191
// The domain doesn't have to be checked again, since it is always the same
193
IEnumerable<Addin> result = null;
195
if ((flags & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll) {
196
if (allSetupInfos != null)
197
result = allSetupInfos;
199
else if ((flags & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) {
200
if (addinSetupInfos != null)
201
result = addinSetupInfos;
204
if (rootSetupInfos != null)
205
result = rootSetupInfos;
208
if (result == null) {
209
InternalCheck (domain);
210
using (fileDatabase.LockRead ()) {
211
result = InternalGetInstalledAddins (domain, null, flags & ~AddinSearchFlagsInternal.LatestVersionsOnly);
215
if ((flags & AddinSearchFlagsInternal.LatestVersionsOnly) == AddinSearchFlagsInternal.LatestVersionsOnly)
216
result = result.Where (a => a.IsLatestVersion);
218
if ((flags & AddinSearchFlagsInternal.ExcludePendingUninstall) == AddinSearchFlagsInternal.ExcludePendingUninstall)
219
result = result.Where (a => !IsRegisteredForUninstall (a.Description.Domain, a.Id));
224
IEnumerable<Addin> InternalGetInstalledAddins (string domain, AddinSearchFlagsInternal type)
226
return InternalGetInstalledAddins (domain, null, type);
229
IEnumerable<Addin> InternalGetInstalledAddins (string domain, string idFilter, AddinSearchFlagsInternal type)
231
if ((type & AddinSearchFlagsInternal.LatestVersionsOnly) != 0)
232
throw new InvalidOperationException ("LatestVersionsOnly flag not supported here");
234
if (allSetupInfos == null) {
235
Dictionary<string,Addin> adict = new Dictionary<string, Addin> ();
237
// Global add-ins are valid for any private domain
238
if (domain != AddinDatabase.GlobalDomain)
239
FindInstalledAddins (adict, AddinDatabase.GlobalDomain, idFilter);
241
FindInstalledAddins (adict, domain, idFilter);
242
List<Addin> alist = new List<Addin> (adict.Values);
243
UpdateLastVersionFlags (alist);
244
if (idFilter != null)
246
allSetupInfos = alist;
248
if ((type & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll)
249
return FilterById (allSetupInfos, idFilter);
251
if ((type & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) {
252
if (addinSetupInfos == null) {
253
addinSetupInfos = new List<Addin> ();
254
foreach (Addin adn in allSetupInfos)
255
if (!adn.Description.IsRoot)
256
addinSetupInfos.Add (adn);
258
return FilterById (addinSetupInfos, idFilter);
261
if (rootSetupInfos == null) {
262
rootSetupInfos = new List<Addin> ();
263
foreach (Addin adn in allSetupInfos)
264
if (adn.Description.IsRoot)
265
rootSetupInfos.Add (adn);
267
return FilterById (rootSetupInfos, idFilter);
271
IEnumerable<Addin> FilterById (List<Addin> addins, string id)
275
return addins.Where (a => Addin.GetIdName (a.Id) == id);
278
void FindInstalledAddins (Dictionary<string,Addin> result, string domain, string idFilter)
280
if (idFilter == null)
282
string dir = Path.Combine (AddinCachePath, domain);
283
if (Directory.Exists (dir)) {
284
foreach (string file in fileDatabase.GetDirectoryFiles (dir, idFilter + ",*.maddin")) {
285
string id = Path.GetFileNameWithoutExtension (file);
286
if (!result.ContainsKey (id)) {
287
var adesc = GetInstalledDomainAddin (domain, id, true, false, false);
289
result.Add (id, adesc);
295
void UpdateLastVersionFlags (List<Addin> addins)
297
Dictionary<string,string> versions = new Dictionary<string, string> ();
298
foreach (Addin a in addins) {
301
Addin.GetIdParts (a.Id, out id, out version);
302
if (!versions.TryGetValue (id, out last) || Addin.CompareVersions (last, version) > 0)
303
versions [id] = version;
305
foreach (Addin a in addins) {
307
Addin.GetIdParts (a.Id, out id, out version);
308
a.IsLatestVersion = versions [id] == version;
312
public Addin GetInstalledAddin (string domain, string id)
314
return GetInstalledAddin (domain, id, false, false);
317
public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch)
319
return GetInstalledAddin (domain, id, exactVersionMatch, false);
322
public Addin GetInstalledAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly)
324
// Try the given domain, and if not found, try the shared domain
325
Addin ad = GetInstalledDomainAddin (domain, id, exactVersionMatch, enabledOnly, true);
328
if (domain != AddinDatabase.GlobalDomain)
329
return GetInstalledDomainAddin (AddinDatabase.GlobalDomain, id, exactVersionMatch, enabledOnly, true);
334
Addin GetInstalledDomainAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly, bool dbLockCheck)
337
string idd = id + " " + domain;
338
object ob = cachedAddinSetupInfos [idd];
342
if (!enabledOnly || sinfo.Enabled)
344
if (exactVersionMatch)
347
else if (enabledOnly)
348
// Ignore the 'not installed' flag when disabled add-ins are allowed
353
InternalCheck (domain);
355
using ((dbLockCheck ? fileDatabase.LockRead () : null))
357
string path = GetDescriptionPath (domain, id);
358
if (sinfo == null && fileDatabase.Exists (path)) {
359
sinfo = new Addin (this, path);
360
cachedAddinSetupInfos [idd] = sinfo;
361
if (!enabledOnly || sinfo.Enabled)
363
if (exactVersionMatch) {
364
// Cache lookups with negative result
365
cachedAddinSetupInfos [idd] = this;
370
// Exact version not found. Look for a compatible version
371
if (!exactVersionMatch) {
373
string version, name, bestVersion = null;
374
Addin.GetIdParts (id, out name, out version);
376
foreach (Addin ia in InternalGetInstalledAddins (domain, name, AddinSearchFlagsInternal.IncludeAll))
378
if ((!enabledOnly || ia.Enabled) &&
379
(version.Length == 0 || ia.SupportsVersion (version)) &&
380
(bestVersion == null || Addin.CompareVersions (bestVersion, ia.Version) > 0))
382
bestVersion = ia.Version;
387
cachedAddinSetupInfos [idd] = sinfo;
392
// Cache lookups with negative result
393
// Ignore the 'not installed' flag when disabled add-ins are allowed
395
cachedAddinSetupInfos [idd] = this;
400
public void Shutdown ()
405
public Addin GetAddinForHostAssembly (string domain, string assemblyLocation)
407
InternalCheck (domain);
410
object ob = cachedAddinSetupInfos [assemblyLocation];
412
return ob as Addin; // Don't use a cast here is ob may not be an Addin.
414
AddinHostIndex index = GetAddinHostIndex ();
415
string addin, addinFile, rdomain;
416
if (index.GetAddinForAssembly (assemblyLocation, out addin, out addinFile, out rdomain)) {
417
string sid = addin + " " + rdomain;
418
ainfo = cachedAddinSetupInfos [sid] as Addin;
420
ainfo = new Addin (this, GetDescriptionPath (rdomain, addin));
421
cachedAddinSetupInfos [assemblyLocation] = ainfo;
422
cachedAddinSetupInfos [addin + " " + rdomain] = ainfo;
429
public bool IsAddinEnabled (string domain, string id)
431
Addin ainfo = GetInstalledAddin (domain, id);
433
return ainfo.Enabled;
438
internal bool IsAddinEnabled (string domain, string id, bool exactVersionMatch)
440
if (!exactVersionMatch)
441
return IsAddinEnabled (domain, id);
442
Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false);
445
return Configuration.IsEnabled (id, ainfo.AddinInfo.EnabledByDefault);
448
public void EnableAddin (string domain, string id)
450
EnableAddin (domain, id, true);
453
internal void EnableAddin (string domain, string id, bool exactVersionMatch)
455
Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false);
457
// It may be an add-in root
460
if (IsAddinEnabled (domain, id))
463
// Enable required add-ins
465
foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
466
if (dep is AddinDependency) {
467
AddinDependency adep = dep as AddinDependency;
468
string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
469
EnableAddin (domain, adepid, false);
473
Configuration.SetStatus (id, true, ainfo.AddinInfo.EnabledByDefault);
474
SaveConfiguration ();
476
if (addinEngine != null && addinEngine.IsInitialized)
477
addinEngine.ActivateAddin (id);
480
public void DisableAddin (string domain, string id)
482
Addin ai = GetInstalledAddin (domain, id, true);
484
throw new InvalidOperationException ("Add-in '" + id + "' not installed.");
486
if (!IsAddinEnabled (domain, id))
489
Configuration.SetStatus (id, false, ai.AddinInfo.EnabledByDefault);
490
SaveConfiguration ();
492
// Disable all add-ins which depend on it
495
string idName = Addin.GetIdName (id);
497
foreach (Addin ainfo in GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) {
498
foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
499
AddinDependency adep = dep as AddinDependency;
503
string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, null);
504
if (adepid != idName)
507
// The add-in that has been disabled, might be a requeriment of this one, or maybe not
508
// if there is an older version available. Check it now.
510
adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
511
Addin adepinfo = GetInstalledAddin (domain, adepid, false, true);
513
if (adepinfo == null) {
514
DisableAddin (domain, ainfo.Id);
521
// If something goes wrong, enable the add-in again
522
Configuration.SetStatus (id, true, ai.AddinInfo.EnabledByDefault);
523
SaveConfiguration ();
527
if (addinEngine != null && addinEngine.IsInitialized)
528
addinEngine.UnloadAddin (id);
531
public void RegisterForUninstall (string domain, string id, IEnumerable<string> files)
533
DisableAddin (domain, id);
534
Configuration.RegisterForUninstall (id, files);
535
SaveConfiguration ();
538
public bool IsRegisteredForUninstall (string domain, string addinId)
540
return Configuration.IsRegisteredForUninstall (addinId);
543
internal bool HasPendingUninstalls (string domain)
545
return Configuration.HasPendingUninstalls;
548
internal string GetDescriptionPath (string domain, string id)
550
return Path.Combine (Path.Combine (AddinCachePath, domain), id + ".maddin");
553
void InternalCheck (string domain)
555
// If the database is broken, don't try to regenerate it at every check.
556
if (fatalDatabseError)
560
using (fileDatabase.LockRead ()) {
561
if (!Directory.Exists (AddinCachePath)) {
566
Update (null, domain);
569
void GenerateAddinExtensionMapsInternal (IProgressStatus monitor, List<string> addinsToUpdate, List<string> addinsToUpdateRelations, List<string> removedAddins)
571
AddinUpdateData updateData = new AddinUpdateData (this, monitor);
574
cachedAddinSetupInfos.Clear ();
576
// Collect all information
578
AddinIndex addinHash = new AddinIndex ();
580
if (monitor.LogLevel > 1)
581
monitor.Log ("Generating add-in extension maps");
583
Hashtable changedAddins = null;
584
ArrayList descriptionsToSave = new ArrayList ();
585
ArrayList files = new ArrayList ();
587
bool partialGeneration = addinsToUpdate != null;
588
string[] domains = GetDomains ();
590
// Get the files to be updated
592
if (partialGeneration) {
593
changedAddins = new Hashtable ();
595
if (monitor.LogLevel > 2)
596
monitor.Log ("Doing a partial registry update.\nAdd-ins to be updated:");
597
// Get the files and ids of all add-ins that have to be updated
598
// Include removed add-ins: if there are several instances of the same add-in, removing one of
599
// them will make other instances to show up. If there is a single instance, its files are
601
foreach (string sa in addinsToUpdate.Union (removedAddins)) {
602
changedAddins [sa] = sa;
603
if (monitor.LogLevel > 2)
604
monitor.Log (" - " + sa);
605
foreach (string file in GetAddinFiles (sa, domains)) {
606
if (!files.Contains (file)) {
608
string an = Path.GetFileNameWithoutExtension (file);
609
changedAddins [an] = an;
610
if (monitor.LogLevel > 2 && an != sa)
611
monitor.Log (" - " + an);
616
if (monitor.LogLevel > 2)
617
monitor.Log ("Add-ins whose relations have to be updated:");
619
// Get the files and ids of all add-ins whose relations have to be updated
620
foreach (string sa in addinsToUpdateRelations) {
621
foreach (string file in GetAddinFiles (sa, domains)) {
622
if (!files.Contains (file)) {
623
if (monitor.LogLevel > 2) {
624
string an = Path.GetFileNameWithoutExtension (file);
625
monitor.Log (" - " + an);
633
foreach (string dom in domains)
634
files.AddRange (fileDatabase.GetDirectoryFiles (Path.Combine (AddinCachePath, dom), "*.maddin"));
637
// Load the descriptions.
638
foreach (string file in files) {
640
AddinDescription conf;
641
if (!ReadAddinDescription (monitor, file, out conf)) {
642
SafeDelete (monitor, file);
646
// If the original file does not exist, the description can be deleted
647
if (!fs.FileExists (conf.AddinFile)) {
648
SafeDelete (monitor, file);
652
// Remove old data from the description. Remove the data of the add-ins that
653
// have changed. This data will be re-added later.
655
conf.UnmergeExternalData (changedAddins);
656
descriptionsToSave.Add (conf);
658
addinHash.Add (conf);
661
// Sort the add-ins, to make sure add-ins are processed before
662
// all their dependencies
664
var sorted = addinHash.GetSortedAddins ();
666
// Register extension points and node sets
667
foreach (AddinDescription conf in sorted)
668
CollectExtensionPointData (conf, updateData);
670
if (monitor.LogLevel > 2)
671
monitor.Log ("Registering new extensions:");
673
// Register extensions
674
foreach (AddinDescription conf in sorted) {
675
if (changedAddins == null || changedAddins.ContainsKey (conf.AddinId)) {
676
if (monitor.LogLevel > 2)
677
monitor.Log ("- " + conf.AddinId + " (" + conf.Domain + ")");
678
CollectExtensionData (monitor, addinHash, conf, updateData);
683
foreach (AddinDescription conf in descriptionsToSave) {
684
ConsolidateExtensions (conf);
685
conf.SaveBinary (fileDatabase);
688
if (monitor.LogLevel > 1) {
689
monitor.Log ("Addin relation map generated.");
690
monitor.Log (" Addins Updated: " + descriptionsToSave.Count);
691
monitor.Log (" Extension points: " + updateData.RelExtensionPoints);
692
monitor.Log (" Extensions: " + updateData.RelExtensions);
693
monitor.Log (" Extension nodes: " + updateData.RelExtensionNodes);
694
monitor.Log (" Node sets: " + updateData.RelNodeSetTypes);
698
void ConsolidateExtensions (AddinDescription conf)
700
// Merges extensions with the same path
702
foreach (ModuleDescription module in conf.AllModules) {
703
Dictionary<string,Extension> extensions = new Dictionary<string, Extension> ();
704
foreach (Extension ext in module.Extensions) {
706
if (extensions.TryGetValue (ext.Path, out mainExt)) {
707
ArrayList list = new ArrayList ();
708
EnsureInsertionsSorted (ext.ExtensionNodes);
709
list.AddRange (ext.ExtensionNodes);
711
foreach (ExtensionNodeDescription node in list) {
712
ext.ExtensionNodes.Remove (node);
713
AddNodeSorted (mainExt.ExtensionNodes, node, ref pos);
716
extensions [ext.Path] = ext;
717
EnsureInsertionsSorted (ext.ExtensionNodes);
725
void EnsureInsertionsSorted (ExtensionNodeDescriptionCollection list)
727
// Makes sure that the nodes in the collections are properly sorted wrt insertafter and insertbefore attributes
728
Dictionary<string,ExtensionNodeDescription> added = new Dictionary<string, ExtensionNodeDescription> ();
729
List<ExtensionNodeDescription> halfSorted = new List<ExtensionNodeDescription> ();
730
bool orderChanged = false;
732
for (int n = list.Count - 1; n >= 0; n--) {
733
ExtensionNodeDescription node = list [n];
734
if (node.Id.Length > 0)
735
added [node.Id] = node;
736
if (node.InsertAfter.Length > 0) {
737
ExtensionNodeDescription relNode;
738
if (added.TryGetValue (node.InsertAfter, out relNode)) {
739
// Out of order. Move it before the referenced node
740
int i = halfSorted.IndexOf (relNode);
741
halfSorted.Insert (i, node);
744
halfSorted.Add (node);
747
halfSorted.Add (node);
749
halfSorted.Reverse ();
750
List<ExtensionNodeDescription> fullSorted = new List<ExtensionNodeDescription> ();
753
foreach (ExtensionNodeDescription node in halfSorted) {
754
if (node.Id.Length > 0)
755
added [node.Id] = node;
756
if (node.InsertBefore.Length > 0) {
757
ExtensionNodeDescription relNode;
758
if (added.TryGetValue (node.InsertBefore, out relNode)) {
759
// Out of order. Move it before the referenced node
760
int i = fullSorted.IndexOf (relNode);
761
fullSorted.Insert (i, node);
764
fullSorted.Add (node);
767
fullSorted.Add (node);
771
foreach (ExtensionNodeDescription node in fullSorted)
776
void AddNodeSorted (ExtensionNodeDescriptionCollection list, ExtensionNodeDescription node, ref int curPos)
778
// Adds the node at the correct position, taking into account insertbefore and insertafter
780
if (node.InsertAfter.Length > 0) {
781
string afterId = node.InsertAfter;
782
for (int n=0; n<list.Count; n++) {
783
if (list[n].Id == afterId) {
784
list.Insert (n + 1, node);
790
else if (node.InsertBefore.Length > 0) {
791
string beforeId = node.InsertBefore;
792
for (int n=0; n<list.Count; n++) {
793
if (list[n].Id == beforeId) {
794
list.Insert (n, node);
803
list.Insert (curPos++, node);
807
IEnumerable GetAddinFiles (string fullId, string[] domains)
809
// Look for all versions of the add-in, because this id may be the id of a reference,
810
// and the exact reference version may not be installed.
812
int i = s.LastIndexOf (',');
814
s = s.Substring (0, i);
817
// Look for the add-in in any of the existing folders
818
foreach (string domain in domains) {
819
string mp = GetDescriptionPath (domain, s);
820
string dir = Path.GetDirectoryName (mp);
821
string pat = Path.GetFileName (mp);
822
foreach (string fmp in fileDatabase.GetDirectoryFiles (dir, pat))
827
// Collects extension data in a hash table. The key is the path, the value is a list
828
// of add-ins ids that extend that path
830
void CollectExtensionPointData (AddinDescription conf, AddinUpdateData updateData)
832
foreach (ExtensionNodeSet nset in conf.ExtensionNodeSets) {
834
updateData.RegisterNodeSet (conf, nset);
835
updateData.RelNodeSetTypes++;
836
} catch (Exception ex) {
837
throw new InvalidOperationException ("Error reading node set: " + nset.Id, ex);
841
foreach (ExtensionPoint ep in conf.ExtensionPoints) {
843
updateData.RegisterExtensionPoint (conf, ep);
844
updateData.RelExtensionPoints++;
845
} catch (Exception ex) {
846
throw new InvalidOperationException ("Error reading extension point: " + ep.Path, ex);
851
void CollectExtensionData (IProgressStatus monitor, AddinIndex addinHash, AddinDescription conf, AddinUpdateData updateData)
853
IEnumerable<string> missingDeps = addinHash.GetMissingDependencies (conf, conf.MainModule);
854
if (missingDeps.Any ()) {
855
string w = "The add-in '" + conf.AddinId + "' could not be updated because some of its dependencies are missing or not compatible:";
856
w += BuildMissingAddinsList (addinHash, conf, missingDeps);
857
monitor.ReportWarning (w);
861
CollectModuleExtensionData (conf, conf.MainModule, updateData);
863
foreach (ModuleDescription module in conf.OptionalModules) {
864
missingDeps = addinHash.GetMissingDependencies (conf, module);
865
if (missingDeps.Any ()) {
866
if (monitor.LogLevel > 1) {
867
string w = "An optional module of the add-in '" + conf.AddinId + "' could not be updated because some of its dependencies are missing or not compatible:";
868
w += BuildMissingAddinsList (addinHash, conf, missingDeps);
872
CollectModuleExtensionData (conf, module, updateData);
876
string BuildMissingAddinsList (AddinIndex addinHash, AddinDescription conf, IEnumerable<string> missingDeps)
879
foreach (string dep in missingDeps) {
880
var found = addinHash.GetSimilarExistingAddin (conf, dep);
882
w += "\n missing: " + dep;
884
w += "\n required: " + dep + ", found: " + found.AddinId;
889
void CollectModuleExtensionData (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData)
891
foreach (Extension ext in module.Extensions) {
892
updateData.RelExtensions++;
893
updateData.RegisterExtension (conf, module, ext);
894
AddChildExtensions (conf, module, updateData, ext.Path, ext.ExtensionNodes, false);
898
void AddChildExtensions (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData, string path, ExtensionNodeDescriptionCollection nodes, bool conditionChildren)
900
// Don't register conditions as extension nodes.
901
if (!conditionChildren)
902
updateData.RegisterExtension (conf, module, path);
904
foreach (ExtensionNodeDescription node in nodes) {
905
if (node.NodeName == "ComplexCondition")
907
updateData.RelExtensionNodes++;
908
string id = node.GetAttribute ("id");
910
AddChildExtensions (conf, module, updateData, path + "/" + id, node.ChildNodes, node.NodeName == "Condition");
914
string[] GetDomains ()
916
string[] dirs = fileDatabase.GetDirectories (AddinCachePath);
917
string[] ids = new string [dirs.Length];
918
for (int n=0; n<dirs.Length; n++)
919
ids [n] = Path.GetFileName (dirs [n]);
923
public string GetUniqueDomainId ()
925
if (lastDomainId != 0) {
927
return lastDomainId.ToString ();
930
foreach (string s in fileDatabase.GetDirectories (AddinCachePath)) {
931
string dn = Path.GetFileName (s);
932
if (dn == GlobalDomain)
935
int n = int.Parse (dn);
936
if (n >= lastDomainId)
937
lastDomainId = n + 1;
941
return lastDomainId.ToString ();
944
internal void ResetBasicCachedData ()
946
allSetupInfos = null;
947
addinSetupInfos = null;
948
rootSetupInfos = null;
951
internal void ResetCachedData ()
953
ResetBasicCachedData ();
955
cachedAddinSetupInfos.Clear ();
956
if (addinEngine != null)
957
addinEngine.ResetCachedData ();
961
public bool AddinDependsOn (string domain, string id1, string id2)
963
Hashtable visited = new Hashtable ();
964
return AddinDependsOn (visited, domain, id1, id2);
967
bool AddinDependsOn (Hashtable visited, string domain, string id1, string id2)
969
if (visited.Contains (id1))
972
visited.Add (id1, id1);
974
Addin addin1 = GetInstalledAddin (domain, id1, false);
976
// We can assumbe that if the add-in is not returned here, it may be a root addin.
980
id2 = Addin.GetIdName (id2);
981
foreach (Dependency dep in addin1.AddinInfo.Dependencies) {
982
AddinDependency adep = dep as AddinDependency;
985
string depid = Addin.GetFullId (addin1.AddinInfo.Namespace, adep.AddinId, null);
988
else if (AddinDependsOn (visited, domain, depid, id2))
994
public void Repair (IProgressStatus monitor, string domain)
996
using (fileDatabase.LockWrite ()) {
998
if (Directory.Exists (AddinCachePath))
999
Directory.Delete (AddinCachePath, true);
1000
if (Directory.Exists (AddinFolderCachePath))
1001
Directory.Delete (AddinFolderCachePath, true);
1002
if (File.Exists (HostIndexFile))
1003
File.Delete (HostIndexFile);
1005
catch (Exception ex) {
1006
monitor.ReportError ("The add-in registry could not be rebuilt. It may be due to lack of write permissions to the directory: " + AddinDbDir, ex);
1009
ResetBasicCachedData ();
1011
Update (monitor, domain);
1014
public void Update (IProgressStatus monitor, string domain)
1016
if (monitor == null)
1017
monitor = new ConsoleProgressStatus (false);
1019
if (RunningSetupProcess)
1022
fatalDatabseError = false;
1024
DateTime tim = DateTime.Now;
1026
RunPendingUninstalls (monitor);
1028
Hashtable installed = new Hashtable ();
1029
bool changesFound = CheckFolders (monitor, domain);
1031
if (monitor.IsCanceled)
1034
if (monitor.LogLevel > 1)
1035
monitor.Log ("Folders checked (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
1038
// Something has changed, the add-ins need to be re-scanned, but it has
1039
// to be done in an external process
1041
if (domain != null) {
1042
using (fileDatabase.LockRead ()) {
1043
foreach (Addin ainfo in InternalGetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) {
1044
installed [ainfo.Id] = ainfo.Id;
1049
RunScannerProcess (monitor);
1053
registry.NotifyDatabaseUpdated ();
1056
if (fatalDatabseError)
1057
monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", null);
1059
// Update the currently loaded add-ins
1060
if (changesFound && domain != null && addinEngine != null && addinEngine.IsInitialized) {
1061
Hashtable newInstalled = new Hashtable ();
1062
foreach (Addin ainfo in GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) {
1063
newInstalled [ainfo.Id] = ainfo.Id;
1066
foreach (string aid in installed.Keys) {
1067
// Always try to unload, event if the add-in was not currently loaded.
1068
// Required since the add-ins has to be marked as 'disabled', to avoid
1069
// extensions from this add-in to be loaded
1070
if (!newInstalled.Contains (aid))
1071
addinEngine.UnloadAddin (aid);
1074
foreach (string aid in newInstalled.Keys) {
1075
if (!installed.Contains (aid)) {
1076
Addin addin = addinEngine.Registry.GetAddin (aid);
1078
addinEngine.ActivateAddin (aid);
1084
void RunPendingUninstalls (IProgressStatus monitor)
1086
bool changesDone = false;
1088
foreach (var adn in Configuration.GetPendingUninstalls ()) {
1089
HashSet<string> files = new HashSet<string> (adn.Files);
1090
if (AddinManager.CheckAssembliesLoaded (files))
1093
if (monitor.LogLevel > 1)
1094
monitor.Log ("Uninstalling " + adn.AddinId);
1096
// Make sure all files can be deleted before doing so
1097
bool canUninstall = true;
1098
foreach (string f in adn.Files) {
1099
if (!File.Exists (f))
1102
File.OpenWrite (f).Close ();
1104
canUninstall = false;
1112
foreach (string f in adn.Files) {
1114
if (File.Exists (f))
1117
canUninstall = false;
1122
Configuration.UnregisterForUninstall (adn.AddinId);
1127
SaveConfiguration ();
1130
void RunScannerProcess (IProgressStatus monitor)
1132
ISetupHandler setup = GetSetupHandler ();
1134
IProgressStatus scanMonitor = monitor;
1135
ArrayList pparams = new ArrayList ();
1140
if (monitor.LogLevel > 1)
1141
monitor.Log ("Looking for addins");
1142
setup.Scan (scanMonitor, registry, null, (string[]) pparams.ToArray (typeof(string)));
1145
catch (Exception ex) {
1146
ProcessFailedException pex = ex as ProcessFailedException;
1148
// Get the last logged operation.
1149
if (pex.LastLog.StartsWith ("scan:")) {
1150
// It crashed while scanning a file. Add the file to the ignore list and try again.
1151
string file = pex.LastLog.Substring (5);
1153
monitor.ReportWarning ("Could not scan file: " + file);
1158
fatalDatabseError = true;
1159
// If the process has crashed, try to do a new scan, this time using verbose log,
1160
// to give the user more information about the origin of the crash.
1161
if (pex != null && !retry) {
1162
monitor.ReportError ("Add-in scan operation failed. The Mono runtime may have encountered an error while trying to load an assembly.", null);
1163
if (monitor.LogLevel <= 1) {
1164
// Re-scan again using verbose log, to make it easy to find the origin of the error.
1166
scanMonitor = new ConsoleProgressStatus (true);
1172
monitor.ReportError ("Add-in scan operation failed", (ex is ProcessFailedException ? null : ex));
1181
bool DatabaseInfrastructureCheck (IProgressStatus monitor)
1183
// Do some sanity check, to make sure the basic database infrastructure can be created
1185
bool hasChanges = false;
1189
if (!Directory.Exists (AddinCachePath)) {
1190
Directory.CreateDirectory (AddinCachePath);
1194
if (!Directory.Exists (AddinFolderCachePath)) {
1195
Directory.CreateDirectory (AddinFolderCachePath);
1199
// Make sure we can write in those folders
1201
Util.CheckWrittableFloder (AddinCachePath);
1202
Util.CheckWrittableFloder (AddinFolderCachePath);
1204
fatalDatabseError = false;
1206
catch (Exception ex) {
1207
monitor.ReportError ("Add-in cache directory could not be created", ex);
1208
fatalDatabseError = true;
1215
internal bool CheckFolders (IProgressStatus monitor, string domain)
1217
using (fileDatabase.LockRead ()) {
1218
AddinScanResult scanResult = new AddinScanResult ();
1219
scanResult.CheckOnly = true;
1220
scanResult.Domain = domain;
1221
InternalScanFolders (monitor, scanResult);
1222
return scanResult.ChangesFound;
1226
internal void ScanFolders (IProgressStatus monitor, string currentDomain, string folderToScan, StringCollection filesToIgnore)
1228
AddinScanResult res = new AddinScanResult ();
1229
res.Domain = currentDomain;
1230
res.AddPathsToIgnore (filesToIgnore);
1231
ScanFolders (monitor, res);
1234
void ScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
1236
IDisposable checkLock = null;
1238
if (scanResult.CheckOnly)
1239
checkLock = fileDatabase.LockRead ();
1241
// All changes are done in a transaction, which won't be committed until
1242
// all files have been updated.
1244
if (!fileDatabase.BeginTransaction ()) {
1245
// The database is already being updated. Can't do anything for now.
1250
EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
1251
ResolveEventHandler resolver = new ResolveEventHandler (OnResolveAddinAssembly);
1255
// Perform the add-in scan
1257
if (!scanResult.CheckOnly) {
1258
AppDomain.CurrentDomain.AssemblyResolve += resolver;
1259
if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
1262
InternalScanFolders (monitor, scanResult);
1264
if (!scanResult.CheckOnly)
1265
fileDatabase.CommitTransaction ();
1268
if (!scanResult.CheckOnly)
1269
fileDatabase.RollbackTransaction ();
1273
currentScanResult = null;
1275
if (scanResult.CheckOnly)
1276
checkLock.Dispose ();
1278
AppDomain.CurrentDomain.AssemblyResolve -= resolver;
1279
if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
1284
void InternalScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
1288
InternalScanFolders2 (monitor, scanResult);
1294
void InternalScanFolders2 (IProgressStatus monitor, AddinScanResult scanResult)
1296
DateTime tim = DateTime.Now;
1298
DatabaseInfrastructureCheck (monitor);
1299
if (monitor.IsCanceled)
1303
scanResult.HostIndex = GetAddinHostIndex ();
1305
catch (Exception ex) {
1306
if (scanResult.CheckOnly) {
1307
scanResult.ChangesFound = true;
1310
monitor.ReportError ("Add-in root index is corrupt. The add-in database will be regenerated.", ex);
1311
scanResult.RegenerateAllData = true;
1314
AddinScanner scanner = new AddinScanner (this, scanResult, monitor);
1316
// Check if any of the previously scanned folders has been deleted
1318
foreach (string file in Directory.GetFiles (AddinFolderCachePath, "*.data")) {
1319
AddinScanFolderInfo folderInfo;
1320
bool res = ReadFolderInfo (monitor, file, out folderInfo);
1321
bool validForDomain = scanResult.Domain == null || folderInfo.Domain == GlobalDomain || folderInfo.Domain == scanResult.Domain;
1322
if (!res || (validForDomain && !fs.DirectoryExists (folderInfo.Folder))) {
1324
// Folder has been deleted. Remove the add-ins it had.
1325
scanner.UpdateDeletedAddins (monitor, folderInfo, scanResult);
1328
// Folder info file corrupt. Regenerate all.
1329
scanResult.ChangesFound = true;
1330
scanResult.RegenerateRelationData = true;
1333
if (!scanResult.CheckOnly)
1334
SafeDelete (monitor, file);
1335
else if (scanResult.ChangesFound)
1340
// Look for changes in the add-in folders
1342
if (registry.StartupDirectory != null)
1343
scanner.ScanFolder (monitor, registry.StartupDirectory, null, scanResult);
1345
if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled))
1348
if (scanResult.Domain == null)
1349
scanner.ScanFolder (monitor, HostsPath, GlobalDomain, scanResult);
1351
if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled))
1354
foreach (string dir in registry.GlobalAddinDirectories) {
1355
if (scanResult.CheckOnly && (scanResult.ChangesFound || monitor.IsCanceled))
1357
scanner.ScanFolderRec (monitor, dir, GlobalDomain, scanResult);
1360
if (scanResult.CheckOnly || !scanResult.ChangesFound)
1363
// Scan the files which have been modified
1365
currentScanResult = scanResult;
1367
foreach (FileToScan file in scanResult.FilesToScan)
1368
scanner.ScanFile (monitor, file.File, file.AddinScanFolderInfo, scanResult);
1372
foreach (AddinScanFolderInfo finfo in scanResult.ModifiedFolderInfos)
1373
SaveFolderInfo (monitor, finfo);
1375
if (monitor.LogLevel > 1)
1376
monitor.Log ("Folders scan completed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
1378
SaveAddinHostIndex ();
1381
if (!scanResult.ChangesFound) {
1382
if (monitor.LogLevel > 1)
1383
monitor.Log ("No changes found");
1389
if (scanResult.RegenerateRelationData) {
1390
if (monitor.LogLevel > 1)
1391
monitor.Log ("Regenerating all add-in relations.");
1392
scanResult.AddinsToUpdate = null;
1393
scanResult.AddinsToUpdateRelations = null;
1396
GenerateAddinExtensionMapsInternal (monitor, scanResult.AddinsToUpdate, scanResult.AddinsToUpdateRelations, scanResult.RemovedAddins);
1398
catch (Exception ex) {
1399
fatalDatabseError = true;
1400
monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", ex);
1403
if (monitor.LogLevel > 1)
1404
monitor.Log ("Add-in relations analyzed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
1406
SaveAddinHostIndex ();
1409
public void ParseAddin (IProgressStatus progressStatus, string domain, string file, string outFile, bool inProcess)
1412
ISetupHandler setup = GetSetupHandler ();
1413
setup.GetAddinDescription (progressStatus, registry, Path.GetFullPath (file), outFile);
1417
using (fileDatabase.LockRead ())
1419
// First of all, check if the file belongs to a registered add-in
1420
AddinScanFolderInfo finfo;
1421
if (GetFolderInfoForPath (progressStatus, Path.GetDirectoryName (file), out finfo) && finfo != null) {
1422
AddinFileInfo afi = finfo.GetAddinFileInfo (file);
1423
if (afi != null && afi.IsAddin) {
1424
AddinDescription adesc;
1425
GetAddinDescription (progressStatus, afi.Domain, afi.AddinId, file, out adesc);
1427
adesc.Save (outFile);
1432
AddinScanResult sr = new AddinScanResult ();
1434
AddinScanner scanner = new AddinScanner (this, sr, progressStatus);
1436
SingleFileAssemblyResolver res = new SingleFileAssemblyResolver (progressStatus, registry, scanner);
1437
ResolveEventHandler resolver = new ResolveEventHandler (res.Resolve);
1439
EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
1442
AppDomain.CurrentDomain.AssemblyResolve += resolver;
1443
if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
1445
AddinDescription desc = scanner.ScanSingleFile (progressStatus, file, sr);
1447
desc.Save (outFile);
1450
AppDomain.CurrentDomain.AssemblyResolve -= resolver;
1451
if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
1456
public string GetFolderDomain (IProgressStatus progressStatus, string path)
1458
AddinScanFolderInfo folderInfo;
1459
if (GetFolderInfoForPath (progressStatus, path, out folderInfo) && folderInfo != null && !folderInfo.SharedFolder)
1460
return folderInfo.Domain;
1462
return UnknownDomain;
1465
Assembly OnResolveAddinAssembly (object s, ResolveEventArgs args)
1467
string file = currentScanResult != null ? currentScanResult.GetAssemblyLocation (args.Name) : null;
1469
return Util.LoadAssemblyForReflection (file);
1471
if (!args.Name.StartsWith ("Mono.Addins.CecilReflector"))
1472
Console.WriteLine ("Assembly not found: " + args.Name);
1477
public string GetFolderConfigFile (string path)
1479
path = Path.GetFullPath (path);
1481
string s = path.Replace ("_", "__");
1482
s = s.Replace (Path.DirectorySeparatorChar, '_');
1483
s = s.Replace (Path.AltDirectorySeparatorChar, '_');
1484
s = s.Replace (Path.VolumeSeparatorChar, '_');
1486
return Path.Combine (AddinFolderCachePath, s + ".data");
1489
internal void UninstallAddin (IProgressStatus monitor, string domain, string addinId, string addinFile, AddinScanResult scanResult)
1491
AddinDescription desc;
1493
if (!GetAddinDescription (monitor, domain, addinId, addinFile, out desc)) {
1494
// If we can't get information about the old assembly, just regenerate all relation data
1495
scanResult.RegenerateRelationData = true;
1499
scanResult.AddRemovedAddin (addinId);
1501
// If the add-in didn't exist, there is nothing left to do
1506
// If the add-in already existed, the dependencies of the old add-in need to be re-analized
1508
Util.AddDependencies (desc, scanResult);
1510
scanResult.HostIndex.RemoveHostData (desc.AddinId, desc.AddinFile);
1512
RemoveAddinDescriptionFile (monitor, desc.FileName);
1515
public bool GetAddinDescription (IProgressStatus monitor, string domain, string addinId, string addinFile, out AddinDescription description)
1517
// If the same add-in is installed in different folders (in the same domain) there will be several .maddin files for it,
1518
// using the suffix "_X" where X is a number > 1 (for example: someAddin,1.0.maddin, someAddin,1.0.maddin_2, someAddin,1.0.maddin_3, ...)
1519
// We need to return the .maddin whose AddinFile matches the one being requested
1521
addinFile = Path.GetFullPath (addinFile);
1523
string baseFile = GetDescriptionPath (domain, addinId);
1524
string file = baseFile;
1525
bool failed = false;
1528
if (!ReadAddinDescription (monitor, file, out description)) {
1529
// Remove the AddinDescription here since it is corrupted.
1530
// Avoids creating alternate versions of corrupted files when later calling SaveDescription.
1531
RemoveAddinDescriptionFile (monitor, file);
1535
if (description == null)
1537
if (Path.GetFullPath (description.AddinFile) == addinFile)
1539
file = baseFile + "_" + (++altNum);
1541
while (fileDatabase.Exists (file));
1543
// File not found. Return false only if there has been any read error.
1548
bool RemoveAddinDescriptionFile (IProgressStatus monitor, string file)
1550
// Removes an add-in description and shifts up alternate instances of the description file
1551
// (so xxx,1.0.maddin_2 will become xxx,1.0.maddin, xxx,1.0.maddin_3 -> xxx,1.0.maddin_2, etc)
1553
if (!SafeDelete (monitor, file))
1557
if (file.EndsWith (".maddin"))
1560
int i = file.LastIndexOf ('_');
1561
dversion = 1 + int.Parse (file.Substring (i + 1));
1562
file = file.Substring (0, i);
1565
while (fileDatabase.Exists (file + "_" + dversion)) {
1566
string newFile = dversion == 2 ? file : file + "_" + (dversion-1);
1568
fileDatabase.Rename (file + "_" + dversion, newFile);
1569
} catch (Exception ex) {
1570
if (monitor.LogLevel > 1) {
1571
monitor.Log ("Could not rename file '" + file + "_" + dversion + "' to '" + newFile + "'");
1572
monitor.Log (ex.ToString ());
1577
string dir = Path.GetDirectoryName (file);
1578
if (fileDatabase.DirectoryIsEmpty (dir))
1579
SafeDeleteDir (monitor, dir);
1581
if (dversion == 2) {
1582
// All versions of the add-in removed.
1583
SafeDeleteDir (monitor, Path.Combine (AddinPrivateDataPath, Path.GetFileNameWithoutExtension (file)));
1589
public bool ReadAddinDescription (IProgressStatus monitor, string file, out AddinDescription description)
1592
description = AddinDescription.ReadBinary (fileDatabase, file);
1593
if (description != null)
1594
description.OwnerDatabase = this;
1597
catch (Exception ex) {
1598
if (monitor == null)
1601
monitor.ReportError ("Could not read folder info file", ex);
1606
public bool SaveDescription (IProgressStatus monitor, AddinDescription desc, string replaceFileName)
1609
if (replaceFileName != null)
1610
desc.SaveBinary (fileDatabase, replaceFileName);
1612
string file = GetDescriptionPath (desc.Domain, desc.AddinId);
1613
string dir = Path.GetDirectoryName (file);
1614
if (!fileDatabase.DirExists (dir))
1615
fileDatabase.CreateDir (dir);
1616
if (fileDatabase.Exists (file)) {
1617
// Another AddinDescription already exists with the same name.
1618
// Create an alternate AddinDescription file
1620
while (fileDatabase.Exists (file + "_" + altNum))
1622
file = file + "_" + altNum;
1624
desc.SaveBinary (fileDatabase, file);
1628
catch (Exception ex) {
1629
monitor.ReportError ("Add-in info file could not be saved", ex);
1634
public bool AddinDescriptionExists (string domain, string addinId)
1636
string file = GetDescriptionPath (domain, addinId);
1637
return fileDatabase.Exists (file);
1640
public bool ReadFolderInfo (IProgressStatus monitor, string file, out AddinScanFolderInfo folderInfo)
1643
folderInfo = AddinScanFolderInfo.Read (fileDatabase, file);
1646
catch (Exception ex) {
1648
monitor.ReportError ("Could not read folder info file", ex);
1653
public bool GetFolderInfoForPath (IProgressStatus monitor, string path, out AddinScanFolderInfo folderInfo)
1656
folderInfo = AddinScanFolderInfo.Read (fileDatabase, AddinFolderCachePath, path);
1659
catch (Exception ex) {
1661
if (monitor != null)
1662
monitor.ReportError ("Could not read folder info file", ex);
1667
public bool SaveFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo)
1670
folderInfo.Write (fileDatabase, AddinFolderCachePath);
1673
catch (Exception ex) {
1674
monitor.ReportError ("Could not write folder info file", ex);
1679
public bool DeleteFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo)
1681
return SafeDelete (monitor, folderInfo.FileName);
1684
public bool SafeDelete (IProgressStatus monitor, string file)
1687
fileDatabase.Delete (file);
1690
catch (Exception ex) {
1691
if (monitor.LogLevel > 1) {
1692
monitor.Log ("Could not delete file: " + file);
1693
monitor.Log (ex.ToString ());
1699
public bool SafeDeleteDir (IProgressStatus monitor, string dir)
1702
fileDatabase.DeleteDir (dir);
1705
catch (Exception ex) {
1706
if (monitor.LogLevel > 1) {
1707
monitor.Log ("Could not delete directory: " + dir);
1708
monitor.Log (ex.ToString ());
1714
AddinHostIndex GetAddinHostIndex ()
1716
if (hostIndex != null)
1719
using (fileDatabase.LockRead ()) {
1720
if (fileDatabase.Exists (HostIndexFile))
1721
hostIndex = AddinHostIndex.Read (fileDatabase, HostIndexFile);
1723
hostIndex = new AddinHostIndex ();
1728
void SaveAddinHostIndex ()
1730
if (hostIndex != null)
1731
hostIndex.Write (fileDatabase, HostIndexFile);
1734
internal string GetUniqueAddinId (string file, string oldId, string ns, string version)
1736
string baseId = "__" + Path.GetFileNameWithoutExtension (file);
1738
if (Path.GetExtension (baseId) == ".addin")
1739
baseId = Path.GetFileNameWithoutExtension (baseId);
1741
string name = baseId;
1742
string id = Addin.GetFullId (ns, name, version);
1744
// If the old Id is already an automatically generated one, reuse it
1745
if (oldId != null && oldId.StartsWith (id))
1749
while (AddinIdExists (id)) {
1750
name = baseId + "_" + n;
1751
id = Addin.GetFullId (ns, name, version);
1757
bool AddinIdExists (string id)
1759
foreach (string d in fileDatabase.GetDirectories (AddinCachePath)) {
1760
if (fileDatabase.Exists (Path.Combine (d, id + ".addin")))
1766
ISetupHandler GetSetupHandler ()
1769
// return new SetupProcess ();
1771
if (fs.RequiresIsolation)
1772
return new SetupDomain ();
1774
return new SetupLocal ();
1777
public void ResetConfiguration ()
1779
if (File.Exists (ConfigFile))
1780
File.Delete (ConfigFile);
1785
DatabaseConfiguration Configuration {
1787
if (config == null) {
1788
using (fileDatabase.LockRead ()) {
1789
if (fileDatabase.Exists (ConfigFile))
1790
config = DatabaseConfiguration.Read (ConfigFile);
1792
config = new DatabaseConfiguration ();
1799
void SaveConfiguration ()
1801
if (config != null) {
1802
using (fileDatabase.LockWrite ()) {
1803
config.Write (ConfigFile);
1809
class SingleFileAssemblyResolver
1811
AddinScanResult scanResult;
1812
AddinScanner scanner;
1813
AddinRegistry registry;
1814
IProgressStatus progressStatus;
1816
public SingleFileAssemblyResolver (IProgressStatus progressStatus, AddinRegistry registry, AddinScanner scanner)
1818
this.scanner = scanner;
1819
this.registry = registry;
1820
this.progressStatus = progressStatus;
1823
public Assembly Resolve (object s, ResolveEventArgs args)
1825
if (scanResult == null) {
1826
scanResult = new AddinScanResult ();
1827
scanResult.LocateAssembliesOnly = true;
1829
if (registry.StartupDirectory != null)
1830
scanner.ScanFolder (progressStatus, registry.StartupDirectory, null, scanResult);
1831
foreach (string dir in registry.GlobalAddinDirectories)
1832
scanner.ScanFolderRec (progressStatus, dir, AddinDatabase.GlobalDomain, scanResult);
1835
string afile = scanResult.GetAssemblyLocation (args.Name);
1837
return Util.LoadAssemblyForReflection (afile);
1845
Dictionary<string, List<AddinDescription>> addins = new Dictionary<string, List<AddinDescription>> ();
1847
public void Add (AddinDescription desc)
1849
string id = Addin.GetFullId (desc.Namespace, desc.LocalId, null);
1850
List<AddinDescription> list;
1851
if (!addins.TryGetValue (id, out list))
1852
addins [id] = list = new List<AddinDescription> ();
1856
List<AddinDescription> FindDescriptions (string domain, string fullid)
1858
// Returns all registered add-ins which are compatible with the provided
1859
// fullid. Compatible means that the id is the same and the version is within
1860
// the range of compatible versions of the add-in.
1862
var res = new List<AddinDescription> ();
1863
string id = Addin.GetIdName (fullid);
1864
List<AddinDescription> list;
1865
if (!addins.TryGetValue (id, out list))
1867
string version = Addin.GetIdVersion (fullid);
1868
foreach (AddinDescription desc in list) {
1869
if ((desc.Domain == domain || domain == AddinDatabase.GlobalDomain) && desc.SupportsVersion (version))
1875
public IEnumerable<string> GetMissingDependencies (AddinDescription desc, ModuleDescription mod)
1877
foreach (Dependency dep in mod.Dependencies) {
1878
AddinDependency adep = dep as AddinDependency;
1881
var descs = FindDescriptions (desc.Domain, adep.FullAddinId);
1882
if (descs.Count == 0)
1883
yield return adep.FullAddinId;
1887
public AddinDescription GetSimilarExistingAddin (AddinDescription conf, string addinId)
1889
string domain = conf.Domain;
1890
List<AddinDescription> list;
1891
if (!addins.TryGetValue (Addin.GetIdName (addinId), out list))
1893
string version = Addin.GetIdVersion (addinId);
1894
foreach (AddinDescription desc in list) {
1895
if ((desc.Domain == domain || domain == AddinDatabase.GlobalDomain) && !desc.SupportsVersion (version))
1901
public List<AddinDescription> GetSortedAddins ()
1903
var inserted = new HashSet<string> ();
1904
var lists = new Dictionary<string,List<AddinDescription>> ();
1906
foreach (List<AddinDescription> dlist in addins.Values) {
1907
foreach (AddinDescription desc in dlist)
1908
InsertSortedAddin (inserted, lists, desc);
1911
// Merge all domain lists into a single list.
1912
// Make sure the global domain is inserted the last
1914
List<AddinDescription> global;
1915
lists.TryGetValue (AddinDatabase.GlobalDomain, out global);
1916
lists.Remove (AddinDatabase.GlobalDomain);
1918
List<AddinDescription> sortedAddins = new List<AddinDescription> ();
1919
foreach (var dl in lists.Values) {
1920
sortedAddins.AddRange (dl);
1923
sortedAddins.AddRange (global);
1924
return sortedAddins;
1927
void InsertSortedAddin (HashSet<string> inserted, Dictionary<string,List<AddinDescription>> lists, AddinDescription desc)
1929
string sid = desc.AddinId + " " + desc.Domain;
1930
if (!inserted.Add (sid))
1933
foreach (ModuleDescription mod in desc.AllModules) {
1934
foreach (Dependency dep in mod.Dependencies) {
1935
AddinDependency adep = dep as AddinDependency;
1938
var descs = FindDescriptions (desc.Domain, adep.FullAddinId);
1939
if (descs.Count > 0) {
1940
foreach (AddinDescription sd in descs)
1941
InsertSortedAddin (inserted, lists, sd);
1945
List<AddinDescription> list;
1946
if (!lists.TryGetValue (desc.Domain, out list))
1947
lists [desc.Domain] = list = new List<AddinDescription> ();
1953
// Keep in sync with AddinSearchFlags
1954
enum AddinSearchFlagsInternal
1957
IncludeRoots = 1 << 1,
1958
IncludeAll = IncludeAddins | IncludeRoots,
1959
LatestVersionsOnly = 1 << 3,
1960
ExcludePendingUninstall = 1 << 4