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;
39
namespace Mono.Addins.Database
43
const string VersionTag = "000";
45
ArrayList addinSetupInfos;
46
internal static bool RunningSetupProcess;
47
bool fatalDatabseError;
48
Hashtable cachedAddinSetupInfos = new Hashtable ();
49
AddinScanResult currentScanResult;
50
AddinHostIndex hostIndex;
51
FileDatabase fileDatabase;
53
DatabaseConfiguration config = null;
54
AddinRegistry registry;
56
public AddinDatabase (AddinRegistry registry)
58
this.registry = registry;
59
addinDbDir = Path.Combine (registry.RegistryPath, "addin-db-" + VersionTag);
60
fileDatabase = new FileDatabase (AddinDbDir);
64
get { return addinDbDir; }
67
public string AddinCachePath {
68
get { return Path.Combine (AddinDbDir, "addin-data"); }
71
public string AddinFolderCachePath {
72
get { return Path.Combine (AddinDbDir, "addin-dir-data"); }
75
public string AddinPrivateDataPath {
76
get { return Path.Combine (AddinDbDir, "addin-priv-data"); }
79
string HostIndexFile {
80
get { return Path.Combine (AddinDbDir, "host-index"); }
84
get { return Path.Combine (AddinDbDir, "config.xml"); }
87
internal bool IsGlobalRegistry {
89
return registry.RegistryPath == AddinRegistry.GlobalRegistryPath;
95
if (Directory.Exists (AddinCachePath))
96
Directory.Delete (AddinCachePath, true);
97
if (Directory.Exists (AddinFolderCachePath))
98
Directory.Delete (AddinFolderCachePath, true);
101
public ExtensionNodeSet FindNodeSet (string addinId, string id)
103
return FindNodeSet (addinId, id, new Hashtable ());
106
ExtensionNodeSet FindNodeSet (string addinId, string id, Hashtable visited)
108
if (visited.Contains (addinId))
110
visited.Add (addinId, addinId);
111
Addin addin = GetInstalledAddin (addinId, true, false);
113
foreach (Addin root in GetAddinRoots ()) {
114
if (root.Id == addinId) {
122
AddinDescription desc = addin.Description;
125
foreach (ExtensionNodeSet nset in desc.ExtensionNodeSets)
129
// Not found in the add-in. Look on add-ins on which it depends
131
foreach (Dependency dep in desc.MainModule.Dependencies) {
132
AddinDependency adep = dep as AddinDependency;
133
if (adep == null) continue;
135
string aid = Addin.GetFullId (desc.Namespace, adep.AddinId, adep.Version);
136
ExtensionNodeSet nset = FindNodeSet (aid, id, visited);
143
public AddinDescription GetDescription (string id)
146
IDisposable dblock = fileDatabase.LockRead ();
148
string path = GetDescriptionPath (id);
149
AddinDescription desc = AddinDescription.ReadBinary (fileDatabase, path);
151
desc.OwnerDatabase = this;
154
catch (FileNotFoundException) {
155
throw new InvalidOperationException ("Add-in not found: " + id);
161
public ArrayList GetInstalledAddins ()
163
if (addinSetupInfos != null)
164
return addinSetupInfos;
167
using (fileDatabase.LockRead ()) {
168
return InternalGetInstalledAddins ();
172
public ArrayList GetAddinRoots ()
174
ArrayList list = new ArrayList ();
175
foreach (string file in fileDatabase.GetDirectoryFiles (AddinCachePath, "*.mroot")) {
176
list.Add (new Addin (this, file));
181
ArrayList InternalGetInstalledAddins ()
183
if (addinSetupInfos != null)
184
return addinSetupInfos;
186
addinSetupInfos = new ArrayList ();
188
foreach (string file in fileDatabase.GetDirectoryFiles (AddinCachePath, "*.maddin")) {
189
addinSetupInfos.Add (new Addin (this, file));
191
return addinSetupInfos;
194
public Addin GetInstalledAddin (string id)
196
return GetInstalledAddin (id, false, false);
199
public Addin GetInstalledAddin (string id, bool exactVersionMatch)
201
return GetInstalledAddin (id, exactVersionMatch, false);
204
public Addin GetInstalledAddin (string id, bool exactVersionMatch, bool enabledOnly)
207
object ob = cachedAddinSetupInfos [id];
211
if (!enabledOnly || sinfo.Enabled)
213
if (exactVersionMatch)
216
else if (enabledOnly)
217
// Ignore the 'not installed' flag when disabled add-ins are allowed
223
using (fileDatabase.LockRead ())
225
string path = GetDescriptionPath (id);
226
if (sinfo == null && fileDatabase.Exists (path)) {
227
sinfo = new Addin (this, path);
228
cachedAddinSetupInfos [id] = sinfo;
229
if (!enabledOnly || sinfo.Enabled)
231
if (exactVersionMatch) {
232
// Cache lookups with negative result
233
cachedAddinSetupInfos [id] = this;
238
// Exact version not found. Look for a compatible version
239
if (!exactVersionMatch) {
241
string version, name, bestVersion = null;
242
Addin.GetIdParts (id, out name, out version);
244
// FIXME: Not very efficient, will load all descriptions
245
foreach (Addin ia in InternalGetInstalledAddins ())
247
if (Addin.GetIdName (ia.Id) == name &&
248
(!enabledOnly || ia.Enabled) &&
249
(version.Length == 0 || ia.SupportsVersion (version)) &&
250
(bestVersion == null || Addin.CompareVersions (bestVersion, ia.Version) > 0))
252
bestVersion = ia.Version;
257
cachedAddinSetupInfos [id] = sinfo;
262
// Cache lookups with negative result
263
// Ignore the 'not installed' flag when disabled add-ins are allowed
265
cachedAddinSetupInfos [id] = this;
270
public Addin GetInstalledAddin (string id, string version)
272
foreach (Addin ia in GetInstalledAddins ()) {
273
if ((id == null || ia.Id == id) && (version == null || ia.Version == version))
279
public void Shutdown ()
281
addinSetupInfos = null;
284
public Addin GetAddinForHostAssembly (string assemblyLocation)
289
object ob = cachedAddinSetupInfos [assemblyLocation];
298
AddinHostIndex index = GetAddinHostIndex ();
299
string addin, addinFile;
300
if (index.GetAddinForAssembly (assemblyLocation, out addin, out addinFile)) {
301
ainfo = new Addin (this, addin, addinFile);
302
cachedAddinSetupInfos [assemblyLocation] = ainfo;
309
public bool IsAddinEnabled (string id)
311
Addin ainfo = GetInstalledAddin (id);
313
return ainfo.Enabled;
318
internal bool IsAddinEnabled (string id, bool exactVersionMatch)
320
if (!exactVersionMatch)
321
return IsAddinEnabled (id);
322
Addin ainfo = GetInstalledAddin (id, exactVersionMatch, false);
325
return Configuration.IsEnabled (id, ainfo.AddinInfo.EnabledByDefault);
328
public void EnableAddin (string id)
330
EnableAddin (id, true);
333
internal void EnableAddin (string id, bool exactVersionMatch)
335
Addin ainfo = GetInstalledAddin (id, exactVersionMatch, false);
337
// It may be an add-in root
340
if (IsAddinEnabled (id))
343
// Enable required add-ins
345
foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
346
if (dep is AddinDependency) {
347
AddinDependency adep = dep as AddinDependency;
348
string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
349
EnableAddin (adepid, false);
353
Configuration.SetStatus (id, true, ainfo.AddinInfo.EnabledByDefault);
354
SaveConfiguration ();
356
if (AddinManager.IsInitialized && AddinManager.Registry.RegistryPath == registry.RegistryPath)
357
AddinManager.SessionService.ActivateAddin (id);
360
public void DisableAddin (string id)
362
Addin ai = GetInstalledAddin (id, true);
364
throw new InvalidOperationException ("Add-in '" + id + "' not installed.");
366
if (!IsAddinEnabled (id))
369
Configuration.SetStatus (id, false, ai.AddinInfo.EnabledByDefault);
370
SaveConfiguration ();
372
// Disable all add-ins which depend on it
375
string idName = Addin.GetIdName (id);
377
foreach (Addin ainfo in GetInstalledAddins ()) {
378
foreach (Dependency dep in ainfo.AddinInfo.Dependencies) {
379
AddinDependency adep = dep as AddinDependency;
383
string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, null);
384
if (adepid != idName)
387
// The add-in that has been disabled, might be a requeriment of this one, or maybe not
388
// if there is an older version available. Check it now.
390
adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version);
391
Addin adepinfo = GetInstalledAddin (adepid, false, true);
393
if (adepinfo == null) {
394
DisableAddin (ainfo.Id);
401
// If something goes wrong, enable the add-in again
402
Configuration.SetStatus (id, true, ai.AddinInfo.EnabledByDefault);
403
SaveConfiguration ();
407
if (AddinManager.IsInitialized && AddinManager.Registry.RegistryPath == registry.RegistryPath)
408
AddinManager.SessionService.UnloadAddin (id);
411
internal string GetDescriptionPath (string id)
413
return Path.Combine (AddinCachePath, id + ".maddin");
416
void InternalCheck ()
418
// If the database is broken, don't try to regenerate it at every check.
419
if (fatalDatabseError)
423
using (fileDatabase.LockRead ()) {
424
if (!Directory.Exists (AddinCachePath)) {
432
void GenerateAddinExtensionMapsInternal (IProgressStatus monitor, ArrayList addinsToUpdate, ArrayList removedAddins)
434
AddinUpdateData updateData = new AddinUpdateData (this);
437
cachedAddinSetupInfos.Clear ();
439
// Collect all information
441
Hashtable addinHash = new Hashtable ();
443
if (monitor.LogLevel > 1)
444
monitor.Log ("Generating add-in extension maps");
446
Hashtable changedAddins = null;
447
ArrayList descriptionsToSave = new ArrayList ();
448
ArrayList files = new ArrayList ();
450
bool partialGeneration = addinsToUpdate != null;
452
// Get the files to be updated
454
if (partialGeneration) {
455
changedAddins = new Hashtable ();
456
foreach (string s in addinsToUpdate) {
457
changedAddins [s] = s;
458
string mp = GetDescriptionPath (s);
459
if (fileDatabase.Exists (mp))
462
files.AddRange (fileDatabase.GetObjectSharedFiles (this.AddinCachePath, s, ".mroot"));
464
foreach (string s in removedAddins)
465
changedAddins [s] = s;
468
files.AddRange (fileDatabase.GetDirectoryFiles (AddinCachePath, "*.maddin"));
469
files.AddRange (fileDatabase.GetDirectoryFiles (AddinCachePath, "*.mroot"));
472
// Load the descriptions.
473
foreach (string file in files) {
475
AddinDescription conf;
476
if (!ReadAddinDescription (monitor, file, out conf)) {
477
SafeDelete (monitor, file);
481
// If the original file does not exist, the description can be deleted
482
if (!File.Exists (conf.AddinFile)) {
483
SafeDelete (monitor, file);
487
// Remove old data from the description. If changedAddins==null, removes all data.
488
// Otherwise, removes data only from the addins in the table.
490
conf.UnmergeExternalData (changedAddins);
491
descriptionsToSave.Add (conf);
493
// Register extension points and node sets from root add-ins
495
foreach (ExtensionPoint ep in conf.ExtensionPoints)
496
updateData.RegisterAddinRootExtensionPoint (conf, ep);
497
foreach (ExtensionNodeSet ns in conf.ExtensionNodeSets)
498
updateData.RegisterAddinRootNodeSet (conf, ns);
501
addinHash [conf.AddinId] = conf;
504
foreach (AddinDescription conf in addinHash.Values) {
505
CollectExtensionData (conf, updateData);
508
updateData.ResolveExtensions (monitor, addinHash);
510
// Update the extension points defined by this add-in
511
foreach (ExtensionPoint ep in updateData.GetUnresolvedExtensionPoints ()) {
512
AddinDescription am = (AddinDescription) addinHash [ep.RootAddin];
513
ExtensionPoint amep = am.ExtensionPoints [ep.Path];
515
amep.MergeWith (am.AddinId, ep);
516
amep.RootAddin = ep.RootAddin;
520
// Now update the node sets
521
foreach (ExtensionPoint ep in updateData.GetUnresolvedExtensionSets ()) {
522
AddinDescription am = (AddinDescription) addinHash [ep.RootAddin];
523
ExtensionNodeSet nset = am.ExtensionNodeSets [ep.Path];
525
nset.MergeWith (am.AddinId, ep.NodeSet);
529
foreach (AddinDescription conf in descriptionsToSave)
530
conf.SaveBinary (fileDatabase);
532
if (monitor.LogLevel > 1) {
533
monitor.Log ("Addin relation map generated.");
534
monitor.Log (" Addins Updated: " + descriptionsToSave.Count);
535
monitor.Log (" Extension points: " + updateData.RelExtensionPoints);
536
monitor.Log (" Extensions: " + updateData.RelExtensions);
537
monitor.Log (" Extension nodes: " + updateData.RelExtensionNodes);
538
monitor.Log (" Node sets: " + updateData.RelNodeSetTypes);
542
// Collects extension data in a hash table. The key is the path, the value is a list
543
// of add-ins ids that extend that path
545
void CollectExtensionData (AddinDescription conf, AddinUpdateData updateData)
547
foreach (ExtensionNodeSet nset in conf.ExtensionNodeSets) {
549
updateData.RegisterNodeSet (conf, nset);
550
updateData.RelNodeSetTypes++;
551
} catch (Exception ex) {
552
throw new InvalidOperationException ("Error reading node set: " + nset.Id, ex);
556
foreach (ExtensionPoint ep in conf.ExtensionPoints) {
558
updateData.RegisterExtensionPoint (conf, ep);
559
updateData.RelExtensionPoints++;
560
} catch (Exception ex) {
561
throw new InvalidOperationException ("Error reading extension point: " + ep.Path, ex);
565
foreach (ModuleDescription module in conf.AllModules) {
566
foreach (Extension ext in module.Extensions) {
567
updateData.RelExtensions++;
568
updateData.RegisterExtension (conf, module, ext);
569
AddChildExtensions (conf, module, updateData, ext.Path, ext.ExtensionNodes, false);
574
void AddChildExtensions (AddinDescription conf, ModuleDescription module, AddinUpdateData updateData, string path, ExtensionNodeDescriptionCollection nodes, bool conditionChildren)
576
// Don't register conditions as extension nodes.
577
if (!conditionChildren)
578
updateData.RegisterExtension (conf, module, path);
580
foreach (ExtensionNodeDescription node in nodes) {
581
if (node.NodeName == "ComplexCondition")
583
updateData.RelExtensionNodes++;
584
string id = node.GetAttribute ("id");
586
AddChildExtensions (conf, module, updateData, path + "/" + id, node.ChildNodes, node.NodeName == "Condition");
590
internal void ResetCachedData ()
592
addinSetupInfos = null;
594
cachedAddinSetupInfos.Clear ();
598
public bool AddinDependsOn (string id1, string id2)
600
Addin addin1 = GetInstalledAddin (id1, false);
602
// We can assumbe that if the add-in is not returned here, it may be a root addin.
606
id2 = Addin.GetIdName (id2);
607
foreach (Dependency dep in addin1.AddinInfo.Dependencies) {
608
AddinDependency adep = dep as AddinDependency;
611
string depid = Addin.GetFullId (addin1.AddinInfo.Namespace, adep.AddinId, null);
614
else if (AddinDependsOn (depid, id2))
620
public void Repair (IProgressStatus monitor)
622
using (fileDatabase.LockWrite ()) {
624
if (Directory.Exists (AddinCachePath))
625
Directory.Delete (AddinCachePath, true);
626
if (Directory.Exists (AddinFolderCachePath))
627
Directory.Delete (AddinFolderCachePath, true);
628
if (File.Exists (HostIndexFile))
629
File.Delete (HostIndexFile);
631
catch (Exception ex) {
632
monitor.ReportError ("The add-in registry could not be rebuilt. It may be due to lack of write permissions to the directory: " + AddinDbDir, ex);
638
public void Update (IProgressStatus monitor)
641
monitor = new ConsoleProgressStatus (false);
643
if (RunningSetupProcess)
646
fatalDatabseError = false;
648
DateTime tim = DateTime.Now;
650
Hashtable installed = new Hashtable ();
651
bool changesFound = CheckFolders (monitor);
653
if (monitor.IsCanceled)
656
if (monitor.LogLevel > 1)
657
monitor.Log ("Folders checked (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
660
// Something has changed, the add-ins need to be re-scanned, but it has
661
// to be done in an external process
663
using (fileDatabase.LockRead ()) {
664
foreach (Addin ainfo in InternalGetInstalledAddins ()) {
665
installed [ainfo.Id] = ainfo.Id;
669
RunScannerProcess (monitor);
674
if (fatalDatabseError)
675
monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", null);
677
// Update the currently loaded add-ins
678
if (changesFound && AddinManager.IsInitialized && AddinManager.Registry.RegistryPath == registry.RegistryPath) {
679
Hashtable newInstalled = new Hashtable ();
680
foreach (Addin ainfo in GetInstalledAddins ()) {
681
newInstalled [ainfo.Id] = ainfo.Id;
684
foreach (string aid in installed.Keys) {
685
if (!newInstalled.Contains (aid))
686
AddinManager.SessionService.UnloadAddin (aid);
689
foreach (string aid in newInstalled.Keys) {
690
if (!installed.Contains (aid)) {
691
AddinManager.SessionService.ActivateAddin (aid);
697
void RunScannerProcess (IProgressStatus monitor)
699
IProgressStatus scanMonitor = monitor;
700
ArrayList pparams = new ArrayList ();
701
pparams.Add (null); // scan folder
706
if (monitor.LogLevel > 1)
707
monitor.Log ("Looking for addins");
708
SetupProcess.ExecuteCommand (scanMonitor, registry.RegistryPath, AddinManager.StartupDirectory, "scan", (string[]) pparams.ToArray (typeof(string)));
711
catch (Exception ex) {
712
ProcessFailedException pex = ex as ProcessFailedException;
714
// Get the last logged operation.
715
if (pex.LastLog.StartsWith ("scan:")) {
716
// It crashed while scanning a file. Add the file to the ignore list and try again.
717
string file = pex.LastLog.Substring (5);
719
monitor.ReportWarning ("Could not scan file: " + file);
724
fatalDatabseError = true;
725
// If the process has crashed, try to do a new scan, this time using verbose log,
726
// to give the user more information about the origin of the crash.
727
if (pex != null && !retry) {
728
monitor.ReportError ("Add-in scan operation failed. The Mono runtime may have encountered an error while trying to load an assembly.", null);
729
if (monitor.LogLevel <= 1) {
730
// Re-scan again using verbose log, to make it easy to find the origin of the error.
732
scanMonitor = new ConsoleProgressStatus (true);
738
monitor.ReportError ("Add-in scan operation failed", (ex is ProcessFailedException ? null : ex));
747
bool DatabaseInfrastructureCheck (IProgressStatus monitor)
749
// Do some sanity check, to make sure the basic database infrastructure can be created
751
bool hasChanges = false;
755
if (!Directory.Exists (AddinCachePath)) {
756
Directory.CreateDirectory (AddinCachePath);
760
if (!Directory.Exists (AddinFolderCachePath)) {
761
Directory.CreateDirectory (AddinFolderCachePath);
765
// Make sure we can write in those folders
767
Util.CheckWrittableFloder (AddinCachePath);
768
Util.CheckWrittableFloder (AddinFolderCachePath);
770
fatalDatabseError = false;
772
catch (Exception ex) {
773
monitor.ReportError ("Add-in cache directory could not be created", ex);
774
fatalDatabseError = true;
781
internal bool CheckFolders (IProgressStatus monitor)
783
using (fileDatabase.LockRead ()) {
784
AddinScanResult scanResult = new AddinScanResult ();
785
scanResult.CheckOnly = true;
786
InternalScanFolders (monitor, scanResult);
787
return scanResult.ChangesFound;
791
internal void ScanFolders (IProgressStatus monitor, string folderToScan, StringCollection filesToIgnore)
793
AddinScanResult res = new AddinScanResult ();
794
res.FilesToIgnore = filesToIgnore;
795
ScanFolders (monitor, res);
798
internal void ScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
800
IDisposable checkLock = null;
802
if (scanResult.CheckOnly)
803
checkLock = fileDatabase.LockRead ();
805
// All changes are done in a transaction, which won't be committed until
806
// all files have been updated.
808
if (!fileDatabase.BeginTransaction ()) {
809
// The database is already being updated. Can't do anything for now.
814
EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
815
ResolveEventHandler resolver = new ResolveEventHandler (OnResolveAddinAssembly);
819
// Perform the add-in scan
821
if (!scanResult.CheckOnly) {
822
AppDomain.CurrentDomain.AssemblyResolve += resolver;
823
if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
826
InternalScanFolders (monitor, scanResult);
828
if (!scanResult.CheckOnly)
829
fileDatabase.CommitTransaction ();
832
if (!scanResult.CheckOnly)
833
fileDatabase.RollbackTransaction ();
837
currentScanResult = null;
839
if (scanResult.CheckOnly)
840
checkLock.Dispose ();
842
AppDomain.CurrentDomain.AssemblyResolve -= resolver;
843
if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
848
void InternalScanFolders (IProgressStatus monitor, AddinScanResult scanResult)
850
DateTime tim = DateTime.Now;
852
DatabaseInfrastructureCheck (monitor);
853
if (monitor.IsCanceled)
857
scanResult.HostIndex = GetAddinHostIndex ();
859
catch (Exception ex) {
860
if (scanResult.CheckOnly) {
861
scanResult.ChangesFound = true;
864
monitor.ReportError ("Add-in root index is corrupt. The add-in database will be regenerated.", ex);
865
scanResult.RegenerateAllData = true;
868
AddinScanner scanner = new AddinScanner (this);
870
// Check if any of the previously scanned folders has been deleted
872
foreach (string file in Directory.GetFiles (AddinFolderCachePath, "*.data")) {
873
AddinScanFolderInfo folderInfo;
874
bool res = ReadFolderInfo (monitor, file, out folderInfo);
875
if (!res || !Directory.Exists (folderInfo.Folder)) {
877
// Folder has been deleted. Remove the add-ins it had.
878
scanner.UpdateDeletedAddins (monitor, folderInfo, scanResult);
881
// Folder info file corrupt. Regenerate all.
882
scanResult.ChangesFound = true;
883
scanResult.RegenerateRelationData = true;
886
if (!scanResult.CheckOnly)
887
SafeDelete (monitor, file);
893
// Look for changes in the add-in folders
895
foreach (string dir in registry.AddinDirectories) {
896
if (dir == registry.DefaultAddinsFolder)
897
scanner.ScanFolderRec (monitor, dir, scanResult);
899
scanner.ScanFolder (monitor, dir, scanResult);
900
if (scanResult.CheckOnly) {
901
if (scanResult.ChangesFound || monitor.IsCanceled)
906
if (scanResult.CheckOnly)
909
// Scan the files which have been modified
911
currentScanResult = scanResult;
913
foreach (FileToScan file in scanResult.FilesToScan)
914
scanner.ScanFile (monitor, file.File, file.AddinScanFolderInfo, scanResult);
918
foreach (AddinScanFolderInfo finfo in scanResult.ModifiedFolderInfos)
919
SaveFolderInfo (monitor, finfo);
921
if (monitor.LogLevel > 1)
922
monitor.Log ("Folders scan completed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
924
SaveAddinHostIndex ();
927
if (!scanResult.ChangesFound) {
928
if (monitor.LogLevel > 1)
929
monitor.Log ("No changes found");
935
if (scanResult.RegenerateRelationData)
936
scanResult.AddinsToUpdateRelations = null;
938
GenerateAddinExtensionMapsInternal (monitor, scanResult.AddinsToUpdateRelations, scanResult.RemovedAddins);
940
catch (Exception ex) {
941
fatalDatabseError = true;
942
monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", ex);
945
if (monitor.LogLevel > 1)
946
monitor.Log ("Add-in relations analyzed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)");
948
SaveAddinHostIndex ();
951
public void ParseAddin (IProgressStatus progressStatus, string file, string outFile, bool inProcess)
954
SetupProcess.ExecuteCommand (progressStatus, registry.RegistryPath, AddinManager.StartupDirectory, "get-desc", Path.GetFullPath (file), outFile);
958
using (fileDatabase.LockRead ())
960
// First of all, check if the file belongs to a registered add-in
961
AddinScanFolderInfo finfo;
962
if (GetFolderInfoForPath (progressStatus, Path.GetDirectoryName (file), out finfo) && finfo != null) {
963
AddinFileInfo afi = finfo.GetAddinFileInfo (file);
964
if (afi != null && afi.AddinId != null) {
965
AddinDescription adesc;
967
GetHostDescription (progressStatus, afi.AddinId, file, out adesc);
969
GetAddinDescription (progressStatus, afi.AddinId, out adesc);
971
adesc.Save (outFile);
977
AddinScanner scanner = new AddinScanner (this);
979
SingleFileAssemblyResolver res = new SingleFileAssemblyResolver (progressStatus, registry, scanner);
980
ResolveEventHandler resolver = new ResolveEventHandler (res.Resolve);
982
EventInfo einfo = typeof(AppDomain).GetEvent ("ReflectionOnlyAssemblyResolve");
985
AppDomain.CurrentDomain.AssemblyResolve += resolver;
986
if (einfo != null) einfo.AddEventHandler (AppDomain.CurrentDomain, resolver);
988
AddinDescription desc = scanner.ScanSingleFile (progressStatus, file, new AddinScanResult ());
993
AppDomain.CurrentDomain.AssemblyResolve -= resolver;
994
if (einfo != null) einfo.RemoveEventHandler (AppDomain.CurrentDomain, resolver);
999
Assembly OnResolveAddinAssembly (object s, ResolveEventArgs args)
1001
string file = currentScanResult.GetAssemblyLocation (args.Name);
1003
return Util.LoadAssemblyForReflection (file);
1008
public string GetFolderConfigFile (string path)
1010
path = Path.GetFullPath (path);
1012
string s = path.Replace ("_", "__");
1013
s = s.Replace (Path.DirectorySeparatorChar, '_');
1014
s = s.Replace (Path.AltDirectorySeparatorChar, '_');
1015
s = s.Replace (Path.VolumeSeparatorChar, '_');
1017
return Path.Combine (AddinFolderCachePath, s + ".data");
1020
internal void UninstallAddin (IProgressStatus monitor, string addinId, AddinScanResult scanResult)
1022
scanResult.AddRemovedAddin (addinId);
1023
string file = GetDescriptionPath (addinId);
1024
DeleteAddin (monitor, file, scanResult);
1027
internal void UninstallRootAddin (IProgressStatus monitor, string addinId, string addinFile, AddinScanResult scanResult)
1029
string file = fileDatabase.GetSharedObjectFile (AddinCachePath, addinId, ".mroot", addinFile);
1030
DeleteAddin (monitor, file, scanResult);
1033
void DeleteAddin (IProgressStatus monitor, string file, AddinScanResult scanResult)
1035
if (!fileDatabase.Exists (file))
1038
// Add-in already existed. The dependencies of the old add-in need to be re-analized
1040
AddinDescription desc;
1041
if (ReadAddinDescription (monitor, file, out desc)) {
1042
Util.AddDependencies (desc, scanResult);
1044
scanResult.HostIndex.RemoveHostData (desc.AddinId, desc.AddinFile);
1046
// If we can't get information about the old assembly, just regenerate all relation data
1047
scanResult.RegenerateRelationData = true;
1049
SafeDelete (monitor, file);
1050
SafeDeleteDir (monitor, Path.Combine (AddinPrivateDataPath, Path.GetFileNameWithoutExtension (file)));
1053
public bool GetHostDescription (IProgressStatus monitor, string addinId, string fileName, out AddinDescription description)
1056
description = AddinDescription.ReadHostBinary (fileDatabase, AddinCachePath, addinId, fileName);
1057
if (description != null)
1058
description.OwnerDatabase = this;
1061
catch (Exception ex) {
1062
if (monitor == null)
1065
monitor.ReportError ("Could not read folder info file", ex);
1070
public bool GetAddinDescription (IProgressStatus monitor, string addinId, out AddinDescription description)
1072
string file = GetDescriptionPath (addinId);
1073
return ReadAddinDescription (monitor, file, out description);
1076
public bool ReadAddinDescription (IProgressStatus monitor, string file, out AddinDescription description)
1079
description = AddinDescription.ReadBinary (fileDatabase, file);
1080
if (description != null)
1081
description.OwnerDatabase = this;
1084
catch (Exception ex) {
1085
if (monitor == null)
1088
monitor.ReportError ("Could not read folder info file", ex);
1093
public bool SaveDescription (IProgressStatus monitor, AddinDescription desc, string replaceFileName)
1096
if (replaceFileName != null)
1097
desc.SaveBinary (fileDatabase, replaceFileName);
1098
else if (desc.IsRoot)
1099
desc.SaveHostBinary (fileDatabase, AddinCachePath);
1101
desc.SaveBinary (fileDatabase, GetDescriptionPath (desc.AddinId));
1104
catch (Exception ex) {
1105
monitor.ReportError ("Add-in info file could not be saved", ex);
1110
public bool AddinDescriptionExists (string addinId)
1112
return fileDatabase.Exists (GetDescriptionPath (addinId));
1115
public bool HostDescriptionExists (string addinId, string sourceFile)
1117
return fileDatabase.SharedObjectExists (AddinCachePath, addinId, ".mroot", sourceFile);
1120
public bool ReadFolderInfo (IProgressStatus monitor, string file, out AddinScanFolderInfo folderInfo)
1123
folderInfo = AddinScanFolderInfo.Read (fileDatabase, file);
1126
catch (Exception ex) {
1128
monitor.ReportError ("Could not read folder info file", ex);
1133
public bool GetFolderInfoForPath (IProgressStatus monitor, string path, out AddinScanFolderInfo folderInfo)
1136
folderInfo = AddinScanFolderInfo.Read (fileDatabase, AddinFolderCachePath, path);
1139
catch (Exception ex) {
1141
monitor.ReportError ("Could not read folder info file", ex);
1146
public bool SaveFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo)
1149
folderInfo.Write (fileDatabase, AddinFolderCachePath);
1152
catch (Exception ex) {
1153
monitor.ReportError ("Could not write folder info file", ex);
1158
public bool DeleteFolderInfo (IProgressStatus monitor, AddinScanFolderInfo folderInfo)
1160
return SafeDelete (monitor, folderInfo.FileName);
1163
public bool SafeDelete (IProgressStatus monitor, string file)
1166
fileDatabase.Delete (file);
1169
catch (Exception ex) {
1170
if (monitor.LogLevel > 1) {
1171
monitor.Log ("Could not delete file: " + file);
1172
monitor.Log (ex.ToString ());
1178
public bool SafeDeleteDir (IProgressStatus monitor, string dir)
1181
fileDatabase.DeleteDir (dir);
1184
catch (Exception ex) {
1185
if (monitor.LogLevel > 1) {
1186
monitor.Log ("Could not delete directory: " + dir);
1187
monitor.Log (ex.ToString ());
1193
AddinHostIndex GetAddinHostIndex ()
1195
if (hostIndex != null)
1198
using (fileDatabase.LockRead ()) {
1199
if (fileDatabase.Exists (HostIndexFile))
1200
hostIndex = AddinHostIndex.Read (fileDatabase, HostIndexFile);
1202
hostIndex = new AddinHostIndex ();
1207
void SaveAddinHostIndex ()
1209
if (hostIndex != null)
1210
hostIndex.Write (fileDatabase, HostIndexFile);
1213
internal string GetUniqueAddinId (string file, string oldId, string ns, string version)
1215
string baseId = "__" + Path.GetFileNameWithoutExtension (file);
1217
if (Path.GetExtension (baseId) == ".addin")
1218
baseId = Path.GetFileNameWithoutExtension (baseId);
1220
string name = baseId;
1221
string id = Addin.GetFullId (ns, name, version);
1223
// If the old Id is already an automatically generated one, reuse it
1224
if (oldId != null && oldId.StartsWith (id))
1228
while (fileDatabase.Exists (GetDescriptionPath (id))) {
1229
name = baseId + "_" + n;
1230
id = Addin.GetFullId (ns, name, version);
1236
public void ResetConfiguration ()
1238
if (File.Exists (ConfigFile))
1239
File.Delete (ConfigFile);
1242
DatabaseConfiguration Configuration {
1244
if (config == null) {
1245
using (fileDatabase.LockRead ()) {
1246
if (fileDatabase.Exists (ConfigFile))
1247
config = DatabaseConfiguration.Read (ConfigFile);
1249
config = new DatabaseConfiguration ();
1256
void SaveConfiguration ()
1258
if (config != null) {
1259
using (fileDatabase.LockWrite ()) {
1260
config.Write (ConfigFile);
1266
class SingleFileAssemblyResolver
1268
AddinScanResult scanResult;
1269
AddinScanner scanner;
1270
AddinRegistry registry;
1271
IProgressStatus progressStatus;
1273
public SingleFileAssemblyResolver (IProgressStatus progressStatus, AddinRegistry registry, AddinScanner scanner)
1275
this.scanner = scanner;
1276
this.registry = registry;
1277
this.progressStatus = progressStatus;
1280
public Assembly Resolve (object s, ResolveEventArgs args)
1282
if (scanResult == null) {
1283
scanResult = new AddinScanResult ();
1284
scanResult.LocateAssembliesOnly = true;
1286
foreach (string dir in registry.AddinDirectories)
1287
scanner.ScanFolder (progressStatus, dir, scanResult);
1290
string afile = scanResult.GetAssemblyLocation (args.Name);
1292
return Util.LoadAssemblyForReflection (afile);